can: Add support for poll

This commit is contained in:
Paul A. Patience 2016-06-21 07:45:47 -04:00
parent 205a4b8498
commit 2f76f6e848
3 changed files with 381 additions and 140 deletions

View File

@ -208,6 +208,14 @@ config CAN_LOOPBACK
driver does support loopback mode, the setting will enable it. (If the
driver does not, this setting will have no effect).
config CAN_NPOLLWAITERS
int "Number of poll waiters"
default 2
depends on !DISABLE_POLL
---help---
The maximum number of threads that may be waiting on the
poll method.
endif # CAN
config ARCH_HAVE_PWM_PULSECOUNT

View File

@ -4,6 +4,9 @@
* Copyright (C) 2008-2009, 2011-2012, 2014-2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Copyright (C) 2016 Omni Hoverboards Inc. All rights reserved.
* Author: Paul Alexander Patience <paul-a.patience@polymtl.ca>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -47,6 +50,7 @@
#include <semaphore.h>
#include <fcntl.h>
#include <assert.h>
#include <poll.h>
#include <errno.h>
#include <debug.h>
@ -105,6 +109,17 @@
* Private Function Prototypes
****************************************************************************/
/* Semaphore helpers */
static int can_takesem(FAR sem_t *sem);
/* Poll helpers */
#ifndef CONFIG_DISABLE_POLL
static void can_pollnotify(FAR struct can_dev_s *dev,
pollevent_t eventset);
#endif
/* CAN helpers */
static uint8_t can_dlc2bytes(uint8_t dlc);
@ -120,14 +135,18 @@ static void can_txready_work(FAR void *arg);
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);
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);
FAR const char *buffer, size_t buflen);
static inline ssize_t can_rtrread(FAR struct can_dev_s *dev,
FAR struct canioc_rtr_s *rtr);
FAR struct canioc_rtr_s *rtr);
static int can_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
unsigned long arg);
#ifndef CONFIG_DISABLE_POLL
static int can_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
#endif
/****************************************************************************
* Private Data
@ -139,13 +158,13 @@ static const struct file_operations g_canops =
can_close, /* close */
can_read, /* read */
can_write, /* write */
0, /* seek */
NULL, /* seek */
can_ioctl /* ioctl */
#ifndef CONFIG_DISABLE_POLL
, 0 /* poll */
, can_poll /* poll */
#endif
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
, 0 /* unlink */
, NULL /* unlink */
#endif
};
@ -153,6 +172,64 @@ static const struct file_operations g_canops =
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: can_takesem
****************************************************************************/
static int can_takesem(FAR sem_t *sem)
{
int errcode;
/* Take a count from the semaphore, possibly waiting */
if (sem_wait(sem) < 0)
{
/* The only case that an error should occur here is if the wait
* was awakened by a signal
*/
errcode = get_errno();
DEBUGASSERT(errcode == EINTR);
return -errcode;
}
return OK;
}
/****************************************************************************
* Name: can_givesem
****************************************************************************/
#define can_givesem(sem) sem_post(sem)
/****************************************************************************
* Name: can_pollnotify
****************************************************************************/
#ifndef CONFIG_DISABLE_POLL
static void can_pollnotify(FAR struct can_dev_s *dev, pollevent_t eventset)
{
FAR struct pollfd *fds;
int i;
for (i = 0; i < CONFIG_CAN_NPOLLWAITERS; i++)
{
fds = dev->cd_fds[i];
if (fds != NULL)
{
fds->revents |= fds->events & eventset;
if (fds->revents != 0)
{
caninfo("Report events: %02x\n", fds->revents);
sem_post(fds->sem);
}
}
}
}
#else
# define can_pollnotify(dev, eventset)
#endif
/****************************************************************************
* Name: can_dlc2bytes
*
@ -309,7 +386,7 @@ static void can_txready_work(FAR void *arg)
{
/* Yes.. Inform them that new xmit space is available */
(void)sem_post(&dev->cd_xmit.tx_sem);
can_givesem(&dev->cd_xmit.tx_sem);
}
}
}
@ -331,72 +408,70 @@ static int can_open(FAR struct file *filep)
FAR struct inode *inode = filep->f_inode;
FAR struct can_dev_s *dev = inode->i_private;
uint8_t tmp;
int ret = OK;
int ret;
caninfo("ocount: %d\n", dev->cd_ocount);
/* If the port is the middle of closing, wait until the close is finished */
if (sem_wait(&dev->cd_closesem) != OK)
ret = can_takesem(&dev->cd_closesem);
if (ret < 0)
{
ret = -get_errno();
return ret;
}
/* Increment the count of references to the device. If this is the first
* time that the driver has been opened for this device, then initialize
* the device.
*/
tmp = dev->cd_ocount + 1;
if (tmp == 0)
{
/* More than 255 opens; uint8_t overflows to zero */
ret = -EMFILE;
}
else
{
/* Increment the count of references to the device. If this is the first
* time that the driver has been opened for this device, then initialize
* the device.
*/
/* Check if this is the first time that the driver has been opened. */
tmp = dev->cd_ocount + 1;
if (tmp == 0)
if (tmp == 1)
{
/* More than 255 opens; uint8_t overflows to zero */
/* Yes.. perform one time hardware initialization. */
ret = -EMFILE;
irqstate_t flags = enter_critical_section();
ret = dev_setup(dev);
if (ret >= 0)
{
/* Mark the FIFOs empty */
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 */
dev_rxint(dev, true);
/* Save the new open count only on success */
dev->cd_ocount = 1;
}
leave_critical_section(flags);
}
else
{
/* Check if this is the first time that the driver has been opened. */
/* Save the incremented open count */
if (tmp == 1)
{
/* Yes.. perform one time hardware initialization. */
irqstate_t flags = enter_critical_section();
ret = dev_setup(dev);
if (ret == OK)
{
/* Mark the FIFOs empty */
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 */
dev_rxint(dev, true);
/* Save the new open count only on success */
dev->cd_ocount = 1;
}
leave_critical_section(flags);
}
else
{
/* Save the incremented open count */
dev->cd_ocount = tmp;
}
dev->cd_ocount = tmp;
}
sem_post(&dev->cd_closesem);
}
can_givesem(&dev->cd_closesem);
return ret;
}
@ -414,67 +489,64 @@ 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;
int ret = OK;
int ret;
caninfo("ocount: %d\n", dev->cd_ocount);
if (sem_wait(&dev->cd_closesem) != OK)
ret = can_takesem(&dev->cd_closesem);
if (ret < 0)
{
ret = -get_errno();
}
else
{
/* Decrement the references to the driver. If the reference count will
* decrement to 0, then uninitialize the driver.
*/
if (dev->cd_ocount > 1)
{
dev->cd_ocount--;
sem_post(&dev->cd_closesem);
}
else
{
/* There are no more references to the port */
dev->cd_ocount = 0;
/* Stop accepting input */
dev_rxint(dev, false);
/* Now we wait for the transmit FIFO to clear */
while (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail)
{
#ifndef CONFIG_DISABLE_SIGNALS
usleep(HALF_SECOND_USEC);
#else
up_mdelay(HALF_SECOND_MSEC);
#endif
}
/* And wait for the TX hardware FIFO to drain */
while (!dev_txempty(dev))
{
#ifndef CONFIG_DISABLE_SIGNALS
usleep(HALF_SECOND_USEC);
#else
up_mdelay(HALF_SECOND_MSEC);
#endif
}
/* Free the IRQ and disable the CAN device */
flags = enter_critical_section(); /* Disable interrupts */
dev_shutdown(dev); /* Disable the CAN */
leave_critical_section(flags);
sem_post(&dev->cd_closesem);
}
return ret;
}
/* Decrement the references to the driver. If the reference count will
* decrement to 0, then uninitialize the driver.
*/
if (dev->cd_ocount > 1)
{
dev->cd_ocount--;
goto errout;
}
/* There are no more references to the port */
dev->cd_ocount = 0;
/* Stop accepting input */
dev_rxint(dev, false);
/* Now we wait for the transmit FIFO to clear */
while (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail)
{
#ifndef CONFIG_DISABLE_SIGNALS
usleep(HALF_SECOND_USEC);
#else
up_mdelay(HALF_SECOND_MSEC);
#endif
}
/* And wait for the TX hardware FIFO to drain */
while (!dev_txempty(dev))
{
#ifndef CONFIG_DISABLE_SIGNALS
usleep(HALF_SECOND_USEC);
#else
up_mdelay(HALF_SECOND_MSEC);
#endif
}
/* Free the IRQ and disable the CAN device */
flags = enter_critical_section(); /* Disable interrupts */
dev_shutdown(dev); /* Disable the CAN */
leave_critical_section(flags);
errout:
can_givesem(&dev->cd_closesem);
return ret;
}
@ -557,18 +629,12 @@ static ssize_t can_read(FAR struct file *filep, FAR char *buffer,
/* Wait for a message to be received */
DEBUGASSERT(dev->cd_nrxwaiters < 255);
dev->cd_nrxwaiters++;
do
{
ret = sem_wait(&dev->cd_recv.rx_sem);
}
while (ret >= 0 && dev->cd_recv.rx_head == dev->cd_recv.rx_tail);
ret = can_takesem(&dev->cd_recv.rx_sem);
dev->cd_nrxwaiters--;
if (ret < 0)
{
ret = -get_errno();
goto return_with_irqdisabled;
}
}
@ -685,7 +751,7 @@ static int can_xmit(FAR struct can_dev_s *dev)
/* Send the next message at the FIFO queue index */
ret = dev_send(dev, &dev->cd_xmit.tx_buffer[tmpndx]);
if (ret != OK)
if (ret < 0)
{
canerr("dev_send failed: %d\n", ret);
break;
@ -779,20 +845,14 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer,
/* Wait for a message to be sent */
do
DEBUGASSERT(dev->cd_ntxwaiters < 255);
dev->cd_ntxwaiters++;
ret = can_takesem(&fifo->tx_sem);
dev->cd_ntxwaiters--;
if (ret < 0)
{
DEBUGASSERT(dev->cd_ntxwaiters < 255);
dev->cd_ntxwaiters++;
ret = sem_wait(&fifo->tx_sem);
dev->cd_ntxwaiters--;
if (ret < 0 && get_errno() != EINTR)
{
ret = -get_errno();
goto return_with_irqdisabled;
}
goto return_with_irqdisabled;
}
while (ret < 0);
/* Re-check the FIFO state */
@ -818,7 +878,7 @@ static ssize_t can_write(FAR struct file *filep, FAR const char *buffer,
}
/* We get here after all messages have been added to the FIFO. Check if
* we need to kick of the XMIT sequence.
* we need to kick off the XMIT sequence.
*/
if (inactive)
@ -878,11 +938,11 @@ static inline ssize_t can_rtrread(FAR struct can_dev_s *dev,
/* Send the remote transmission request */
ret = dev_remoterequest(dev, wait->cr_id);
if (ret == OK)
if (ret >= 0)
{
/* Then wait for the response */
ret = sem_wait(&wait->cr_sem);
ret = can_takesem(&wait->cr_sem);
}
}
@ -929,6 +989,157 @@ static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
return ret;
}
/****************************************************************************
* Name: can_poll
****************************************************************************/
#ifndef CONFIG_DISABLE_POLL
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;
pollevent_t eventset;
int ndx;
int ret;
int i;
/* Some sanity checking */
#ifdef CONFIG_DEBUG_FEATURES
if (dev == NULL || fds == NULL)
{
return -ENODEV;
}
#endif
/* Get exclusive access to the poll structures */
ret = can_takesem(&dev->cd_pollsem);
if (ret < 0)
{
/* A signal received while waiting for access to the poll data
* will abort the operation
*/
return ret;
}
/* Are we setting up the poll? Or tearing it down? */
if (setup)
{
/* This is a request to set up the poll. Find an available
* slot for the poll structure reference.
*/
for (i = 0; i < CONFIG_CAN_NPOLLWAITERS; i++)
{
/* Find an available slot */
if (dev->cd_fds[i] == NULL)
{
/* Bind the poll structure and this slot */
dev->cd_fds[i] = fds;
fds->priv = &dev->cd_fds[i];
break;
}
}
if (i >= CONFIG_CAN_NPOLLWAITERS)
{
fds->priv = NULL;
ret = -EBUSY;
goto errout;
}
/* Should we immediately notify on any of the requested events?
* First, check if the xmit buffer is full.
*
* Get exclusive access to the cd_xmit buffer indices. NOTE: that
* we do not let this wait be interrupted by a signal (we probably
* should, but that would be a little awkward).
*/
eventset = 0;
DEBUGASSERT(dev->cd_ntxwaiters < 255);
dev->cd_ntxwaiters++;
do
{
ret = can_takesem(&dev->cd_xmit.tx_sem);
}
while (ret < 0);
dev->cd_ntxwaiters--;
ndx = dev->cd_xmit.tx_head + 1;
if (ndx >= CONFIG_CAN_FIFOSIZE)
{
ndx = 0;
}
if (ndx != dev->cd_xmit.tx_tail)
{
eventset |= fds->events & POLLOUT;
}
can_givesem(&dev->cd_xmit.tx_sem);
/* Check if the receive buffer is empty.
*
* Get exclusive access to the cd_recv buffer indices. NOTE: that
* we do not let this wait be interrupted by a signal (we probably
* should, but that would be a little awkward).
*/
DEBUGASSERT(dev->cd_nrxwaiters < 255);
dev->cd_nrxwaiters++;
do
{
ret = can_takesem(&dev->cd_recv.rx_sem);
}
while (ret < 0);
dev->cd_nrxwaiters--;
if (dev->cd_recv.rx_head != dev->cd_recv.rx_tail)
{
eventset |= fds->events & POLLIN;
}
can_givesem(&dev->cd_recv.rx_sem);
if (eventset != 0)
{
can_pollnotify(dev, eventset);
}
}
else if (fds->priv != NULL)
{
/* This is a request to tear down the poll */
struct pollfd **slot = (struct pollfd **)fds->priv;
#ifdef CONFIG_DEBUG_FEATURES
if (slot == NULL)
{
ret = -EIO;
goto errout;
}
#endif
/* Remove all memory of the poll setup */
*slot = NULL;
fds->priv = NULL;
}
errout:
can_givesem(&dev->cd_pollsem);
return ret;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -955,9 +1166,12 @@ int can_register(FAR const char *path, FAR struct can_dev_s *dev)
dev->cd_error = 0;
#endif
sem_init(&dev->cd_xmit.tx_sem, 0, 0);
sem_init(&dev->cd_recv.rx_sem, 0, 0);
sem_init(&dev->cd_closesem, 0, 1);
sem_init(&dev->cd_xmit.tx_sem, 0, 1);
sem_init(&dev->cd_recv.rx_sem, 0, 1);
sem_init(&dev->cd_closesem, 0, 1);
#ifndef CONFIG_DISABLE_POLL
sem_init(&dev->cd_pollsem, 0, 1);
#endif
for (i = 0; i < CONFIG_CAN_NPENDINGRTR; i++)
{
@ -1055,7 +1269,7 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
/* And restart the waiting thread */
sem_post(&rtr->cr_sem);
can_givesem(&rtr->cr_sem);
}
}
}
@ -1095,10 +1309,16 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
if (dev->cd_nrxwaiters > 0)
{
sem_post(&fifo->rx_sem);
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);
}
#ifdef CONFIG_CAN_ERRORS
else
@ -1220,7 +1440,7 @@ int can_txdone(FAR struct can_dev_s *dev)
{
/* Yes.. Inform them that new xmit space is available */
ret = sem_post(&dev->cd_xmit.tx_sem);
ret = can_givesem(&dev->cd_xmit.tx_sem);
}
else
{
@ -1228,6 +1448,12 @@ int can_txdone(FAR struct can_dev_s *dev)
}
}
/* Notify all poll/select waiters that they can write to the cd_xmit
* buffer
*/
can_pollnotify(dev, POLLOUT);
return ret;
}

View File

@ -504,6 +504,9 @@ struct can_dev_s
uint8_t cd_error; /* Flags to indicate internal device errors */
#endif
sem_t cd_closesem; /* Locks out new opens while close is in progress */
#ifndef CONFIG_DISABLE_POLL
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
@ -513,6 +516,10 @@ struct can_dev_s
struct can_rtrwait_s cd_rtr[CONFIG_CAN_NPENDINGRTR];
FAR const struct can_ops_s *cd_ops; /* Arch-specific operations */
FAR void *cd_priv; /* Used by the arch-specific logic */
#ifndef CONFIG_DISABLE_POLL
FAR struct pollfd *cd_fds[CONFIG_CAN_NPOLLWAITERS];
#endif
};
/* Structures used with ioctl calls */