+\r
+\r
+/**\r
+ In github PR #129, some users appears to get a false positive from\r
+ usb_poll, which returns true, but the usb_read operation\r
+ still returns 0.\r
+ This check is basically the same as above, but also checks\r
+ that the length available to read is non-zero, thus hopefully fixes the\r
+ bug.\r
+**/\r
+bool usb_poll_validate_length() {\r
+ if (!usb_check()) return false;\r
+ if (!(AT91C_BASE_UDP->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) return false;\r
+ return (AT91C_BASE_UDP->UDP_CSR[AT91C_EP_OUT] >> 16) > 0;\r
+}\r
+\r
+\r
+//*----------------------------------------------------------------------------\r
+//* \fn usb_read\r
+//* \brief Read available data from Endpoint OUT\r
+//*----------------------------------------------------------------------------\r
+static uint32_t usb_read(uint8_t* data, size_t len) {\r
+ uint8_t bank = btReceiveBank;\r
+ uint32_t packetSize, nbBytesRcv = 0;\r
+ uint32_t time_out = 0;\r
+\r
+ while (len) {\r
+ if (!usb_check()) break;\r
+\r
+ if ( AT91C_BASE_UDP->UDP_CSR[AT91C_EP_OUT] & bank ) {\r
+ packetSize = MIN(AT91C_BASE_UDP->UDP_CSR[AT91C_EP_OUT] >> 16, len);\r
+ len -= packetSize;\r
+ while (packetSize--)\r
+ data[nbBytesRcv++] = AT91C_BASE_UDP->UDP_FDR[AT91C_EP_OUT];\r
+ UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, bank);\r
+ if (bank == AT91C_UDP_RX_DATA_BK0) {\r
+ bank = AT91C_UDP_RX_DATA_BK1;\r
+ } else {\r
+ bank = AT91C_UDP_RX_DATA_BK0;\r
+ }\r
+ }\r
+ if (time_out++ == 0x1fff) break;\r
+ }\r
+\r
+ btReceiveBank = bank;\r
+ return nbBytesRcv;\r
+}\r
+\r
+\r
+//*----------------------------------------------------------------------------\r
+//* \fn usb_write\r
+//* \brief Send through endpoint 2\r
+//*----------------------------------------------------------------------------\r
+static uint32_t usb_write(const uint8_t* data, const size_t len) {\r
+ size_t length = len;\r
+ uint32_t cpt = 0;\r
+\r
+ if (!length) return 0;\r
+ if (!usb_check()) return 0;\r
+\r
+ // Send the first packet\r
+ cpt = MIN(length, AT91C_EP_IN_SIZE);\r
+ length -= cpt;\r
+ while (cpt--) {\r
+ AT91C_BASE_UDP->UDP_FDR[AT91C_EP_IN] = *data++;\r
+ }\r
+ UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY);\r
+ while (!(AT91C_BASE_UDP->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY))\r
+ /* wait */;\r
+\r
+ while (length) {\r
+ // Fill the next bank\r
+ cpt = MIN(length, AT91C_EP_IN_SIZE);\r
+ length -= cpt;\r
+ while (cpt--) {\r
+ AT91C_BASE_UDP->UDP_FDR[AT91C_EP_IN] = *data++;\r
+ }\r
+ // Wait for the previous bank to be sent\r
+ while (!(AT91C_BASE_UDP->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) {\r
+ if (!usb_check()) return length;\r
+ }\r
+ UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY);\r
+ while (!(AT91C_BASE_UDP->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY))\r
+ /* wait */;\r
+ UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP);\r
+ while (AT91C_BASE_UDP->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)\r
+ /* wait */;\r
+ }\r
+\r
+ // Wait for the end of transfer\r
+ while (!(AT91C_BASE_UDP->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) {\r
+ if (!usb_check()) return length;\r
+ }\r
+ UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP);\r
+ while (AT91C_BASE_UDP->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)\r
+ /* wait */;\r
+\r
+ if (len % AT91C_EP_IN_SIZE == 0) { // need to send a zero length packet to complete the transfer\r
+ AT91F_USB_SendZlp(AT91C_EP_IN);\r
+ }\r
+\r
+ return length;\r
+}\r
+\r
+\r
+//***************************************************************************\r
+// Interface to the main program\r
+//***************************************************************************\r
+\r
+// The function to receive a command from the client via USB\r
+bool cmd_receive(UsbCommand* cmd) {\r
+\r
+ // Check if there is a usb packet available\r
+ if (!usb_poll())\r
+ return false;\r
+\r
+ // Try to retrieve the available command frame\r
+ size_t rxlen = usb_read((uint8_t*)cmd, sizeof(UsbCommand));\r
+\r
+ // Check if the transfer was complete\r
+ if (rxlen != sizeof(UsbCommand))\r
+ return false;\r
+\r
+ // Received command successfully\r
+ return true;\r
+}\r
+\r
+\r
+// The function to send a response to the client via USB\r
+bool cmd_send(uint16_t cmd, uint32_t arg0, uint32_t arg1, uint32_t arg2, void* data, uint16_t datalen) {\r
+\r
+ UsbResponse txcmd;\r
+\r
+ // Compose the outgoing response frame\r
+ txcmd.cmd = cmd | CMD_VARIABLE_SIZE_FLAG;\r
+ txcmd.arg[0] = arg0;\r
+ txcmd.arg[1] = arg1;\r
+ txcmd.arg[2] = arg2;\r
+\r
+ // Add the (optional) content to the frame, with a maximum size of USB_CMD_DATA_SIZE\r
+ if (data) {\r
+ datalen = MIN(datalen, USB_CMD_DATA_SIZE);\r
+ for (uint16_t i = 0; i < datalen; i++) {\r
+ txcmd.d.asBytes[i] = ((uint8_t*)data)[i];\r
+ }\r
+ txcmd.datalen = datalen;\r
+ } else {\r
+ txcmd.datalen = 0;\r
+ }\r
+\r
+ // Send frame and make sure all bytes are transmitted\r
+ size_t tx_size = offsetof(UsbResponse, d) + datalen;\r
+ if (usb_write((uint8_t*)&txcmd, tx_size) != 0) return false;\r
+\r
+ return true;\r
+}\r
+\r
+\r
+// For compatibility only: legacy function to send a response with fixed size to the client via USB\r
+bool cmd_send_old(uint16_t cmd, uint32_t arg0, uint32_t arg1, uint32_t arg2, void* data, uint16_t datalen) {\r
+\r
+ UsbCommand txcmd;\r
+\r
+ // Compose the outgoing response frame\r
+ txcmd.cmd = cmd;\r
+ txcmd.arg[0] = arg0;\r
+ txcmd.arg[1] = arg1;\r
+ txcmd.arg[2] = arg2;\r
+\r
+ // Add the (optional) content to the frame, with a maximum size of USB_CMD_DATA_SIZE\r
+ if (data) {\r
+ datalen = MIN(datalen, USB_CMD_DATA_SIZE);\r
+ for (uint16_t i = 0; i < datalen; i++) {\r
+ txcmd.d.asBytes[i] = ((uint8_t*)data)[i];\r
+ }\r
+ }\r
+\r
+ // Send frame and make sure all bytes are transmitted\r
+ if (usb_write((uint8_t*)&txcmd, sizeof(UsbCommand)) != 0) return false;\r
+\r
+ return true;\r
+}\r
+\r