* drivers/can/mcp2515.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <strings.h>
#include <nuttx/arch.h>
#include <nuttx/signal.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spinlock.h>
#include <nuttx/spi/spi.h>
#include <nuttx/can/can.h>
#include <nuttx/can/mcp2515.h>
#ifdef CONFIG_CAN_TIMESTAMP
#include <time.h>
#endif
#include "mcp2515.h"
#if defined(CONFIG_CAN) && defined(CONFIG_CAN_MCP2515)
* Pre-processor Definitions
****************************************************************************/
#define CAN_FRAME_MAX_DATA_LEN 8
#define SPI_TRANSFER_BUF_LEN (6 + CAN_FRAME_MAX_DATA_LEN)
#define MCP2515_NUM_TX_BUFFERS 3
#define MCP2515_PROPSEG CONFIG_MCP2515_PROPSEG
#define MCP2515_PHSEG1 CONFIG_MCP2515_PHASESEG1
#define MCP2515_PHSEG2 CONFIG_MCP2515_PHASESEG2
#define MCP2515_TSEG1 (MCP2515_PROPSEG + MCP2515_PHSEG1)
#define MCP2515_TSEG2 MCP2515_PHSEG2
#define MCP2515_BRP ((uint8_t)(((float)(MCP2515_CANCLK_FREQUENCY) / \
((float)(MCP2515_TSEG1 + MCP2515_TSEG2 + 1) * \
(float)(2 * CONFIG_MCP2515_BITRATE))) - 1))
#define MCP2515_SJW CONFIG_MCP2515_SJW
#if MCP2515_PROPSEG < 1
# error Invalid PROPSEG. It cannot be lower than 1
#endif
#if MCP2515_PROPSEG > 8
# error Invalid PROPSEG. It cannot be greater than 8
#endif
#if MCP2515_PHSEG1 < 1
# error Invalid PHSEG1. It cannot be lower than 1
#endif
#if MCP2515_PHSEG1 > 8
# error Invalid PHSEG1. It cannot be greater than 1
#endif
#if MCP2515_TSEG2 < 2
# error Invalid TSEG2. It cannot be lower than 2
#endif
#if MCP2515_TSEG2 > 8
# error Invalid TSEG2. It cannot be greater than 8
#endif
#if MCP2515_SJW > 4
# error Invalid SJW. It cannot be greater than 4
#endif
#ifndef CONFIG_MCP2515_NSTDFILTERS
# define CONFIG_MCP2515_NSTDFILTERS 0
#endif
#if (CONFIG_MCP2515_NSTDFILTERS > 128)
# error Invalid MCP25150 number of Standard Filters
#endif
#ifndef CONFIG_MCP2515_NEXTFILTERS
# define CONFIG_MCP2515_NEXTFILTERS 0
#endif
#if (CONFIG_MCP2515_NEXTFILTERS > 64)
# error Invalid MCP25150 number of Extended Filters
#endif
#define MCP2515_STDFILTER_BYTES \
MCP2515_ALIGN_UP(CONFIG_MCP2515_NSTDFILTERS << 2)
#define MCP2515_STDFILTER_WORDS (MCP2515_STDFILTER_BYTES >> 2)
#define MCP2515_EXTFILTER_BYTES \
MCP2515_ALIGN_UP(CONFIG_MCP2515_NEXTFILTERS << 3)
#define MCP2515_EXTFILTER_WORDS (MCP2515_EXTFILTER_BYTES >> 2)
#undef MCP2515_LOOPBACK
#if defined(CONFIG_MCP2515_LOOPBACK)
# define MCP2515_LOOPBACK 1
#endif
*
* MCP2515_INT_MERR - Message Error Interrupt Flag bit
* MCP2515_INT_ERR - Error Interrupt Flag bit (mult src in EFLG register)
*/
#define MCP2515_ERROR_INTS (MCP2515_INT_MERR | MCP2515_INT_ERR)
*
* MCP2515_INT_RX0 - Receive Buffer 0 New Message
* MCP2515_INT_RX1 - Receive Buffer 1 New Message
*/
#define MCP2515_RXBUFFER_INTS (MCP2515_INT_RX0 | MCP2515_INT_RX1)
*
* MCP2515_INT_TX0 - Transmit Buffer 0 Empty Interrupt
* MCP2515_INT_TX1 - Transmit Buffer 1 Empty Interrupt
* MCP2515_INT_TX2 - Transmit Buffer 2 Empty Interrupt
*/
#define MCP2515_TXBUFFER_INTS (MCP2515_INT_TX0 | MCP2515_INT_TX1 | MCP2515_INT_TX2)
#define TXREGVAL(reg) priv->spi_txbuf[reg - MCP2515_TXB0CTRL]
#ifndef CONFIG_DEBUG_CAN_INFO
# undef CONFIG_MCP2515_REGDEBUG
#endif
#ifdef CONFIG_MCP2515_REGDEBUG
# define reginfo caninfo
#else
# define reginfo(x...)
#endif
* Private Types
****************************************************************************/
enum can_state_s
{
MCP2515_STATE_UNINIT = 0,
MCP2515_STATE_RESET,
MCP2515_STATE_SETUP,
};
struct mcp2515_can_s
{
FAR struct mcp2515_config_s *config;
uint8_t state;
uint8_t nalloc;
mutex_t lock;
sem_t txfsem;
uint32_t btp;
uint8_t rxints;
uint8_t txints;
#ifdef CONFIG_CAN_ERRORS
uint32_t olderrors;
#endif
uint8_t filters;
uint8_t txbuffers;
FAR uint8_t *spi_txbuf;
FAR uint8_t *spi_rxbuf;
#ifdef CONFIG_MCP2515_REGDEBUG
uintptr_t regaddr;
uint32_t regval;
unsigned int count;
#endif
};
* Private Function Prototypes
****************************************************************************/
static void mcp2515_config_spi(FAR struct mcp2515_can_s *priv);
static void mcp2515_readregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr,
FAR uint8_t *buffer, uint8_t len);
static void mcp2515_writeregs(FAR struct mcp2515_can_s *priv,
uint8_t regaddr,
FAR const uint8_t *buffer, uint8_t len);
static void mcp2515_modifyreg(FAR struct mcp2515_can_s *priv,
uint8_t regaddr, uint8_t mask, uint8_t value);
#ifdef CONFIG_MCP2515_REGDEBUG
static void mcp2515_dumpregs(FAR struct mcp2515_can_s *priv,
FAR const char *msg);
#else
# define mcp2515_dumpregs(priv,msg)
#endif
#ifdef CONFIG_CAN_EXTID
static int mcp2515_add_extfilter(FAR struct mcp2515_can_s *priv,
FAR struct canioc_extfilter_s *extconfig);
static int mcp2515_del_extfilter(FAR struct mcp2515_can_s *priv, int ndx);
#endif
static int mcp2515_add_stdfilter(FAR struct mcp2515_can_s *priv,
FAR struct canioc_stdfilter_s *stdconfig);
static int mcp2515_del_stdfilter(FAR struct mcp2515_can_s *priv, int ndx);
static void mcp2515_reset(FAR struct can_dev_s *dev);
static int mcp2515_setup(FAR struct can_dev_s *dev);
static void mcp2515_shutdown(FAR struct can_dev_s *dev);
static void mcp2515_rxint(FAR struct can_dev_s *dev, bool enable);
static void mcp2515_txint(FAR struct can_dev_s *dev, bool enable);
static int mcp2515_ioctl(FAR struct can_dev_s *dev, int cmd,
unsigned long arg);
static int mcp2515_remoterequest(FAR struct can_dev_s *dev, uint16_t id);
static int mcp2515_send(FAR struct can_dev_s *dev,
FAR struct can_msg_s *msg);
static bool mcp2515_txready(FAR struct can_dev_s *dev);
static bool mcp2515_txempty(FAR struct can_dev_s *dev);
#ifdef CONFIG_CAN_ERRORS
static void mcp2515_error(FAR struct can_dev_s *dev, uint8_t status,
uint8_t oldstatus);
#endif
static void mcp2515_receive(FAR struct can_dev_s *dev, uint8_t offset);
static int mcp2515_interrupt(FAR struct mcp2515_config_s *config,
FAR void *arg);
static int mcp2515_hw_initialize(FAR struct mcp2515_can_s *priv);
* Private Data
****************************************************************************/
static const struct can_ops_s g_mcp2515ops =
{
mcp2515_reset,
mcp2515_setup,
mcp2515_shutdown,
mcp2515_rxint,
mcp2515_txint,
mcp2515_ioctl,
mcp2515_remoterequest,
mcp2515_send,
mcp2515_txready,
mcp2515_txempty
};
* Private Functions
****************************************************************************/
static void mcp2515_config_spi(FAR struct mcp2515_can_s *priv)
{
SPI_SETFREQUENCY(priv->config->spi, CONFIG_MCP2515_SPI_SCK_FREQUENCY);
SPI_SETMODE(priv->config->spi, MCP2515_SPI_MODE);
SPI_SETBITS(priv->config->spi, 8);
SPI_HWFEATURES(priv->config->spi, 0);
}
static void mcp2515_read_2regs(FAR struct mcp2515_can_s *priv, uint8_t reg,
FAR uint8_t *v1, FAR uint8_t *v2)
{
priv->spi_txbuf[0] = MCP2515_READ;
priv->spi_txbuf[1] = reg;
SPI_LOCK(priv->config->spi, true);
mcp2515_config_spi(priv);
SPI_SELECT(priv->config->spi, SPIDEV_CANBUS(priv->config->devid), true);
SPI_EXCHANGE(priv->config->spi, priv->spi_txbuf, priv->spi_rxbuf, 4);
SPI_SELECT(priv->config->spi, SPIDEV_CANBUS(priv->config->devid), false);
SPI_LOCK(priv->config->spi, false);
*v1 = priv->spi_rxbuf[2];
*v2 = priv->spi_rxbuf[3];
}
* Name: mcp2515_readregs
*
* Description:
* Read value(s) of MCP2515 register(s).
*
* Input Parameters:
* priv - A reference to the MCP2515 peripheral state
* offset - The offset to the register to read
*
* Returned Value:
*
****************************************************************************/
static void mcp2515_readregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr,
FAR uint8_t *buffer, uint8_t len)
{
FAR struct mcp2515_config_s *config = priv->config;
#ifdef CONFIG_CANBUS_REGDEBUG
int i;
#endif
SPI_LOCK(config->spi, true);
mcp2515_config_spi(priv);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);
SPI_SEND(config->spi, MCP2515_READ);
SPI_SEND(config->spi, regaddr);
SPI_RECVBLOCK(config->spi, buffer, len);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);
SPI_LOCK(config->spi, false);
#ifdef CONFIG_CANBUS_REGDEBUG
for (i = 0; i < len; i++)
{
caninfo("%02x->%02x\n", regaddr, buffer[i]);
}
#endif
}
static void mcp2515_transfer(FAR struct mcp2515_can_s *priv, uint8_t len)
{
FAR struct mcp2515_config_s *config = priv->config;
SPI_LOCK(config->spi, true);
mcp2515_config_spi(priv);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);
SPI_EXCHANGE(config->spi, priv->spi_txbuf, priv->spi_rxbuf, len);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);
SPI_LOCK(config->spi, false);
}
* Name: mcp2515_writeregs
*
* Description:
* Set the value of a MCP2515 register.
*
* Input Parameters:
* priv - A reference to the MCP2515 peripheral state
* offset - The offset to the register to write
* regval - The value to write to the register
*
* Returned Value:
* None
*
****************************************************************************/
static void mcp2515_writeregs(FAR struct mcp2515_can_s *priv,
uint8_t regaddr,
FAR const uint8_t *buffer, uint8_t len)
{
FAR struct mcp2515_config_s *config = priv->config;
#ifdef CONFIG_CANBUS_REGDEBUG
int i;
for (i = 0; i < len; i++)
{
caninfo("%02x<-%02x\n", regaddr + i, buffer[i]);
}
#endif
SPI_LOCK(config->spi, true);
mcp2515_config_spi(priv);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);
SPI_SEND(config->spi, MCP2515_WRITE);
SPI_SEND(config->spi, regaddr);
SPI_SNDBLOCK(config->spi, buffer, len);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);
SPI_LOCK(config->spi, false);
}
* Name: mcp2515_modifyreg
*
* Description:
* Modify individuals bits of MCP2515 register
*
* Input Parameters:
* priv - A reference to the MCP2515 peripheral state
* offset - The offset to the register to write
* regval - The value to write to the register
*
* Returned Value:
* None
*
****************************************************************************/
static void mcp2515_modifyreg(FAR struct mcp2515_can_s *priv,
uint8_t regaddr, uint8_t mask, uint8_t value)
{
FAR struct mcp2515_config_s *config = priv->config;
uint8_t wr[4] =
{
MCP2515_BITMOD, regaddr, mask, value
};
SPI_LOCK(config->spi, true);
mcp2515_config_spi(priv);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);
SPI_SNDBLOCK(config->spi, wr, 4);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);
SPI_LOCK(config->spi, false);
}
* Name: mcp2515_dumpregs
*
* Description:
* Dump the contents of all MCP2515 control registers
*
* Input Parameters:
* priv - A reference to the MCP2515 peripheral state
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_MCP2515_REGDEBUG
static void mcp2515_dumpregs(FAR struct mcp2515_can_s *priv,
FAR const char *msg)
{
FAR struct mcp2515_config_s *config = priv->config;
}
#endif
* Name: mcp2515_add_extfilter
*
* Description:
* Add an address filter for a extended 29 bit address.
*
* Input Parameters:
* priv - An instance of the MCP2515 driver state structure.
* extconfig - The configuration of the extended filter
*
* Returned Value:
* A non-negative filter ID is returned on success. Otherwise a negated
* errno value is returned to indicate the nature of the error.
*
****************************************************************************/
#ifdef CONFIG_CAN_EXTID
static int mcp2515_add_extfilter(FAR struct mcp2515_can_s *priv,
FAR struct canioc_extfilter_s *extconfig)
{
FAR struct mcp2515_config_s *config;
uint8_t regval;
uint8_t offset;
uint8_t mode = CAN_FILTER_MASK;
int ndx;
int ret;
DEBUGASSERT(priv != NULL && priv->config != NULL);
config = priv->config;
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
for (ndx = 0; ndx < config->nfilters; ndx++)
{
if ((priv->filters & (1 << ndx)) == 0)
{
DEBUGASSERT(priv->nalloc < priv->config->nfilters);
priv->filters |= (1 << ndx);
priv->nalloc++;
DEBUGASSERT(extconfig->xf_id1 <= CAN_MAX_STDMSGID);
DEBUGASSERT(extconfig->xf_id2 <= CAN_MAX_STDMSGID);
* RXFnEID8 and RXFnEID0) using this formula:
*
* filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ;
* maskN = RXM0reg + offset
*/
if (priv->nalloc <= 3)
{
offset = 0;
}
else
{
offset = 4;
}
#if 0
* but to separate these messages we will make this
* driver more complex. So let to consider that the
* first 2 IDs inserted in the filter will have more
* priority than the latest 4 IDs.
*/
if (extconfig->sf_prio == CAN_MSGPRIO_LOW)
{
}
else
{
}
#endif
switch (extconfig->xf_type)
{
default:
case CAN_FILTER_DUAL:
mode = CAN_FILTER_DUAL;
break;
case CAN_FILTER_MASK:
mode = CAN_FILTER_MASK;
break;
case CAN_FILTER_RANGE:
break;
}
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
if (mode == CAN_FILTER_DUAL)
{
regval = (uint8_t)(extconfig->xf_id1 & 0xff);
mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset +
((priv->nalloc - 1) * 4), ®val, 1);
regval = (uint8_t)((extconfig->xf_id1 & 0xff00) >> 8);
mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset +
((priv->nalloc - 1) * 4), ®val, 1);
regval = (uint8_t)((extconfig->xf_id1 & 0x30000) >> 16);
regval = (regval) |
(uint8_t)(((extconfig->xf_id1 &
0x1c0000) >> 16) << 3);
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
((priv->nalloc - 1) * 4), ®val, 1);
regval = (uint8_t)((extconfig->xf_id1 & 0x1fe00000) >> 21);
regval |= RXFSIDL_EXIDE;
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
((priv->nalloc - 1) * 4), ®val, 1);
regval = (uint8_t)(extconfig->xf_id2 & 0xff);
mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, ®val, 1);
regval = (uint8_t)((extconfig->xf_id2 & 0xff00) >> 8);
mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, ®val, 1);
regval = (uint8_t)((extconfig->xf_id2 & 0x30000) >> 16);
regval = (regval) |
(uint8_t)(((extconfig->xf_id2 &
0x1c0000) >> 16) << 3);
mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1);
regval = (uint8_t)((extconfig->xf_id2 & 0x1fe00000) >> 21);
mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1);
}
else
{
* (Mask == Filter)
*/
regval = (uint8_t)(extconfig->xf_id1 & 0xff);
mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset +
((priv->nalloc - 1) * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, ®val, 1);
regval = (uint8_t)((extconfig->xf_id1 & 0xff00) >> 8);
mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset +
((priv->nalloc - 1) * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, ®val, 1);
regval = (uint8_t)((extconfig->xf_id1 & 0x30000) >> 16);
regval = (regval) | (uint8_t)(((extconfig->xf_id1 &
0x1c0000) >> 16) << 3) | RXFSIDL_EXIDE;
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
((priv->nalloc - 1) * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1);
regval = (uint8_t)((extconfig->xf_id1 & 0x1fe00000) >> 21);
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
((priv->nalloc - 1) * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset, ®val, 1);
}
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
nxmutex_unlock(&priv->lock);
return ndx;
}
}
nxmutex_unlock(&priv->lock);
return -EAGAIN;
}
#endif
* Name: mcp2515_del_extfilter
*
* Description:
* Remove an address filter for a standard 29 bit address.
*
* Input Parameters:
* priv - An instance of the MCP2515 driver state structure.
* ndx - The filter index previously returned by the
* mcp2515_add_extfilter().
*
* Returned Value:
* Zero (OK) is returned on success. Otherwise a negated errno value is
* returned to indicate the nature of the error.
*
****************************************************************************/
#ifdef CONFIG_CAN_EXTID
static int mcp2515_del_extfilter(FAR struct mcp2515_can_s *priv, int ndx)
{
FAR struct mcp2515_config_s *config;
uint8_t regval;
uint8_t offset;
int ret;
DEBUGASSERT(priv != NULL && priv->config != NULL);
config = priv->config;
DEBUGASSERT(ndx >= 0 || ndx < config->nfilters);
caninfo("ndx = %d\n", ndx);
if (ndx < 0 || ndx >= config->nfilters)
{
return -EINVAL;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
if ((priv->filters & (1 << ndx)) == 0)
{
nxmutex_unlock(&priv->lock);
return -ENOENT;
}
priv->filters &= ~(1 << ndx);
DEBUGASSERT(priv->nalloc > 0);
priv->nalloc--;
* RXFnEID8 and RXFnEID0) using this formula:
*
* filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ;
* maskN = RXM0reg + offset
*/
if (ndx < 3)
{
offset = 0;
}
else
{
offset = 4;
}
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = 0;
mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset + (ndx * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + (ndx * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset + (ndx * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset + (ndx * 4), ®val, 1);
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
nxmutex_unlock(&priv->lock);
return OK;
}
#endif
* Name: mcp2515_add_stdfilter
*
* Description:
* Add an address filter for a standard 11 bit address.
*
* Input Parameters:
* priv - An instance of the MCP2515 driver state structure.
* stdconfig - The configuration of the standard filter
*
* Returned Value:
* A non-negative filter ID is returned on success. Otherwise a negated
* errno value is returned to indicate the nature of the error.
*
****************************************************************************/
static int mcp2515_add_stdfilter(FAR struct mcp2515_can_s *priv,
FAR struct canioc_stdfilter_s *stdconfig)
{
FAR struct mcp2515_config_s *config;
uint8_t regval;
uint8_t offset;
uint8_t mode = CAN_FILTER_MASK;
int ndx;
int ret;
DEBUGASSERT(priv != NULL && priv->config != NULL);
config = priv->config;
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
for (ndx = 0; ndx < config->nfilters; ndx++)
{
if ((priv->filters & (1 << ndx)) == 0)
{
DEBUGASSERT(priv->nalloc < priv->config->nfilters);
priv->filters |= (1 << ndx);
priv->nalloc++;
DEBUGASSERT(stdconfig->sf_id1 <= CAN_MAX_STDMSGID);
DEBUGASSERT(stdconfig->sf_id2 <= CAN_MAX_STDMSGID);
* RXFnEID8 and RXFnEID0) using this formula:
*
* filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ;
* maskN = RXM0reg + offset
*/
if (priv->nalloc <= 3)
{
offset = 0;
}
else
{
offset = 4;
}
#if 0
* but to separate these messages we will make this
* driver more complex. So let to consider that the
* first 2 IDs inserted in the filter will have more
* priority than the latest 4 IDs.
*/
if (stdconfig->sf_prio == CAN_MSGPRIO_LOW)
{
}
else
{
}
#endif
switch (stdconfig->sf_type)
{
default:
case CAN_FILTER_DUAL:
mode = CAN_FILTER_DUAL;
break;
case CAN_FILTER_MASK:
mode = CAN_FILTER_MASK;
break;
case CAN_FILTER_RANGE:
break;
}
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
if (mode == CAN_FILTER_DUAL)
{
regval = (uint8_t)(((stdconfig->sf_id1) & 0x7f8) >> 3);
mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset +
((priv->nalloc - 1) * 4), ®val, 1);
regval = (uint8_t)((stdconfig->sf_id1 & 0x07) << 5);
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
((priv->nalloc - 1) * 4), ®val, 1);
regval = (uint8_t)(((stdconfig->sf_id2) & 0x7f8) >> 3);
mcp2515_writeregs(priv, MCP2515_RXM0SIDH + offset, ®val, 1);
regval = (uint8_t)((stdconfig->sf_id2 & 0x07) << 5);
mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1);
}
else
{
* (Mask == Filter)
*/
regval = (uint8_t) (((stdconfig->sf_id1) & 0x7f8) >> 3);
mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset +
((priv->nalloc - 1) * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0SIDH + offset, ®val, 1);
regval = (uint8_t)((stdconfig->sf_id1 & 0x07) << 5);
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
((priv->nalloc - 1) * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1);
}
regval = 0;
mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset +
((priv->nalloc - 1) * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset +
((priv->nalloc - 1) * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, ®val, 1);
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
nxmutex_unlock(&priv->lock);
return ndx;
}
}
nxmutex_unlock(&priv->lock);
return -EAGAIN;
}
* Name: mcp2515_del_stdfilter
*
* Description:
* Remove an address filter for a standard 29 bit address.
*
* Input Parameters:
* priv - An instance of the MCP2515 driver state structure.
* ndx - The filter index previously returned by the
* mcp2515_add_stdfilter().
*
* Returned Value:
* Zero (OK) is returned on success. Otherwise a negated errno value is
* returned to indicate the nature of the error.
*
****************************************************************************/
static int mcp2515_del_stdfilter(FAR struct mcp2515_can_s *priv, int ndx)
{
FAR struct mcp2515_config_s *config;
uint8_t regval;
uint8_t offset;
int ret;
DEBUGASSERT(priv != NULL && priv->config != NULL);
config = priv->config;
DEBUGASSERT(ndx >= 0 || ndx < config->nfilters);
caninfo("ndx = %d\n", ndx);
if (ndx < 0 || ndx >= config->nfilters)
{
return -EINVAL;
}
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
if ((priv->filters & (1 << ndx)) == 0)
{
nxmutex_unlock(&priv->lock);
return -ENOENT;
}
priv->filters &= ~(1 << ndx);
DEBUGASSERT(priv->nalloc > 0);
priv->nalloc--;
* RXFnEID8 and RXFnEID0) using this formula:
*
* filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ;
* maskN = RXM0reg + offset
*/
if (ndx < 3)
{
offset = 0;
}
else
{
offset = 4;
}
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = 0;
mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset + (ndx * 4), ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + (ndx * 4), ®val, 1);
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
nxmutex_unlock(&priv->lock);
return OK;
}
* Name: mcp2515_reset_lowlevel
*
* Description:
* Reset the MCP2515 device. Called early to initialize the hardware. This
* function is called, before mcp2515_setup() and on error conditions.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void mcp2515_reset_lowlevel(FAR struct mcp2515_can_s *priv)
{
FAR struct mcp2515_config_s *config;
int ret;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
UNUSED(config);
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return;
}
SPI_LOCK(config->spi, true);
mcp2515_config_spi(priv);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);
SPI_SEND(config->spi, MCP2515_RESET);
SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);
SPI_LOCK(config->spi, false);
nxsig_usleep(1000);
nxsem_reset(&priv->txfsem, MCP2515_NUM_TX_BUFFERS);
priv->txbuffers = (1 << MCP2515_NUM_TX_BUFFERS) - 1;
priv->state = MCP2515_STATE_RESET;
nxmutex_unlock(&priv->lock);
}
* Name: mcp2515_reset
*
* Description:
* Reset the MCP2515 device. Called early to initialize the hardware. This
* function is called, before mcp2515_setup() and on error conditions.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void mcp2515_reset(FAR struct can_dev_s *dev)
{
FAR struct mcp2515_can_s *priv;
DEBUGASSERT(dev);
priv = dev->cd_priv;
DEBUGASSERT(priv);
mcp2515_reset_lowlevel(priv);
}
* Name: mcp2515_setup
*
* Description:
* Configure the MCP2515. This method is called the first time that the
* MCP2515 device is opened. This will occur when the device file is
* first opened. This setup includes configuring and attaching MCP2515
* interrupts. All MCP2515 interrupts are disabled upon return.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int mcp2515_setup(FAR struct can_dev_s *dev)
{
FAR struct mcp2515_can_s *priv;
FAR struct mcp2515_config_s *config;
int ret;
DEBUGASSERT(dev);
priv = dev->cd_priv;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
ret = mcp2515_hw_initialize(priv);
if (ret < 0)
{
canerr("ERROR: MCP2515%d H/W initialization failed: %d\n",
config->devid, ret);
return ret;
}
ret = config->attach(config, (mcp2515_handler_t)mcp2515_interrupt,
(FAR void *)dev);
if (ret < 0)
{
canerr("ERROR: Failed to attach to IRQ Handler!\n");
return ret;
}
priv->state = MCP2515_STATE_SETUP;
mcp2515_rxint(dev, true);
nxmutex_unlock(&priv->lock);
return OK;
}
* Name: mcp2515_shutdown
*
* Description:
* Disable the MCP2515. This method is called when the MCP2515 device is
* closed. This method reverses the operation the setup method.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void mcp2515_shutdown(FAR struct can_dev_s *dev)
{
}
* Name: mcp2515_rxint
*
* Description:
* Call to enable or disable RX interrupts.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void mcp2515_rxint(FAR struct can_dev_s *dev, bool enable)
{
FAR struct mcp2515_can_s *priv;
FAR struct mcp2515_config_s *config;
irqstate_t flags;
DEBUGASSERT(dev);
priv = dev->cd_priv;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
caninfo("CAN%d enable: %d\n", config->devid, enable);
UNUSED(config);
flags = enter_critical_section();
if (enable)
{
mcp2515_modifyreg(priv, MCP2515_CANINTE,
priv->rxints | MCP2515_ERROR_INTS,
priv->rxints | MCP2515_ERROR_INTS);
}
else
{
mcp2515_modifyreg(priv, MCP2515_CANINTE, priv->rxints, ~priv->rxints);
}
leave_critical_section(flags);
}
* Name: mcp2515_txint
*
* Description:
* Call to enable or disable TX interrupts.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void mcp2515_txint(FAR struct can_dev_s *dev, bool enable)
{
FAR struct mcp2515_can_s *priv = dev->cd_priv;
irqstate_t flags;
DEBUGASSERT(priv && priv->config);
caninfo("CAN%d enable: %d\n", priv->config->devid, enable);
flags = enter_critical_section();
if (enable)
{
mcp2515_modifyreg(priv, MCP2515_CANINTE,
priv->txints | MCP2515_ERROR_INTS,
priv->txints | MCP2515_ERROR_INTS);
}
else
{
mcp2515_modifyreg(priv, MCP2515_CANINTE, priv->txints, ~priv->txints);
}
leave_critical_section(flags);
}
* Name: mcp2515_ioctl
*
* Description:
* All ioctl calls will be routed through this method
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int mcp2515_ioctl(FAR struct can_dev_s *dev, int cmd,
unsigned long arg)
{
FAR struct mcp2515_can_s *priv;
int ret = -ENOTTY;
caninfo("cmd=%04x arg=%lu\n", cmd, arg);
DEBUGASSERT(dev && dev->cd_priv);
priv = dev->cd_priv;
switch (cmd)
{
* Description: Return the current bit timing settings
* Argument: A pointer to a write-able instance of struct
* canioc_bittiming_s in which current bit timing
* values will be returned.
* Returned Value: Zero (OK) is returned on success. Otherwise -1
* (ERROR) is returned with the errno variable set
* to indicate the nature of the error.
* Dependencies: None
*/
case CANIOC_GET_BITTIMING:
{
FAR struct canioc_bittiming_s *bt =
(FAR struct canioc_bittiming_s *)arg;
uint8_t regval;
uint8_t brp;
DEBUGASSERT(bt != NULL);
mcp2515_readregs(priv, MCP2515_CNF1, ®val, 1);
bt->bt_sjw = ((regval & CNF1_SJW_MASK) >> CNF1_SJW_SHIFT) + 1;
brp = (((regval & CNF1_BRP_MASK) >>
CNF1_BRP_SHIFT) + 1) * 2;
mcp2515_readregs(priv, MCP2515_CNF2, ®val, 1);
bt->bt_tseg1 = ((regval & CNF2_PRSEG_MASK) >>
CNF2_PRSEG_SHIFT) + 1;
bt->bt_tseg1 += ((regval & CNF2_PHSEG1_MASK) >>
CNF2_PHSEG1_SHIFT) + 1;
mcp2515_readregs(priv, MCP2515_CNF3, ®val, 1);
bt->bt_tseg2 = ((regval & CNF3_PHSEG2_MASK) >>
CNF3_PHSEG2_SHIFT) + 1;
bt->bt_baud = MCP2515_CANCLK_FREQUENCY / brp /
(bt->bt_tseg1 + bt->bt_tseg2 + 1);
ret = OK;
}
break;
* Description: Set new current bit timing values
* Argument: A pointer to a read-able instance of struct
* canioc_bittiming_s in which the new bit timing
* values are provided.
* Returned Value: Zero (OK) is returned on success. Otherwise -1
* (ERROR) is returned with the errno variable set to
* indicate the nature of the error.
* Dependencies: None
*
* REVISIT: There is probably a limitation here: If there are multiple
* threads trying to send CAN packets, when one of these threads
* reconfigures the bitrate, the MCP2515 hardware will be reset and
* the context of operation will be lost. Hence, this IOCTL can only
* safely be executed in quiescent time periods.
*/
case CANIOC_SET_BITTIMING:
{
FAR const struct canioc_bittiming_s *bt =
(FAR const struct canioc_bittiming_s *)arg;
irqstate_t flags;
uint8_t brp;
uint8_t sjw;
uint8_t tseg1;
uint8_t tseg2;
uint8_t prseg;
uint8_t phseg1;
uint8_t regval;
DEBUGASSERT(bt != NULL);
DEBUGASSERT(bt->bt_baud < MCP2515_CANCLK_FREQUENCY);
DEBUGASSERT(bt->bt_sjw > 0 && bt->bt_sjw <= 4);
DEBUGASSERT(bt->bt_tseg1 > 1 && bt->bt_tseg1 <= 16);
DEBUGASSERT(bt->bt_tseg2 > 1 && bt->bt_tseg2 <= 8);
DEBUGASSERT(bt->bt_tseg1 > bt->bt_tseg2);
DEBUGASSERT(bt->bt_tseg2 > bt->bt_sjw);
tseg1 = bt->bt_tseg1 - 1;
tseg2 = bt->bt_tseg2 - 1;
sjw = bt->bt_sjw - 1;
* Because we don't have PHSEG1 then let us to assume:
* PHSEG1 == PHSEG2 (PHSEG2 = TSEG2)
*
* See more at:
* http://www.analog.com/en/analog-dialogue/articles/
* configure-can-bit-timing.html
*
*/
phseg1 = tseg2;
prseg = tseg1 - phseg1;
brp = (uint32_t)(((float) MCP2515_CANCLK_FREQUENCY /
((float)(tseg1 + tseg2 + 1) * (float)(2 * bt->bt_baud))) - 1);
flags = enter_critical_section();
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
mcp2515_readregs(priv, MCP2515_CNF1, ®val, 1);
regval = (regval & ~CNF1_BRP_MASK) |
(brp << CNF1_BRP_SHIFT);
regval = (regval & ~CNF1_SJW_MASK) |
((sjw) << CNF1_SJW_SHIFT);
mcp2515_writeregs(priv, MCP2515_CNF1, ®val, 1);
mcp2515_readregs(priv, MCP2515_CNF2, ®val, 1);
regval = (regval & ~CNF2_PRSEG_MASK) |
((prseg - 1) << CNF2_PRSEG_SHIFT);
regval = (regval & ~CNF2_PHSEG1_MASK) |
(phseg1 << CNF2_PHSEG1_SHIFT);
regval = (regval | CNF2_SAM | CNF2_BTLMODE);
mcp2515_writeregs(priv, MCP2515_CNF2, ®val, 1);
mcp2515_readregs(priv, MCP2515_CNF3, ®val, 1);
regval = (regval & ~CNF3_PHSEG2_MASK) |
(tseg2 << CNF3_PHSEG2_SHIFT);
regval = (regval | CNF3_SOF);
mcp2515_writeregs(priv, MCP2515_CNF3, ®val, 1);
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
leave_critical_section(flags);
ret = OK;
}
break;
#ifdef CONFIG_CAN_EXTID
* Description: Add an address filter for a extended 29 bit
* address.
* Argument: A reference to struct canioc_extfilter_s
* Returned Value: A non-negative filter ID is returned on success.
* Otherwise -1 (ERROR) is returned with the errno
* variable set to indicate the nature of the error.
*/
case CANIOC_ADD_EXTFILTER:
{
DEBUGASSERT(arg != 0);
ret = mcp2515_add_extfilter(priv,
(FAR struct canioc_extfilter_s *)arg);
}
break;
* Description: Remove an address filter for a standard 29 bit
* address.
* Argument: The filter index previously returned by the
* CANIOC_ADD_EXTFILTER command
* Returned Value: Zero (OK) is returned on success. Otherwise -1
* (ERROR) is returned with the errno variable set to
* indicate the nature of the error.
*/
case CANIOC_DEL_EXTFILTER:
{
FAR int *ndx = (FAR int *)((uintptr_t)arg);
DEBUGASSERT(*ndx <= priv->config->nfilters);
ret = mcp2515_del_extfilter(priv, (int)*ndx);
}
break;
#endif
* Description: Add an address filter for a standard 11 bit
* address.
* Argument: A reference to struct canioc_stdfilter_s
* Returned Value: A non-negative filter ID is returned on success.
* Otherwise -1 (ERROR) is returned with the errno
* variable set to indicate the nature of the error.
*/
case CANIOC_ADD_STDFILTER:
{
DEBUGASSERT(arg != 0);
ret = mcp2515_add_stdfilter(priv,
(FAR struct canioc_stdfilter_s *)arg);
}
break;
* Description: Remove an address filter for a standard 11 bit
* address.
* Argument: The filter index previously returned by the
* CANIOC_ADD_STDFILTER command
* Returned Value: Zero (OK) is returned on success. Otherwise -1
* (ERROR) is returned with the errno variable set to
* indicate the nature of the error.
*/
case CANIOC_DEL_STDFILTER:
{
FAR int *ndx = (FAR int *)((uintptr_t)arg);
DEBUGASSERT(*ndx <= priv->config->nfilters);
ret = mcp2515_del_stdfilter(priv, (int)*ndx);
}
break;
default:
canerr("ERROR: Unrecognized command: %04x\n", cmd);
break;
}
return ret;
}
* Name: mcp2515_remoterequest
*
* Description:
* Send a remote request
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int mcp2515_remoterequest(FAR struct can_dev_s *dev, uint16_t id)
{
return -ENOSYS;
}
* Name: mcp2515_send
*
* Description:
* Send one can message.
*
* One CAN-message consists of a maximum of 10 bytes. A message is
* composed of at least the first 2 bytes (when there are no data bytes).
*
* Byte 0: Bits 0-7: Bits 3-10 of the 11-bit CAN identifier
* Byte 1: Bits 5-7: Bits 0-2 of the 11-bit CAN identifier
* Bit 4: Remote Transmission Request (RTR)
* Bits 0-3: Data Length Code (DLC)
* Bytes 2-10: CAN data
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int mcp2515_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg)
{
FAR struct mcp2515_can_s *priv;
FAR struct mcp2515_config_s *config;
uint8_t regval;
uint8_t txbuf;
unsigned int nbytes;
int ret;
DEBUGASSERT(dev);
priv = dev->cd_priv;
DEBUGASSERT(priv && priv->config);
config = priv->config;
caninfo("CAN%d\n", config->devid);
caninfo("CAN%d ID: %" PRId32 " DLC: %d\n",
config->devid, (uint32_t)msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
UNUSED(config);
ret = nxmutex_lock(&priv->lock);
if (ret < 0)
{
return ret;
}
ret = nxsem_wait(&priv->txfsem);
if (ret < 0)
{
nxmutex_unlock(&priv->lock);
return ret;
}
DEBUGASSERT(priv->txbuffers != 0);
txbuf = ffs(priv->txbuffers) - 1;
priv->txbuffers &= ~(1 << txbuf);
#ifdef CONFIG_CAN_EXTID
if (msg->cm_hdr.ch_extid)
{
DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_EXTMSGID);
regval = (msg->cm_hdr.ch_id & 0xff);
TXREGVAL(MCP2515_TXB0EID0) = regval;
regval = (msg->cm_hdr.ch_id & 0xff00) >> 8;
TXREGVAL(MCP2515_TXB0EID8) = regval;
regval = (msg->cm_hdr.ch_id & 0x30000) >> 16;
regval |= TXBSIDL_EXIDE;
regval |= (msg->cm_hdr.ch_id & 0x1c0000) >> 13;
TXREGVAL(MCP2515_TXB0SIDL) = regval;
regval = (msg->cm_hdr.ch_id & 0x1fe00000) >> 21;
TXREGVAL(MCP2515_TXB0SIDH) = regval;
}
else
#endif
{
DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_STDMSGID);
regval = (msg->cm_hdr.ch_id & 0x7f8) >> 3;
TXREGVAL(MCP2515_TXB0SIDH) = regval;
regval = (msg->cm_hdr.ch_id & 0x007) << 5;
TXREGVAL(MCP2515_TXB0SIDL) = regval;
}
regval = (msg->cm_hdr.ch_dlc & 0xf);
if (msg->cm_hdr.ch_rtr)
{
regval |= TXBDLC_RTR;
}
TXREGVAL(MCP2515_TXB0DLC) = regval;
nbytes = msg->cm_hdr.ch_dlc;
memcpy(&TXREGVAL(MCP2515_TXB0D0), msg->cm_data, nbytes);
TXREGVAL(MCP2515_TXB0CTRL) = MCP2515_LOAD_TXB(txbuf);
mcp2515_transfer(priv, SPI_TRANSFER_BUF_LEN);
priv->spi_txbuf[0] = MCP2515_RTS(txbuf);
mcp2515_transfer(priv, 1);
nxmutex_unlock(&priv->lock);
* course, the transfer is not complete, but this early notification
* allows the upper half logic to free resources sooner.
*
* REVISIT: Should we disable interrupts? can_txdone() was designed to
* be called from an interrupt handler and, hence, may be unsafe when
* called from the tasking level.
*/
can_txdone(dev);
return OK;
}
* Name: mcp2515_txready
*
* Description:
* Return true if the MCP2515 hardware can accept another TX message.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* True if the MCP2515 hardware is ready to accept another TX message.
*
****************************************************************************/
static bool mcp2515_txready(FAR struct can_dev_s *dev)
{
FAR struct mcp2515_can_s *priv;
DEBUGASSERT(dev);
priv = dev->cd_priv;
DEBUGASSERT(priv);
return priv->txbuffers != 0;
}
* Name: mcp2515_txempty
*
* Description:
* Return true if all message have been sent. If for example, the MCP2515
* hardware implements FIFOs, then this would mean the transmit FIFO is
* empty. This method is called when the driver needs to make sure that
* all characters are "drained" from the TX hardware before calling
* co_shutdown().
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* True if there are no pending TX transfers in the MCP2515 hardware.
*
****************************************************************************/
static bool mcp2515_txempty(FAR struct can_dev_s *dev)
{
FAR struct mcp2515_can_s *priv;
DEBUGASSERT(dev);
priv = dev->cd_priv;
DEBUGASSERT(priv);
return priv->txbuffers == (1 << MCP2515_NUM_TX_BUFFERS) - 1;
}
* Name: mcp2515_error
*
* Description:
* Report a CAN error
*
* Input Parameters:
* dev - CAN-common state data
* status - Interrupt status with error bits set
* oldstatus - Previous Interrupt status with error bits set
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_CAN_ERRORS
static void mcp2515_error(FAR struct can_dev_s *dev, uint8_t status,
uint8_t oldstatus)
{
FAR struct mcp2515_can_s *priv = dev->cd_priv;
struct can_hdr_s hdr;
uint8_t eflg;
uint8_t txerr;
uint8_t txb0err;
uint8_t txb1err;
uint8_t txb2err;
uint16_t errbits;
uint8_t data[CAN_ERROR_DLC];
int ret;
errbits = 0;
memset(data, 0, sizeof(data));
* happened. It doesn't report what error it is.
* We need to check EFLG and TXBnCTRL to discover.
*/
mcp2515_readregs(priv, MCP2515_EFLG, &eflg, 1);
if (eflg & EFLG_TXBO)
{
errbits |= CAN_ERROR_BUSOFF;
}
if (eflg & EFLG_RXEP)
{
data[1] |= CAN_ERROR1_RXPASSIVE;
}
if (eflg & EFLG_TXEP)
{
data[1] |= CAN_ERROR1_TXPASSIVE;
}
if (eflg & EFLG_RXWAR)
{
data[1] |= CAN_ERROR1_RXWARNING;
}
if (eflg & EFLG_TXWAR)
{
data[1] |= CAN_ERROR1_TXWARNING;
}
if (eflg & (EFLG_RX0OVR | EFLG_RX1OVR))
{
data[1] |= CAN_ERROR1_RXOVERFLOW;
}
mcp2515_readregs(priv, MCP2515_TXB0CTRL, &txb0err, 1);
mcp2515_readregs(priv, MCP2515_TXB1CTRL, &txb1err, 1);
mcp2515_readregs(priv, MCP2515_TXB2CTRL, &txb2err, 1);
txerr = txb0err | txb1err | txb2err;
if (txerr & (TXBCTRL_MLOA))
{
errbits |= CAN_ERROR_LOSTARB;
}
if (txerr & (TXBCTRL_ABTF))
{
errbits |= CAN_ERROR_LOSTARB;
}
if (txerr & (TXBCTRL_MLOA))
{
data[0] |= CAN_ERROR0_UNSPEC;
}
if ((status & (MCP2515_INT_ERR | MCP2515_INT_MERR)) != 0)
{
errbits |= CAN_ERROR_CONTROLLER;
}
else if ((oldstatus & (MCP2515_INT_ERR | MCP2515_INT_MERR)) != 0)
{
errbits |= CAN_ERROR_CONTROLLER;
}
if (errbits != 0)
{
hdr.ch_id = errbits;
hdr.ch_dlc = CAN_ERROR_DLC;
hdr.ch_rtr = 0;
hdr.ch_error = 1;
#ifdef CONFIG_CAN_EXTID
hdr.ch_extid = 0;
#endif
hdr.ch_tcf = 0;
ret = can_receive(dev, &hdr, data);
if (ret < 0)
{
canerr("ERROR: can_receive failed: %d\n", ret);
}
}
}
#endif
* Name: mcp2515_receive
*
* Description:
* Receive an MCP2515 messages
*
* Input Parameters:
* dev - CAN-common state data
* rxbuffer - The RX buffer containing the received messages
*
* Returned Value:
* None
*
****************************************************************************/
#define RXREGVAL(reg) priv->spi_rxbuf[reg-MCP2515_RXB0CTRL]
static void mcp2515_receive(FAR struct can_dev_s *dev, uint8_t offset)
{
#ifdef CONFIG_CAN_TIMESTAMP
clock_t clkval;
struct timespec ts;
#endif
FAR struct mcp2515_can_s *priv;
struct can_hdr_s hdr;
int ret;
uint8_t regval;
#ifdef CONFIG_CAN_TIMESTAMP
clkval = up_perf_gettime();
up_perf_convert(clkval, &ts);
#endif
DEBUGASSERT(dev);
priv = dev->cd_priv;
DEBUGASSERT(priv);
mcp2515_readregs(priv, MCP2515_RXB0CTRL + offset, priv->spi_rxbuf,
SPI_TRANSFER_BUF_LEN);
regval = RXREGVAL(MCP2515_RXB0SIDL);
#ifdef CONFIG_CAN_EXTID
if ((regval & RXBSIDL_IDE) != 0)
{
regval = RXREGVAL(MCP2515_RXB0EID0);
hdr.ch_id = regval ;
regval = RXREGVAL(MCP2515_RXB0EID8);
hdr.ch_id = hdr.ch_id | (regval << 8);
regval = RXREGVAL(MCP2515_RXB0SIDL);
hdr.ch_id = hdr.ch_id | ((regval & RXBSIDL_EID_MASK) << 16);
hdr.ch_id = hdr.ch_id | ((regval >> 5) << 18);
regval = RXREGVAL(MCP2515_RXB0SIDH);
hdr.ch_id = hdr.ch_id | (regval << 21);
hdr.ch_extid = true;
}
else
{
regval = RXREGVAL(MCP2515_RXB0SIDH);
hdr.ch_id = regval;
regval = RXREGVAL(MCP2515_RXB0SIDL);
hdr.ch_id = (hdr.ch_id << 3) | (regval >> 5);
hdr.ch_extid = false;
}
#else
if ((regval & RXBSIDL_IDE) != 0)
{
canerr("ERROR: Extended MSG in Standard Mode\n");
return;
}
regval = RXREGVAL(MCP2515_RXB0SIDH);
hdr.ch_id = regval;
regval = RXREGVAL(MCP2515_RXB0SIDL);
hdr.ch_id = (hdr.ch_id << 3) | (regval >> 5);
#endif
#ifdef CONFIG_CAN_ERRORS
hdr.ch_error = 0;
#endif
hdr.ch_tcf = 0;
regval = RXREGVAL(MCP2515_RXB0CTRL);
hdr.ch_rtr = (regval & RXBCTRL_RXRTR) != 0;
regval = RXREGVAL(MCP2515_RXB0DLC);
hdr.ch_dlc = (regval & RXBDLC_DLC_MASK) >> RXBDLC_DLC_SHIFT;
#ifdef CONFIG_CAN_TIMESTAMP
hdr.ch_ts.tv_sec = ts.tv_sec;
hdr.ch_ts.tv_usec = ts.tv_nsec / 1000u;
#endif
ret = can_receive(dev, &hdr, (FAR uint8_t *)&RXREGVAL(MCP2515_RXB0D0));
if (ret < 0)
{
canerr("ERROR: can_receive failed: %d\n", ret);
}
}
* Name: mcp2515_interrupt
*
* Description:
* Common MCP2515 interrupt handler
*
* Input Parameters:
* dev - CAN-common state data
*
* Returned Value:
* None
*
****************************************************************************/
static int mcp2515_interrupt(FAR struct mcp2515_config_s *config,
FAR void *arg)
{
FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg;
FAR struct mcp2515_can_s *priv;
uint8_t ir;
uint8_t ie;
uint8_t pending;
uint8_t clrmask;
bool handled;
DEBUGASSERT(dev);
priv = dev->cd_priv;
DEBUGASSERT(priv != NULL);
DEBUGASSERT(priv && priv->config);
do
{
mcp2515_read_2regs(priv, MCP2515_CANINTE, &ie, &ir);
pending = (ir & ie);
handled = false;
clrmask = 0;
if (pending == 0)
{
return OK;
}
if ((pending & MCP2515_ERROR_INTS) != 0)
{
pending &= ~MCP2515_ERROR_INTS;
clrmask |= MCP2515_ERROR_INTS;
#ifdef CONFIG_CAN_ERRORS
mcp2515_error(dev, pending & MCP2515_ERROR_INTS, priv->olderrors);
priv->olderrors = (pending & MCP2515_ERROR_INTS);
#endif
handled = true;
}
#ifdef CONFIG_CAN_ERRORS
else if (priv->olderrors != 0)
{
canerr("ERROR: CLEARED\n");
mcp2515_error(dev, 0, priv->olderrors);
priv->olderrors = 0;
handled = true;
}
#endif
if ((pending & MCP2515_TXBUFFER_INTS) != 0)
{
* other TX-related interrupts)
*/
if (pending & MCP2515_INT_TX0)
{
caninfo("TX0 is empty to transmit new message!\n");
priv->txbuffers |= (1 << 0);
nxsem_post(&priv->txfsem);
pending &= ~MCP2515_INT_TX0;
clrmask |= MCP2515_INT_TX0;
}
if (pending & MCP2515_INT_TX1)
{
caninfo("TX1 is empty to transmit new message!\n");
priv->txbuffers |= (1 << 1);
nxsem_post(&priv->txfsem);
pending &= ~MCP2515_INT_TX1;
clrmask |= MCP2515_INT_TX1;
}
if (pending & MCP2515_INT_TX2)
{
caninfo("TX2 is empty to transmit new message!\n");
priv->txbuffers |= (1 << 2);
nxsem_post(&priv->txfsem);
pending &= ~MCP2515_INT_TX2;
clrmask |= MCP2515_INT_TX2;
}
handled = true;
#ifdef CONFIG_CAN_TXREADY
* data in mcp2515_send().
*/
can_txready(dev);
#endif
}
else if ((pending & priv->txints) != 0)
{
handled = true;
}
if ((pending & MCP2515_RXBUFFER_INTS) != 0)
{
* all messages in RXB0 before processing any message from RX
* RXB1.
*/
if ((pending & MCP2515_INT_RX0) != 0)
{
mcp2515_receive(dev, MCP2515_RX0_OFFSET);
pending &= ~MCP2515_INT_RX0;
clrmask |= MCP2515_INT_RX0;
}
else
{
if ((pending & MCP2515_INT_RX1) != 0)
{
mcp2515_receive(dev, MCP2515_RX1_OFFSET);
pending &= ~MCP2515_INT_RX1;
clrmask |= MCP2515_INT_RX1;
}
}
handled = true;
}
mcp2515_modifyreg(priv, MCP2515_CANINTF, clrmask, pending);
}
while (handled);
return OK;
}
* Name: mcp2515_hw_initialize
*
* Description:
* MCP2515 hardware initialization
*
* Input Parameters:
* priv - A pointer to the private data structure for this MCP2515
* peripheral
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int mcp2515_hw_initialize(struct mcp2515_can_s *priv)
{
FAR struct mcp2515_config_s *config = priv->config;
uint8_t regval;
caninfo("CAN%d\n", config->devid);
UNUSED(config);
mcp2515_readregs(priv, MCP2515_CNF1, ®val, 1);
regval = (regval & ~CNF1_BRP_MASK) |
(MCP2515_BRP << CNF1_BRP_SHIFT);
regval = (regval & ~CNF1_SJW_MASK) |
((MCP2515_SJW - 1) << CNF1_SJW_SHIFT);
mcp2515_writeregs(priv, MCP2515_CNF1, ®val, 1);
mcp2515_readregs(priv, MCP2515_CNF2, ®val, 1);
regval = (regval & ~CNF2_PRSEG_MASK) |
((MCP2515_PROPSEG - 1) << CNF2_PRSEG_SHIFT);
regval = (regval & ~CNF2_PHSEG1_MASK) |
((MCP2515_PHSEG1 - 1) << CNF2_PHSEG1_SHIFT);
regval = (regval | CNF2_SAM | CNF2_BTLMODE);
mcp2515_writeregs(priv, MCP2515_CNF2, ®val, 1);
mcp2515_readregs(priv, MCP2515_CNF3, ®val, 1);
regval = (regval & ~CNF3_PHSEG2_MASK) |
((MCP2515_PHSEG2 - 1) << CNF3_PHSEG2_SHIFT);
regval = (regval | CNF3_SOF);
mcp2515_writeregs(priv, MCP2515_CNF3, ®val, 1);
mcp2515_readregs(priv, MCP2515_RXB0CTRL, ®val, 1);
regval = (regval & ~RXBCTRL_RXM_MASK) |
(RXBCTRL_RXM_ALLVALID << RXBCTRL_RXM_SHIFT);
regval = (regval | RXB0CTRL_BUKT);
mcp2515_writeregs(priv, MCP2515_RXB0CTRL, ®val, 1);
mcp2515_readregs(priv, MCP2515_RXB1CTRL, ®val, 1);
regval = (regval & ~RXBCTRL_RXM_MASK) |
(RXBCTRL_RXM_ALLVALID << RXBCTRL_RXM_SHIFT);
mcp2515_writeregs(priv, MCP2515_RXB1CTRL, ®val, 1);
regval = 0x00;
mcp2515_writeregs(priv, MCP2515_RXM0SIDH, ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0SIDL, ®val, 1);
#ifdef CONFIG_CAN_EXTID
mcp2515_writeregs(priv, MCP2515_RXM0EID8, ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM0EID0, ®val, 1);
#endif
mcp2515_writeregs(priv, MCP2515_RXM1SIDH, ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM1SIDL, ®val, 1);
#ifdef CONFIG_CAN_EXTID
mcp2515_writeregs(priv, MCP2515_RXM1EID8, ®val, 1);
mcp2515_writeregs(priv, MCP2515_RXM1EID0, ®val, 1);
#endif
#ifdef CONFIG_CAN_EXTID
mcp2515_modifyreg(priv, MCP2515_RXM0SIDL, RXFSIDL_EXIDE, RXFSIDL_EXIDE);
mcp2515_modifyreg(priv, MCP2515_RXM1SIDL, RXFSIDL_EXIDE, RXFSIDL_EXIDE);
#endif
mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1);
regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1);
nxsig_usleep(100);
mcp2515_readregs(priv, MCP2515_CANINTF, ®val, 1);
caninfo("CANINFT = 0x%02X\n", regval);
#ifdef MCP2515_LOOPBACK
if (config->loopback)
{
}
#endif
priv->rxints = MCP2515_RXBUFFER_INTS;
priv->txints = MCP2515_TXBUFFER_INTS;
* RXM[1:0] bits (RXBnCTRL[6:5]). In both registers:
* RXB0CTRL and RXB1CTRL.
* 11 = Turns mask/filters off; receives any message.
*
* In this mode it is tested that it receives both
* extended and standard id messages.
*/
#ifdef CONFIG_CAN_EXTID
mcp2515_readregs(priv, MCP2515_RXB0CTRL, ®val, 1);
regval &= ~RXBCTRL_RXM_ALLMSG;
regval |= RXBCTRL_RXM_ALLMSG;
mcp2515_writeregs(priv, MCP2515_RXB0CTRL, ®val, 1);
mcp2515_readregs(priv, MCP2515_RXB1CTRL, ®val, 1);
regval &= ~RXBCTRL_RXM_ALLMSG;
regval |= RXBCTRL_RXM_ALLMSG;
mcp2515_writeregs(priv, MCP2515_RXB1CTRL, ®val, 1);
#endif
return OK;
}
* Public Functions
****************************************************************************/
* Name: mcp2515_instantiate
*
* Description:
* Initialize the selected MCP2515 CAN Bus Controller over SPI
*
* Input Parameters:
* config - The configuration structure passed by the board.
*
* Returned Value:
* Valid CAN device structure reference on success; a NULL on failure
*
****************************************************************************/
FAR struct mcp2515_can_s *
mcp2515_instantiate(FAR struct mcp2515_config_s *config)
{
FAR struct mcp2515_can_s *priv;
uint8_t canctrl;
caninfo("Starting mcp2515_instantiate()!\n");
priv = (FAR struct mcp2515_can_s *)
kmm_malloc(sizeof(struct mcp2515_can_s));
if (priv == NULL)
{
canerr("ERROR: Failed to allocate instance of mcp2515_can_s!\n");
return NULL;
}
memset(priv, 0, sizeof(struct mcp2515_can_s));
priv->config = config;
priv->spi_txbuf = kmm_zalloc(SPI_TRANSFER_BUF_LEN);
priv->spi_rxbuf = kmm_zalloc(SPI_TRANSFER_BUF_LEN);
* due to IOCTL command processing.
*/
nxmutex_init(&priv->lock);
nxsem_init(&priv->txfsem, 0, MCP2515_NUM_TX_BUFFERS);
priv->txbuffers = (1 << MCP2515_NUM_TX_BUFFERS) - 1;
mcp2515_reset_lowlevel(priv);
mcp2515_readregs(priv, MCP2515_CANCTRL, &canctrl, 1);
if (canctrl != DEFAULT_CANCTRL_CONFMODE)
{
canerr("ERROR: CANCTRL = 0x%02X ! It should be 0x87\n", canctrl);
nxmutex_destroy(&priv->lock);
kmm_free(priv);
return NULL;
}
return priv;
}
* Name: mcp2515_initialize
*
* Description:
* Initialize the selected MCP2515 CAN Bus Controller over SPI
*
* Input Parameters:
* config - The configuration structure passed by the board.
*
* Returned Value:
* Valid CAN device structure reference on success; a NULL on failure
*
****************************************************************************/
FAR struct can_dev_s *mcp2515_initialize(
FAR struct mcp2515_can_s *mcp2515can)
{
FAR struct can_dev_s *dev;
caninfo("Starting mcp2515_initialize()!\n");
dev = kmm_zalloc(sizeof(struct can_dev_s));
if (dev == NULL)
{
canerr("ERROR: Failed to allocate instance of can_dev_s!\n");
return NULL;
}
dev->cd_ops = &g_mcp2515ops;
dev->cd_priv = (FAR void *)mcp2515can;
return dev;
}
#endif