Fixes to avoid some hang conditions using STM32 CAN

This commit is contained in:
Gregory Nutt 2014-09-17 08:35:03 -06:00
parent f285498921
commit b37c0a832a
2 changed files with 85 additions and 68 deletions

View File

@ -1235,19 +1235,6 @@ static int can_txinterrupt(int irq, void *context)
}
}
/* Were all transmissions complete in all mailboxes when we entered this
* handler?
*/
if ((regval & CAN_ALL_MAILBOXES) == CAN_ALL_MAILBOXES)
{
/* Yes.. disable further TX interrupts */
regval = can_getreg(priv, STM32_CAN_IER_OFFSET);
regval &= ~CAN_IER_TMEIE;
can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
}
return OK;
}

View File

@ -1,7 +1,7 @@
/****************************************************************************
* drivers/can.c
*
* Copyright (C) 2008-2009, 2011-2012 Gregory Nutt. All rights reserved.
* Copyright (C) 2008-2009, 2011-2012, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -91,11 +91,15 @@
static int can_open(FAR struct file *filep);
static int can_close(FAR struct file *filep);
static ssize_t can_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
static ssize_t can_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
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);
static inline ssize_t can_rtrread(FAR struct can_dev_s *dev, FAR struct canioctl_rtr_s *rtr);
static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
static ssize_t can_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static inline ssize_t can_rtrread(FAR struct can_dev_s *dev,
FAR struct canioctl_rtr_s *rtr);
static int can_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
/****************************************************************************
* Private Data
@ -118,13 +122,13 @@ static const struct file_operations g_canops =
* Private Functions
****************************************************************************/
/************************************************************************************
/****************************************************************************
* Name: can_open
*
* Description:
* This function is called whenever the CAN device is opened.
*
************************************************************************************/
****************************************************************************/
static int can_open(FAR struct file *filep)
{
@ -188,17 +192,18 @@ static int can_open(FAR struct file *filep)
}
sem_post(&dev->cd_closesem);
}
return ret;
}
/************************************************************************************
/****************************************************************************
* Name: can_close
*
* Description:
* This routine is called when the CAN device is closed.
* It waits for the last remaining data to be sent.
*
************************************************************************************/
****************************************************************************/
static int can_close(FAR struct file *filep)
{
@ -265,18 +270,20 @@ static int can_close(FAR struct file *filep)
sem_post(&dev->cd_closesem);
}
}
return ret;
}
/************************************************************************************
/****************************************************************************
* Name: can_read
*
* Description:
* Read standard CAN messages
*
************************************************************************************/
****************************************************************************/
static ssize_t can_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
static ssize_t can_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct can_dev_s *dev = inode->i_private;
@ -286,8 +293,9 @@ static ssize_t can_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
canvdbg("buflen: %d\n", buflen);
/* The caller must provide enough memory to catch the smallest possible message
* This is not a system error condition, but we won't permit it, Hence we return 0.
/* The caller must provide enough memory to catch the smallest possible
* message. This is not a system error condition, but we won't permit
* it, Hence we return 0.
*/
if (buflen >= CAN_MSGLEN(0))
@ -355,10 +363,11 @@ static ssize_t can_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
return_with_irqdisabled:
irqrestore(flags);
}
return ret;
}
/************************************************************************************
/****************************************************************************
* Name: can_xmit
*
* Description:
@ -367,7 +376,7 @@ return_with_irqdisabled:
* Assumptions:
* Called with interrupts disabled
*
************************************************************************************/
****************************************************************************/
static int can_xmit(FAR struct can_dev_s *dev)
{
@ -430,11 +439,12 @@ static int can_xmit(FAR struct can_dev_s *dev)
return ret;
}
/************************************************************************************
/****************************************************************************
* Name: can_write
************************************************************************************/
****************************************************************************/
static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
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;
@ -453,9 +463,10 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, size_t
flags = irqsave();
/* 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 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);
@ -466,8 +477,8 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, size_t
while ((buflen - nsent) >= CAN_MSGLEN(0))
{
/* Check if adding this new message would over-run the drivers ability to enqueue
* xmit data.
/* Check if adding this new message would over-run the drivers ability
* to enqueue xmit data.
*/
nexttail = fifo->tx_tail + 1;
@ -492,11 +503,12 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer, size_t
{
ret = nsent;
}
goto return_with_irqdisabled;
}
/* If the TX hardware was inactive when we started, then we will have
* start the XMIT sequence generate the TX done interrrupts needed
* start the XMIT sequence generate the TX done interrupts needed
* to clear the FIFO.
*/
@ -562,17 +574,19 @@ return_with_irqdisabled:
return ret;
}
/************************************************************************************
/****************************************************************************
* Name: can_rtrread
*
* Description:
* Read RTR messages. The RTR message is a special message -- it is an outgoing
* message that says "Please re-transmit the message with the same identifier as
* this message. So the RTR read is really a send-wait-receive operation.
* Read RTR messages. The RTR message is a special message -- it is an
* outgoing message that says "Please re-transmit the message with the
* same identifier as this message. So the RTR read is really a
* send-wait-receive operation.
*
************************************************************************************/
****************************************************************************/
static inline ssize_t can_rtrread(FAR struct can_dev_s *dev, FAR struct canioctl_rtr_s *rtr)
static inline ssize_t can_rtrread(FAR struct can_dev_s *dev,
FAR struct canioctl_rtr_s *rtr)
{
FAR struct can_rtrwait_s *wait = NULL;
irqstate_t flags;
@ -583,7 +597,7 @@ static inline ssize_t can_rtrread(FAR struct can_dev_s *dev, FAR struct canioctl
flags = irqsave();
/* Find an avaiable slot in the pending RTR list */
/* Find an available slot in the pending RTR list */
for (i = 0; i < CONFIG_CAN_NPENDINGRTR; i++)
{
@ -610,13 +624,14 @@ static inline ssize_t can_rtrread(FAR struct can_dev_s *dev, FAR struct canioctl
ret = sem_wait(&wait->cr_sem);
}
}
irqrestore(flags);
return ret;
}
/************************************************************************************
/****************************************************************************
* Name: can_ioctl
************************************************************************************/
****************************************************************************/
static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
@ -630,22 +645,26 @@ static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
switch (cmd)
{
/* CANIOCTL_RTR: Send the remote transmission request and wait for the response.
* Argument is a reference to struct canioctl_rtr_s (casting to uintptr_t first
* eliminates complaints on some architectures where the sizeof long is different
* from the size of a pointer).
/* CANIOCTL_RTR: Send the remote transmission request and wait for the
* response. Argument is a reference to struct canioctl_rtr_s
* (casting to uintptr_t first eliminates complaints on some
* architectures where the sizeof long is different from the size of
* a pointer).
*/
case CANIOCTL_RTR:
ret = can_rtrread(dev, (struct canioctl_rtr_s*)((uintptr_t)arg));
break;
/* Not a "built-in" ioctl command.. perhaps it is unique to this device driver */
/* Not a "built-in" ioctl command.. perhaps it is unique to this
* device driver.
*/
default:
ret = dev_ioctl(dev, cmd, arg);
break;
}
return ret;
}
@ -653,13 +672,13 @@ static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
* Public Functions
****************************************************************************/
/************************************************************************************
/****************************************************************************
* Name: can_register
*
* Description:
* Register serial console and serial ports.
*
************************************************************************************/
****************************************************************************/
int can_register(FAR const char *path, FAR struct can_dev_s *dev)
{
@ -690,7 +709,7 @@ int can_register(FAR const char *path, FAR struct can_dev_s *dev)
return register_driver(path, &g_canops, 0666, dev);
}
/************************************************************************************
/****************************************************************************
* Name: can_receive
*
* Description:
@ -704,9 +723,10 @@ int can_register(FAR const char *path, FAR struct can_dev_s *dev)
* Assumptions:
* CAN interrupts are disabled.
*
************************************************************************************/
****************************************************************************/
int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, FAR uint8_t *data)
int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
FAR uint8_t *data)
{
FAR struct can_rxfifo_s *fifo = &dev->cd_recv;
FAR uint8_t *dest;
@ -716,8 +736,8 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, FAR uint8_
canllvdbg("ID: %d DLC: %d\n", hdr->ch_id, hdr->ch_dlc);
/* Check if adding this new message would over-run the drivers ability to enqueue
* read data.
/* Check if adding this new message would over-run the drivers ability to
* enqueue read data.
*/
nexttail = fifo->rx_tail + 1;
@ -726,7 +746,9 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, FAR uint8_
nexttail = 0;
}
/* First, check if this response matches any RTR response that we may be waiting for */
/* First, check if this response matches any RTR response that we may be
* waiting for.
*/
if (dev->cd_npendrtr > 0)
{
@ -739,8 +761,8 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, FAR uint8_
FAR struct can_rtrwait_s *rtr = &dev->cd_rtr[i];
FAR struct can_msg_s *msg = rtr->cr_msg;
/* Check if the entry is valid and if the ID matches. A valid entry has
* a non-NULL receiving address
/* Check if the entry is valid and if the ID matches. A valid
* entry has a non-NULL receiving address
*/
if (msg && hdr->ch_id == rtr->cr_id)
@ -781,20 +803,23 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, FAR uint8_
fifo->rx_tail = nexttail;
/* The increment the counting semaphore. The maximum value should be
* CONFIG_CAN_FIFOSIZE -- one possible count for each allocated message buffer.
* CONFIG_CAN_FIFOSIZE -- one possible count for each allocated
* message buffer.
*/
sem_post(&fifo->rx_sem);
err = OK;
}
return err;
}
/************************************************************************************
/****************************************************************************
* Name: can_txdone
*
* Description:
* Called from the CAN interrupt handler at the completion of a send operation.
* Called from the CAN interrupt handler at the completion of a send
* operation.
*
* Parameters:
* dev - The specific CAN device
@ -804,7 +829,7 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr, FAR uint8_
* Return:
* OK on success; a negated errno on failure.
*
************************************************************************************/
****************************************************************************/
int can_txdone(FAR struct can_dev_s *dev)
{
@ -828,17 +853,22 @@ int can_txdone(FAR struct can_dev_s *dev)
/* Send the next message in the FIFO */
ret = can_xmit(dev);
(void)can_xmit(dev);
/* Are there any threads waiting for space in the TX FIFO? */
if (ret == OK && dev->cd_ntxwaiters > 0)
if (dev->cd_ntxwaiters > 0)
{
/* Yes.. Inform them that new xmit space is available */
ret = sem_post(&dev->cd_xmit.tx_sem);
}
else
{
ret = OK;
}
}
return ret;
}