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; canvdbg("buflen: %d\n", buflen); /* The caller must provide enough memory to catch the smallest possible * message. This is not a system error condition, but we won't permit * it, Hence we return 0. */ if (buflen >= CAN_MSGLEN(0)) { /* Interrupts must be disabled while accessing the cd_recv FIFO */ flags = irqsave(); while (dev->cd_recv.rx_head == dev->cd_recv.rx_tail) { /* The receive FIFO is empty -- was non-blocking mode selected? */ if (filep->f_oflags & O_NONBLOCK) { ret = -EAGAIN; goto return_with_irqdisabled; } /* Wait for a message to be received */ 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); dev->cd_nrxwaiters--; if (ret < 0) { ret = -get_errno(); goto return_with_irqdisabled; } } /* The cd_recv FIFO is not empty. Copy all buffered data that will fit * in 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 msglen = CAN_MSGLEN(msg->cm_hdr.ch_dlc); if (nread + msglen > buflen) { break; } /* Copy the message to the user buffer */ memcpy(&buffer[nread], msg, msglen); nread += msglen; /* Increment the head of the circular message buffer */ if (++dev->cd_recv.rx_head >= CONFIG_CAN_FIFOSIZE) { dev->cd_recv.rx_head = 0; } } while (dev->cd_recv.rx_head != dev->cd_recv.rx_tail); /* All on the messages have bee transferred. Return the number of bytes * that were read. */ ret = nread; return_with_irqdisabled: irqrestore(flags); } 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; }
int can_main(int argc, FAR char *argv[]) #endif { struct canioc_bittiming_s bt; #ifdef CONFIG_EXAMPLES_CAN_WRITE struct can_msg_s txmsg; #ifdef CONFIG_CAN_EXTID bool extended = true; uint32_t msgid; #else uint16_t msgid; #endif long minid = 1; long maxid = MAX_ID; int msgdlc; uint8_t msgdata; int i; #endif #ifdef CONFIG_EXAMPLES_CAN_READ struct can_msg_s rxmsg; #endif size_t msgsize; ssize_t nbytes; bool badarg = false; bool help = false; #ifdef CONFIG_NSH_BUILTIN_APPS long nmsgs = CONFIG_EXAMPLES_CAN_NMSGS; long msgno; #endif int option; int fd; int errval = 0; int ret; /* Parse command line parameters */ while ((option = getopt(argc, argv, OPT_STR)) != ERROR) { switch (option) { #ifdef CONFIG_EXAMPLES_CAN_WRITE #ifdef CONFIG_CAN_EXTID case 's': extended = false; break; #endif case 'a': minid = strtol(optarg, NULL, 10); if (minid < 1 || minid > maxid) { fprintf(stderr, "<min-id> out of range\n"); badarg = true; } break; case 'b': maxid = strtol(optarg, NULL, 10); if (maxid < minid || maxid > MAX_ID) { fprintf(stderr, "ERROR: <max-id> out of range\n"); badarg = true; } break; #endif case 'h': help = true; break; #ifdef CONFIG_NSH_BUILTIN_APPS case 'n': nmsgs = strtol(optarg, NULL, 10); if (nmsgs < 1) { fprintf(stderr, "ERROR: <nmsgs> out of range\n"); badarg = true; } break; #endif case ':': fprintf(stderr, "ERROR: Bad option argument\n"); badarg = true; break; case '?': default: fprintf(stderr, "ERROR: Unrecognized option\n"); badarg = true; break; } } if (badarg) { show_usage(argv[0]); return EXIT_FAILURE; } if (help) { show_usage(argv[0]); return EXIT_SUCCESS; } #if defined(CONFIG_EXAMPLES_CAN_WRITE) && defined(CONFIG_CAN_EXTID) if (!extended && maxid > CAN_MAX_STDMSGID) { maxid = CAN_MAX_STDMSGID; if (minid > maxid) { minid = maxid; } } #endif if (optind != argc) { fprintf(stderr, "ERROR: Garbage on command line\n"); show_usage(argv[0]); return EXIT_FAILURE; } #ifdef CONFIG_NSH_BUILTIN_APPS printf("nmsgs: %d\n", nmsgs); #endif #ifdef CONFIG_EXAMPLES_CAN_WRITE printf("min ID: %ld max ID: %ld\n", minid, maxid); #endif /* Initialization of the CAN hardware is performed by logic external to * this test. */ ret = boardctl(BOARDIOC_CAN_INITIALIZE, 0); if (ret < 0) { printf("ERROR: BOARDIOC_CAN_INITIALIZE failed: %d\n", ret); errval = 1; goto errout; } /* Open the CAN device for reading */ fd = open(CONFIG_EXAMPLES_CAN_DEVPATH, CAN_OFLAGS); if (fd < 0) { printf("ERROR: open %s failed: %d\n", CONFIG_EXAMPLES_CAN_DEVPATH, errno); errval = 2; goto errout_with_dev; } /* Show bit timing information if provided by the driver. Not all CAN * drivers will support this IOCTL. */ ret = ioctl(fd, CANIOC_GET_BITTIMING, (unsigned long)((uintptr_t)&bt)); if (ret < 0) { printf("Bit timing not available: %d\n", errno); } else { printf("Bit timing:\n"); printf(" Baud: %lu\n", (unsigned long)bt.bt_baud); printf(" TSEG1: %u\n", bt.bt_tseg1); printf(" TSEG2: %u\n", bt.bt_tseg2); printf(" SJW: %u\n", bt.bt_sjw); } /* Now loop the appropriate number of times, performing one loopback test * on each pass. */ #ifdef CONFIG_EXAMPLES_CAN_WRITE msgdlc = 1; msgid = minid; msgdata = 0; #endif #ifdef CONFIG_NSH_BUILTIN_APPS for (msgno = 0; msgno < nmsgs; msgno++) #else for (; ; ) #endif { /* Flush any output before the loop entered or from the previous pass * through the loop. */ fflush(stdout); #ifdef CONFIG_EXAMPLES_CAN_WRITE /* Construct the next TX message */ txmsg.cm_hdr.ch_id = msgid; txmsg.cm_hdr.ch_rtr = false; txmsg.cm_hdr.ch_dlc = msgdlc; #ifdef CONFIG_CAN_ERRORS txmsg.cm_hdr.ch_error = 0; #endif #ifdef CONFIG_CAN_EXTID txmsg.cm_hdr.ch_extid = extended; #endif txmsg.cm_hdr.ch_unused = 0; for (i = 0; i < msgdlc; i++) { txmsg.cm_data[i] = msgdata + i; } /* Send the TX message */ msgsize = CAN_MSGLEN(msgdlc); nbytes = write(fd, &txmsg, msgsize); if (nbytes != msgsize) { printf("ERROR: write(%ld) returned %ld\n", (long)msgsize, (long)nbytes); errval = 3; goto errout_with_dev; } printf(" ID: %4u DLC: %d\n", msgid, msgdlc); #endif #ifdef CONFIG_EXAMPLES_CAN_READ /* Read the RX message */ msgsize = sizeof(struct can_msg_s); nbytes = read(fd, &rxmsg, msgsize); if (nbytes < CAN_MSGLEN(0) || nbytes > msgsize) { printf("ERROR: read(%ld) returned %ld\n", (long)msgsize, (long)nbytes); errval = 4; goto errout_with_dev; } printf(" ID: %4u DLC: %u\n", rxmsg.cm_hdr.ch_id, rxmsg.cm_hdr.ch_dlc); #ifdef CONFIG_CAN_ERRORS /* Check for error reports */ if (rxmsg.cm_hdr.ch_error != 0) { printf("ERROR: CAN error report: [0x%04x]\n", rxmsg.cm_hdr.ch_id); if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_TXTIMEOUT) != 0) { printf(" TX timeout\n"); } if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_LOSTARB) != 0) { printf(" Lost arbitration: %02x\n", rxmsg.cm_data[0]); } if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_CONTROLLER) != 0) { printf(" Controller error: %02x\n", rxmsg.cm_data[1]); } if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_PROTOCOL) != 0) { printf(" Protocol error: %02x %02x\n", rxmsg.cm_data[2], rxmsg.cm_data[3]); } if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_TRANSCEIVER) != 0) { printf(" Transceiver error: %02x\n", rxmsg.cm_data[4]); } if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_NOACK) != 0) { printf(" No ACK received on transmission\n"); } if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_BUSOFF) != 0) { printf(" Bus off\n"); } if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_BUSERROR) != 0) { printf(" Bus error\n"); } if ((rxmsg.cm_hdr.ch_id & CAN_ERROR_RESTARTED) != 0) { printf(" Controller restarted\n"); } } else #endif { #if defined(CONFIG_EXAMPLES_CAN_WRITE) && defined(CONFIG_CAN_LOOPBACK) /* Verify that the received messages are the same */ if (memcmp(&txmsg.cm_hdr, &rxmsg.cm_hdr, sizeof(struct can_hdr_s)) != 0) { printf("ERROR: Sent header does not match received header:\n"); lib_dumpbuffer("Sent header", (FAR const uint8_t *)&txmsg.cm_hdr, sizeof(struct can_hdr_s)); lib_dumpbuffer("Received header", (FAR const uint8_t *)&rxmsg.cm_hdr, sizeof(struct can_hdr_s)); errval = 4; goto errout_with_dev; } if (memcmp(txmsg.cm_data, rxmsg.cm_data, msgdlc) != 0) { printf("ERROR: Data does not match. DLC=%d\n", msgdlc); for (i = 0; i < msgdlc; i++) { printf(" %d: TX 0x%02x RX 0x%02x\n", i, txmsg.cm_data[i], rxmsg.cm_data[i]); errval = 5; goto errout_with_dev; } } /* Report success */ printf(" ID: %4u DLC: %d -- OK\n", msgid, msgdlc); #else /* Print the data received */ printf("Data received:\n"); for (i = 0; i < msgdlc; i++) { printf(" %d: 0x%02x\n", i, rxmsg.cm_data[i]); } #endif } #endif #ifdef CONFIG_EXAMPLES_CAN_WRITE /* Set up for the next pass */ msgdata += msgdlc; if (++msgid > maxid) { msgid = minid; } if (++msgdlc > CAN_MAXDATALEN) { msgdlc = 1; } #endif } errout_with_dev: close(fd); errout: printf("Terminating!\n"); fflush(stdout); return errval; }