drivers/can/can.c: Support multiple readers of the same port

This commit is contained in:
Valmantas Paliksa 2019-04-12 11:13:42 -06:00 committed by Gregory Nutt
parent 2bb15a49a6
commit 7d112f5639
2 changed files with 190 additions and 101 deletions

View file

@ -1,8 +1,8 @@
/****************************************************************************
* drivers/can/can.c
*
* Copyright (C) 2008-2009, 2011-2012, 2014-2015, 2017 Gregory Nutt. All
* rights reserved.
* Copyright (C) 2008-2009, 2011-2012, 2014-2015, 2017, 2019 Gregory Nutt.
* All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Copyright (C) 2016 Omni Hoverboards Inc. All rights reserved.
@ -60,6 +60,7 @@
#include <nuttx/semaphore.h>
#include <nuttx/fs/fs.h>
#include <nuttx/can/can.h>
#include <nuttx/kmalloc.h>
#ifdef CONFIG_CAN_TXREADY
# include <nuttx/wqueue.h>
@ -72,6 +73,7 @@
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifdef CONFIG_CAN_TXREADY
@ -256,16 +258,22 @@ static uint8_t can_dlc2bytes(uint8_t dlc)
{
case 9:
return 12;
case 10:
return 16;
case 11:
return 20;
case 12:
return 24;
case 13:
return 32;
case 14:
return 48;
default:
case 15:
return 64;
@ -394,6 +402,20 @@ static void can_txready_work(FAR void *arg)
}
#endif
static FAR struct can_reader_s *init_can_reader(FAR struct file *filep)
{
FAR struct can_reader_s *reader = kmm_zalloc(sizeof(struct can_reader_s));
DEBUGASSERT(reader != NULL);
reader->fifo.rx_head = 0;
reader->fifo.rx_tail = 0;
nxsem_init(&reader->fifo.rx_sem, 0, 1);
reader->filep = filep;
return reader;
}
/****************************************************************************
* Name: can_open
*
@ -448,8 +470,6 @@ static int can_open(FAR struct file *filep)
dev->cd_xmit.tx_head = 0;
dev->cd_xmit.tx_queue = 0;
dev->cd_xmit.tx_tail = 0;
dev->cd_recv.rx_head = 0;
dev->cd_recv.rx_tail = 0;
/* Finally, Enable the CAN RX interrupt */
@ -458,6 +478,8 @@ static int can_open(FAR struct file *filep)
/* Save the new open count only on success */
dev->cd_ocount = 1;
list_initialize(&dev->cd_readers);
}
leave_critical_section(flags);
@ -468,6 +490,12 @@ static int can_open(FAR struct file *filep)
dev->cd_ocount = tmp;
}
irqstate_t flags = enter_critical_section();
list_add_head(&dev->cd_readers,
(FAR struct list_node *)init_can_reader(filep));
leave_critical_section(flags);
}
can_givesem(&dev->cd_closesem);
@ -488,6 +516,8 @@ 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;
FAR struct list_node *tmp;
int ret;
caninfo("ocount: %d\n", dev->cd_ocount);
@ -498,6 +528,16 @@ static int can_close(FAR struct file *filep)
return ret;
}
list_for_every_safe(&dev->cd_readers, node, tmp)
{
if (((FAR struct can_reader_s *)node)->filep == filep)
{
list_delete(node);
kmm_free(node);
break;
}
}
/* Decrement the references to the driver. If the reference count will
* decrement to 0, then uninitialize the driver.
*/
@ -560,11 +600,14 @@ errout:
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;
size_t nread;
irqstate_t flags;
int ret = 0;
FAR struct inode *inode = filep->f_inode;
FAR struct can_dev_s *dev = inode->i_private;
FAR sstruct can_reader_s *reader = NULL;
FAR sstruct list_node *node;
FAR sstruct can_rxfifo_s *fifo;
size_t nread;
irqstate_t flags;
int ret = 0;
caninfo("buflen: %d\n", buflen);
@ -616,7 +659,20 @@ static ssize_t can_read(FAR struct file *filep, FAR char *buffer,
}
#endif /* CONFIG_CAN_ERRORS */
while (dev->cd_recv.rx_head == dev->cd_recv.rx_tail)
list_for_every(&dev->cd_readers, node)
{
if (((FAR struct can_reader_s *) node)->filep == filep)
{
reader = (FAR struct can_reader_s *)node;
break;
}
}
DEBUGASSERT(reader != NULL);
fifo = &reader->fifo;
while (fifo->rx_head == fifo->rx_tail)
{
/* The receive FIFO is empty -- was non-blocking mode selected? */
@ -630,8 +686,9 @@ static ssize_t can_read(FAR struct file *filep, FAR char *buffer,
DEBUGASSERT(dev->cd_nrxwaiters < 255);
dev->cd_nrxwaiters++;
ret = can_takesem(&dev->cd_recv.rx_sem);
ret = can_takesem(&fifo->rx_sem);
dev->cd_nrxwaiters--;
if (ret < 0)
{
goto return_with_irqdisabled;
@ -642,33 +699,33 @@ static ssize_t can_read(FAR struct file *filep, FAR char *buffer,
* in the user buffer.
*/
nread = 0;
do
{
/* Will the next message in the FIFO fit into the user buffer? */
nread = 0;
do
{
/* Will the next message in the FIFO fit into the user buffer? */
FAR struct can_msg_s *msg = &dev->cd_recv.rx_buffer[dev->cd_recv.rx_head];
int nbytes = can_dlc2bytes(msg->cm_hdr.ch_dlc);
int msglen = CAN_MSGLEN(nbytes);
FAR struct can_msg_s *msg = &fifo->rx_buffer[fifo->rx_head];
int nbytes = can_dlc2bytes(msg->cm_hdr.ch_dlc);
int msglen = CAN_MSGLEN(nbytes);
if (nread + msglen > buflen)
{
break;
}
if (nread + msglen > buflen)
{
break;
}
/* Copy the message to the user buffer */
/* Copy the message to the user buffer */
memcpy(&buffer[nread], msg, msglen);
nread += msglen;
memcpy(&buffer[nread], msg, msglen);
nread += msglen;
/* Increment the head of the circular message buffer */
/* Increment the head of the circular message buffer */
if (++dev->cd_recv.rx_head >= CONFIG_CAN_FIFOSIZE)
{
dev->cd_recv.rx_head = 0;
}
if (++fifo->rx_head >= CONFIG_CAN_FIFOSIZE)
{
fifo->rx_head = 0;
}
}
while (dev->cd_recv.rx_head != dev->cd_recv.rx_tail);
while (fifo->rx_head != fifo->rx_tail);
/* All on the messages have bee transferred. Return the number of bytes
* that were read.
@ -721,7 +778,8 @@ static int can_xmit(FAR struct can_dev_s *dev)
/* Check if we have already queued all of the data in the TX fifo.
*
* tx_tail: Incremented in can_write each time a message is queued in the FIFO
* 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.
*
@ -832,9 +890,9 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer,
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 interrupts needed
* to clear the FIFO.
/* 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.
*/
if (inactive)
@ -996,8 +1054,10 @@ static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
static int can_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup)
{
FAR struct inode *inode = (FAR struct inode *)filep->f_inode;
FAR struct can_dev_s *dev = (FAR struct can_dev_s *)inode->i_private;
FAR struct inode *inode = (FAR struct inode *)filep->f_inode;
FAR struct can_dev_s *dev = (FAR struct can_dev_s *)inode->i_private;
FAR struct can_reader_s *reader = NULL;
FAR struct list_node *node;
pollevent_t eventset;
int ndx;
int ret;
@ -1012,6 +1072,17 @@ static int can_poll(FAR struct file *filep, FAR struct pollfd *fds,
}
#endif
list_for_every(&dev->cd_readers, node)
{
if (((FAR struct can_reader_s *)node)->filep == filep)
{
reader = (FAR struct can_reader_s *)node;
break;
}
}
DEBUGASSERT(reader != NULL);
/* Get exclusive access to the poll structures */
ret = can_takesem(&dev->cd_pollsem);
@ -1096,17 +1167,17 @@ static int can_poll(FAR struct file *filep, FAR struct pollfd *fds,
dev->cd_nrxwaiters++;
do
{
ret = can_takesem(&dev->cd_recv.rx_sem);
ret = can_takesem(&reader->fifo.rx_sem);
}
while (ret < 0);
dev->cd_nrxwaiters--;
if (dev->cd_recv.rx_head != dev->cd_recv.rx_tail)
if (reader->fifo.rx_head != reader->fifo.rx_tail)
{
eventset |= fds->events & POLLIN;
}
can_givesem(&dev->cd_recv.rx_sem);
can_givesem(&reader->fifo.rx_sem);
if (eventset != 0)
{
@ -1117,7 +1188,7 @@ static int can_poll(FAR struct file *filep, FAR struct pollfd *fds,
{
/* This is a request to tear down the poll */
struct pollfd **slot = (struct pollfd **)fds->priv;
FAR struct pollfd **slot = (FAR struct pollfd **)fds->priv;
#ifdef CONFIG_DEBUG_FEATURES
if (slot == NULL)
@ -1168,7 +1239,6 @@ int can_register(FAR const char *path, FAR struct can_dev_s *dev)
/* Initialize semaphores */
nxsem_init(&dev->cd_xmit.tx_sem, 0, 1);
nxsem_init(&dev->cd_recv.rx_sem, 0, 1);
nxsem_init(&dev->cd_closesem, 0, 1);
#ifndef CONFIG_DISABLE_POLL
nxsem_init(&dev->cd_pollsem, 0, 1);
@ -1217,8 +1287,10 @@ int can_register(FAR const char *path, FAR struct can_dev_s *dev)
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 struct can_rxfifo_s *fifo;
FAR uint8_t *dest;
FAR struct list_node *node;
FAR struct list_node *node;
int nexttail;
int errcode = -ENOMEM;
int i;
@ -1229,12 +1301,6 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
* enqueue read data.
*/
nexttail = fifo->rx_tail + 1;
if (nexttail >= CONFIG_CAN_FIFOSIZE)
{
nexttail = 0;
}
/* First, check if this response matches any RTR response that we may be
* waiting for.
*/
@ -1280,60 +1346,72 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
}
}
/* Refuse the new data if the FIFO is full */
if (nexttail != fifo->rx_head)
list_for_every_safe(&dev->cd_readers, node, tmp)
{
int nbytes;
FAR struct can_reader_s *reader = (FAR struct can_reader_s *)node;
fifo = &reader->fifo;
/* Add the new, decoded CAN message at the tail of the FIFO.
*
* REVISIT: In the CAN FD format, the coding of the DLC differs from
* the standard CAN format. The DLC codes 0 to 8 have the same coding
* as in standard CAN, the codes 9 to 15, which in standard CAN all
* code a data field of 8 bytes, are encoded:
*
* 9->12, 10->16, 11->20, 12->24, 13->32, 14->48, 15->64
*/
memcpy(&fifo->rx_buffer[fifo->rx_tail].cm_hdr, hdr, sizeof(struct can_hdr_s));
nbytes = can_dlc2bytes(hdr->ch_dlc);
for (i = 0, dest = fifo->rx_buffer[fifo->rx_tail].cm_data; i < nbytes; i++)
nexttail = fifo->rx_tail + 1;
if (nexttail >= CONFIG_CAN_FIFOSIZE)
{
*dest++ = *data++;
nexttail = 0;
}
/* Increment the tail of the circular buffer */
/* Refuse the new data if the FIFO is full */
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.
*/
if (dev->cd_nrxwaiters > 0)
if (nexttail != fifo->rx_head)
{
can_givesem(&fifo->rx_sem);
int nbytes;
int sval;
/* Add the new, decoded CAN message at the tail of the FIFO.
*
* REVISIT: In the CAN FD format, the coding of the DLC differs
* from the standard CAN format. The DLC codes 0 to 8 have the
* same coding as in standard CAN, the codes 9 to 15, which in
* standard CAN all code a data field of 8 bytes, are encoded:
*
* 9->12, 10->16, 11->20, 12->24, 13->32, 14->48, 15->64
*/
memcpy(&fifo->rx_buffer[fifo->rx_tail].cm_hdr, hdr,
sizeof(struct can_hdr_s));
nbytes = can_dlc2bytes(hdr->ch_dlc);
memcpy(fifo->rx_buffer[fifo->rx_tail].cm_data, data, nbytes);
/* Increment the tail of the circular buffer */
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.
*/
sval = 0;
if (nxsem_getvalue(&fifo->rx_sem, &sval) <= 0)
{
can_givesem(&fifo->rx_sem);
}
errcode = OK;
/* Notify all poll/select waiters that they can read from the
* cd_recv buffer
*/
can_pollnotify(dev, POLLIN);
}
errcode = OK;
/* Notify all poll/select waiters that they can read from the
* cd_recv buffer
*/
can_pollnotify(dev, POLLIN);
}
#ifdef CONFIG_CAN_ERRORS
else
{
/* Report rx overflow error */
else
{
/* Report rx overflow error */
dev->cd_error |= CAN_ERROR5_RXOVERFLOW;
}
dev->cd_error |= CAN_ERROR5_RXOVERFLOW;
}
#endif
}
return errcode;
}
@ -1556,15 +1634,11 @@ 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.
*
* REVISIT: Assertion can fire in certain race conditions, i.e, when
* all waiters have been awakened but have not yet had a chance to
* decrement cd_ntxwaiters.
* FIFO 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.
*/
//DEBUGASSERT(dev->cd_ntxwaiters == 0);
#if 0 /* REVISIT */
/* When the H/W FIFO has been emptied, we can disable further TX
* interrupts.

View file

@ -1,7 +1,8 @@
/************************************************************************************
* include/nuttx/can/can.h
*
* Copyright (C) 2008, 2009, 2011-2012, 2015-2017 Gregory Nutt. All rights reserved.
* Copyright (C) 2008, 2009, 2011-2012, 2015-2017, 2019 Gregory Nutt. All rights
* reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -48,6 +49,7 @@
#include <stdbool.h>
#include <semaphore.h>
#include <nuttx/list.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
@ -62,6 +64,7 @@
************************************************************************************/
/* Configuration ********************************************************************/
/* CONFIG_CAN - Enables CAN support (MCU-specific selections are also required. For
* STM32, as an example, one or both of CONFIG_STM32_CAN1 or CONFIG_STM32_CAN2
* must also be defined)
@ -106,6 +109,7 @@
#endif
/* Ioctl Commands *******************************************************************/
/* Ioctl commands supported by the upper half CAN driver.
*
* CANIOC_RTR:
@ -360,6 +364,7 @@
#endif /* CONFIG_CAN_ERRORS */
/* CAN filter support ***************************************************************/
/* Some CAN hardware supports a notion of prioritizing messages that match filters.
* Only two priority levels are currently supported and are encoded as defined
* below:
@ -377,6 +382,7 @@
/************************************************************************************
* Public Types
************************************************************************************/
/* CAN-message Format (without Extended ID support)
*
* One based CAN-message is represented with a maximum of 10 bytes. A message is
@ -431,6 +437,7 @@ begin_packed_struct struct can_hdr_s
uint8_t ch_extid : 1; /* Extended ID indication */
uint8_t ch_unused : 1; /* Unused */
} end_packed_struct;
#else
begin_packed_struct struct can_hdr_s
{
@ -550,12 +557,21 @@ struct can_ops_s
* The common logic will initialize all semaphores.
*/
struct can_reader_s
{
struct list_node list;
sem_t read_sem;
FAR struct file *filep;
struct can_rxfifo_s fifo; /* Describes receive FIFO */
};
struct can_dev_s
{
uint8_t cd_ocount; /* The number of times the device has been opened */
uint8_t cd_npendrtr; /* Number of pending RTR messages */
volatile uint8_t cd_ntxwaiters; /* Number of threads waiting to enqueue a message */
volatile uint8_t cd_nrxwaiters; /* Number of threads waiting to receive a message */
struct list_node cd_readers;
#ifdef CONFIG_CAN_ERRORS
uint8_t cd_error; /* Flags to indicate internal device errors */
#endif
@ -564,7 +580,6 @@ struct can_dev_s
sem_t cd_pollsem; /* Manages exclusive access to cd_fds[] */
#endif
struct can_txfifo_s cd_xmit; /* Describes transmit FIFO */
struct can_rxfifo_s cd_recv; /* Describes receive FIFO */
#ifdef CONFIG_CAN_TXREADY
struct work_s cd_work; /* Use to manage can_txready() work */
#endif