diff --git a/drivers/can/can.c b/drivers/can/can.c index a0d6d3ad96..4be8099fe7 100644 --- a/drivers/can/can.c +++ b/drivers/can/can.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,7 @@ #include #include +#include #include #include #include @@ -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. * diff --git a/include/nuttx/can/can.h b/include/nuttx/can/can.h index 09a685e1fa..b0d9ee9bcc 100644 --- a/include/nuttx/can/can.h +++ b/include/nuttx/can/can.h @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -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: