mirror of
https://github.com/apache/nuttx.git
synced 2026-05-09 23:12:17 +08:00
drivers/can: Fix close drain, write-only reader lifecycle, and STM32 RX header
Always allocate per-file can_reader on open so write-only descriptors get msgalign / ioctl state; free that context on close when it was never linked into cd_readers (fixes leak). Cap each drain loop (20×500 ms) so close() cannot block forever when bxCAN retries without ACK or dev_txempty never clears. Fix stm32can_vputreg debug log to print the written value (correct parameter name). Signed-off-by: Alexey Matveev <tippet@yandex.ru>
This commit is contained in:
committed by
Alan C. Assis
parent
786b315947
commit
369755d00b
@@ -365,7 +365,7 @@ static void stm32can_vputreg(uint32_t addr, uint32_t value)
|
||||
{
|
||||
/* Show the register value being written */
|
||||
|
||||
caninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
|
||||
caninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, value);
|
||||
|
||||
/* Write the value */
|
||||
|
||||
|
||||
+53
-14
@@ -89,6 +89,13 @@
|
||||
#define HALF_SECOND_MSEC 500
|
||||
#define HALF_SECOND_USEC 500000L
|
||||
|
||||
/* can_close waits for SW queue and H/W TX to drain. Without a second bus
|
||||
* node (no ACK) bxCAN may retry indefinitely; dev_txempty() then never
|
||||
* becomes true. Bound the wait so close() does not hang forever.
|
||||
*/
|
||||
|
||||
#define CAN_CLOSE_DRAIN_LOOPS 20u /* 20 * 500 ms = 10 s per stage */
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
@@ -257,14 +264,22 @@ static int can_open(FAR struct file *filep)
|
||||
|
||||
if (ret == OK)
|
||||
{
|
||||
FAR struct can_reader_s *reader;
|
||||
|
||||
dev->cd_crefs++;
|
||||
|
||||
/* Update the reader list only if driver was open for reading */
|
||||
/* Per-file context (msgalign, optional ioctl FIFO). Always
|
||||
* allocated: write-only needs msgalign / CANIOC_* without O_RDOK.
|
||||
* Receive path and poll() only use readers that are also queued
|
||||
* on cd_readers (see below).
|
||||
*/
|
||||
|
||||
reader = init_can_reader(filep);
|
||||
|
||||
if ((filep->f_oflags & O_RDOK) != 0)
|
||||
{
|
||||
list_add_head(&dev->cd_readers,
|
||||
(FAR struct list_node *)init_can_reader(filep));
|
||||
(FAR struct list_node *)reader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,11 +302,14 @@ errout:
|
||||
|
||||
static int can_close(FAR struct file *filep)
|
||||
{
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
FAR struct can_dev_s *dev = inode->i_private;
|
||||
irqstate_t flags;
|
||||
FAR struct list_node *node;
|
||||
int ret;
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
FAR struct can_dev_s *dev = inode->i_private;
|
||||
irqstate_t flags;
|
||||
FAR struct list_node *node;
|
||||
FAR struct can_reader_s *priv = (FAR struct can_reader_s *)filep->f_priv;
|
||||
bool onlist = false;
|
||||
int ret;
|
||||
unsigned int n;
|
||||
|
||||
#ifdef CONFIG_DEBUG_CAN_INFO
|
||||
caninfo("ocount: %u\n", dev->cd_crefs);
|
||||
@@ -307,10 +325,10 @@ static int can_close(FAR struct file *filep)
|
||||
|
||||
list_for_every(&dev->cd_readers, node)
|
||||
{
|
||||
if (((FAR struct can_reader_s *)node) ==
|
||||
((FAR struct can_reader_s *)filep->f_priv))
|
||||
if (((FAR struct can_reader_s *)node) == priv)
|
||||
{
|
||||
FAR struct can_reader_s *reader = (FAR struct can_reader_s *)node;
|
||||
FAR struct can_reader_s *reader =
|
||||
(FAR struct can_reader_s *)node;
|
||||
FAR struct can_rxfifo_s *fifo = &reader->fifo;
|
||||
|
||||
/* Unlock the binary semaphore, waking up can_read if it
|
||||
@@ -319,18 +337,26 @@ static int can_close(FAR struct file *filep)
|
||||
|
||||
nxsem_post(&fifo->rx_sem);
|
||||
|
||||
/* Notify specific poll/select waiter that they can read from the
|
||||
* cd_recv buffer
|
||||
/* Notify specific poll/select waiter that they can read from
|
||||
* the cd_recv buffer
|
||||
*/
|
||||
|
||||
poll_notify(&reader->cd_fds, 1, POLLHUP);
|
||||
reader->cd_fds = NULL;
|
||||
list_delete(node);
|
||||
kmm_free(node);
|
||||
onlist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write-only opens use init_can_reader() but are not on cd_readers */
|
||||
|
||||
if (!onlist && priv != NULL)
|
||||
{
|
||||
kmm_free(priv);
|
||||
}
|
||||
|
||||
filep->f_priv = NULL;
|
||||
dev->cd_crefs--;
|
||||
|
||||
@@ -347,18 +373,31 @@ static int can_close(FAR struct file *filep)
|
||||
|
||||
/* Now we wait for the sender to clear */
|
||||
|
||||
while (!TX_EMPTY(&dev->cd_sender))
|
||||
for (n = 0;
|
||||
!TX_EMPTY(&dev->cd_sender) && n < CAN_CLOSE_DRAIN_LOOPS;
|
||||
n++)
|
||||
{
|
||||
nxsched_usleep(HALF_SECOND_USEC);
|
||||
}
|
||||
|
||||
if (!TX_EMPTY(&dev->cd_sender))
|
||||
{
|
||||
canerr("CAN close: SW TX queue still not empty after timeout\n");
|
||||
}
|
||||
|
||||
/* And wait for the hardware sender to drain */
|
||||
|
||||
while (!dev_txempty(dev))
|
||||
for (n = 0; !dev_txempty(dev) && n < CAN_CLOSE_DRAIN_LOOPS; n++)
|
||||
{
|
||||
nxsched_usleep(HALF_SECOND_USEC);
|
||||
}
|
||||
|
||||
if (!dev_txempty(dev))
|
||||
{
|
||||
canerr("CAN close: H/W TX still busy after timeout "
|
||||
"(no ACK / bus-off / stuck mailbox)\n");
|
||||
}
|
||||
|
||||
/* Free the IRQ and disable the CAN device */
|
||||
|
||||
dev_shutdown(dev); /* Disable the CAN */
|
||||
|
||||
Reference in New Issue
Block a user