This is an automated email from the ASF dual-hosted git repository. ligd pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new cfc90ad1f3 nuttx/can: support to Send message priority sorting function. cfc90ad1f3 is described below commit cfc90ad1f3763a65e4baae6b3d557c37f2d192f5 Author: gaohedong <gaohed...@xiaomi.com> AuthorDate: Thu Oct 10 19:45:46 2024 +0800 nuttx/can: support to Send message priority sorting function. Linked list-based priority sorting function for sending messages. Signed-off-by: gaohedong <gaohed...@xiaomi.com> --- drivers/can/CMakeLists.txt | 2 +- drivers/can/Kconfig | 6 + drivers/can/Make.defs | 2 +- drivers/can/can.c | 276 ++++++++++++++++++----------------------- drivers/can/can_sender.c | 256 ++++++++++++++++++++++++++++++++++++++ include/nuttx/can/can.h | 50 ++++++-- include/nuttx/can/can_sender.h | 239 +++++++++++++++++++++++++++++++++++ 7 files changed, 670 insertions(+), 161 deletions(-) diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index 0402b41b4a..d73f924da7 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -19,7 +19,7 @@ # ############################################################################## if(CONFIG_CAN) - set(SRCS can.c) + set(SRCS can.c can_sender.c) if(CONFIG_CAN_MCP2515) list(APPEND SRCS mcp2515.c) diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 3bc18943f7..fd13f1ef49 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -126,6 +126,12 @@ config CAN_TXREADY no longer full. can_txready() will then awaken the can_write() logic and the hang condition is avoided. +config CAN_TXPRIORITY + bool "Prioritize sending based on canid" + default n + ---help--- + Prioritize sending based on canid. + choice prompt "TX Ready Work Queue" default CAN_TXREADY_HIPRI diff --git a/drivers/can/Make.defs b/drivers/can/Make.defs index 49a4e7ca0e..cefe78cac4 100644 --- a/drivers/can/Make.defs +++ b/drivers/can/Make.defs @@ -22,7 +22,7 @@ ifeq ($(CONFIG_CAN),y) -CSRCS += can.c +CSRCS += can.c can_sender.c ifeq ($(CONFIG_CAN_MCP2515),y) CSRCS += mcp2515.c diff --git a/drivers/can/can.c b/drivers/can/can.c index 111ee38cca..2eed29fce7 100644 --- a/drivers/can/can.c +++ b/drivers/can/can.c @@ -42,6 +42,7 @@ #include <nuttx/signal.h> #include <nuttx/fs/fs.h> #include <nuttx/can/can.h> +#include <nuttx/can/can_sender.h> #include <nuttx/kmalloc.h> #include <nuttx/irq.h> @@ -147,35 +148,35 @@ static void can_txready_work(FAR void *arg) irqstate_t flags; int ret; - caninfo("xmit head: %d queue: %d tail: %d\n", - dev->cd_xmit.tx_head, dev->cd_xmit.tx_queue, - dev->cd_xmit.tx_tail); + caninfo("xmit pending_count: %d sending_count: %d free_space: %d\n", + PENDING_COUNT(&dev->cd_sender), SENDING_COUNT(&dev->cd_sender), + FREE_COUNT(&dev->cd_sender)); - /* Verify that the xmit FIFO is not empty. The following operations must + /* Verify that the sender is not empty. The following operations must * be performed with interrupt disabled. */ flags = enter_critical_section(); - if (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail) + if (!TX_EMPTY(&dev->cd_sender)) { - /* Send the next message in the FIFO. */ + /* Send the next message in the sender. */ ret = can_xmit(dev); - /* If the message was successfully queued in the H/W FIFO, then - * can_txdone() should have been called. If the S/W FIFO were - * full before then there should now be free space in the S/W FIFO. + /* If the message was successfully queued in the H/W sender, then + * can_txdone() should have been called. If the S/W sender were + * full before then there should now be free space in the S/W sender. */ if (ret >= 0) { - /* Are there any threads waiting for space in the TX FIFO? */ + /* Are there any threads waiting for space in the sender? */ if (dev->cd_ntxwaiters > 0) { /* Yes.. Inform them that new xmit space is available */ - nxsem_post(&dev->cd_xmit.tx_sem); + nxsem_post(&dev->cd_sender.tx_sem); } } } @@ -240,11 +241,9 @@ static int can_open(FAR struct file *filep) ret = dev_setup(dev); if (ret == OK) { - /* Mark the FIFOs empty */ + /* Mark the sender empty */ - dev->cd_xmit.tx_head = 0; - dev->cd_xmit.tx_queue = 0; - dev->cd_xmit.tx_tail = 0; + can_sender_init(&dev->cd_sender); /* Finally, Enable the CAN RX interrupt */ @@ -327,14 +326,14 @@ static int can_close(FAR struct file *filep) dev_rxint(dev, false); - /* Now we wait for the transmit FIFO to clear */ + /* Now we wait for the sender to clear */ - while (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail) + while (!TX_EMPTY(&dev->cd_sender)) { nxsig_usleep(HALF_SECOND_USEC); } - /* And wait for the TX hardware FIFO to drain */ + /* And wait for the hardware sender to drain */ while (!dev_txempty(dev)) { @@ -493,7 +492,7 @@ return_with_irqdisabled: * Name: can_xmit * * Description: - * Send the message at the head of the cd_xmit FIFO + * Send the message at the head of the sender * * Assumptions: * Called with interrupts disabled @@ -502,21 +501,22 @@ return_with_irqdisabled: static int can_xmit(FAR struct can_dev_s *dev) { - int tmpndx; + FAR struct can_msg_s *msg; int ret = -EBUSY; - caninfo("xmit head: %d queue: %d tail: %d\n", - dev->cd_xmit.tx_head, dev->cd_xmit.tx_queue, dev->cd_xmit.tx_tail); + caninfo("xmit pending_count: %d sending_count: %d free_space: %d\n", + PENDING_COUNT(&dev->cd_sender), SENDING_COUNT(&dev->cd_sender), + FREE_COUNT(&dev->cd_sender)); /* If there is nothing to send, then just disable interrupts and return */ - if (dev->cd_xmit.tx_head == dev->cd_xmit.tx_tail) + if (TX_EMPTY(&dev->cd_sender)) { - DEBUGASSERT(dev->cd_xmit.tx_queue == dev->cd_xmit.tx_head); + DEBUGASSERT(SENDING_COUNT(&dev->cd_sender) == 0); #ifndef CONFIG_CAN_TXREADY - /* We can disable CAN TX interrupts -- unless there is a H/W FIFO. In - * that case, TX interrupts must stay enabled until the H/W FIFO is + /* We can disable CAN TX interrupts -- unless there is a H/W sender. In + * that case, TX interrupts must stay enabled until the H/W sender is * fully emptied. */ @@ -525,42 +525,39 @@ static int can_xmit(FAR struct can_dev_s *dev) return -EIO; } - /* Check if we have already queued all of the data in the TX fifo. + /* Check if we have already queued all of the data in the sender. * * tx_tail: Incremented in can_write each time a message is queued in the - * FIFO + * sender * tx_head: Incremented in can_txdone each time a message completes * tx_queue: Incremented each time that a message is sent to the hardware. * * Logically (ignoring buffer wrap-around): tx_head <= tx_queue <= tx_tail - * tx_head == tx_queue == tx_tail means that the FIFO is empty + * tx_head == tx_queue == tx_tail means that the sender is empty * tx_head < tx_queue == tx_tail means that all data has been queued, but * we are still waiting for transmissions to complete. */ - while (dev->cd_xmit.tx_queue != dev->cd_xmit.tx_tail && dev_txready(dev)) + while (TX_PENDING(&dev->cd_sender) && dev_txready(dev)) { - /* No.. The FIFO should not be empty in this case */ + /* No.. The sender should not be empty in this case */ - DEBUGASSERT(dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail); + DEBUGASSERT(!TX_EMPTY(&dev->cd_sender)); - /* Increment the FIFO queue index before sending (because dev_send() - * might call can_txdone()). - */ + msg = can_get_msg(&dev->cd_sender); - tmpndx = dev->cd_xmit.tx_queue; - if (++dev->cd_xmit.tx_queue >= CONFIG_CAN_TXFIFOSIZE) + if (msg == NULL) { - dev->cd_xmit.tx_queue = 0; + break; } - /* Send the next message at the FIFO queue index */ + /* Send the next message at the sender */ - ret = dev_send(dev, &dev->cd_xmit.tx_buffer[tmpndx]); + ret = dev_send(dev, msg); if (ret < 0) { canerr("dev_send failed: %d\n", ret); - dev->cd_xmit.tx_queue = tmpndx; + can_revert_msg(&dev->cd_sender, msg); break; } } @@ -578,17 +575,16 @@ static int can_xmit(FAR struct can_dev_s *dev) static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { - FAR struct inode *inode = filep->f_inode; - FAR struct can_dev_s *dev = inode->i_private; - FAR struct can_txfifo_s *fifo = &dev->cd_xmit; - FAR struct can_msg_s *msg; - bool inactive; - ssize_t nsent = 0; - irqstate_t flags; - int nexttail; - int nbytes; - int msglen; - int ret = 0; + FAR struct inode *inode = filep->f_inode; + FAR struct can_dev_s *dev = inode->i_private; + FAR struct can_txcache_s *sender = &dev->cd_sender; + FAR struct can_msg_s *msg; + bool inactive; + ssize_t nsent = 0; + irqstate_t flags; + int nbytes; + int msglen; + int ret = 0; caninfo("buflen: %zu\n", buflen); @@ -596,37 +592,27 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, flags = enter_critical_section(); - /* Check if the TX is inactive when we started. In certain race conditions, - * there may be a pending interrupt to kick things back off, but we will - * be sure here that there is not. That the hardware is IDLE and will - * need to be kick-started. + /* Check if the H/W TX is inactive when we started. In certain race + * conditions, there may be a pending interrupt to kick things back off, + * but we will be sure here that there is not. That the hardware is IDLE + * and will need to be kick-started. */ inactive = dev_txempty(dev); - /* Add the messages to the FIFO. Ignore any trailing messages that are + /* Add the messages to the sender. Ignore any trailing messages that are * shorter than the minimum. */ while (buflen - nsent >= CAN_MSGLEN(0)) { - /* Check if adding this new message would over-run the drivers ability - * to enqueue xmit data. - */ - - nexttail = fifo->tx_tail + 1; - if (nexttail >= CONFIG_CAN_TXFIFOSIZE) - { - nexttail = 0; - } - - /* If the XMIT FIFO becomes full, then wait for space to become + /* If the sender becomes full, then wait for space to become * available. */ - while (nexttail == fifo->tx_head) + while (TX_FULL(sender)) { - /* The transmit FIFO is full -- was non-blocking mode selected? */ + /* The transmit sender is full -- non-blocking mode selected? */ if ((filep->f_oflags & O_NONBLOCK) != 0) { @@ -644,7 +630,7 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, /* If the TX hardware was inactive when we started, then we will * have start the XMIT sequence generate the TX done interrupts - * needed to clear the FIFO. + * needed to clear the sender. */ if (inactive) @@ -656,37 +642,34 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, DEBUGASSERT(dev->cd_ntxwaiters < 255); dev->cd_ntxwaiters++; - ret = nxsem_wait(&fifo->tx_sem); + ret = nxsem_wait(&sender->tx_sem); dev->cd_ntxwaiters--; if (ret < 0) { goto return_with_irqdisabled; } - /* Re-check the FIFO state */ + /* Re-check the H/W sender state */ inactive = dev_txempty(dev); } - /* We get here if there is space at the end of the FIFO. Add the new - * CAN message at the tail of the FIFO. + /* We get here if there is space in sender. Add the new + * CAN message at sutibal. */ msg = (FAR struct can_msg_s *)&buffer[nsent]; nbytes = can_dlc2bytes(msg->cm_hdr.ch_dlc); msglen = CAN_MSGLEN(nbytes); - memcpy(&fifo->tx_buffer[fifo->tx_tail], msg, msglen); - - /* Increment the tail of the circular buffer */ - fifo->tx_tail = nexttail; + can_add_sendnode(sender, msg, msglen); /* Increment the number of bytes that were sent */ nsent += msglen; } - /* We get here after all messages have been added to the FIFO. Check if + /* We get here after all messages have been added to the sender. Check if * we need to kick off the XMIT sequence. */ @@ -853,9 +836,7 @@ static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case CANIOC_OFLUSH: { - dev->cd_xmit.tx_head = 0; - dev->cd_xmit.tx_queue = 0; - dev->cd_xmit.tx_tail = 0; + can_sender_init(&dev->cd_sender); /* invoke lower half ioctl */ @@ -869,9 +850,8 @@ static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case CANIOC_IOFLUSH: { - dev->cd_xmit.tx_head = 0; - dev->cd_xmit.tx_queue = 0; - dev->cd_xmit.tx_tail = 0; + can_sender_init(&dev->cd_sender); + reader->fifo.rx_head = 0; reader->fifo.rx_tail = 0; @@ -885,8 +865,8 @@ static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case FIONWRITE: { - *(FAR uint8_t *)arg = CONFIG_CAN_TXFIFOSIZE - 1 - - (dev->cd_xmit.tx_tail - dev->cd_xmit.tx_head); + *(FAR int *)arg = PENDING_COUNT(&dev->cd_sender) - + SENDING_COUNT(&dev->cd_sender); } break; @@ -976,7 +956,6 @@ static int can_poll(FAR struct file *filep, FAR struct pollfd *fds, FAR struct can_reader_s *reader = NULL; pollevent_t eventset = 0; irqstate_t flags; - int ndx; int ret; int i; @@ -989,7 +968,7 @@ static int can_poll(FAR struct file *filep, FAR struct pollfd *fds, } #endif - /* Ensure exclusive access to FIFO indices - don't want can_receive or + /* Ensure exclusive access to sender indices - don't want can_receive or * can_read changing them in the middle of the comparison */ @@ -1040,16 +1019,10 @@ static int can_poll(FAR struct file *filep, FAR struct pollfd *fds, } /* Should we immediately notify on any of the requested events? - * First, check if the xmit buffer is full. + * First, check if the sender is full. */ - ndx = dev->cd_xmit.tx_tail + 1; - if (ndx >= CONFIG_CAN_TXFIFOSIZE) - { - ndx = 0; - } - - if (ndx != dev->cd_xmit.tx_head) + if (!TX_FULL(&dev->cd_sender)) { eventset |= POLLOUT; } @@ -1121,7 +1094,7 @@ int can_register(FAR const char *path, FAR struct can_dev_s *dev) /* Initialize semaphores */ - nxsem_init(&dev->cd_xmit.tx_sem, 0, 0); + nxsem_init(&dev->cd_sender.tx_sem, 0, 0); nxmutex_init(&dev->cd_closelock); nxmutex_init(&dev->cd_polllock); @@ -1324,15 +1297,15 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, * Description: * Called when the hardware has processed the outgoing TX message. This * normally means that the CAN messages was sent out on the wire. But - * if the CAN hardware supports a H/W TX FIFO, then this call may mean - * only that the CAN message has been added to the H/W FIFO. In either + * if the CAN hardware supports a H/W TX sender, then this call may mean + * only that the CAN message has been added to the H/W sender. In either * case, the upper-half CAN driver can remove the outgoing message from - * the S/W FIFO and discard it. + * the S/W sender and discard it. * * This function may be called in different contexts, depending upon the * nature of the underlying CAN hardware. * - * 1. No H/W TX FIFO (CONFIG_CAN_TXREADY not defined) + * 1. No H/W sender (CONFIG_CAN_TXREADY not defined) * * This function is only called from the CAN interrupt handler at the * completion of a send operation. @@ -1341,37 +1314,37 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, * CAN interrupt -> can_txdone() * * If the CAN hardware is busy, then the call to dev_send() will - * fail, the S/W TX FIFO will accumulate outgoing messages, and the + * fail, the S/W TX sender will accumulate outgoing messages, and the * thread calling can_write() may eventually block waiting for space in - * the S/W TX FIFO. + * the S/W sender. * * When the CAN hardware completes the transfer and processes the * CAN interrupt, the call to can_txdone() will make space in the S/W - * TX FIFO and will awaken the waiting can_write() thread. + * sender and will awaken the waiting can_write() thread. * - * 2a. H/W TX FIFO (CONFIG_CAN_TXREADY=y) and S/W TX FIFO not full + * 2a. H/W sender (CONFIG_CAN_TXREADY=y) and S/W sender not full * * This function will be called back from dev_send() immediately when a - * new CAN message is added to H/W TX FIFO: + * new CAN message is added to H/W sender: * * can_write() -> can_xmit() -> dev_send() -> can_txdone() * - * When the H/W TX FIFO becomes full, dev_send() will fail and - * can_txdone() will not be called. In this case the S/W TX FIFO will + * When the H/W sender becomes full, dev_send() will fail and + * can_txdone() will not be called. In this case the S/W sender will * accumulate outgoing messages, and the thread calling can_write() may - * eventually block waiting for space in the S/W TX FIFO. + * eventually block waiting for space in the S/W sender. * - * 2b. H/W TX FIFO (CONFIG_CAN_TXREADY=y) and S/W TX FIFO full + * 2b. H/W sender (CONFIG_CAN_TXREADY=y) and S/W sender full * * In this case, the thread calling can_write() is blocked waiting for - * space in the S/W TX FIFO. can_txdone() will be called, indirectly, + * space in the S/W sender. can_txdone() will be called, indirectly, * from can_txready_work() running on the thread of the work queue. * * CAN interrupt -> can_txready() -> Schedule can_txready_work() * can_txready_work() -> can_xmit() -> dev_send() -> can_txdone() * * The call dev_send() should not fail in this case and the subsequent - * call to can_txdone() will make space in the S/W TX FIFO and will + * call to can_txdone() will make space in the S/W sender and will * awaken the waiting thread. * * Input Parameters: @@ -1393,14 +1366,15 @@ int can_txdone(FAR struct can_dev_s *dev) int ret = -ENOENT; irqstate_t flags; - caninfo("xmit head: %d queue: %d tail: %d\n", - dev->cd_xmit.tx_head, dev->cd_xmit.tx_queue, dev->cd_xmit.tx_tail); + caninfo("xmit pending_count: %d sending_count: %d free_space: %d\n", + PENDING_COUNT(&dev->cd_sender), SENDING_COUNT(&dev->cd_sender), + FREE_COUNT(&dev->cd_sender)); flags = enter_critical_section(); - /* Verify that the xmit FIFO is not empty */ + /* Verify that the sender is not empty */ - if (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail) + if (!TX_EMPTY(&dev->cd_sender)) { /* The tx_queue index is incremented each time can_xmit() queues * the transmission. When can_txdone() is called, the tx_queue @@ -1408,32 +1382,29 @@ int can_txdone(FAR struct can_dev_s *dev) * index. */ - DEBUGASSERT(dev->cd_xmit.tx_head != dev->cd_xmit.tx_queue); + DEBUGASSERT(SENDING_COUNT(&dev->cd_sender) != 0); - /* Remove the message at the head of the xmit FIFO */ + /* Remove the message at the head of the sender */ - if (++dev->cd_xmit.tx_head >= CONFIG_CAN_TXFIFOSIZE) - { - dev->cd_xmit.tx_head = 0; - } + can_send_done(&dev->cd_sender); - /* Send the next message in the FIFO */ + /* Send the next message in the sender */ can_xmit(dev); - /* Notify all poll/select waiters that they can write to the cd_xmit + /* Notify all poll/select waiters that they can write to the sender * buffer */ poll_notify(dev->cd_fds, CONFIG_CAN_NPOLLWAITERS, POLLOUT); - /* Are there any threads waiting for space in the TX FIFO? */ + /* Are there any threads waiting for space in the sender? */ if (dev->cd_ntxwaiters > 0) { /* Yes.. Inform them that new xmit space is available */ - ret = nxsem_post(&dev->cd_xmit.tx_sem); + ret = nxsem_post(&dev->cd_sender.tx_sem); } else { @@ -1451,43 +1422,43 @@ int can_txdone(FAR struct can_dev_s *dev) * Description: * Called from the CAN interrupt handler at the completion of a send * operation. This interface is needed only for CAN hardware that - * supports queueing of outgoing messages in a H/W FIFO. + * supports queueing of outgoing messages in a H/W sender. * * The CAN upper half driver also supports a queue of output messages in a - * S/W FIFO. Messages are added to that queue when when can_write() is + * S/W sender. Messages are added to that queue when when can_write() is * called and removed from the queue in can_txdone() when each TX message * is complete. * - * After each message is added to the S/W FIFO, the CAN upper half driver + * After each message is added to the S/W sender, the CAN upper half driver * will attempt to send the message by calling into the lower half driver. * That send will not be performed if the lower half driver is busy, i.e., * if dev_txready() returns false. In that case, the number of messages in - * the S/W FIFO can grow. If the S/W FIFO becomes full, then can_write() - * will wait for space in the S/W FIFO. + * the S/W sender can grow. If the S/W sender becomes full, then + * can_write() will wait for space in the S/W sender. * - * If the CAN hardware does not support a H/W FIFO then busy means that + * If the CAN hardware does not support a H/W sender then busy means that * the hardware is actively sending the message and is guaranteed to * become non-busy (i.e, dev_txready()) when the send transfer completes * and can_txdone() is called. So the call to can_txdone() means that the * transfer has completed and also that the hardware is ready to accept * another transfer. * - * If the CAN hardware supports a H/W FIFO, can_txdone() is not called + * If the CAN hardware supports a H/W sender, can_txdone() is not called * when the transfer is complete, but rather when the transfer is queued in - * the H/W FIFO. When the H/W FIFO becomes full, then dev_txready() will - * report false and the number of queued messages in the S/W FIFO will - * grow. + * the H/W sender. When the H/W sender becomes full, then dev_txready() + * will report false and the number of queued messages in the S/W sender + * will grow. * * There is no mechanism in this case to inform the upper half driver when * the hardware is again available, when there is again space in the H/W - * FIFO. can_txdone() will not be called again. If the S/W FIFO becomes - * full, then the upper half driver will wait for space to become + * sender. can_txdone() will not be called again. If the S/W sender + * becomes full, then the upper half driver will wait for space to become * available, but there is no event to awaken it and the driver will hang. * * Enabling this feature adds support for the can_txready() interface. * This function is called from the lower half driver's CAN interrupt * handler each time a TX transfer completes. This is a sure indication - * that the H/W FIFO is no longer full. can_txready() will then awaken + * that the H/W sender is no longer full. can_txready() will then awaken * the can_write() logic and the hang condition is avoided. * * Input Parameters: @@ -1508,18 +1479,19 @@ int can_txready(FAR struct can_dev_s *dev) int ret = -ENOENT; irqstate_t flags; - caninfo("xmit head: %d queue: %d tail: %d waiters: %d\n", - dev->cd_xmit.tx_head, dev->cd_xmit.tx_queue, dev->cd_xmit.tx_tail, - dev->cd_ntxwaiters); + caninfo("xmit pending_count: %d sending_count: %d free_space: %d" + " waiters: %d\n", + PENDING_COUNT(&dev->cd_sender), SENDING_COUNT(&dev->cd_sender), + FREE_COUNT(&dev->cd_sender), dev->cd_ntxwaiters); flags = enter_critical_section(); - /* Verify that the xmit FIFO is not empty. This is safe because interrupts + /* Verify that the sender is not empty. This is safe because interrupts * are always disabled when calling into can_xmit(); this cannot collide * with ongoing activity from can_write(). */ - if (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail) + if (!TX_EMPTY(&dev->cd_sender)) { /* Is work already scheduled? */ @@ -1541,20 +1513,20 @@ int can_txready(FAR struct can_dev_s *dev) } else { - /* There should not be any threads waiting for space in the S/W TX - * FIFO is it is empty. However, an assertion would fire in certain + /* There should not be any threads waiting for space in the S/W sender + * is it is empty. However, an assertion would fire in certain * race conditions, i.e, when all waiters have been awakened but * have not yet had a chance to decrement cd_ntxwaiters. */ #if 0 /* REVISIT */ - /* When the H/W FIFO has been emptied, we can disable further TX + /* When the H/W sender has been emptied, we can disable further TX * interrupts. * - * REVISIT: The fact that the S/W FIFO is empty does not mean that - * the H/W FIFO is also empty. If we really want this to work this + * REVISIT: The fact that the S/W sender is empty does not mean that + * the H/W sender is also empty. If we really want this to work this * way, then we would probably need and additional parameter to tell - * us if the H/W FIFO is empty. + * us if the H/W sender is empty. */ dev_txint(dev, false); diff --git a/drivers/can/can_sender.c b/drivers/can/can_sender.c new file mode 100644 index 0000000000..03976260b6 --- /dev/null +++ b/drivers/can/can_sender.c @@ -0,0 +1,256 @@ +/**************************************************************************** + * drivers/can/can_sender.c + * + * 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 <nuttx/can/can_sender.h> + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: can_xmit_init + * + * Description: + * Initial dev sender. + * + ****************************************************************************/ + +void can_sender_init(FAR struct can_txcache_s *cd_sender) +{ +#if defined(CONFIG_CAN_TXPRIORITY) && CONFIG_CAN_TXFIFOSIZE <= 0 +# error "CONFIG_CAN_TXFIFOSIZE should be positive non-zero value" +#endif + +#ifdef CONFIG_CAN_TXPRIORITY + int i; + + list_initialize(&cd_sender->tx_free); + list_initialize(&cd_sender->tx_pending); + list_initialize(&cd_sender->tx_sending); + + for (i = 0; i < CONFIG_CAN_TXFIFOSIZE; i++) + { + list_add_tail(&cd_sender->tx_free, &cd_sender->tx_buffer[i].list); + } + +#else + cd_sender->tx_head = 0; + cd_sender->tx_queue = 0; + cd_sender->tx_tail = 0; +#endif +} + +/**************************************************************************** + * Name: can_add_sendnode + * + * Description: + * Add message to sender. + * + ****************************************************************************/ + +void can_add_sendnode(FAR struct can_txcache_s *cd_sender, + FAR struct can_msg_s *msg, int msglen) +{ +#ifdef CONFIG_CAN_TXPRIORITY + FAR struct list_node *node; + FAR struct can_msg_node_s *msg_node; + FAR struct can_msg_node_s *tmp_node; + + node = list_remove_head(&cd_sender->tx_free); + msg_node = container_of(node, struct can_msg_node_s, list); + memcpy(&msg_node->msg, msg, msglen); + + list_for_every_entry(&cd_sender->tx_pending, tmp_node, + struct can_msg_node_s, list) + { + if (tmp_node->msg.cm_hdr.ch_id > msg->cm_hdr.ch_id) + { + /* Prioritize tx frame based on canid */ + + break; + } + } + + if (&tmp_node->list == &cd_sender->tx_pending) + { + /* Inserted at the end of the linked list */ + + list_add_tail(&cd_sender->tx_pending, &msg_node->list); + } + else + { + list_add_before(&tmp_node->list, &msg_node->list); + } +#else + memcpy(&cd_sender->tx_buffer[cd_sender->tx_tail], msg, msglen); + + /* Increment the tail of the circular buffer */ + + cd_sender->tx_tail++; + if (cd_sender->tx_tail >= CONFIG_CAN_TXFIFOSIZE) + { + cd_sender->tx_tail = 0; + } +#endif +} + +/**************************************************************************** + * Name: can_get_msg + * + * Description: + * Get send message from sender. + * + ****************************************************************************/ + +FAR struct can_msg_s *can_get_msg(FAR struct can_txcache_s *cd_sender) +{ + FAR struct can_msg_s *msg = NULL; + +#ifdef CONFIG_CAN_TXPRIORITY + FAR struct can_msg_node_s *msg_node; + FAR struct can_msg_node_s *tmp_node; + + if (list_is_empty(&cd_sender->tx_pending)) + { + return NULL; + } + + msg_node = list_first_entry(&cd_sender->tx_pending, + struct can_msg_node_s, list); + msg = &msg_node->msg; + + /* Sort unconfirmed messages in ascending order */ + + list_for_every_entry(&cd_sender->tx_sending, tmp_node, + struct can_msg_node_s, list) + { + if (tmp_node->msg.cm_hdr.ch_id == msg->cm_hdr.ch_id) + { + /* In order to prevent messages with the same ID from being + * sent out of order, as long as there is a message with the + * same ID that has not been sent in H/W, no data will be + * written to H/W + */ + + return NULL; + } + + if (tmp_node->msg.cm_hdr.ch_id > msg->cm_hdr.ch_id) + { + break; + } + } + + /* Move the node from tx_pending to tx_sending before + * sending(because dev_send() might call can_txdone()). + */ + + list_delete(&msg_node->list); + + if (&tmp_node->list == &cd_sender->tx_sending) + { + list_add_tail(&cd_sender->tx_sending, &msg_node->list); + } + else + { + list_add_before(&tmp_node->list, &msg_node->list); + } + +#else + msg = &cd_sender->tx_buffer[cd_sender->tx_queue]; + + /* Increment the FIFO queue index before sending (because dev_send() + * might call can_txdone()). + */ + + cd_sender->tx_queue++; + + if (cd_sender->tx_queue == CONFIG_CAN_TXFIFOSIZE) + { + cd_sender->tx_queue = 0; + } +#endif + + return msg; +} + +/**************************************************************************** + * Name: can_revert_msg + * + * Description: + * Rever msg in sender, because sending failed. + * + ****************************************************************************/ + +void can_revert_msg(FAR struct can_txcache_s *cd_sender, + FAR struct can_msg_s *msg) +{ +#ifdef CONFIG_CAN_TXPRIORITY + FAR struct can_msg_node_s *msg_node; + + msg_node = container_of(msg, struct can_msg_node_s, msg); + + list_delete(&msg_node->list); + + list_add_head(&cd_sender->tx_pending, &msg_node->list); +#else + UNUSED(msg); + if (cd_sender->tx_queue == 0) + { + cd_sender->tx_queue = CONFIG_CAN_TXFIFOSIZE - 1; + } + else + { + cd_sender->tx_queue--; + } +#endif +} + +/**************************************************************************** + * Name: can_send_done + * + * Description: + * Release the sender resources, after the tragic message is successfully + * sent to the bus. + * + ****************************************************************************/ + +void can_send_done(FAR struct can_txcache_s *cd_sender) +{ +#ifdef CONFIG_CAN_TXPRIORITY + FAR struct list_node *node; + + node = list_remove_head(&cd_sender->tx_sending); + list_add_head(&cd_sender->tx_free, node); +#else + /* Remove the message at the head of the xmit FIFO */ + + cd_sender->tx_head++; + if (cd_sender->tx_head >= CONFIG_CAN_TXFIFOSIZE) + { + cd_sender->tx_head = 0; + } +#endif +} diff --git a/include/nuttx/can/can.h b/include/nuttx/can/can.h index 841aca0001..dc544c3299 100644 --- a/include/nuttx/can/can.h +++ b/include/nuttx/can/can.h @@ -648,14 +648,50 @@ struct can_rxfifo_s struct can_msg_s rx_buffer[CONFIG_CAN_RXFIFOSIZE]; }; -struct can_txfifo_s +#ifdef CONFIG_CAN_TXPRIORITY +struct can_msg_node_s { - sem_t tx_sem; /* Counting semaphore */ - uint8_t tx_head; /* Index to the head [IN] in the circular buffer */ - uint8_t tx_queue; /* Index to next message to send */ - uint8_t tx_tail; /* Index to the tail [OUT] in the circular buffer */ - /* Circular buffer of CAN messages */ + struct list_node list; + struct can_msg_s msg; +}; +#endif + +struct can_txcache_s +{ + sem_t tx_sem; /* Counting semaphore */ +#ifdef CONFIG_CAN_TXPRIORITY + /* tx_buffer - Buffer of CAN message. And this buffer is managed by + * tx_free/tx_pending/tx_sending + * tx_free - Link all buffer node in the initial step + * tx_pending - Get node from tx_free. Message to send, in order of can_id + * tx_sending - Get node from tx_pending. CAN message write to H/W, but + * not confirmed. Release node to tx_free when the message + * confirmed + * + * tx_free -> tx_pending -> tx_sending -> tx_free + */ + + struct list_node tx_free; + struct list_node tx_pending; + struct list_node tx_sending; + struct can_msg_node_s tx_buffer[CONFIG_CAN_TXFIFOSIZE]; +#else + /* tx_buffer - Circular buffer of CAN messages. And this buffer is managed + * by tx_head/tx_queue/tx_tail. + * tx_head - Index to the head [IN] in the circular buffer + * tx_queue - Index to next message to send + * tx_tail - Index to the tail [OUT] in the circular buffer + * tx_buffer | 0 | 1 | 2 | | | ... | | | ... | | ... |TXFIFOSIZE| + * | | | + * \|/ \|/ \|/ + * tx_head tx_queue tx_tail + */ + + uint8_t tx_head; + uint8_t tx_queue; + uint8_t tx_tail; struct can_msg_s tx_buffer[CONFIG_CAN_TXFIFOSIZE]; +#endif }; /* The following structure define the logic to handle @@ -780,7 +816,7 @@ struct can_dev_s struct list_node cd_readers; /* List of readers */ mutex_t cd_closelock; /* Locks out new opens while close is in progress */ mutex_t cd_polllock; /* Manages exclusive access to cd_fds[] */ - struct can_txfifo_s cd_xmit; /* Describes transmit FIFO */ + struct can_txcache_s cd_sender; /* Describes transmit cache */ #ifdef CONFIG_CAN_TXREADY struct work_s cd_work; /* Use to manage can_txready() work */ #endif diff --git a/include/nuttx/can/can_sender.h b/include/nuttx/can/can_sender.h new file mode 100644 index 0000000000..706aa666ed --- /dev/null +++ b/include/nuttx/can/can_sender.h @@ -0,0 +1,239 @@ +/**************************************************************************** + * include/nuttx/can/can_sender.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_CAN_SENDER_H +#define __INCLUDE_NUTTX_CAN_SENDER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/compiler.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> + +#include <nuttx/list.h> +#include <nuttx/fs/fs.h> +#include <nuttx/fs/ioctl.h> +#include <nuttx/mutex.h> +#include <nuttx/can/can.h> + +#ifdef CONFIG_CAN + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_CAN_TXPRIORITY + +/* There are three linked lists to manage TX buffer: + * tx_free - can_write function get a tx_free node, write message, then + * move this node to tx_pending list. + * tx_pending - cache message send from application but not send to H/W. + * Sorted in ascending order based on can_id. + * tx_sending - Message in H/W is sending to bus, but not confirmation. + * Sorted in ascending order based on can_id. + */ + +# define TX_EMPTY(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (list_length(&__sender->tx_free) == CONFIG_CAN_TXFIFOSIZE); \ + }) \ + +# define TX_FULL(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + list_is_empty(&__sender->tx_free); \ + }) \ + +# define TX_PENDING(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + !list_is_empty(&__sender->tx_pending); \ + }) \ + +# define PENDING_COUNT(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (list_length(&__sender->tx_pending)); \ + }) \ + +# define SENDING_COUNT(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (list_length(&__sender->tx_sending)); \ + }) \ + +# define FREE_COUNT(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (list_length(&__sender->tx_free)); \ + }) \ + +#else +/* There are three fields used to manage TX FIFO buffer: + * tx_tail: Incremented in can_write each time a message is queued in the + * FIFO + * tx_head: Incremented in can_txdone each time a message completes + * tx_queue: Incremented each time that a message is sent to the hardware. + * + * Logically (ignoring buffer wrap-around): tx_head <= tx_queue <= tx_tail + * tx_head == tx_queue == tx_tail means that the FIFO is empty + * tx_head < tx_queue == tx_tail means that all data has been queued, but + * we are still waiting for transmissions to complete. + */ +# define TX_EMPTY(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (__sender->tx_head == __sender->tx_tail); \ + }) \ + +# define TX_FULL(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (__sender->tx_head == \ + (__sender->tx_tail + 1) % CONFIG_CAN_TXFIFOSIZE); \ + }) \ + +# define TX_PENDING(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (__sender->tx_queue != __sender->tx_tail); \ + }) \ + +# define PENDING_COUNT(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (CONFIG_CAN_TXFIFOSIZE + __sender->tx_tail - __sender->tx_queue) \ + % CONFIG_CAN_TXFIFOSIZE; \ + }) \ + +# define SENDING_COUNT(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (CONFIG_CAN_TXFIFOSIZE + __sender->tx_queue - __sender->tx_head) \ + % CONFIG_CAN_TXFIFOSIZE; \ + }) \ + +# define FREE_COUNT(sender) \ + ({ \ + FAR struct can_txcache_s *__sender = (sender); \ + (CONFIG_CAN_TXFIFOSIZE - 1 - __sender->tx_tail + __sender->tx_head) \ + % CONFIG_CAN_TXFIFOSIZE; \ + }) \ + +#endif /* CONFIG_CAN_TXPRIORITY */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: can_sender_init + * + * Description: + * Initial dev sender. + * + * Input Parameters: + * cd_sender - The CAN device sender. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void can_sender_init(FAR struct can_txcache_s *cd_sender); + +/**************************************************************************** + * Name: can_add_sendnode + * + * Description: + * Add message to sender.. + * + * Input Parameters: + * cd_sender - The CAN device sender. + * + * Returned Value: + * CAN message which to be send. + * + ****************************************************************************/ + +void can_add_sendnode(FAR struct can_txcache_s *cd_sender, + FAR struct can_msg_s *msg, int msglen); + +/**************************************************************************** + * Name: can_get_msg + * + * Description: + * Get message from sender, and change sender recoder. + * + * Input Parameters: + * cd_sender - The CAN device sender. + * + * Returned Value: + * CAN message which to be send. + * + ****************************************************************************/ + +struct can_msg_s *can_get_msg(FAR struct can_txcache_s *cd_sender); + +/**************************************************************************** + * Name: can_revert_msg + * + * Description: + * Revert sender recoder when send failed. + * + * Input Parameters: + * cd_sender - The CAN device sender. + * msg - message which to be revert. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void can_revert_msg(FAR struct can_txcache_s *cd_sender, + FAR struct can_msg_s *msg); + +/**************************************************************************** + * Name: can_send_done + * + * Description: + * Release the sender resources, after the tragic message is successfully + * sent to the bus. + * + * Input Parameters: + * cd_sender - The CAN device sender. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void can_send_done(FAR struct can_txcache_s *cd_sender); + +#endif /* CONFIG_CAN */ +#endif /* __INCLUDE_NUTTX_CAN_SENDER_H */