+//*----------------------------------------------------------------------------\r
+//* \fn usb_check\r
+//* \brief Test if the device is configured and handle enumeration\r
+//*----------------------------------------------------------------------------\r
+static bool usb_check() {\r
+ AT91_REG isr = AT91C_BASE_UDP->UDP_ISR;\r
+\r
+ if (isr & AT91C_UDP_ENDBUSRES) {\r
+ AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_ENDBUSRES;\r
+ // reset all endpoints\r
+ AT91C_BASE_UDP->UDP_RSTEP = (unsigned int)-1;\r
+ AT91C_BASE_UDP->UDP_RSTEP = 0;\r
+ // Enable the function\r
+ AT91C_BASE_UDP->UDP_FADDR = AT91C_UDP_FEN;\r
+ // Configure endpoint 0\r
+ AT91C_BASE_UDP->UDP_CSR[AT91C_EP_CONTROL] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL);\r
+ } else if (isr & AT91C_UDP_EPINT0) {\r
+ AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_EPINT0;\r
+ AT91F_CDC_Enumerate();\r
+ }\r
+ return (btConfiguration) ? true : false;\r
+}\r
+\r
+\r
+bool usb_poll() {\r
+ if (!usb_check()) return false;\r
+ return (AT91C_BASE_UDP->UDP_CSR[AT91C_EP_OUT] & btReceiveBank);\r
+}\r
+\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