int can_txdone(FAR struct can_dev_s *dev) { int ret = -ENOENT; canllvdbg("xmit head: %d queue: %d tail: %d\n", dev->cd_xmit.tx_head, dev->cd_xmit.tx_queue, dev->cd_xmit.tx_tail); /* Verify that the xmit FIFO is not empty */ if (dev->cd_xmit.tx_head != dev->cd_xmit.tx_tail) { DEBUGASSERT(dev->cd_xmit.tx_head != dev->cd_xmit.tx_queue); /* Remove the message at the head of the xmit FIFO */ if (++dev->cd_xmit.tx_head >= CONFIG_CAN_FIFOSIZE) { dev->cd_xmit.tx_head = 0; } /* Send the next message in the FIFO */ ret = can_xmit(dev); /* Are there any threads waiting for space in the TX FIFO? */ if (ret == OK && dev->cd_ntxwaiters > 0) { /* Yes.. Inform them that new xmit space is available */ ret = sem_post(&dev->cd_xmit.tx_sem); } } return ret; }
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; FAR struct can_txfifo_s *fifo = &dev->cd_xmit; FAR struct can_msg_s *msg; bool inactive; ssize_t nsent = 0; irqstate_t flags; int nexttail; int msglen; int ret = 0; canvdbg("buflen: %d\n", buflen); /* Interrupts must disabled throughout the following */ 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. */ inactive = dev_txempty(dev); /* Add the messages to the FIFO. Ignore any trailing messages that are * shorter than the minimum. */ while ((buflen - nsent) >= CAN_MSGLEN(0)) { /* Check if adding this new message would over-run the drivers ability * to enqueue xmit data. */ nexttail = fifo->tx_tail + 1; if (nexttail >= CONFIG_CAN_FIFOSIZE) { nexttail = 0; } /* If the XMIT FIFO becomes full, then wait for space to become available */ while (nexttail == fifo->tx_head) { /* The transmit FIFO is full -- was non-blocking mode selected? */ if (filep->f_oflags & O_NONBLOCK) { if (nsent == 0) { ret = -EAGAIN; } else { 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 interrupts needed * to clear the FIFO. */ if (inactive) { can_xmit(dev); } /* Wait for a message to be sent */ do { 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; } } while (ret < 0); /* Re-check the FIFO state */ inactive = dev_txempty(dev); } /* We get here if there is space at the end of the FIFO. Add the new * CAN message at the tail of the FIFO. */ msg = (FAR struct can_msg_s *)&buffer[nsent]; msglen = CAN_MSGLEN(msg->cm_hdr.ch_dlc); memcpy(&fifo->tx_buffer[fifo->tx_tail], msg, msglen); /* Increment the tail of the circular buffer */ fifo->tx_tail = nexttail; /* Increment the number of bytes that were sent */ nsent += msglen; } /* We get here after all messages have been added to the FIFO. Check if * we need to kick of the XMIT sequence. */ if (inactive) { can_xmit(dev); } /* Return the number of bytes that were sent */ ret = nsent; return_with_irqdisabled: irqrestore(flags); return ret; }