| static void free_Pipe(Pipe_t *q); | static void free_Pipe(Pipe_t *q); | ||||
| static Transfer_t * allocate_Transfer(void); | static Transfer_t * allocate_Transfer(void); | ||||
| static void free_Transfer(Transfer_t *q); | static void free_Transfer(Transfer_t *q); | ||||
| static bool allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen, | |||||
| uint32_t interval, uint32_t direction, uint32_t *offset_out, | |||||
| uint32_t *smask_out, uint32_t *cmask_out); | |||||
| protected: | protected: | ||||
| static void print(const Transfer_t *transfer); | static void print(const Transfer_t *transfer); | ||||
| static void print(const Transfer_t *first, const Transfer_t *last); | static void print(const Transfer_t *first, const Transfer_t *last); | ||||
| static void print_token(uint32_t token); | static void print_token(uint32_t token); | ||||
| static void print(const Pipe_t *pipe); | static void print(const Pipe_t *pipe); | ||||
| static void print_hexbytes(const void *ptr, uint32_t len); | static void print_hexbytes(const void *ptr, uint32_t len); | ||||
| static void print(const char *s); | |||||
| static void print(const char *s, int num); | |||||
| static void print(const char *s) { Serial.print(s); } | |||||
| static void print(int n) { Serial.print(n); } | |||||
| static void print(unsigned int n) { Serial.print(n); } | |||||
| static void print(long n) { Serial.print(n); } | |||||
| static void print(unsigned long n) { Serial.print(n); } | |||||
| static void println(const char *s) { Serial.println(s); } | |||||
| static void println(int n) { Serial.println(n); } | |||||
| static void println(unsigned int n) { Serial.println(n); } | |||||
| static void println(long n) { Serial.println(n); } | |||||
| static void println(unsigned long n) { Serial.println(n); } | |||||
| static void println() { Serial.println(); } | |||||
| static void print(uint32_t n, uint8_t b) { Serial.print(n, b); } | |||||
| static void println(uint32_t n, uint8_t b) { Serial.print(n, b); } | |||||
| static void println(const char *s, int n) { | |||||
| Serial.print(s); Serial.println(n); } | |||||
| static void println(const char *s, unsigned int n) { | |||||
| Serial.print(s); Serial.println(n); } | |||||
| static void println(const char *s, long n) { | |||||
| Serial.print(s); Serial.println(n); } | |||||
| static void println(const char *s, unsigned long n) { | |||||
| Serial.print(s); Serial.println(n); } | |||||
| static void println(const char *s, int n, uint8_t b) { | |||||
| Serial.print(s); Serial.println(n, b); } | |||||
| static void println(const char *s, unsigned int n, uint8_t b) { | |||||
| Serial.print(s); Serial.println(n, b); } | |||||
| static void println(const char *s, long n, uint8_t b) { | |||||
| Serial.print(s); Serial.println(n, b); } | |||||
| static void println(const char *s, unsigned long n, uint8_t b) { | |||||
| Serial.print(s); Serial.println(n, b); } | |||||
| static void mk_setup(setup_t &s, uint32_t bmRequestType, uint32_t bRequest, | static void mk_setup(setup_t &s, uint32_t bmRequestType, uint32_t bRequest, | ||||
| uint32_t wValue, uint32_t wIndex, uint32_t wLength) { | uint32_t wValue, uint32_t wIndex, uint32_t wLength) { | ||||
| s.word1 = bmRequestType | (bRequest << 8) | (wValue << 16); | s.word1 = bmRequestType | (bRequest << 8) | (wValue << 16); |
| static void remove_from_async_followup_list(Transfer_t *transfer); | static void remove_from_async_followup_list(Transfer_t *transfer); | ||||
| static void add_to_periodic_followup_list(Transfer_t *first, Transfer_t *last); | static void add_to_periodic_followup_list(Transfer_t *first, Transfer_t *last); | ||||
| static void remove_from_periodic_followup_list(Transfer_t *transfer); | static void remove_from_periodic_followup_list(Transfer_t *transfer); | ||||
| static bool allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen, | |||||
| uint32_t interval, uint32_t direction, uint32_t *offset, uint32_t *smask, | |||||
| uint32_t *cmask); | |||||
| void USBHost::begin() | void USBHost::begin() | ||||
| { | { | ||||
| GPIOE_PDDR |= (1<<6); | GPIOE_PDDR |= (1<<6); | ||||
| GPIOE_PSOR = (1<<6); // turn on USB host power | GPIOE_PSOR = (1<<6); // turn on USB host power | ||||
| delay(10); | delay(10); | ||||
| Serial.print("sizeof Device = "); | |||||
| Serial.println(sizeof(Device_t)); | |||||
| Serial.print("sizeof Pipe = "); | |||||
| Serial.println(sizeof(Pipe_t)); | |||||
| Serial.print("sizeof Transfer = "); | |||||
| Serial.println(sizeof(Transfer_t)); | |||||
| println("sizeof Device = ", sizeof(Device_t)); | |||||
| println("sizeof Pipe = ", sizeof(Pipe_t)); | |||||
| println("sizeof Transfer = ", sizeof(Transfer_t)); | |||||
| // configure the MPU to allow USBHS DMA to access memory | // configure the MPU to allow USBHS DMA to access memory | ||||
| MPU_RGDAAC0 |= 0x30000000; | MPU_RGDAAC0 |= 0x30000000; | ||||
| //Serial.print("MPU_RGDAAC0 = "); | |||||
| //Serial.println(MPU_RGDAAC0, HEX); | |||||
| //println("MPU_RGDAAC0 = ", MPU_RGDAAC0, HEX); | |||||
| // turn on clocks | // turn on clocks | ||||
| MCG_C1 |= MCG_C1_IRCLKEN; // enable MCGIRCLK 32kHz | MCG_C1 |= MCG_C1_IRCLKEN; // enable MCGIRCLK 32kHz | ||||
| OSC0_CR |= OSC_ERCLKEN; | OSC0_CR |= OSC_ERCLKEN; | ||||
| SIM_SOPT2 |= SIM_SOPT2_USBREGEN; // turn on USB regulator | SIM_SOPT2 |= SIM_SOPT2_USBREGEN; // turn on USB regulator | ||||
| SIM_SOPT2 &= ~SIM_SOPT2_USBSLSRC; // use IRC for slow clock | SIM_SOPT2 &= ~SIM_SOPT2_USBSLSRC; // use IRC for slow clock | ||||
| print("power up USBHS PHY"); | |||||
| println("power up USBHS PHY"); | |||||
| SIM_USBPHYCTL |= SIM_USBPHYCTL_USBDISILIM; // disable USB current limit | SIM_USBPHYCTL |= SIM_USBPHYCTL_USBDISILIM; // disable USB current limit | ||||
| //SIM_USBPHYCTL = SIM_USBPHYCTL_USBDISILIM | SIM_USBPHYCTL_USB3VOUTTRG(6); // pg 237 | //SIM_USBPHYCTL = SIM_USBPHYCTL_USBDISILIM | SIM_USBPHYCTL_USB3VOUTTRG(6); // pg 237 | ||||
| SIM_SCGC3 |= SIM_SCGC3_USBHSDCD | SIM_SCGC3_USBHSPHY | SIM_SCGC3_USBHS; | SIM_SCGC3 |= SIM_SCGC3_USBHSDCD | SIM_SCGC3_USBHSPHY | SIM_SCGC3_USBHS; | ||||
| while ((USBPHY_PLL_SIC & USBPHY_PLL_SIC_PLL_LOCK) == 0) { | while ((USBPHY_PLL_SIC & USBPHY_PLL_SIC_PLL_LOCK) == 0) { | ||||
| count++; | count++; | ||||
| } | } | ||||
| //Serial.print("PLL locked, waited "); | |||||
| //Serial.println(count); | |||||
| //println("PLL locked, waited ", count); | |||||
| // turn on power to PHY | // turn on power to PHY | ||||
| USBPHY_PWD = 0; | USBPHY_PWD = 0; | ||||
| while (USBHS_USBCMD & USBHS_USBCMD_RST) { | while (USBHS_USBCMD & USBHS_USBCMD_RST) { | ||||
| //count++; | //count++; | ||||
| } | } | ||||
| //print(" reset waited ", count); | |||||
| //println(" reset waited ", count); | |||||
| init_Device_Pipe_Transfer_memory(); | init_Device_Pipe_Transfer_memory(); | ||||
| for (int i=0; i < 32; i++) { | for (int i=0; i < 32; i++) { | ||||
| //USBHS_PORTSC1 |= USBHS_PORTSC_PFSC; // force 12 Mbit/sec | //USBHS_PORTSC1 |= USBHS_PORTSC_PFSC; // force 12 Mbit/sec | ||||
| //USBHS_PORTSC1 |= USBHS_PORTSC_PHCD; // phy off | //USBHS_PORTSC1 |= USBHS_PORTSC_PHCD; // phy off | ||||
| //Serial.print("USBHS_ASYNCLISTADDR = "); | |||||
| //Serial.println(USBHS_ASYNCLISTADDR, HEX); | |||||
| //Serial.print("USBHS_PERIODICLISTBASE = "); | |||||
| //Serial.println(USBHS_PERIODICLISTBASE, HEX); | |||||
| //Serial.print("periodictable = "); | |||||
| //Serial.println((uint32_t)periodictable, HEX); | |||||
| //println("USBHS_ASYNCLISTADDR = ", USBHS_ASYNCLISTADDR, HEX); | |||||
| //println("USBHS_PERIODICLISTBASE = ", USBHS_PERIODICLISTBASE, HEX); | |||||
| //println("periodictable = ", (uint32_t)periodictable, HEX); | |||||
| // enable interrupts, after this point interruts to all the work | // enable interrupts, after this point interruts to all the work | ||||
| attachInterruptVector(IRQ_USBHS, isr); | attachInterruptVector(IRQ_USBHS, isr); | ||||
| uint32_t stat = USBHS_USBSTS; | uint32_t stat = USBHS_USBSTS; | ||||
| USBHS_USBSTS = stat; // clear pending interrupts | USBHS_USBSTS = stat; // clear pending interrupts | ||||
| //stat &= USBHS_USBINTR; // mask away unwanted interrupts | //stat &= USBHS_USBINTR; // mask away unwanted interrupts | ||||
| Serial.println(); | |||||
| Serial.print("ISR: "); | |||||
| Serial.print(stat, HEX); | |||||
| Serial.println(); | |||||
| //if (stat & USBHS_USBSTS_UI) Serial.println(" USB Interrupt"); | |||||
| if (stat & USBHS_USBSTS_UEI) Serial.println(" USB Error"); | |||||
| if (stat & USBHS_USBSTS_PCI) Serial.println(" Port Change"); | |||||
| //if (stat & USBHS_USBSTS_FRI) Serial.println(" Frame List Rollover"); | |||||
| if (stat & USBHS_USBSTS_SEI) Serial.println(" System Error"); | |||||
| if (stat & USBHS_USBSTS_AAI) Serial.println(" Async Advance (doorbell)"); | |||||
| if (stat & USBHS_USBSTS_URI) Serial.println(" Reset Recv"); | |||||
| //if (stat & USBHS_USBSTS_SRI) Serial.println(" SOF"); | |||||
| if (stat & USBHS_USBSTS_SLI) Serial.println(" Suspend"); | |||||
| if (stat & USBHS_USBSTS_HCH) Serial.println(" Host Halted"); | |||||
| //if (stat & USBHS_USBSTS_RCL) Serial.println(" Reclamation"); | |||||
| //if (stat & USBHS_USBSTS_PS) Serial.println(" Periodic Sched En"); | |||||
| //if (stat & USBHS_USBSTS_AS) Serial.println(" Async Sched En"); | |||||
| if (stat & USBHS_USBSTS_NAKI) Serial.println(" NAK"); | |||||
| if (stat & USBHS_USBSTS_UAI) Serial.println(" USB Async"); | |||||
| if (stat & USBHS_USBSTS_UPI) Serial.println(" USB Periodic"); | |||||
| if (stat & USBHS_USBSTS_TI0) Serial.println(" Timer0"); | |||||
| if (stat & USBHS_USBSTS_TI1) Serial.println(" Timer1"); | |||||
| println(); | |||||
| println("ISR: ", stat, HEX); | |||||
| //if (stat & USBHS_USBSTS_UI) println(" USB Interrupt"); | |||||
| if (stat & USBHS_USBSTS_UEI) println(" USB Error"); | |||||
| if (stat & USBHS_USBSTS_PCI) println(" Port Change"); | |||||
| //if (stat & USBHS_USBSTS_FRI) println(" Frame List Rollover"); | |||||
| if (stat & USBHS_USBSTS_SEI) println(" System Error"); | |||||
| if (stat & USBHS_USBSTS_AAI) println(" Async Advance (doorbell)"); | |||||
| if (stat & USBHS_USBSTS_URI) println(" Reset Recv"); | |||||
| //if (stat & USBHS_USBSTS_SRI) println(" SOF"); | |||||
| if (stat & USBHS_USBSTS_SLI) println(" Suspend"); | |||||
| if (stat & USBHS_USBSTS_HCH) println(" Host Halted"); | |||||
| //if (stat & USBHS_USBSTS_RCL) println(" Reclamation"); | |||||
| //if (stat & USBHS_USBSTS_PS) println(" Periodic Sched En"); | |||||
| //if (stat & USBHS_USBSTS_AS) println(" Async Sched En"); | |||||
| if (stat & USBHS_USBSTS_NAKI) println(" NAK"); | |||||
| if (stat & USBHS_USBSTS_UAI) println(" USB Async"); | |||||
| if (stat & USBHS_USBSTS_UPI) println(" USB Periodic"); | |||||
| if (stat & USBHS_USBSTS_TI0) println(" Timer0"); | |||||
| if (stat & USBHS_USBSTS_TI1) println(" Timer1"); | |||||
| if (stat & USBHS_USBSTS_UAI) { // completed qTD(s) from the async schedule | if (stat & USBHS_USBSTS_UAI) { // completed qTD(s) from the async schedule | ||||
| Serial.println("Async Followup"); | |||||
| println("Async Followup"); | |||||
| //print(async_followup_first, async_followup_last); | //print(async_followup_first, async_followup_last); | ||||
| Transfer_t *p = async_followup_first; | Transfer_t *p = async_followup_first; | ||||
| while (p) { | while (p) { | ||||
| //print(async_followup_first, async_followup_last); | //print(async_followup_first, async_followup_last); | ||||
| } | } | ||||
| if (stat & USBHS_USBSTS_UPI) { // completed qTD(s) from the periodic schedule | if (stat & USBHS_USBSTS_UPI) { // completed qTD(s) from the periodic schedule | ||||
| Serial.println("Periodic Followup"); | |||||
| println("Periodic Followup"); | |||||
| Transfer_t *p = periodic_followup_first; | Transfer_t *p = periodic_followup_first; | ||||
| while (p) { | while (p) { | ||||
| if (followup_Transfer(p)) { | if (followup_Transfer(p)) { | ||||
| if (stat & USBHS_USBSTS_PCI) { // port change detected | if (stat & USBHS_USBSTS_PCI) { // port change detected | ||||
| const uint32_t portstat = USBHS_PORTSC1; | const uint32_t portstat = USBHS_PORTSC1; | ||||
| Serial.print("port change: "); | |||||
| Serial.print(portstat, HEX); | |||||
| Serial.println(); | |||||
| println("port change: ", portstat, HEX); | |||||
| USBHS_PORTSC1 = portstat | (USBHS_PORTSC_OCC|USBHS_PORTSC_PEC|USBHS_PORTSC_CSC); | USBHS_PORTSC1 = portstat | (USBHS_PORTSC_OCC|USBHS_PORTSC_PEC|USBHS_PORTSC_CSC); | ||||
| if (portstat & USBHS_PORTSC_OCC) { | if (portstat & USBHS_PORTSC_OCC) { | ||||
| Serial.println(" overcurrent change"); | |||||
| println(" overcurrent change"); | |||||
| } | } | ||||
| if (portstat & USBHS_PORTSC_CSC) { | if (portstat & USBHS_PORTSC_CSC) { | ||||
| if (portstat & USBHS_PORTSC_CCS) { | if (portstat & USBHS_PORTSC_CCS) { | ||||
| Serial.println(" connect"); | |||||
| println(" connect"); | |||||
| if (port_state == PORT_STATE_DISCONNECTED | if (port_state == PORT_STATE_DISCONNECTED | ||||
| || port_state == PORT_STATE_DEBOUNCE) { | || port_state == PORT_STATE_DEBOUNCE) { | ||||
| // 100 ms debounce (USB 2.0: TATTDB, page 150 & 188) | // 100 ms debounce (USB 2.0: TATTDB, page 150 & 188) | ||||
| stat &= ~USBHS_USBSTS_TI0; | stat &= ~USBHS_USBSTS_TI0; | ||||
| } | } | ||||
| } else { | } else { | ||||
| Serial.println(" disconnect"); | |||||
| println(" disconnect"); | |||||
| port_state = PORT_STATE_DISCONNECTED; | port_state = PORT_STATE_DISCONNECTED; | ||||
| USBPHY_CTRL_CLR = USBPHY_CTRL_ENHOSTDISCONDETECT; | USBPHY_CTRL_CLR = USBPHY_CTRL_ENHOSTDISCONDETECT; | ||||
| // TODO: delete & clean up device state... | // TODO: delete & clean up device state... | ||||
| } | } | ||||
| if (portstat & USBHS_PORTSC_PEC) { | if (portstat & USBHS_PORTSC_PEC) { | ||||
| // PEC bit only detects disable | // PEC bit only detects disable | ||||
| Serial.println(" disable"); | |||||
| println(" disable"); | |||||
| } else if (port_state == PORT_STATE_RESET && portstat & USBHS_PORTSC_PE) { | } else if (port_state == PORT_STATE_RESET && portstat & USBHS_PORTSC_PE) { | ||||
| Serial.println(" port enabled"); | |||||
| println(" port enabled"); | |||||
| port_state = PORT_STATE_RECOVERY; | port_state = PORT_STATE_RECOVERY; | ||||
| // 10 ms reset recover (USB 2.0: TRSTRCY, page 151 & 188) | // 10 ms reset recover (USB 2.0: TRSTRCY, page 151 & 188) | ||||
| USBHS_GPTIMER0LD = 10000; // microseconds | USBHS_GPTIMER0LD = 10000; // microseconds | ||||
| } | } | ||||
| } | } | ||||
| if (portstat & USBHS_PORTSC_FPR) { | if (portstat & USBHS_PORTSC_FPR) { | ||||
| Serial.println(" force resume"); | |||||
| println(" force resume"); | |||||
| } | } | ||||
| } | } | ||||
| if (stat & USBHS_USBSTS_TI0) { // timer 0 | if (stat & USBHS_USBSTS_TI0) { // timer 0 | ||||
| Serial.println("timer"); | |||||
| println("timer"); | |||||
| if (port_state == PORT_STATE_DEBOUNCE) { | if (port_state == PORT_STATE_DEBOUNCE) { | ||||
| port_state = PORT_STATE_RESET; | port_state = PORT_STATE_RESET; | ||||
| USBHS_PORTSC1 |= USBHS_PORTSC_PR; // begin reset sequence | USBHS_PORTSC1 |= USBHS_PORTSC_PR; // begin reset sequence | ||||
| Serial.println(" begin reset"); | |||||
| println(" begin reset"); | |||||
| } else if (port_state == PORT_STATE_RECOVERY) { | } else if (port_state == PORT_STATE_RECOVERY) { | ||||
| port_state = PORT_STATE_ACTIVE; | port_state = PORT_STATE_ACTIVE; | ||||
| Serial.println(" end recovery"); | |||||
| println(" end recovery"); | |||||
| // HCSPARAMS TTCTRL page 1671 | // HCSPARAMS TTCTRL page 1671 | ||||
| uint32_t speed = (USBHS_PORTSC1 >> 26) & 3; | uint32_t speed = (USBHS_PORTSC1 >> 26) & 3; | ||||
| Transfer_t *halt; | Transfer_t *halt; | ||||
| uint32_t c=0, dtc=0, smask=0, cmask=0, offset=0; | uint32_t c=0, dtc=0, smask=0, cmask=0, offset=0; | ||||
| Serial.println("new_Pipe"); | |||||
| println("new_Pipe"); | |||||
| pipe = allocate_Pipe(); | pipe = allocate_Pipe(); | ||||
| if (!pipe) return NULL; | if (!pipe) return NULL; | ||||
| halt = allocate_Transfer(); | halt = allocate_Transfer(); | ||||
| pipe->qh.horizontal_link = (uint32_t)&(pipe->qh) | 2; // 2=QH | pipe->qh.horizontal_link = (uint32_t)&(pipe->qh) | 2; // 2=QH | ||||
| USBHS_ASYNCLISTADDR = (uint32_t)&(pipe->qh); | USBHS_ASYNCLISTADDR = (uint32_t)&(pipe->qh); | ||||
| USBHS_USBCMD |= USBHS_USBCMD_ASE; // enable async schedule | USBHS_USBCMD |= USBHS_USBCMD_ASE; // enable async schedule | ||||
| //Serial.println(" first in async list"); | |||||
| //println(" first in async list"); | |||||
| } else { | } else { | ||||
| // EHCI 1.0: section 4.8.1, page 72 | // EHCI 1.0: section 4.8.1, page 72 | ||||
| pipe->qh.horizontal_link = list->qh.horizontal_link; | pipe->qh.horizontal_link = list->qh.horizontal_link; | ||||
| list->qh.horizontal_link = (uint32_t)&(pipe->qh) | 2; | list->qh.horizontal_link = (uint32_t)&(pipe->qh) | 2; | ||||
| //Serial.println(" added to async list"); | |||||
| //println(" added to async list"); | |||||
| } | } | ||||
| } else if (type == 3) { | } else if (type == 3) { | ||||
| // interrupt: add to periodic schedule | // interrupt: add to periodic schedule | ||||
| // quick hack for testing, just put it into the first table entry | // quick hack for testing, just put it into the first table entry | ||||
| pipe->qh.horizontal_link = periodictable[0]; | pipe->qh.horizontal_link = periodictable[0]; | ||||
| periodictable[0] = (uint32_t)&(pipe->qh) | 2; // 2=QH | periodictable[0] = (uint32_t)&(pipe->qh) | 2; // 2=QH | ||||
| Serial.print("init periodictable with "); | |||||
| Serial.println(periodictable[0], HEX); | |||||
| println("init periodictable with ", periodictable[0], HEX); | |||||
| } | } | ||||
| return pipe; | return pipe; | ||||
| } | } | ||||
| Transfer_t *transfer, *data, *status; | Transfer_t *transfer, *data, *status; | ||||
| uint32_t status_direction; | uint32_t status_direction; | ||||
| Serial.println("new_Control_Transfer"); | |||||
| println("new_Control_Transfer"); | |||||
| if (setup->wLength > 16384) return false; // max 16K data for control | if (setup->wLength > 16384) return false; // max 16K data for control | ||||
| transfer = allocate_Transfer(); | transfer = allocate_Transfer(); | ||||
| if (!transfer) return false; | if (!transfer) return false; | ||||
| transfer->qtd.next = (uint32_t)status; | transfer->qtd.next = (uint32_t)status; | ||||
| status_direction = 1; // always IN, USB 2.0 page 226 | status_direction = 1; // always IN, USB 2.0 page 226 | ||||
| } | } | ||||
| //Serial.print("setup address "); | |||||
| //Serial.println((uint32_t)setup, HEX); | |||||
| //println("setup address ", (uint32_t)setup, HEX); | |||||
| init_qTD(transfer, setup, 8, 2, 0, false); | init_qTD(transfer, setup, 8, 2, 0, false); | ||||
| init_qTD(status, NULL, 0, status_direction, 1, true); | init_qTD(status, NULL, 0, status_direction, 1, true); | ||||
| status->pipe = dev->control_pipe; | status->pipe = dev->control_pipe; | ||||
| // TODO: option for zero length packet? Maybe in Pipe_t fields? | // TODO: option for zero length packet? Maybe in Pipe_t fields? | ||||
| Serial.println("new_Data_Transfer"); | |||||
| println("new_Data_Transfer"); | |||||
| // allocate qTDs | // allocate qTDs | ||||
| transfer = allocate_Transfer(); | transfer = allocate_Transfer(); | ||||
| if (!transfer) return false; | if (!transfer) return false; | ||||
| static bool followup_Transfer(Transfer_t *transfer) | static bool followup_Transfer(Transfer_t *transfer) | ||||
| { | { | ||||
| //Serial.print(" Followup "); | |||||
| //Serial.println((uint32_t)transfer, HEX); | |||||
| //println(" Followup ", (uint32_t)transfer, HEX); | |||||
| if (!(transfer->qtd.token & 0x80)) { | if (!(transfer->qtd.token & 0x80)) { | ||||
| // TODO: check error status | // TODO: check error status | ||||
| } | } | ||||
| } | } | ||||
| // do callback function... | // do callback function... | ||||
| //Serial.println(" completed"); | |||||
| //println(" completed"); | |||||
| return true; | return true; | ||||
| } | } | ||||
| return false; | return false; | ||||
| // smask: [out] Start Mask | // smask: [out] Start Mask | ||||
| // cmask: [out] Complete Mask | // cmask: [out] Complete Mask | ||||
| // | // | ||||
| static bool allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen, | |||||
| bool USBHost::allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen, | |||||
| uint32_t interval, uint32_t direction, uint32_t *offset_out, | uint32_t interval, uint32_t direction, uint32_t *offset_out, | ||||
| uint32_t *smask_out, uint32_t *cmask_out) | uint32_t *smask_out, uint32_t *cmask_out) | ||||
| { | { | ||||
| Serial.println("allocate_interrupt_pipe_bandwidth"); | |||||
| println("allocate_interrupt_pipe_bandwidth"); | |||||
| maxlen = (maxlen * 76459) >> 16; // worst case bit stuffing | maxlen = (maxlen * 76459) >> 16; // worst case bit stuffing | ||||
| if (speed == 2) { | if (speed == 2) { | ||||
| // high speed 480 Mbit/sec | // high speed 480 Mbit/sec | ||||
| min_offset = offset; | min_offset = offset; | ||||
| } | } | ||||
| } | } | ||||
| Serial.print(" min_bw = "); | |||||
| Serial.print(min_bw); | |||||
| Serial.print(", at offset = "); | |||||
| Serial.println(min_offset); | |||||
| print(" min_bw = "); | |||||
| print(min_bw); | |||||
| print(", at offset = "); | |||||
| println(min_offset); | |||||
| if (min_bw > 187) return false; | if (min_bw > 187) return false; | ||||
| for (uint32_t i=min_offset; i < PERIODIC_LIST_SIZE*8; i += interval) { | for (uint32_t i=min_offset; i < PERIODIC_LIST_SIZE*8; i += interval) { | ||||
| uframe_bandwidth[i] += stime; | uframe_bandwidth[i] += stime; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Serial.print(" min_bw = "); | |||||
| Serial.println(min_bw); | |||||
| Serial.print(", at offset = "); | |||||
| Serial.print(min_offset); | |||||
| Serial.print(", shift= "); | |||||
| Serial.println(min_shift); | |||||
| print(" min_bw = "); | |||||
| println(min_bw); | |||||
| print(", at offset = "); | |||||
| print(min_offset); | |||||
| print(", shift= "); | |||||
| println(min_shift); | |||||
| if (min_bw > 187) return false; | if (min_bw > 187) return false; | ||||
| for (uint32_t i=min_offset; i < PERIODIC_LIST_SIZE; i += interval) { | for (uint32_t i=min_offset; i < PERIODIC_LIST_SIZE; i += interval) { | ||||
| uint32_t n = (i << 3) + min_shift; | uint32_t n = (i << 3) + min_shift; |
| { | { | ||||
| Device_t *dev; | Device_t *dev; | ||||
| Serial.print("new_Device: "); | |||||
| print("new_Device: "); | |||||
| switch (speed) { | switch (speed) { | ||||
| case 0: Serial.print("12"); break; | |||||
| case 1: Serial.print("1.5"); break; | |||||
| case 2: Serial.print("480"); break; | |||||
| default: Serial.print("??"); | |||||
| case 0: print("12"); break; | |||||
| case 1: print("1.5"); break; | |||||
| case 2: print("480"); break; | |||||
| default: print("??"); | |||||
| } | } | ||||
| Serial.println(" Mbit/sec"); | |||||
| println(" Mbit/sec"); | |||||
| dev = allocate_Device(); | dev = allocate_Device(); | ||||
| if (!dev) return NULL; | if (!dev) return NULL; | ||||
| memset(dev, 0, sizeof(Device_t)); | memset(dev, 0, sizeof(Device_t)); | ||||
| return; | return; | ||||
| } | } | ||||
| Serial.println("enumeration:"); | |||||
| println("enumeration:"); | |||||
| //print_hexbytes(transfer->buffer, transfer->length); | //print_hexbytes(transfer->buffer, transfer->length); | ||||
| //print(transfer); | //print(transfer); | ||||
| dev = transfer->pipe->device; | dev = transfer->pipe->device; | ||||
| return; | return; | ||||
| case 12: // read 9 bytes, request all of config desc | case 12: // read 9 bytes, request all of config desc | ||||
| enumlen = enumbuf[2] | (enumbuf[3] << 8); | enumlen = enumbuf[2] | (enumbuf[3] << 8); | ||||
| Serial.print("Config data length = "); | |||||
| Serial.println(enumlen); | |||||
| println("Config data length = ", enumlen); | |||||
| if (enumlen > sizeof(enumbuf)) { | if (enumlen > sizeof(enumbuf)) { | ||||
| // TODO: how to handle device with too much config data | // TODO: how to handle device with too much config data | ||||
| } | } | ||||
| dev->enum_state = 13; | dev->enum_state = 13; | ||||
| return; | return; | ||||
| case 13: // read all config desc, send set config | case 13: // read all config desc, send set config | ||||
| Serial.print("bNumInterfaces = "); | |||||
| Serial.println(enumbuf[4]); | |||||
| Serial.print("bConfigurationValue = "); | |||||
| Serial.println(enumbuf[5]); | |||||
| println("bNumInterfaces = ", enumbuf[4]); | |||||
| println("bConfigurationValue = ", enumbuf[5]); | |||||
| dev->bmAttributes = enumbuf[7]; | dev->bmAttributes = enumbuf[7]; | ||||
| dev->bMaxPower = enumbuf[8]; | dev->bMaxPower = enumbuf[8]; | ||||
| // TODO: actually do something with interface descriptor? | // TODO: actually do something with interface descriptor? | ||||
| while (p < end) { | while (p < end) { | ||||
| uint8_t desclen = *p; | uint8_t desclen = *p; | ||||
| uint8_t desctype = *(p+1); | uint8_t desctype = *(p+1); | ||||
| Serial.print("Descriptor "); | |||||
| Serial.print(desctype); | |||||
| Serial.print(" = "); | |||||
| if (desctype == 4) Serial.println("INTERFACE"); | |||||
| else if (desctype == 5) Serial.println("ENDPOINT"); | |||||
| else if (desctype == 6) Serial.println("DEV_QUALIFIER"); | |||||
| else if (desctype == 7) Serial.println("OTHER_SPEED"); | |||||
| else if (desctype == 11) Serial.println("IAD"); | |||||
| else if (desctype == 33) Serial.println("HID"); | |||||
| else Serial.println(" ???"); | |||||
| print("Descriptor "); | |||||
| print(desctype); | |||||
| print(" = "); | |||||
| if (desctype == 4) println("INTERFACE"); | |||||
| else if (desctype == 5) println("ENDPOINT"); | |||||
| else if (desctype == 6) println("DEV_QUALIFIER"); | |||||
| else if (desctype == 7) println("OTHER_SPEED"); | |||||
| else if (desctype == 11) println("IAD"); | |||||
| else if (desctype == 33) println("HID"); | |||||
| else println(" ???"); | |||||
| if (desctype == 11 && desclen == 8) { | if (desctype == 11 && desclen == 8) { | ||||
| // TODO: parse IAD, ask drivers for claim | // TODO: parse IAD, ask drivers for claim | ||||
| // TODO: how to skip over all interfaces IAD represented | // TODO: how to skip over all interfaces IAD represented | ||||
| static void pipe_set_maxlen(Pipe_t *pipe, uint32_t maxlen) | static void pipe_set_maxlen(Pipe_t *pipe, uint32_t maxlen) | ||||
| { | { | ||||
| Serial.print("pipe_set_maxlen "); | |||||
| Serial.println(maxlen); | |||||
| pipe->qh.capabilities[0] = (pipe->qh.capabilities[0] & 0x8000FFFF) | (maxlen << 16); | pipe->qh.capabilities[0] = (pipe->qh.capabilities[0] & 0x8000FFFF) | (maxlen << 16); | ||||
| } | } | ||||
| static void pipe_set_addr(Pipe_t *pipe, uint32_t addr) | static void pipe_set_addr(Pipe_t *pipe, uint32_t addr) | ||||
| { | { | ||||
| Serial.print("pipe_set_addr "); | |||||
| Serial.println(addr); | |||||
| pipe->qh.capabilities[0] = (pipe->qh.capabilities[0] & 0xFFFFFF80) | addr; | pipe->qh.capabilities[0] = (pipe->qh.capabilities[0] & 0xFFFFFF80) | addr; | ||||
| } | } | ||||
| // only claim entire device, never at interface level | // only claim entire device, never at interface level | ||||
| if (type != 0) return false; | if (type != 0) return false; | ||||
| Serial.print("USBHub claim_device this="); | |||||
| Serial.println((uint32_t)this, HEX); | |||||
| println("USBHub claim_device this=", (uint32_t)this, HEX); | |||||
| // check for HUB type | // check for HUB type | ||||
| if (dev->bDeviceClass != 9 || dev->bDeviceSubClass != 0) return false; | if (dev->bDeviceClass != 9 || dev->bDeviceSubClass != 0) return false; | ||||
| if (maxsize == 0) return false; | if (maxsize == 0) return false; | ||||
| if (maxsize > 1) return false; // do hub chips with > 7 ports exist? | if (maxsize > 1) return false; // do hub chips with > 7 ports exist? | ||||
| Serial.println(descriptors[9]); | |||||
| Serial.println(descriptors[10]); | |||||
| Serial.println(descriptors[11], HEX); | |||||
| Serial.println(maxsize); | |||||
| println(descriptors[9]); | |||||
| println(descriptors[10]); | |||||
| println(descriptors[11], HEX); | |||||
| println(maxsize); | |||||
| // bDeviceProtocol = 0 is full speed | // bDeviceProtocol = 0 is full speed | ||||
| // bDeviceProtocol = 1 is high speed single TT | // bDeviceProtocol = 1 is high speed single TT | ||||
| // bDeviceProtocol = 2 is high speed multiple TT | // bDeviceProtocol = 2 is high speed multiple TT | ||||
| Serial.print("bDeviceClass = "); | |||||
| Serial.println(dev->bDeviceClass); | |||||
| Serial.print("bDeviceSubClass = "); | |||||
| Serial.println(dev->bDeviceSubClass); | |||||
| Serial.print("bDeviceProtocol = "); | |||||
| Serial.println(dev->bDeviceProtocol); | |||||
| println("bDeviceClass = ", dev->bDeviceClass); | |||||
| println("bDeviceSubClass = ", dev->bDeviceSubClass); | |||||
| println("bDeviceProtocol = ", dev->bDeviceProtocol); | |||||
| changepipe = NULL; | changepipe = NULL; | ||||
| changebits = 0; | changebits = 0; | ||||
| void USBHub::control(const Transfer_t *transfer) | void USBHub::control(const Transfer_t *transfer) | ||||
| { | { | ||||
| Serial.println("USBHub control callback"); | |||||
| println("USBHub control callback"); | |||||
| print_hexbytes(transfer->buffer, transfer->length); | print_hexbytes(transfer->buffer, transfer->length); | ||||
| if (state == 0) { | if (state == 0) { | ||||
| powertime = hub_desc[5]; | powertime = hub_desc[5]; | ||||
| // TODO: do we need to use the DeviceRemovable | // TODO: do we need to use the DeviceRemovable | ||||
| // bits to mke synthetic device connect events? | // bits to mke synthetic device connect events? | ||||
| Serial.print("Hub has "); | |||||
| Serial.print(numports); | |||||
| Serial.println(" ports"); | |||||
| print("Hub has "); | |||||
| print(numports); | |||||
| println(" ports"); | |||||
| state = 1; | state = 1; | ||||
| poweron(1); | poweron(1); | ||||
| } | } | ||||
| // turn on power to all ports | // turn on power to all ports | ||||
| poweron(++state); | poweron(++state); | ||||
| } else if (state == numports) { | } else if (state == numports) { | ||||
| Serial.println("power turned on to all ports"); | |||||
| Serial.print("device addr = "); | |||||
| Serial.println(device->address); | |||||
| println("power turned on to all ports"); | |||||
| println("device addr = ", device->address); | |||||
| changepipe = new_Pipe(device, 3, endpoint, 1, 1, 512); | changepipe = new_Pipe(device, 3, endpoint, 1, 1, 512); | ||||
| Serial.print("pipe cap1 = "); | |||||
| Serial.println(changepipe->qh.capabilities[0], HEX); | |||||
| println("pipe cap1 = ", changepipe->qh.capabilities[0], HEX); | |||||
| changepipe->callback_function = callback; | changepipe->callback_function = callback; | ||||
| queue_Data_Transfer(changepipe, &changebits, 1, this); | queue_Data_Transfer(changepipe, &changebits, 1, this); | ||||
| state = 255; | state = 255; | ||||
| // up and running... | // up and running... | ||||
| switch (setup.word1) { | switch (setup.word1) { | ||||
| case 0x000000A0: // get hub status | case 0x000000A0: // get hub status | ||||
| Serial.println("New Hub Status"); | |||||
| println("New Hub Status"); | |||||
| clearstatus(0); | clearstatus(0); | ||||
| return; | return; | ||||
| case 0x000000A3: // get port status | case 0x000000A3: // get port status | ||||
| clearstatus(setup.wIndex); | clearstatus(setup.wIndex); | ||||
| return; | return; | ||||
| case 0x00100120: // clear hub status | case 0x00100120: // clear hub status | ||||
| Serial.println("Hub Status Cleared"); | |||||
| println("Hub Status Cleared"); | |||||
| changebits &= ~1; | changebits &= ~1; | ||||
| break; | break; | ||||
| case 0x00100123: // clear port status | case 0x00100123: // clear port status | ||||
| Serial.print("Port Status Cleared, port="); | |||||
| Serial.println(setup.wIndex); | |||||
| println("Port Status Cleared, port=", setup.wIndex); | |||||
| changebits &= ~(1 << setup.wIndex); | changebits &= ~(1 << setup.wIndex); | ||||
| break; | break; | ||||
| } | } | ||||
| void USBHub::callback(const Transfer_t *transfer) | void USBHub::callback(const Transfer_t *transfer) | ||||
| { | { | ||||
| Serial.println("HUB Callback (static)"); | |||||
| println("HUB Callback (static)"); | |||||
| if (transfer->driver) ((USBHub *)(transfer->driver))->status_change(transfer); | if (transfer->driver) ((USBHub *)(transfer->driver))->status_change(transfer); | ||||
| } | } | ||||
| void USBHub::status_change(const Transfer_t *transfer) | void USBHub::status_change(const Transfer_t *transfer) | ||||
| { | { | ||||
| Serial.println("HUB Callback (member)"); | |||||
| Serial.print("status = "); | |||||
| Serial.println(changebits, HEX); | |||||
| println("HUB Callback (member)"); | |||||
| println("status = ", changebits, HEX); | |||||
| // TODO: do something with the status change info | // TODO: do something with the status change info | ||||
| update_status(); | update_status(); | ||||
| queue_Data_Transfer(changepipe, &changebits, 1, this); | queue_Data_Transfer(changepipe, &changebits, 1, this); | ||||
| uint32_t priorstatus = portstatus[port - 1]; | uint32_t priorstatus = portstatus[port - 1]; | ||||
| portstatus[port] = status; | portstatus[port] = status; | ||||
| Serial.print("New Port Status, port="); | |||||
| Serial.print(port); | |||||
| Serial.print(", status="); | |||||
| Serial.println(status, HEX); | |||||
| print("New Port Status, port="); | |||||
| print(port); | |||||
| print(", status="); | |||||
| println(status, HEX); | |||||
| // status bits, USB 2.0: 11.24.2.7.1 page 427 | // status bits, USB 2.0: 11.24.2.7.1 page 427 | ||||
| if (status & 0x0001) Serial.println(" Device is present: "); | |||||
| if (status & 0x0001) println(" Device is present: "); | |||||
| if (status & 0x0002) { | if (status & 0x0002) { | ||||
| Serial.println(" Enabled, speed = "); | |||||
| println(" Enabled, speed = "); | |||||
| if (status & 0x0200) { | if (status & 0x0200) { | ||||
| Serial.print("1.5"); | |||||
| print("1.5"); | |||||
| } else { | } else { | ||||
| if (status & 0x0400) { | if (status & 0x0400) { | ||||
| Serial.print("480"); | |||||
| print("480"); | |||||
| } else { | } else { | ||||
| Serial.print("12"); | |||||
| print("12"); | |||||
| } | } | ||||
| } | } | ||||
| Serial.println(" Mbit/sec"); | |||||
| println(" Mbit/sec"); | |||||
| } | } | ||||
| if (status & 0x0004) Serial.println(" Suspended"); | |||||
| if (status & 0x0008) Serial.println(" Over-current"); | |||||
| if (status & 0x0010) Serial.println(" Reset"); | |||||
| if (status & 0x0100) Serial.println(" Has Power"); | |||||
| if (status & 0x0800) Serial.println(" Test Mode"); | |||||
| if (status & 0x1000) Serial.println(" Software Controls LEDs"); | |||||
| if (status & 0x0004) println(" Suspended"); | |||||
| if (status & 0x0008) println(" Over-current"); | |||||
| if (status & 0x0010) println(" Reset"); | |||||
| if (status & 0x0100) println(" Has Power"); | |||||
| if (status & 0x0800) println(" Test Mode"); | |||||
| if (status & 0x1000) println(" Software Controls LEDs"); | |||||
| if ((status & 0x0001) && !(priorstatus & 0x0001)) { | if ((status & 0x0001) && !(priorstatus & 0x0001)) { | ||||
| Serial.println(" connect"); | |||||
| println(" connect"); | |||||
| // 100 ms debounce (USB 2.0: TATTDB, page 150 & 188) | // 100 ms debounce (USB 2.0: TATTDB, page 150 & 188) | ||||
| delay(100); // TODO: horribly bad... need timing events | delay(100); // TODO: horribly bad... need timing events | ||||
| reset(port); | reset(port); | ||||
| // TODO... reset timer? | // TODO... reset timer? | ||||
| } else if (!(status & 0x0001) && (priorstatus & 0x0001)) { | } else if (!(status & 0x0001) && (priorstatus & 0x0001)) { | ||||
| Serial.println(" disconnect"); | |||||
| println(" disconnect"); | |||||
| } | } |
| bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | ||||
| { | { | ||||
| Serial.print("KeyboardController claim this="); | |||||
| Serial.println((uint32_t)this, HEX); | |||||
| println("KeyboardController claim this=", (uint32_t)this, HEX); | |||||
| // only claim at interface level | // only claim at interface level | ||||
| if (type != 1) return false; | if (type != 1) return false; | ||||
| if (descriptors[18] != 7) return false; | if (descriptors[18] != 7) return false; | ||||
| if (descriptors[19] != 5) return false; // endpoint descriptor | if (descriptors[19] != 5) return false; // endpoint descriptor | ||||
| uint32_t endpoint = descriptors[20]; | uint32_t endpoint = descriptors[20]; | ||||
| Serial.print("ep = "); | |||||
| Serial.println(endpoint, HEX); | |||||
| println("ep = ", endpoint, HEX); | |||||
| if ((endpoint & 0xF0) != 0x80) return false; // must be IN direction | if ((endpoint & 0xF0) != 0x80) return false; // must be IN direction | ||||
| endpoint &= 0x0F; | endpoint &= 0x0F; | ||||
| if (endpoint == 0) return false; | if (endpoint == 0) return false; | ||||
| if (descriptors[21] != 3) return false; // must be interrupt type | if (descriptors[21] != 3) return false; // must be interrupt type | ||||
| uint32_t size = descriptors[22] | (descriptors[23] << 8); | uint32_t size = descriptors[22] | (descriptors[23] << 8); | ||||
| Serial.print("packet size = "); | |||||
| Serial.println(size); | |||||
| println("packet size = ", size); | |||||
| if (size != 8) return false; // must be 8 bytes for Keyboard Boot Protocol | if (size != 8) return false; // must be 8 bytes for Keyboard Boot Protocol | ||||
| uint32_t interval = descriptors[24]; | uint32_t interval = descriptors[24]; | ||||
| Serial.print("polling interval = "); | |||||
| Serial.println(interval); | |||||
| println("polling interval = ", interval); | |||||
| datapipe = new_Pipe(dev, 3, endpoint, 1, 8, 64); | datapipe = new_Pipe(dev, 3, endpoint, 1, 8, 64); | ||||
| datapipe->callback_function = callback; | datapipe->callback_function = callback; | ||||
| queue_Data_Transfer(datapipe, report, 8, this); | queue_Data_Transfer(datapipe, report, 8, this); | ||||
| void KeyboardController::callback(const Transfer_t *transfer) | void KeyboardController::callback(const Transfer_t *transfer) | ||||
| { | { | ||||
| Serial.println("KeyboardController Callback (static)"); | |||||
| println("KeyboardController Callback (static)"); | |||||
| if (transfer->driver) { | if (transfer->driver) { | ||||
| ((KeyboardController *)(transfer->driver))->new_data(transfer); | ((KeyboardController *)(transfer->driver))->new_data(transfer); | ||||
| } | } | ||||
| void KeyboardController::new_data(const Transfer_t *transfer) | void KeyboardController::new_data(const Transfer_t *transfer) | ||||
| { | { | ||||
| Serial.println("KeyboardController Callback (member)"); | |||||
| Serial.print(" KB Data: "); | |||||
| println("KeyboardController Callback (member)"); | |||||
| print(" KB Data: "); | |||||
| print_hexbytes(transfer->buffer, 8); | print_hexbytes(transfer->buffer, 8); | ||||
| // TODO: parse the new data | // TODO: parse the new data | ||||
| queue_Data_Transfer(datapipe, report, 8, this); | queue_Data_Transfer(datapipe, report, 8, this); |
| Serial.println(); | Serial.println(); | ||||
| } | } | ||||
| void USBHost::print(const char *s) | |||||
| { | |||||
| Serial.println(s); | |||||
| } | |||||
| void USBHost::print(const char *s, int num) | |||||
| { | |||||
| Serial.print(s); | |||||
| Serial.println(num); | |||||
| } | |||||