Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of F401RE-USBHost by
Diff: USBHost/USBHALHost.cpp
- Revision:
- 5:10bfc10afcc8
diff -r 21d651ad6987 -r 10bfc10afcc8 USBHost/USBHALHost.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHost/USBHALHost.cpp Mon Jan 27 11:00:28 2014 +0000
@@ -0,0 +1,397 @@
+// Simple USBHost for FRDM-KL46Z
+#include "USBHALHost.h"
+#include <algorithm>
+
+template <bool>struct CtAssert;
+template <>struct CtAssert<true> {};
+#define CTASSERT(A) CtAssert<A>();
+
+
+#ifdef _USB_DBG
+#define USB_DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
+#define USB_DBG_HEX(A,B) debug_hex(A,B)
+void debug_hex(uint8_t* buf, int size);
+#else
+#define USB_DBG(...) while(0)
+#define USB_DBG_HEX(A,B) while(0)
+#endif
+
+#define USB_TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+#define USB_TEST_ASSERT_FALSE(A) USB_TEST_ASSERT(!(A))
+
+#define BD_OWN_MASK (1<<7)
+#define BD_DATA01_MASK (1<<6)
+#define BD_KEEP_MASK (1<<5)
+#define BD_NINC_MASK (1<<4)
+#define BD_DTS_MASK (1<<3)
+#define BD_STALL_MASK (1<<2)
+
+#define TX 1
+#define RX 0
+
+#define EP0_BDT_IDX(dir, odd) (((2 * dir) + (1 * odd)))
+
+#define SETUP_TOKEN 0x0D
+#define IN_TOKEN 0x09
+#define OUT_TOKEN 0x01
+
+// for each endpt: 8 bytes
+struct BDT {
+ uint8_t info; // BD[0:7]
+ uint8_t dummy; // RSVD: BD[8:15]
+ uint16_t byte_count; // BD[16:32]
+ uint32_t address; // Addr
+ void setBuffer(uint8_t* buf, int size) {
+ address = (uint32_t)buf;
+ byte_count = size;
+ }
+ uint8_t getStatus() {
+ return (info>>2)&0x0f;
+ }
+};
+
+__attribute__((__aligned__(512))) BDT bdt[64];
+
+USBHALHost* USBHALHost::instHost;
+
+USBHALHost::USBHALHost() {
+ instHost = this;
+ report.clear();
+}
+
+void USBHALHost::init() {
+ // Disable IRQ
+ NVIC_DisableIRQ(USB0_IRQn);
+
+ // choose usb src as PLL
+ SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL_MASK);
+
+ // enable OTG clock
+ SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
+
+ // USB Module Configuration
+ // Reset USB Module
+ USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
+ while(USB0->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
+
+ // Clear interrupt flag
+ USB0->ISTAT = 0xff;
+
+ // Set BDT Base Register
+ USB0->BDTPAGE1=(uint8_t)((uint32_t)bdt>>8);
+ USB0->BDTPAGE2=(uint8_t)((uint32_t)bdt>>16);
+ USB0->BDTPAGE3=(uint8_t)((uint32_t)bdt>>24);
+
+ // Set SOF threshold
+ USB0->SOFTHLD = USB_SOFTHLD_CNT(1);
+
+ // pulldown D+ and D-
+ USB0->USBCTRL = USB_USBCTRL_PDE_MASK;
+
+ USB0->USBTRC0 |= 0x40;
+
+ // Host mode
+ USB0->CTL |= USB_CTL_HOSTMODEEN_MASK;
+ // Desable SOF packet generation
+ USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
+
+ NVIC_SetVector(USB0_IRQn, (uint32_t)_usbisr);
+ NVIC_EnableIRQ(USB0_IRQn);
+
+ wait_attach();
+
+ for(int retry = 2; retry > 0; retry--) {
+ // Enable RESET
+ USB0->CTL |= USB_CTL_RESET_MASK;
+ wait_ms(500);
+ USB0->CTL &= ~USB_CTL_RESET_MASK;
+
+ // Enable SOF
+ USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
+ wait_ms(100);
+
+ // token transfer initialize
+ token_transfer_init();
+
+ USB0->INTEN |= USB_INTEN_TOKDNEEN_MASK|
+ USB_INTEN_ERROREN_MASK;
+ USB0->ERREN |= USB_ERREN_PIDERREN_MASK|
+ USB_ERREN_CRC5EOFEN_MASK|
+ USB_ERREN_CRC16EN_MASK|
+ USB_ERREN_DFN8EN_MASK|
+ USB_ERREN_BTOERREN_MASK|
+ USB_ERREN_DMAERREN_MASK|
+ USB_ERREN_BTSERREN_MASK;
+
+ if (enumeration()) {
+ break;
+ }
+ USB_DBG("retry=%d", retry);
+ USB_TEST_ASSERT(retry > 1);
+ }
+}
+
+void USBHALHost::wait_attach() {
+ attach_done = false;
+ USB0->INTEN = USB_INTEN_ATTACHEN_MASK;
+ while(!attach_done);
+ wait_ms(100);
+ USB_TEST_ASSERT_FALSE(USB0->CTL & USB_CTL_SE0_MASK);
+ lowSpeed = (USB0->CTL & USB_CTL_JSTATE_MASK) ? false : true;
+ if (lowSpeed) { // low speed
+ USB0->ENDPOINT[0].ENDPT |= USB_ENDPT_HOSTWOHUB_MASK;
+ }
+ USB_DBG("lowSpeed=%d", lowSpeed);
+}
+
+void USBHALHost::setAddr(int _addr) {
+ USB0->ADDR = (lowSpeed ? USB_ADDR_LSEN_MASK : 0x00) | USB_ADDR_ADDR(_addr);
+}
+
+void USBHALHost::setEndpoint(bool use_retry) {
+ USB0->ENDPOINT[0].ENDPT = (lowSpeed ? USB_ENDPT_HOSTWOHUB_MASK : 0x00)|
+ USB_ENDPT_EPCTLDIS_MASK|
+ (use_retry ? 0x00 : USB_ENDPT_RETRYDIS_MASK)|
+ USB_ENDPT_EPRXEN_MASK|
+ USB_ENDPT_EPTXEN_MASK|
+ USB_ENDPT_EPHSHK_MASK;
+}
+
+void USBHALHost::token_transfer_init() {
+ tx_ptr = ODD;
+ rx_ptr = ODD;
+}
+
+int USBHALHost::token_setup(USBEndpoint* ep, SETUP_PACKET* setup, uint16_t wLength) {
+ for(int retry = 0;; retry++) {
+ token_ready();
+ USB0->ENDPOINT[0].ENDPT = (lowSpeed ? USB_ENDPT_HOSTWOHUB_MASK : 0x00) |
+ USB_ENDPT_RETRYDIS_MASK|
+ USB_ENDPT_EPRXEN_MASK|
+ USB_ENDPT_EPTXEN_MASK|
+ USB_ENDPT_EPHSHK_MASK;
+ CTASSERT(sizeof(SETUP_PACKET) == 8);
+ setup->wLength = wLength;
+ int idx = EP0_BDT_IDX(TX, tx_ptr);
+ bdt[idx].setBuffer((uint8_t*)setup, sizeof(SETUP_PACKET));
+ bdt[idx].info = BD_OWN_MASK |
+ BD_DTS_MASK; // always data0
+ token_done = false;
+ USB0->TOKEN = USB_TOKEN_TOKENPID(SETUP_TOKEN)|
+ USB_TOKEN_TOKENENDPT(ep->getAddress() & 0x7f);
+ while(!token_done);
+ LastStatus = bdt[idx].getStatus();
+ if (LastStatus == ACK) {
+ if (retry > 0) {
+ USB_DBG("retry=%d %02x", retry, prev_LastStatus);
+ }
+ break;
+ } else if (LastStatus == STALL) {
+ report.stall++;
+ return STALL;
+ }
+ if (retry > 10) {
+ USB_DBG("retry=%d", retry);
+ break;
+ }
+ prev_LastStatus = LastStatus;
+ wait_ms(100 * retry);
+ }
+ ep->setData01(DATA1); // next toggle
+ return LastStatus;
+}
+
+int USBHALHost::token_in(USBEndpoint* ep, uint8_t* data, int size, int retryLimit) {
+ for(int retry = 0;; retry++) {
+ token_ready();
+ int idx = EP0_BDT_IDX(RX, rx_ptr);
+ bdt[idx].setBuffer(data, size);
+ bdt[idx].info = BD_OWN_MASK|
+ BD_DTS_MASK|
+ (ep->getData01() == DATA1 ? BD_DATA01_MASK : 0);
+ token_done = false;
+ USB0->TOKEN = USB_TOKEN_TOKENPID(IN_TOKEN)|
+ USB_TOKEN_TOKENENDPT(ep->getAddress()&0x7f);
+ while(!token_done);
+ LastStatus = bdt[idx].getStatus();
+ int len = bdt[idx].byte_count;
+ if (LastStatus == DATA0 || LastStatus == DATA1) {
+ USB_TEST_ASSERT(ep->getData01() == LastStatus);
+ ep->setData01(LastStatus == DATA0 ? DATA1 : DATA0);
+ if (retry > 0) {
+ USB_DBG("len=%d retry=%d %02x", len, retry, prev_LastStatus);
+ }
+ return len;
+ } else if (LastStatus == STALL) {
+ report.stall++;
+ return -1;
+ } else if (LastStatus == NAK) {
+ report.nak++;
+ if (retry >= retryLimit) {
+ if (retryLimit > 0) {
+ USB_DBG("retry=%d retryLimit=%d", retry, retryLimit);
+ }
+ return -1;
+ }
+ wait_ms(100 * retry);
+ } else {
+ return -1;
+ }
+ prev_LastStatus = LastStatus;
+ }
+}
+
+int USBHALHost::token_out(USBEndpoint* ep, const uint8_t* data, int size, int retryLimit) {
+ for(int retry = 0;; retry++) {
+ token_ready();
+ int idx = EP0_BDT_IDX(TX, tx_ptr);
+ bdt[idx].setBuffer((uint8_t*)data, size);
+ bdt[idx].info = BD_OWN_MASK|
+ BD_DTS_MASK|
+ (ep->getData01() == DATA1 ? BD_DATA01_MASK : 0);
+ token_done = false;
+ USB0->TOKEN = USB_TOKEN_TOKENPID(OUT_TOKEN)|
+ USB_TOKEN_TOKENENDPT(ep->getAddress()&0x7f);
+ while(!token_done);
+ LastStatus = bdt[idx].getStatus();
+ int len = bdt[idx].byte_count;
+ //USB_DBG("len=%d %02x", len, LastStatus);
+ if (LastStatus == ACK) {
+ ep->toggleData01();
+ if (retry > 0) {
+ USB_DBG("len=%d retry=%d %02x", len, retry, prev_LastStatus);
+ }
+ return len;
+ } else if (LastStatus == STALL) {
+ report.stall++;
+ return -1;
+ } else if (LastStatus == NAK) {
+ report.nak++;
+ if (retry > retryLimit) {
+ USB_DBG("retry=%d retryLimit=%d", retry, retryLimit);
+ return -1;
+ }
+ wait_ms(100 * retry);
+ } else {
+ return -1;
+ }
+ prev_LastStatus = LastStatus;
+ }
+}
+
+int USBHALHost::token_iso_in(USBEndpoint* ep, uint8_t* data, int size) {
+ while(USB0->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK); // TOKEN_BUSY ?
+ USB0->ISTAT |= USB_ISTAT_SOFTOK_MASK; // Clear SOF
+ while (!(USB0->ISTAT & USB_ISTAT_SOFTOK_MASK));
+ USB0->SOFTHLD = 0; // this is needed as without this you can get errors
+ USB0->ISTAT |= USB_ISTAT_SOFTOK_MASK; // clear SOF
+
+ USB0->ENDPOINT[0].ENDPT = USB_ENDPT_EPCTLDIS_MASK|
+ USB_ENDPT_RETRYDIS_MASK|
+ USB_ENDPT_EPRXEN_MASK|
+ USB_ENDPT_EPTXEN_MASK;
+ int idx = EP0_BDT_IDX(RX, rx_ptr);
+ bdt[idx].setBuffer(data, size);
+ bdt[idx].info = BD_OWN_MASK|
+ BD_DTS_MASK; // always DATA0
+ token_done = false;
+ USB0->TOKEN = USB_TOKEN_TOKENPID(IN_TOKEN)|
+ USB_TOKEN_TOKENENDPT(ep->getAddress()&0x7f);
+ while(!token_done);
+ LastStatus = bdt[idx].getStatus();
+ int len = bdt[idx].byte_count;
+ if (LastStatus == DATA0) {
+ return len;
+ }
+ return -1;
+}
+
+void USBHALHost::token_ready() {
+ while(USB0->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) { // TOKEN_BUSY ?
+ wait_ms(1);
+ }
+ USB0->ISTAT |= USB_ISTAT_SOFTOK_MASK; // Clear SOF
+ while (!(USB0->ISTAT & USB_ISTAT_SOFTOK_MASK));
+ USB0->SOFTHLD = 0; // this is needed as without this you can get errors
+ USB0->ISTAT |= USB_ISTAT_SOFTOK_MASK; // clear SOF
+}
+
+void USBHALHost::_usbisr(void) {
+ if (instHost) {
+ instHost->UsbIrqhandler();
+ }
+}
+
+void USBHALHost::UsbIrqhandler() {
+ uint8_t istat = USB0->ISTAT;
+ if (istat & USB_ISTAT_TOKDNE_MASK) {
+ uint8_t stat = USB0->STAT;
+ ODD_EVEN next_ptr = (stat & USB_STAT_ODD_MASK) ? ODD : EVEN;
+ if (stat & USB_STAT_TX_MASK) {
+ tx_ptr = next_ptr;
+ } else {
+ rx_ptr = next_ptr;
+ }
+ token_done = true;
+ }
+ if (istat & USB_ISTAT_ATTACH_MASK) {
+ USB0->INTEN &= ~USB_INTEN_ATTACHEN_MASK;
+ attach_done = true;
+ }
+ if (istat & USB_ISTAT_ERROR_MASK) {
+ uint8_t errstat = USB0->ERRSTAT;
+ if (errstat & USB_ERRSTAT_PIDERR_MASK) {
+ report.errstat_piderr++;
+ }
+ if (errstat & USB_ERRSTAT_CRC5EOF_MASK) {
+ report.errstat_crc5eof++;
+ }
+ if (errstat & USB_ERRSTAT_CRC16_MASK) {
+ report.errstat_crc16++;
+ }
+ if (errstat & USB_ERRSTAT_DFN8_MASK) {
+ report.errstat_dfn8++;
+ }
+ if (errstat & USB_ERRSTAT_BTOERR_MASK) {
+ report.errstat_btoerr++;
+ }
+ if (errstat & USB_ERRSTAT_DMAERR_MASK) {
+ report.errstat_dmaerr++;
+ }
+ if (errstat & USB_ERRSTAT_BTSERR_MASK) {
+ report.errstat_btoerr++;
+ }
+ USB0->ERRSTAT = errstat;
+ }
+ USB0->ISTAT = istat; // clear
+}
+
+void Report::clear() {
+ errstat_piderr = 0;
+ errstat_crc5eof = 0;
+ errstat_crc16 = 0;
+ errstat_dfn8 = 0;
+ errstat_btoerr = 0;
+ errstat_dmaerr = 0;
+ errstat_bsterr = 0;
+ //
+ nak = 0;
+}
+
+void Report::print_errstat() {
+ printf("ERRSTAT PID: %d, CRC5EOF: %d, CRC16: %d, DFN8: %d, BTO: %d, DMA: %d, BST: %d\n",
+ errstat_piderr, errstat_crc5eof,
+ errstat_crc16, errstat_dfn8,
+ errstat_btoerr, errstat_dmaerr, errstat_bsterr);
+}
+
+void debug_hex(uint8_t* buf, int size) {
+ for(int i = 0; i < size; i++) {
+ fprintf(stderr, "%02x ", buf[i]);
+ if (i%16 == 15) {
+ fprintf(stderr, "\r\n");
+ }
+ }
+ fprintf(stderr, "\r\n");
+}
+
