void usb_stream_reset(struct usb_stream_config const *config) { config->out_desc->flags = DOEPDMA_RXBYTES(config->tx_size) | DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; config->out_desc->addr = config->rx_ram; GR_USB_DOEPDMA(config->endpoint) = (uint32_t)config->out_desc; config->in_desc->flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | DIEPDMA_IOC; config->in_desc->addr = config->tx_ram; GR_USB_DIEPDMA(config->endpoint) = (uint32_t)config->in_desc; GR_USB_DOEPCTL(config->endpoint) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK | DXEPCTL_CNAK | DXEPCTL_EPENA; GR_USB_DIEPCTL(config->endpoint) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK | DXEPCTL_TXFNUM(config->endpoint); GR_USB_DAINTMSK |= DAINT_INEP(config->endpoint) | DAINT_OUTEP(config->endpoint); *config->is_reset = 1; /* Flush any queued data */ hook_call_deferred(config->deferred_tx, 0); hook_call_deferred(config->deferred_rx, 0); }
static void showregs(void) { ccprintf("GINTSTS: 0x%08x\n", GR_USB_GINTSTS); showbits(GR_USB_GINTSTS); ccprintf("GINTMSK: 0x%08x\n", GR_USB_GINTMSK); showbits(GR_USB_GINTMSK); ccprintf("DAINT: 0x%08x\n", GR_USB_DAINT); ccprintf("DAINTMSK: 0x%08x\n", GR_USB_DAINTMSK); ccprintf("DOEPMSK: 0x%08x\n", GR_USB_DOEPMSK); ccprintf("DIEPMSK: 0x%08x\n", GR_USB_DIEPMSK); ccprintf("DCFG: 0x%08x\n", GR_USB_DCFG); ccprintf("DOEPCTL0: 0x%08x\n", GR_USB_DOEPCTL(0)); ccprintf("DIEPCTL0: 0x%08x\n", GR_USB_DIEPCTL(0)); ccprintf("DOEPCTL1: 0x%08x\n", GR_USB_DOEPCTL(1)); ccprintf("DIEPCTL1: 0x%08x\n", GR_USB_DIEPCTL(1)); ccprintf("DOEPCTL2: 0x%08x\n", GR_USB_DOEPCTL(2)); ccprintf("DIEPCTL2: 0x%08x\n", GR_USB_DIEPCTL(2)); }
/* No Data phase, just Status phase (which is IN, since Setup is OUT) */ static void expect_status_phase_in(enum table_case tc) { print_later("expect_status_phase_in(%c)", "0ABCDE67"[tc], 0, 0, 0, 0); what_am_i_doing = NO_DATA_STAGE; /* Expect a zero-length IN for the Status phase */ (void) load_in_fifo(0, 0); /* We apparently have to do this every time we transmit anything */ flush_in_fifo(); /* I don't think we have to do this every time, but the Programmer's * Guide says to, so... */ GR_USB_DIEPDMA(0) = (uint32_t)(cur_in_desc); /* Blindly following instructions here, too. */ if (tc == TABLE_CASE_C) GR_USB_DIEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; else GR_USB_DIEPCTL(0) = DXEPCTL_EPENA; /* The Programmer's Guide instructions for the Normal Two-Stage Control * Transfer leave this next bit out, so we only need it if we intend to * process an Exceptional Two-Stage Control Transfer. Because obviously * we always know in advance what the host is going to do. Idiots. */ /* Be prepared to get a new Setup packet during the Status phase */ next_out_desc->flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) | DOEPDMA_IOC | DOEPDMA_LAST; /* We've already set GR_USB_DOEPDMA(0), so just enable it. */ if (tc == TABLE_CASE_C) GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; else GR_USB_DOEPCTL(0) = DXEPCTL_EPENA; /* Get an interrupt when either IN or OUT arrives */ GR_USB_DAINTMSK |= (DAINT_OUTEP(0) | DAINT_INEP(0)); }
/* The TX FIFO buffer is loaded. Start the Data phase. */ static void expect_data_phase_in(enum table_case tc) { print_later("expect_data_phase_in(%c)", "0ABCDE67"[tc], 0, 0, 0, 0); what_am_i_doing = DATA_STAGE_IN; /* We apparently have to do this every time we transmit anything */ flush_in_fifo(); /* I don't think we have to do this every time, but the Programmer's * Guide says to, so... */ GR_USB_DIEPDMA(0) = (uint32_t)(cur_in_desc); /* Blindly following instructions here, too. */ if (tc == TABLE_CASE_C) GR_USB_DIEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; else GR_USB_DIEPCTL(0) = DXEPCTL_EPENA; /* * When the IN is done, we expect a zero-length OUT for the status * phase but it could be an early SETUP instead. We'll have to deal * with either one when it arrives. */ next_out_desc->flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) | DOEPDMA_IOC | DOEPDMA_LAST; /* And here's this jimmy rustler again... */ if (tc == TABLE_CASE_C) GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; else GR_USB_DOEPCTL(0) = DXEPCTL_EPENA; /* Get an interrupt when either IN or OUT arrives */ GR_USB_DAINTMSK |= (DAINT_OUTEP(0) | DAINT_INEP(0)); }
/* Let the USB HW OUT-from-host FIFO receive some bytes */ static void usb_enable_rx(int len) { struct dwc_usb_ep *ep = &ep_console_ctl; ep->out_data = ep->out_databuffer; ep->out_pending = 0; GR_USB_DOEPTSIZ(USB_EP_CONSOLE) = 0; GR_USB_DOEPTSIZ(USB_EP_CONSOLE) |= DXEPTSIZ_PKTCNT(1); GR_USB_DOEPTSIZ(USB_EP_CONSOLE) |= DXEPTSIZ_XFERSIZE(len); GR_USB_DOEPDMA(USB_EP_CONSOLE) = (uint32_t)ep->out_data; GR_USB_DOEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA; }
/* The next packet from the host should be a Setup packet. Get ready for it. */ static void expect_setup_packet(void) { print_later("expect_setup_packet()", 0, 0, 0, 0, 0); what_am_i_doing = WAITING_FOR_SETUP_PACKET; next_out_desc->flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) | DOEPDMA_IOC | DOEPDMA_LAST; /* We don't care about IN packets right now, only OUT. */ GR_USB_DAINTMSK |= DAINT_OUTEP(0); GR_USB_DAINTMSK &= ~DAINT_INEP(0); /* Let it run. We might need CNAK if we just got an OUT for status */ GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; }
/* We're complaining about something by stalling both IN and OUT packets, * but a SETUP packet will get through anyway, so prepare for it. */ static void stall_both_fifos(void) { print_later("stall_both_fifos()", 0, 0, 0, 0, 0); what_am_i_doing = WAITING_FOR_SETUP_PACKET; next_out_desc->flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) | DOEPDMA_IOC | DOEPDMA_LAST; /* We don't care about IN packets right now, only OUT. */ GR_USB_DAINTMSK |= DAINT_OUTEP(0); GR_USB_DAINTMSK &= ~DAINT_INEP(0); /* Stall both IN and OUT. The hardware will reset them when the next * SETUP comes along. */ GR_USB_DOEPCTL(0) = DXEPCTL_STALL | DXEPCTL_EPENA; flush_in_fifo(); GR_USB_DIEPCTL(0) = DXEPCTL_STALL | DXEPCTL_EPENA; }
/* Rx/OUT interrupt handler */ static void con_ep_rx(void) { struct dwc_usb_ep *ep = &ep_console_ctl; if (GR_USB_DOEPCTL(USB_EP_CONSOLE) & DXEPCTL_EPENA) return; /* Bytes received decrement DOEPTSIZ XFERSIZE */ if (GR_USB_DOEPINT(USB_EP_CONSOLE) & DOEPINT_XFERCOMPL) { ep->out_pending = ep->max_packet - (GR_USB_DOEPTSIZ(USB_EP_CONSOLE) & GC_USB_DOEPTSIZ1_XFERSIZE_MASK); } /* Wake up the Rx FIFO handler */ hook_call_deferred(&rx_fifo_handler_data, 0); /* clear the RX/OUT interrupts */ GR_USB_DOEPINT(USB_EP_CONSOLE) = 0xffffffff; }
static void ep_reset(void) { ep_out_desc.flags = DOEPDMA_RXBYTES(USB_MAX_PACKET_SIZE) | DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; ep_out_desc.addr = ep_buf_rx; GR_USB_DOEPDMA(USB_EP_BLOB) = (uint32_t)&ep_out_desc; ep_in_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | DIEPDMA_IOC; ep_in_desc.addr = ep_buf_tx; GR_USB_DIEPDMA(USB_EP_BLOB) = (uint32_t)&ep_in_desc; GR_USB_DOEPCTL(USB_EP_BLOB) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK | DXEPCTL_CNAK | DXEPCTL_EPENA; GR_USB_DIEPCTL(USB_EP_BLOB) = DXEPCTL_MPS(64) | DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK | DXEPCTL_TXFNUM(USB_EP_BLOB); GR_USB_DAINTMSK |= (1<<USB_EP_BLOB) | (1 << (USB_EP_BLOB+16)); is_reset = 1; /* Flush any queued data */ hook_call_deferred(tx_fifo_handler, 0); hook_call_deferred(rx_fifo_handler, 0); }
/* This handles both IN and OUT interrupts for EP0 */ static void ep0_interrupt(uint32_t intr_on_out, uint32_t intr_on_in) { uint32_t doepint, diepint; enum table_case tc; int sr; /* Determine the interrupt cause and clear the bits quickly, but only * if they really apply. I don't think they're trustworthy if we didn't * actually get an interrupt. */ doepint = GR_USB_DOEPINT(0); if (intr_on_out) GR_USB_DOEPINT(0) = doepint; diepint = GR_USB_DIEPINT(0); if (intr_on_in) GR_USB_DIEPINT(0) = diepint; print_later("doepint%c 0x%08x diepint%c 0x%08x what %d", intr_on_out ? '!' : '_', doepint, intr_on_in ? '!' : '_', diepint, what_am_i_doing); /* Update current and pending RX FIFO buffers */ if (intr_on_out && (doepint & DOEPINT_XFERCOMPL)) got_RX_packet(); /* Decode the situation according to Table 10-7 */ tc = decode_table_10_7(doepint); sr = cur_out_desc->flags & DOEPDMA_SR; print_later("cur_out_idx %d flags 0x%08x case=%c SR=%d", cur_out_idx, cur_out_desc->flags, "0ABCDE67"[tc], !!sr, 0); switch (what_am_i_doing) { case WAITING_FOR_SETUP_PACKET: if (tc == TABLE_CASE_A || tc == TABLE_CASE_C) { if (sr) { handle_setup(tc); } else { report_error(tc); print_later("next_out_idx %d flags 0x%08x", next_out_idx, next_out_desc->flags, 0, 0, 0); expect_setup_packet(); } } /* This only happens if we're stalling, so keep doing it. */ if (tc == TABLE_CASE_B) { print_later("Still waiting for Setup...", 0, 0, 0, 0, 0); stall_both_fifos(); } break; case DATA_STAGE_IN: if (intr_on_in && (diepint & DIEPINT_XFERCOMPL)) { print_later("IN is complete? Maybe? How do we know?", 0, 0, 0, 0, 0); /* I don't *think* we need to do this, unless we need * to transfer more data. Customer support agrees and * it shouldn't matter if the host is well-behaved, but * it seems like we had issues without it. * TODO: Test this case until we know for sure. */ GR_USB_DIEPCTL(0) = DXEPCTL_EPENA; /* * The Programmer's Guide says (p291) to stall any * further INs, but that's stupid because it'll destroy * the packet we just transferred to SPRAM, so don't do * that (we tried it anyway, and Bad Things happened). * Also don't stop here, but keep looking at stuff. */ } /* But we should ignore the OUT endpoint if we didn't actually * get an OUT interrupt. */ if (!intr_on_out) break; if (tc == TABLE_CASE_B) { print_later("IN has been detected...", 0, 0, 0, 0, 0); /* The first IN packet has been seen. Keep going. */ GR_USB_DIEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; break; } if (tc == TABLE_CASE_A) { if (!sr) { /* We've handled the Status phase. All done. */ print_later("Status phase complete", 0, 0, 0, 0, 0); expect_setup_packet(); break; } /* We expected an OUT, but got a Setup. Deal with it. */ print_later("Early Setup", 0, 0, 0, 0, 0); handle_setup(tc); break; } /* From the Exceptional Control Read Transfer section ... */ if (tc == TABLE_CASE_C) { if (sr) { print_later("Early Setup w/Data packet seen", 0, 0, 0, 0, 0); handle_setup(tc); break; } print_later("Status phase complete. I think...", 0, 0, 0, 0, 0); expect_setup_packet(); break; } /* Anything else should be ignorable. Right? */ break; case NO_DATA_STAGE: if (intr_on_in && (diepint & DIEPINT_XFERCOMPL)) { print_later("IN descriptor processed", 0, 0, 0, 0, 0); /* Let the IN proceed */ GR_USB_DIEPCTL(0) = DXEPCTL_EPENA; } /* Done unless we got an OUT interrupt */ if (!intr_on_out) break; if (tc == TABLE_CASE_B) { print_later("IN has been detected...", 0, 0, 0, 0, 0); /* Let the IN proceed */ GR_USB_DIEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; /* Reenable the previously prepared OUT descriptor. */ GR_USB_DOEPCTL(0) = DXEPCTL_CNAK | DXEPCTL_EPENA; break; } if (tc == TABLE_CASE_A || tc == TABLE_CASE_C) { if (sr) { /* We expected an IN, but got a Setup. */ print_later("Early Setup", 0, 0, 0, 0, 0); handle_setup(tc); break; } } /* Anything else means get ready for a Setup packet */ print_later("Status phase complete. Maybe.", 0, 0, 0, 0, 0); expect_setup_packet(); break; } }
/* Let the USB HW OUT-from-host FIFO receive some bytes */ static void usb_enable_rx(struct usb_stream_config const *config, int len) { config->out_desc->flags = DOEPDMA_RXBYTES(len) | DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; GR_USB_DOEPCTL(config->endpoint) |= DXEPCTL_CNAK | DXEPCTL_EPENA; }
/* Let the USB HW OUT-from-host FIFO receive some bytes */ static void usb_enable_rx(int len) { ep_out_desc.flags = DOEPDMA_RXBYTES(len) | DOEPDMA_LAST | DOEPDMA_BS_HOST_RDY | DOEPDMA_IOC; GR_USB_DOEPCTL(USB_EP_BLOB) |= DXEPCTL_CNAK | DXEPCTL_EPENA; }