From c6fc285277f1ce920816af34ed62c7d1f4687edd Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 3 Nov 2015 10:52:58 -0600 Subject: [PATCH] CAN: Add a new CAN upper-half interface, can_txready(), that can be used to break deadlock conditions in certain CAN hardware that supports queuing of TX messages --- ChangeLog | 10 +++++ arch | 2 +- drivers/Kconfig | 48 +++++++++++++++++++++++ drivers/can.c | 95 ++++++++++++++++++++++++++++++++++++++++++++- include/nuttx/can.h | 54 ++++++++++++++++++++++++++ 5 files changed, 206 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index f02f9dc31a..b03be3d6bf 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11060,3 +11060,13 @@ board user LED interfaces (2015-11-01). * sched/clock: Fix error in clock_timespec_subtract(). Found by Lok (2015-11-03). + * drivers/can.c and include/nuttx/can.h: Fix a problem in the CAN + upper-half driver that occurs only for CAN hardware that support a + H/W FIFO of outgoing CAN messages. In this case, there can be a + hang condition if both the H/W and S/W FIFOs are both full. In that + case, there may be no event to awaken the upper half driver. Add a + new (conditional) CAN upper half interface called can_txready() that + can be used by the lower half driver to avoid this hang condition + (2015-11-03). + * arch/arm/src/samv7: Add a call to can_txready() to the MCAN driver + (2015-11-03). diff --git a/arch b/arch index b763efbe64..309bd15763 160000 --- a/arch +++ b/arch @@ -1 +1 @@ -Subproject commit b763efbe64b84ff1cefdd428c7a3d7b7b69f1c9b +Subproject commit 309bd15763dfa0d7e803811216c3b17262255de6 diff --git a/drivers/Kconfig b/drivers/Kconfig index 098c21164d..2ead84ff1f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -125,6 +125,54 @@ config CAN_NPENDINGRTR ---help--- The size of the list of pending RTR requests. Default: 4 +config CAN_TXREADY + bool "can_txready interface" + default n + ---help--- + This selection enables the can_txready() interface. This interface + is needed only for CAN hardware that supports queing of outgoing + messages in a H/W FIFO. + + 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 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 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. + + If the CAN hardware does not support a H/W FIFO 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 when the tranfer 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. + + 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 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 the + can_write() logic and the hang condition is avoided. + config CAN_LOOPBACK bool "CAN loopback mode" default n diff --git a/drivers/can.c b/drivers/can.c index 66ba2b467e..3548729388 100644 --- a/drivers/can.c +++ b/drivers/can.c @@ -554,7 +554,7 @@ static int can_xmit(FAR struct can_dev_s *dev) DEBUGASSERT(dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail); /* Increment the FIFO queue index before sending (because dev_send() - * might call can_txdone(). + * might call can_txdone()). */ tmpndx = dev->cd_xmit.tx_queue; @@ -634,7 +634,7 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, { /* The transmit FIFO is full -- was non-blocking mode selected? */ - if (filep->f_oflags & O_NONBLOCK) + if ((filep->f_oflags & O_NONBLOCK) != 0) { if (nsent == 0) { @@ -1007,6 +1007,12 @@ int can_txdone(FAR struct can_dev_s *dev) if (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail) { + /* The tx_queue index is incremented each time can_xmit() queues + * the transmission. When can_txdone() is called, the tx_queue + * index should always have been advanced beyond the current tx_head + * index. + */ + DEBUGASSERT(dev->cd_xmit.tx_head != dev->cd_xmit.tx_queue); /* Remove the message at the head of the xmit FIFO */ @@ -1037,4 +1043,89 @@ int can_txdone(FAR struct can_dev_s *dev) return ret; } +/**************************************************************************** + * Name: can_txready + * + * Description: + * Called from the CAN interrupt handler at the completion of a send + * operation. This interface is needed only for CAN hardware that + * supports queing of outgoing messages in a H/W FIFO. + * + * 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 + * 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 + * 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. + * + * If the CAN hardware does not support a H/W FIFO 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 + * when the tranfer 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. + * + * 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 + * 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 + * the can_write() logic and the hang condition is avoided. + * + * Parameters: + * dev - The specific CAN device + * + * Return: + * OK on success; a negated errno on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_CAN_TXREADY +int can_txready(FAR struct can_dev_s *dev) +{ + int ret = -ENOENT; + + canllvdbg("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); + + /* Are there any threads waiting for space in the xmit FIFO? */ + + if (dev->cd_ntxwaiters > 0) + { + /* Verify that the xmit FIFO is not empty. + * REVISIT: This probably should be an assertion since we should only + * be waiting for space in the xmit FIFO if the xmit FIFO is full. + */ + + if (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail) + { + /* Send the next message in the FIFO, making space in the xmit FIFO */ + + (void)can_xmit(dev); + } + + /* Inform one waiter that new xmit space is available */ + + ret = sem_post(&dev->cd_xmit.tx_sem); + } + + return ret; +} +#endif /* CONFIG_CAN_TXREADY */ #endif /* CONFIG_CAN */ diff --git a/include/nuttx/can.h b/include/nuttx/can.h index 18c06e8416..59f4242e63 100644 --- a/include/nuttx/can.h +++ b/include/nuttx/can.h @@ -524,6 +524,60 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, int can_txdone(FAR struct can_dev_s *dev); +/************************************************************************************ + * Name: can_txready + * + * Description: + * Called from the CAN interrupt handler at the completion of a send operation. + * This interface is needed only for CAN hardware that supports queing of + * outgoing messages in a H/W FIFO. + * + * 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 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 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. + * + * If the CAN hardware does not support a H/W FIFO 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 when + * the tranfer 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. + * + * 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 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 + * the can_write() logic and the hang condition is avoided. + * + * Parameters: + * dev - The specific CAN device + * + * Return: + * OK on success; a negated errno on failure. + * + ************************************************************************************/ + +#ifdef CONFIG_CAN_TXREADY +int can_txready(FAR struct can_dev_s *dev); +#endif + #undef EXTERN #if defined(__cplusplus) }