can: fix RTR ioctl and support timeout

This commit is contained in:
Matthew Trescott 2021-01-02 23:26:40 -05:00 committed by Xiang Xiao
parent fe96250c40
commit 301ba3a5cd
2 changed files with 135 additions and 33 deletions

View File

@ -49,6 +49,7 @@
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <assert.h>
#include <poll.h>
@ -56,6 +57,7 @@
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/clock.h>
#include <nuttx/signal.h>
#include <nuttx/fs/fs.h>
#include <nuttx/can/can.h>
@ -141,7 +143,7 @@ static ssize_t can_read(FAR struct file *filep, FAR char *buffer,
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,
static inline ssize_t can_rtrread(FAR struct file *filep,
FAR struct canioc_rtr_s *rtr);
static int can_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
@ -916,12 +918,15 @@ return_with_irqdisabled:
*
****************************************************************************/
static inline ssize_t can_rtrread(FAR struct can_dev_s *dev,
FAR struct canioc_rtr_s *rtr)
static inline ssize_t can_rtrread(FAR struct file *filep,
FAR struct canioc_rtr_s *request)
{
FAR struct can_dev_s *dev = filep->f_inode->i_private;
FAR struct can_rtrwait_s *wait = NULL;
struct timespec abstimeout;
irqstate_t flags;
int i;
int sval;
int ret = -ENOMEM;
/* Disable interrupts through this operation */
@ -933,26 +938,79 @@ static inline ssize_t can_rtrread(FAR struct can_dev_s *dev,
for (i = 0; i < CONFIG_CAN_NPENDINGRTR; i++)
{
FAR struct can_rtrwait_s *tmp = &dev->cd_rtr[i];
if (!rtr->ci_msg)
ret = nxsem_get_value(&tmp->cr_sem, &sval);
if (ret < 0)
{
tmp->cr_id = rtr->ci_id;
tmp->cr_msg = rtr->ci_msg;
continue;
}
if (sval == 0)
{
/* No one is waiting on RTR transaction; take it. */
tmp->cr_msg = request->ci_msg;
dev->cd_npendrtr++;
wait = tmp;
wait = tmp;
break;
}
}
if (wait)
{
/* Send the remote transmission request */
/* Send the remote transmission request with the "old method" unless
* the lower-half driver indicates otherwise.
*/
if (dev->cd_ops->co_remoterequest != NULL)
{
if (request->ci_msg->cm_hdr.ch_id < CAN_MAX_STDMSGID
#ifdef CONFIG_CAN_EXTID
&& !request->ci_msg->cm_hdr.ch_extid
#endif
)
{
ret = dev_remoterequest(dev,
(uint16_t)(request->ci_msg->cm_hdr.ch_id));
}
else
{
ret = -EINVAL;
}
}
else
{
#ifdef CONFIG_CAN_USE_RTR
/* Temporarily set the RTR bit, then send the remote transmission
* request message with the lower-half driver's regular function.
*/
request->ci_msg->cm_hdr.ch_rtr = 1;
ret = can_write(filep,
request->ci_msg,
CAN_MSGLEN(request->ci_msg->cm_hdr.ch_dlc));
request->ci_msg->cm_hdr.ch_rtr = 0;
#else
canerr("Error: Driver needs CONFIG_CAN_USE_RTR.\n");
ret = -ENOSYS;
#endif
}
ret = dev_remoterequest(dev, wait->cr_id);
if (ret >= 0)
{
/* Then wait for the response */
ret = can_takesem(&wait->cr_sem);
ret = clock_gettime(CLOCK_REALTIME, &abstimeout);
if (ret >= 0)
{
clock_timespec_add(&abstimeout,
&request->ci_timeout,
&abstimeout);
ret = nxsem_timedwait(&wait->cr_sem, &abstimeout);
}
}
}
@ -984,7 +1042,8 @@ static int can_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
*/
case CANIOC_RTR:
ret = can_rtrread(dev, (FAR struct canioc_rtr_s *)((uintptr_t)arg));
ret = can_rtrread(filep,
(FAR struct canioc_rtr_s *)((uintptr_t)arg));
break;
/* Not a "built-in" ioctl command.. perhaps it is unique to this
@ -1195,7 +1254,6 @@ int can_register(FAR const char *path, FAR struct can_dev_s *dev)
nxsem_init(&dev->cd_rtr[i].cr_sem, 0, 0);
nxsem_set_protocol(&dev->cd_rtr[i].cr_sem, SEM_PRIO_NONE);
dev->cd_rtr[i].cr_msg = NULL;
}
/* Initialize/reset the CAN hardware */
@ -1237,6 +1295,8 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
int nexttail;
int errcode = -ENOMEM;
int i;
int sval;
int ret;
caninfo("ID: %" PRId32 " DLC: %d\n", (uint32_t)hdr->ch_id, hdr->ch_dlc);
@ -1256,35 +1316,44 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
for (i = 0; i < CONFIG_CAN_NPENDINGRTR; i++)
{
FAR struct can_rtrwait_s *rtr = &dev->cd_rtr[i];
FAR struct can_msg_s *msg = rtr->cr_msg;
FAR struct can_rtrwait_s *wait = &dev->cd_rtr[i];
FAR struct can_msg_s *waitmsg = wait->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 in use and whether the ID matches */
if (msg && hdr->ch_id == rtr->cr_id)
ret = nxsem_get_value(&wait->cr_sem, &sval);
if (ret < 0)
{
continue;
}
else if (sval < 0
#ifdef CONFIG_CAN_ERRORS
&& hdr->ch_error == false
#endif
#ifdef CONFIG_CAN_EXTID
&& waitmsg->cm_hdr.ch_extid == hdr->ch_extid
#endif
&& waitmsg->cm_hdr.ch_id == hdr->ch_id)
{
int nbytes;
/* We have the response... copy the data to the user's buffer */
memcpy(&msg->cm_hdr, hdr, sizeof(struct can_hdr_s));
memcpy(&waitmsg->cm_hdr, hdr, sizeof(struct can_hdr_s));
nbytes = can_dlc2bytes(hdr->ch_dlc);
for (i = 0, dest = msg->cm_data; i < nbytes; i++)
for (i = 0, dest = waitmsg->cm_data; i < nbytes; i++)
{
*dest++ = *data++;
}
/* Mark the entry unused */
rtr->cr_msg = NULL;
dev->cd_npendrtr--;
/* And restart the waiting thread */
/* Restart the waiting thread and mark the entry unused */
can_givesem(&rtr->cr_sem);
can_givesem(&wait->cr_sem);
}
}
}
@ -1305,7 +1374,6 @@ int can_receive(FAR struct can_dev_s *dev, FAR struct can_hdr_s *hdr,
if (nexttail != fifo->rx_head)
{
int nbytes;
int sval;
/* Add the new, decoded CAN message at the tail of the FIFO.
*

View File

@ -47,6 +47,7 @@
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <nuttx/list.h>
#include <nuttx/fs/fs.h>
@ -113,8 +114,23 @@
/* Ioctl commands supported by the upper half CAN driver.
*
* CANIOC_RTR:
* Description: Send the remote transmission request and wait for the response.
* Argument: A reference to struct canioc_rtr_s
* Description: Send the given message as a remote request. On sucessful
* return, the passed message structure is updated with
* the contents of the received message; i.e. the message
* ID and the standard/extended ID indication bit stay the
* same, but the DLC and data bits are updated with the
* contents of the received message. If no response is
* received after the specified timeout, ioctl will return.
*
* Note: Lower-half drivers that do not implement
* CONFIG_CAN_USE_RTR and implement co_remoterequest
* will result in EINVAL if this ioctl is called
* with an extended-ID message.
*
* Argument: A pointer to struct canioc_rtr_s
* Returned Value: Zero (OK) is returned on success. Otherwise, -1 (ERROR)
* is returned with the errno variable set to indicate the
* nature of the error (for example, ETIMEDOUT)
*
* Ioctl commands that may or may not be supported by the lower half CAN driver.
*
@ -502,8 +518,7 @@ struct can_txfifo_s
struct can_rtrwait_s
{
sem_t cr_sem; /* Wait for RTR response */
uint16_t cr_id; /* The ID that is waited for */
sem_t cr_sem; /* Wait for response/is the cd_rtr entry available */
FAR struct can_msg_s *cr_msg; /* This is where the RTR response goes */
};
@ -547,7 +562,13 @@ struct can_ops_s
CODE int (*co_ioctl)(FAR struct can_dev_s *dev, int cmd, unsigned long arg);
/* Send a remote request */
/* Send a remote request. Lower-half drivers should NOT implement this if
* they support sending RTR messages with the regular send function
* (i.e. CONFIG_CAN_USE_RTR). Instead, they should mention CAN_USE_RTR
* in their Kconfig help and set this to NULL to indicate that the normal
* send function should be used instead. Lower-half drivers must implement
* either this or CONFIG_CAN_USE_RTR to support CANIOC_RTR.
*/
CODE int (*co_remoterequest)(FAR struct can_dev_s *dev, uint16_t id);
@ -611,8 +632,21 @@ struct can_dev_s
struct canioc_rtr_s
{
uint16_t ci_id; /* The 11-bit ID to use in the RTR message */
FAR struct can_msg_s *ci_msg; /* The location to return the RTR response */
/* How long to wait for the response */
struct timespec ci_timeout;
/* The location to return the RTR response. The arbitration fields
* (i.e. message ID and extended ID indication, if applicable) should be
* set to the values the driver will watch for. On return from the ioctl,
* the DLC and data fields will be updated by the received message.
*
* The block of memory must be large enough to hold an message of size
* CAN_MSGLEN(CAN_MAXDATALEN) even if a smaller DLC is requested, since
* the response DLC may not match the requested one.
*/
FAR struct can_msg_s *ci_msg;
};
/* CANIOC_GET_BITTIMING/CANIOC_SET_BITTIMING: