summaryrefslogtreecommitdiff
path: root/components/drivers/can
diff options
context:
space:
mode:
authorwdfk-prog <[email protected]>2025-09-27 09:42:02 +0800
committerR b b666 <[email protected]>2025-09-28 11:17:21 +0800
commit11156de0de6bea4eb9090d162152caf2b352b232 (patch)
treee0d564de7da98d2a8b2b9b068dd8c0dbcb82423c /components/drivers/can
parented574f693f512fbbeff994d494e36952e0659f07 (diff)
feat[can]: Implement non-blocking send mechanism and enhance CAN driver functionality
- Added support for non-blocking mode CAN message sending, including software ring buffer management and dynamic memory allocation options. - Improved related comments and error handling. - Updated example code to demonstrate the usage of both blocking and non-blocking sending modes, and corrected some structure field naming and macro definitions.
Diffstat (limited to 'components/drivers/can')
-rw-r--r--components/drivers/can/Kconfig69
-rw-r--r--components/drivers/can/dev_can.c335
2 files changed, 338 insertions, 66 deletions
diff --git a/components/drivers/can/Kconfig b/components/drivers/can/Kconfig
index c762a46d6b..ccb4348db2 100644
--- a/components/drivers/can/Kconfig
+++ b/components/drivers/can/Kconfig
@@ -1,31 +1,76 @@
config RT_USING_CAN
bool "Using CAN device drivers"
default n
+ help
+ Enable this option to include the CAN device driver framework.
if RT_USING_CAN
+
config RT_CAN_USING_HDR
- bool "Enable CAN hardware filter"
+ bool "Enable CAN hardware filter support"
default n
-
+ help
+ If your CAN controller supports hardware filtering, and you want to
+ use the framework to configure these filters, enable this option.
+
config RT_CAN_USING_CANFD
- bool "Enable CANFD support"
+ bool "Enable CAN-FD support"
default n
-
+ help
+ Enable this to support CAN with Flexible Data-Rate. This will
+ increase the size of the rt_can_msg structure.
+
config RT_CANMSG_BOX_SZ
- int "CAN message box size"
+ int "Software RX message box size (in messages)"
default 16
help
- Set the size of the CAN message box.
-
+ This sets the capacity of the software buffer (FIFO) for incoming
+ CAN messages. It defines how many messages can be buffered by the
+ driver before the application must read them.
+
config RT_CANSND_BOX_NUM
- int "Number of CAN send queues"
+ int "Number of mailboxes for blocking send"
default 1
help
- Set the number of CAN send queues.
-
+ This sets the number of concurrent blocking send operations that
+ can be in flight. It is typically matched to the number of
+ hardware transmission mailboxes on the CAN controller.
+
config RT_CANSND_MSG_TIMEOUT
- int "CAN send message timeout"
+ int "Timeout for blocking send (in OS ticks)"
default 100
help
- Set the timeout for CAN send messages.
+ This sets the default time a thread will wait for a hardware
+ mailbox to become available when sending in blocking mode.
+
+ config RT_CAN_NB_TX_FIFO_SIZE
+ int "Non-blocking send buffer size (in bytes)"
+ default 256
+ help
+ This defines the size of the software ring buffer used for
+ non-blocking and ISR-based transmissions. When the hardware
+ mailboxes are full, outgoing messages are temporarily stored
+ in this buffer.
+
+ To calculate a suitable size, use:
+ (number of messages to buffer) * sizeof(struct rt_can_msg).
+ For standard CAN, sizeof(struct rt_can_msg) is typically 16 bytes.
+ The default of 256 bytes can buffer 16 standard CAN messages.
+ If using CAN-FD, you will need to increase this size significantly.
+
+ config RT_CAN_MALLOC_NB_TX_BUFFER
+ bool "Dynamically allocate the non-blocking send buffer"
+ depends on RT_USING_HEAP
+ default n
+ help
+ If this option is enabled (y), the non-blocking send buffer will
+ be allocated from the system heap at runtime when the CAN device
+ is opened (using rt_malloc). This saves static RAM but requires
+ a heap to be available.
+
+ If this option is disabled (n), the buffer will be allocated
+ as a static array within the rt_can_device structure. This
+ consumes static RAM but guarantees the memory is always available
+ and avoids heap fragmentation.
+
endif \ No newline at end of file
diff --git a/components/drivers/can/dev_can.c b/components/drivers/can/dev_can.c
index 8f70a1ddfb..73aee2f094 100644
--- a/components/drivers/can/dev_can.c
+++ b/components/drivers/can/dev_can.c
@@ -7,6 +7,7 @@
* Date Author Notes
* 2015-05-14 [email protected] first version
* 2015-07-06 Bernard code cleanup and remove RT_CAN_USING_LED;
+ * 2025-09-20 wdfk_prog Implemented non-blocking, ISR-safe send logic unified under rt_device_write.
*/
#include <rthw.h>
@@ -40,8 +41,19 @@ static rt_err_t rt_can_init(struct rt_device *dev)
return result;
}
-/*
- * can interrupt routines
+/**
+ * @internal
+ * @brief Handles reading messages from the software RX FIFO into a user buffer.
+ *
+ * This function is called by the public `rt_can_read()` API when the device
+ * is opened with interrupt-driven reception enabled. It safely transfers
+ * messages from the internal `uselist` to the user-provided buffer.
+ *
+ * @param[in] can A pointer to the CAN device.
+ * @param[out] data A pointer to the destination buffer for the received messages.
+ * @param[in] msgs The total size in bytes of the destination buffer.
+ *
+ * @return The number of bytes actually read from the FIFO.
*/
rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *data, rt_ssize_t msgs)
{
@@ -63,7 +75,7 @@ rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *d
struct rt_can_msg_list *listmsg = RT_NULL;
/* disable interrupt */
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
#ifdef RT_CAN_USING_HDR
hdr = data->hdr_index;
@@ -97,22 +109,22 @@ rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *d
else
{
/* no data, enable interrupt and break out */
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
break;
}
}
/* enable interrupt */
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (listmsg != RT_NULL)
{
rt_memcpy(data, &listmsg->data, sizeof(struct rt_can_msg));
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rt_list_insert_before(&rx_fifo->freelist, &listmsg->list);
rx_fifo->freenumbers++;
RT_ASSERT(rx_fifo->freenumbers <= can->config.msgboxsz);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
listmsg = RT_NULL;
}
@@ -127,6 +139,25 @@ rt_inline rt_ssize_t _can_int_rx(struct rt_can_device *can, struct rt_can_msg *d
return (size - msgs);
}
+/**
+ * @internal
+ * @brief Handles the blocking (synchronous) transmission of CAN messages.
+ *
+ * This function is the core of the blocking send mechanism. It iterates through
+ * a buffer of messages to be sent. For each message, it:
+ * 1. Acquires a hardware mailbox resource using a semaphore.
+ * 2. Submits the message to the low-level driver for transmission.
+ * 3. Blocks the calling thread by waiting on a completion object.
+ * 4. Is woken up by the TX complete ISR (`rt_hw_can_isr`) when the transmission is finished.
+ *
+ * @note This function will block the calling thread and must not be called from an ISR.
+ *
+ * @param[in] can A pointer to the CAN device.
+ * @param[in] data A pointer to the source buffer of messages to be sent.
+ * @param[in] msgs The total size in bytes of the source buffer.
+ *
+ * @return The number of bytes successfully sent.
+ */
rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
@@ -146,11 +177,11 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da
struct rt_can_sndbxinx_list *tx_tosnd = RT_NULL;
rt_sem_take(&(tx_fifo->sem), RT_WAITING_FOREVER);
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
tx_tosnd = rt_list_entry(tx_fifo->freelist.next, struct rt_can_sndbxinx_list, list);
RT_ASSERT(tx_tosnd != RT_NULL);
rt_list_remove(&tx_tosnd->list);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
no = ((rt_ubase_t)tx_tosnd - (rt_ubase_t)tx_fifo->buffer) / sizeof(struct rt_can_sndbxinx_list);
tx_tosnd->result = RT_CAN_SND_RESULT_WAIT;
@@ -158,9 +189,9 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
/* send failed. */
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_sem_release(&(tx_fifo->sem));
goto err_ret;
}
@@ -168,29 +199,29 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da
can->status.sndchange |= 1<<no;
if (rt_completion_wait(&(tx_tosnd->completion), RT_CANSND_MSG_TIMEOUT) != RT_EOK)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
can->status.sndchange &= ~ (1<<no);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_sem_release(&(tx_fifo->sem));
goto err_ret;
}
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
result = tx_tosnd->result;
if (!rt_list_isempty(&tx_tosnd->list))
{
rt_list_remove(&tx_tosnd->list);
}
rt_list_insert_before(&tx_fifo->freelist, &tx_tosnd->list);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_sem_release(&(tx_fifo->sem));
if (result == RT_CAN_SND_RESULT_OK)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.sndpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
@@ -199,9 +230,9 @@ rt_inline int _can_int_tx(struct rt_can_device *can, const struct rt_can_msg *da
else
{
err_ret:
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.dropedsndpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
break;
}
}
@@ -209,6 +240,20 @@ err_ret:
return (size - msgs);
}
+/**
+ * @internal
+ * @brief Handles blocking transmission in "private mode".
+ *
+ * This is a specialized version of `_can_int_tx` where the target hardware mailbox
+ * for each message is specified by the user in the `priv` field of the `rt_can_msg`
+ * structure, rather than being acquired dynamically from a pool.
+ *
+ * @param[in] can A pointer to the CAN device.
+ * @param[in] data A pointer to the source buffer of messages.
+ * @param[in] msgs The total size in bytes of the source buffer.
+ *
+ * @return The number of bytes successfully sent.
+ */
rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_msg *data, int msgs)
{
int size;
@@ -230,16 +275,16 @@ rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_ms
break;
}
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if ((tx_fifo->buffer[no].result != RT_CAN_SND_RESULT_OK))
{
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_completion_wait(&(tx_fifo->buffer[no].completion), RT_WAITING_FOREVER);
continue;
}
tx_fifo->buffer[no].result = RT_CAN_SND_RESULT_WAIT;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (can->ops->sendmsg(can, data, no) != RT_EOK)
{
@@ -255,18 +300,18 @@ rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_ms
result = tx_fifo->buffer[no].result;
if (result == RT_CAN_SND_RESULT_OK)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.sndpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
data ++;
msgs -= sizeof(struct rt_can_msg);
if (!msgs) break;
}
else
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.dropedsndpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
break;
}
}
@@ -274,6 +319,76 @@ rt_inline int _can_int_tx_priv(struct rt_can_device *can, const struct rt_can_ms
return (size - msgs);
}
+/**
+ * @internal
+ * @brief Internal implementation of non-blocking CAN transmission.
+ *
+ * This function iterates through a buffer of CAN messages and attempts to send each one
+ * using a non-blocking strategy. It first tries to send directly via hardware (fast path).
+ * If the hardware is busy, it enqueues the message into a software ring buffer (slow path).
+ * This function is thread-safe and ISR-safe due to the use of critical sections for
+ * accessing the shared ring buffer.
+ *
+ * @param[in] can A pointer to the CAN device.
+ * @param[in] pmsg A pointer to the buffer of `rt_can_msg` structures.
+ * @param[in] size The total size of the buffer in bytes.
+ *
+ * @return The number of bytes successfully sent or enqueued for later transmission.
+ */
+static rt_ssize_t _can_nonblocking_tx(struct rt_can_device *can, const struct rt_can_msg *pmsg, rt_size_t size)
+{
+ rt_ssize_t sent_size = 0;
+ rt_base_t level;
+
+ if (can->ops->sendmsg_nonblocking == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ while (sent_size < size)
+ {
+ if (can->ops->sendmsg_nonblocking(can, pmsg) == RT_EOK)
+ {
+ pmsg++;
+ sent_size += sizeof(struct rt_can_msg);
+ continue;
+ }
+
+ level = rt_hw_local_irq_disable();
+ if (rt_ringbuffer_space_len(&can->nb_tx_rb) >= sizeof(struct rt_can_msg))
+ {
+ rt_ringbuffer_put(&can->nb_tx_rb, (rt_uint8_t *)pmsg, sizeof(struct rt_can_msg));
+ rt_hw_local_irq_enable(level);
+
+ pmsg++;
+ sent_size += sizeof(struct rt_can_msg);
+ }
+ else
+ {
+ /* Buffer is full, cannot process this message or subsequent ones. */
+ can->status.dropedsndpkg += (size - sent_size) / sizeof(struct rt_can_msg);
+ rt_hw_local_irq_enable(level);
+ break;
+ }
+ }
+
+ return sent_size;
+}
+
+/**
+ * @internal
+ * @brief Opens the CAN device and initializes its resources.
+ *
+ * This function is called when `rt_device_open()` is invoked on a CAN device.
+ * It allocates and initializes software FIFOs for reception and transmission,
+ * sets up semaphores for the blocking send mechanism, configures the non-blocking
+ * send buffer, and starts the periodic status timer.
+ *
+ * @param[in] dev A pointer to the device to be opened.
+ * @param[in] oflag The open flags, e.g., `RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX`.
+ *
+ * @return `RT_EOK` on successful opening, or an error code on failure.
+ */
static rt_err_t rt_can_open(struct rt_device *dev, rt_uint16_t oflag)
{
struct rt_can_device *can;
@@ -369,6 +484,12 @@ static rt_err_t rt_can_open(struct rt_device *dev, rt_uint16_t oflag)
}
#endif
+#ifdef RT_CAN_MALLOC_NB_TX_BUFFER
+ can->nb_tx_rb_pool = (rt_uint8_t *)rt_malloc(RT_CAN_NB_TX_FIFO_SIZE);
+ RT_ASSERT(can->nb_tx_rb_pool != RT_NULL);
+#endif /* RT_CAN_MALLOC_NB_TX_BUFFER */
+ rt_ringbuffer_init(&can->nb_tx_rb, can->nb_tx_rb_pool, RT_CAN_NB_TX_FIFO_SIZE);
+
if (!can->timerinitflag)
{
can->timerinitflag = 1;
@@ -397,6 +518,14 @@ static rt_err_t rt_can_close(struct rt_device *dev)
return RT_EOK;
}
+#ifdef RT_CAN_MALLOC_NB_TX_BUFFER
+ if (can->nb_tx_rb_pool)
+ {
+ rt_free(can->nb_tx_rb_pool);
+ can->nb_tx_rb_pool = RT_NULL;
+ }
+#endif
+
if (can->timerinitflag)
{
can->timerinitflag = 0;
@@ -461,7 +590,7 @@ static rt_ssize_t rt_can_read(struct rt_device *dev,
struct rt_can_device *can;
RT_ASSERT(dev != RT_NULL);
- if (size == 0) return 0;
+ if (size == 0) return -RT_EINVAL;
can = (struct rt_can_device *)dev;
@@ -470,22 +599,64 @@ static rt_ssize_t rt_can_read(struct rt_device *dev,
return _can_int_rx(can, buffer, size);
}
- return 0;
+ return -RT_ENOSYS;
}
+/**
+ * @brief Write data to the CAN device.
+ *
+ * This function serves as the unified entry point for sending CAN messages.
+ * It intelligently routes the request to either a blocking or non-blocking
+ * transmission function based on:
+ * 1. The calling context (thread or ISR).
+ * 2. A user-specified flag (`nonblocking`) in the message structure.
+ *
+ * @param[in] dev A pointer to the device object.
+ * @param[in] pos This parameter is ignored for CAN devices.
+ * @param[in] buffer A pointer to the buffer containing one or more `rt_can_msg` structures.
+ * @param[in] size The total size of the buffer in bytes. Must be a multiple of `sizeof(struct rt_can_msg)`.
+ *
+ * @return The number of bytes successfully written. For non-blocking sends, this means
+ * the data was either sent directly or enqueued in the buffer.
+ */
static rt_ssize_t rt_can_write(struct rt_device *dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_can_device *can;
+ const struct rt_can_msg *pmsg;
RT_ASSERT(dev != RT_NULL);
- if (size == 0) return 0;
+ RT_ASSERT(buffer != RT_NULL);
+
+ if (size == 0) return -RT_EINVAL;
+
+ /* Ensure size is a multiple of the message size for buffer operations */
+ if (size % sizeof(struct rt_can_msg) != 0)
+ {
+ return -RT_EINVAL;
+ }
can = (struct rt_can_device *)dev;
+ pmsg = (const struct rt_can_msg *)buffer;
+
+ if(dev->ref_count == 0)
+ {
+ return -RT_ENOSYS;
+ }
- if ((dev->open_flag & RT_DEVICE_FLAG_INT_TX) && (dev->ref_count > 0))
+ /*
+ * Routing to the non-blocking send scenario:
+ * 1. Called from within an interrupt context.
+ * 2. Called from a thread, but the user explicitly set the nonblocking flag on the first message.
+ */
+ if (rt_interrupt_get_nest() > 0 || pmsg->nonblocking)
+ {
+ return _can_nonblocking_tx(can, pmsg, size);
+ }
+
+ if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
{
if (can->config.privmode)
{
@@ -496,7 +667,7 @@ static rt_ssize_t rt_can_write(struct rt_device *dev,
return _can_int_tx(can, buffer, size);
}
}
- return 0;
+ return -RT_ENOSYS;
}
static rt_err_t rt_can_control(struct rt_device *dev,
@@ -542,7 +713,7 @@ static rt_err_t rt_can_control(struct rt_device *dev,
{
for (i = 0; i < can->config.sndboxnumber; i++)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if(rt_list_isempty(&tx_fifo->buffer[i].list))
{
rt_sem_release(&(tx_fifo->sem));
@@ -551,7 +722,7 @@ static rt_err_t rt_can_control(struct rt_device *dev,
{
rt_list_remove(&tx_fifo->buffer[i].list);
}
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
}
@@ -559,12 +730,12 @@ static rt_err_t rt_can_control(struct rt_device *dev,
{
for (i = 0; i < can->config.sndboxnumber; i++)
{
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if (tx_fifo->buffer[i].result == RT_CAN_SND_RESULT_OK)
{
rt_list_insert_before(&tx_fifo->freelist, &tx_fifo->buffer[i].list);
}
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
}
}
@@ -603,18 +774,18 @@ static rt_err_t rt_can_control(struct rt_device *dev,
continue;
}
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if (!can->hdr[pitem->hdr_bank].connected)
{
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_memcpy(&can->hdr[pitem->hdr_bank].filter, pitem,
sizeof(struct rt_can_filter_item));
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->hdr[pitem->hdr_bank].connected = 1;
can->hdr[pitem->hdr_bank].msgs = 0;
rt_list_init(&can->hdr[pitem->hdr_bank].list);
}
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
count--;
pitem++;
@@ -630,7 +801,7 @@ static rt_err_t rt_can_control(struct rt_device *dev,
pitem++;
continue;
}
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
if (can->hdr[pitem->hdr_bank].connected)
{
@@ -640,13 +811,13 @@ static rt_err_t rt_can_control(struct rt_device *dev,
{
rt_list_remove(can->hdr[pitem->hdr_bank].list.next);
}
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
rt_memset(&can->hdr[pitem->hdr_bank].filter, 0,
sizeof(struct rt_can_filter_item));
}
else
{
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
count--;
pitem++;
@@ -675,8 +846,17 @@ static rt_err_t rt_can_control(struct rt_device *dev,
return res;
}
-/*
- * can timer
+/**
+ * @internal
+ * @brief Periodic timer callback for the CAN device.
+ *
+ * This function is executed periodically by a system timer. Its main purposes are:
+ * 1. To query the current status of the CAN controller (e.g., error counters, bus state).
+ * 2. To invoke a user-registered status indicator callback, if any.
+ * 3. To call a user-registered bus hook function for periodic tasks, if any.
+ *
+ * @param[in] arg The argument passed to the callback, which is a pointer to the `rt_can_device`.
+ * @return void
*/
static void cantimeout(void *arg)
{
@@ -740,6 +920,10 @@ rt_err_t rt_hw_can_register(struct rt_can_device *can,
can->bus_hook = RT_NULL;
#endif /*RT_CAN_USING_BUS_HOOK*/
+#ifdef RT_CAN_MALLOC_NB_TX_BUFFER
+ can->nb_tx_rb_pool = RT_NULL;
+#endif
+
#ifdef RT_USING_DEVICE_OPS
device->ops = &can_device_ops;
#else
@@ -770,6 +954,17 @@ rt_err_t rt_hw_can_register(struct rt_can_device *can,
}
/* ISR for can interrupt */
+/**
+ * @brief The framework-level ISR handler for CAN devices.
+ *
+ * This function is called by the low-level BSP ISR and acts as the central
+ * dispatcher for all CAN-related interrupt events. It handles both receive
+ * events and transmission-complete events.
+ *
+ * @param[in] can A pointer to the CAN device structure.
+ * @param[in] event The interrupt event mask, indicating the cause of the interrupt.
+ * @return void
+ */
void rt_hw_can_isr(struct rt_can_device *can, int event)
{
switch (event & 0xff)
@@ -777,9 +972,9 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
case RT_CAN_EVENT_RXOF_IND:
{
rt_base_t level;
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.dropedrcvpkg++;
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
case RT_CAN_EVENT_RX_IND:
{
@@ -803,7 +998,7 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
if (ch == -1) break;
/* disable interrupt */
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
can->status.rcvpkg++;
can->status.rcvchange = 1;
if (!rt_list_isempty(&rx_fifo->freelist))
@@ -836,12 +1031,12 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
#endif
}
/* enable interrupt */
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (listmsg != RT_NULL)
{
rt_memcpy(&listmsg->data, &tmpmsg, sizeof(struct rt_can_msg));
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rt_list_insert_before(&rx_fifo->uselist, &listmsg->list);
#ifdef RT_CAN_USING_HDR
hdr = tmpmsg.hdr_index;
@@ -857,7 +1052,7 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
}
#endif
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
}
/* invoke callback */
@@ -867,9 +1062,9 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
rt_size_t rx_length;
RT_ASSERT(hdr < can->config.maxhdr && hdr >= 0);
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
rx_length = can->hdr[hdr].msgs * sizeof(struct rt_can_msg);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (rx_length)
{
can->hdr[hdr].filter.ind(&can->parent, can->hdr[hdr].filter.args, hdr, rx_length);
@@ -882,10 +1077,10 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
{
rt_size_t rx_length;
- level = rt_hw_interrupt_disable();
+ level = rt_hw_local_irq_disable();
/* get rx length */
rx_length = rt_list_len(&rx_fifo->uselist)* sizeof(struct rt_can_msg);
- rt_hw_interrupt_enable(level);
+ rt_hw_local_irq_enable(level);
if (rx_length)
{
@@ -904,6 +1099,7 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
no = event >> 8;
tx_fifo = (struct rt_can_tx_fifo *) can->can_tx;
RT_ASSERT(tx_fifo != RT_NULL);
+
if (can->status.sndchange&(1<<no))
{
if ((event & 0xff) == RT_CAN_EVENT_TX_DONE)
@@ -916,6 +1112,37 @@ void rt_hw_can_isr(struct rt_can_device *can, int event)
}
rt_completion_done(&(tx_fifo->buffer[no].completion));
}
+
+ if (can->ops->sendmsg_nonblocking != RT_NULL)
+ {
+ while (RT_TRUE)
+ {
+ struct rt_can_msg msg_to_send;
+ rt_base_t level;
+ rt_bool_t msg_was_present = RT_FALSE;
+
+ level = rt_hw_local_irq_disable();
+ if (rt_ringbuffer_data_len(&can->nb_tx_rb) >= sizeof(struct rt_can_msg))
+ {
+ rt_ringbuffer_get(&can->nb_tx_rb, (rt_uint8_t *)&msg_to_send, sizeof(struct rt_can_msg));
+ msg_was_present = RT_TRUE;
+ }
+ rt_hw_local_irq_enable(level);
+
+ if (!msg_was_present)
+ {
+ break;
+ }
+
+ if (can->ops->sendmsg_nonblocking(can, &msg_to_send) != RT_EOK)
+ {
+ level = rt_hw_local_irq_disable();
+ rt_ringbuffer_put_force(&can->nb_tx_rb, (rt_uint8_t *)&msg_to_send, sizeof(struct rt_can_msg));
+ rt_hw_local_irq_enable(level);
+ break;
+ }
+ }
+ }
break;
}
}