static void handle_in_ep(int ep) { int ep_sts = USB_IEP_STS(ep) & ~USB_IEP_STS_MASK(ep); if (ep > 3) panicf("in_ep > 3?!"); USB_IEP_STS(ep) = ep_sts; /* ack */ if (ep_sts & USB_EP_STAT_BNA) { /* Buffer was not set up */ int ctrl = USB_IEP_CTRL(ep); logf("ep%d IN, status %x ctrl %x (BNA)\n", ep, ep_sts, ctrl); panicf("ep%d IN 0x%x 0x%x (BNA)", ep, ep_sts, ctrl); } if (ep_sts & USB_EP_STAT_TDC) { endpoints[ep][0].state &= ~EP_STATE_BUSY; endpoints[ep][0].rc = 0; logf("EP%d %x %stx done len %x stat %08x\n", ep, ep_sts, endpoints[ep][0].state & EP_STATE_ASYNC ? "async " :"", endpoints[ep][0].len, endpoints[ep][0].uc_desc->status); if (endpoints[ep][0].state & EP_STATE_ASYNC) { endpoints[ep][0].state &= ~EP_STATE_ASYNC; usb_core_transfer_complete(ep, USB_DIR_IN, 0, endpoints[ep][0].len); } else { semaphore_release(&endpoints[ep][0].complete); } ep_sts &= ~USB_EP_STAT_TDC; } if (ep_sts) { logf("ep%d IN, hwstat %lx, epstat %x\n", ep, USB_IEP_STS(ep), endpoints[ep][0].state); panicf("ep%d IN 0x%x", ep, ep_sts); } }
/* UDC ISR function */ void INT_UDC(void) { uint32_t txstat, rxstat; int tmp, ep_num; /* read what caused UDC irq */ uint32_t intsrc = INT2FLAG & 0x7fffff; if (intsrc & (1<<1)) /* setup interrupt */ { setup_received(); } else if (intsrc & (1<<2)) /* ep0 in interrupt */ { txstat = TX0STAT; /* read clears flags */ /* TODO handle errors */ if (txstat & (1<<18)) /* check TxACK flag */ { if (ctrlep[DIR_IN].cnt >= 0) { /* we still have data to send (or ZLP) */ ctr_write(); } else { /* final ack received */ usb_core_transfer_complete(0, /* ep */ USB_DIR_IN, /* dir */ 0, /* status */ ctrlep[DIR_IN].len); /* length */ /* release semaphore for blocking transfer */ if (ctrlep[DIR_IN].block) semaphore_release(&ctrlep[DIR_IN].complete); } } } else if (intsrc & (1<<3)) /* ep0 out interrupt */ { rxstat = RX0STAT; /* TODO handle errors */ if (rxstat & (1<<18)) /* RxACK */ { if (ctrlep[DIR_OUT].cnt > 0) ctr_read(); else usb_core_transfer_complete(0, /* ep */ USB_DIR_OUT, /* dir */ 0, /* status */ ctrlep[DIR_OUT].len); /* length */ } } else if (intsrc & (1<<4)) /* usb reset */ { usb_drv_init(); } else if (intsrc & (1<<5)) /* usb resume */ { TX0CON |= (1<<0); /* TxClr */ TX0CON &= ~(1<<0); RX0CON |= (1<<1); /* RxClr */ RX0CON &= (1<<1); } else if (intsrc & (1<<6)) /* usb suspend */ { } else if (intsrc & (1<<7)) /* usb connect */ { } else { /* lets figure out which ep generated irq */ tmp = intsrc >> 7; for (ep_num=1; ep_num < 15; ep_num++) { tmp >>= ep_num; if (tmp & 0x01) break; } if (intsrc & ((1<<8)|(1<<11)|(1<<14)|(1<<17)|(1<<20))) { /* bulk out */ rxstat = BOUT_RXSTAT(ep_num); /* TODO handle errors */ if (rxstat & (1<<18)) /* RxACK */ { if (endpoints[ep_num].cnt > 0) blk_read(ep_num); else usb_core_transfer_complete(ep_num, /* ep */ USB_DIR_OUT, /* dir */ 0, /* status */ endpoints[ep_num].len); /* length */ } } else if (intsrc & ((1<<9)|(1<<12)|(1<<15)|(1<<18)|(1<<21))) { /* bulk in */ txstat = BIN_TXSTAT(ep_num); /* TODO handle errors */ if (txstat & (1<<18)) /* check TxACK flag */ { if (endpoints[ep_num].cnt >= 0) { /* we still have data to send (or ZLP) */ blk_write(ep_num); } else { /* final ack received */ usb_core_transfer_complete(ep_num, /* ep */ USB_DIR_IN, /* dir */ 0, /* status */ endpoints[ep_num].len); /* length */ /* release semaphore for blocking transfer */ if (endpoints[ep_num].block) semaphore_release(&endpoints[ep_num].complete); } } } else if (intsrc & ((1<<10)|(1<13)|(1<<16)|(1<<19)|(1<<22))) { /* int in */ txstat = IIN_TXSTAT(ep_num); /* TODO handle errors */ if (txstat & (1<<18)) /* check TxACK flag */ { if (endpoints[ep_num].cnt >= 0) { /* we still have data to send (or ZLP) */ int_write(ep_num); } else { /* final ack received */ usb_core_transfer_complete(ep_num, /* ep */ USB_DIR_IN, /* dir */ 0, /* status */ endpoints[ep_num].len); /* length */ /* release semaphore for blocking transfer */ if (endpoints[ep_num].block) semaphore_release(&endpoints[ep_num].complete); } } } } }
/* IRQ handler */ void INT_USB_FUNC(void) { int i; uint32_t ints = GINTSTS; uint32_t epints; if (ints & 0x1000) /* bus reset */ { DCFG = 4; /* Address 0 */ reset_endpoints(1); usb_core_bus_reset(); } if (ints & 0x2000) /* enumeration done, we now know the speed */ { /* Set up the maximum packet sizes accordingly */ uint32_t maxpacket = usb_drv_port_speed() ? 512 : 64; DIEPCTL1 = (DIEPCTL1 & ~0x000003FF) | maxpacket; DOEPCTL2 = (DOEPCTL2 & ~0x000003FF) | maxpacket; DIEPCTL3 = (DIEPCTL3 & ~0x000003FF) | maxpacket; DOEPCTL4 = (DOEPCTL4 & ~0x000003FF) | maxpacket; } if (ints & 0x40000) /* IN EP event */ for (i = 0; i < 4; i += i + 1) // 0, 1, 3 if ((epints = DIEPINT(i))) { if (epints & 1) /* Transfer completed */ { invalidate_dcache(); int bytes = endpoints[i].size - (DIEPTSIZ(i) & 0x3FFFF); if (endpoints[i].busy) { endpoints[i].busy = false; endpoints[i].rc = 0; endpoints[i].done = true; usb_core_transfer_complete(i, USB_DIR_IN, 0, bytes); semaphore_release(&endpoints[i].complete); } } if (epints & 4) /* AHB error */ panicf("USB: AHB error on IN EP%d", i); if (epints & 8) /* Timeout */ { if (endpoints[i].busy) { endpoints[i].busy = false; endpoints[i].rc = 1; endpoints[i].done = true; semaphore_release(&endpoints[i].complete); } } DIEPINT(i) = epints; } if (ints & 0x80000) /* OUT EP event */ for (i = 0; i < USB_NUM_ENDPOINTS; i += 2) if ((epints = DOEPINT(i))) { if (epints & 1) /* Transfer completed */ { invalidate_dcache(); int bytes = endpoints[i].size - (DOEPTSIZ(i) & 0x3FFFF); if (endpoints[i].busy) { endpoints[i].busy = false; endpoints[i].rc = 0; endpoints[i].done = true; usb_core_transfer_complete(i, USB_DIR_OUT, 0, bytes); semaphore_release(&endpoints[i].complete); } } if (epints & 4) /* AHB error */ panicf("USB: AHB error on OUT EP%d", i); if (epints & 8) /* SETUP phase done */ { invalidate_dcache(); if (i == 0) { if (ctrlreq.bRequest == 5) { /* Already set the new address here, before passing the packet to the core. See below (usb_drv_set_address) for details. */ DCFG = (DCFG & ~0x7F0) | (ctrlreq.wValue << 4); } usb_core_control_request(&ctrlreq); } else panicf("USB: SETUP done on OUT EP%d!?", i); } /* Make sure EP0 OUT is set up to accept the next request */ if (!i) { DOEPTSIZ0 = 0x20080040; DOEPDMA0 = &ctrlreq; DOEPCTL0 |= 0x84000000; } DOEPINT(i) = epints; } GINTSTS = ints; }
static void reset_endpoints(int init) { int i; /* * OUT EP 2 is an alias for OUT EP 0 on this HW! * * Resonates with "3 bidirectional- plus 1 in-endpoints in device mode" * from the datasheet, but why ep2 and not ep3? * * Reserve it here so we will skip over it in request_endpoint(). */ endpoints[2][1].state |= EP_STATE_ALLOCATED; for(i = 0; i < USB_NUM_EPS; i++) { /* * MPS sizes depending on speed: * LS: 8 (control), no bulk available * FS: 64 (control), 64 (bulk) * HS: 64 (control), 512 (bulk) * * We don't need to handle LS since there is no low-speed only * host AFAIK. */ int mps = i == 0 ? 64 : (usb_drv_port_speed() ? 512 : 64); if (init) { if (endpoints[i][0].state & EP_STATE_BUSY) { if (endpoints[i][0].state & EP_STATE_ASYNC) { endpoints[i][0].rc = -1; semaphore_release(&endpoints[i][0].complete); } else { usb_core_transfer_complete(i, USB_DIR_IN, -1, 0); } } endpoints[i][0].state = 0; semaphore_wait(&endpoints[i][0].complete, TIMEOUT_NOBLOCK); if (i != 2) { /* Skip the OUT EP0 alias */ if (endpoints[i][1].state & EP_STATE_BUSY) usb_core_transfer_complete(i, USB_DIR_OUT, -1, 0); endpoints[i][1].state = 0; semaphore_wait(&endpoints[i][1].complete, TIMEOUT_NOBLOCK); USB_OEP_SUP_PTR(i) = 0; } } dma_desc_init(i, 0); USB_IEP_CTRL (i) = USB_EP_CTRL_FLUSH|USB_EP_CTRL_SNAK; USB_IEP_MPS (i) = mps; /* in bytes */ /* We don't care about the 'IN token received' event */ USB_IEP_STS_MASK(i) = USB_EP_STAT_IN; /* OF: 0x840 */ USB_IEP_TXFSIZE (i) = mps/2; /* in dwords => mps*2 bytes */ USB_IEP_STS (i) = 0xffffffff; /* clear status */ USB_IEP_DESC_PTR(i) = 0; if (i != 2) { /* Skip the OUT EP0 alias */ dma_desc_init(i, 1); USB_OEP_CTRL (i) = USB_EP_CTRL_FLUSH|USB_EP_CTRL_SNAK; USB_OEP_MPS (i) = (mps/2 << 16) | mps; USB_OEP_STS_MASK(i) = USB_EP_STAT_BNA; /* OF: 0x1800 */ USB_OEP_RXFR (i) = 0; /* Always 0 in OF trace? */ USB_OEP_STS (i) = 0xffffffff; /* clear status */ USB_OEP_DESC_PTR(i) = 0; } } setup_desc_init(setup_desc); USB_OEP_SUP_PTR(0) = AS3525_PHYSICAL_ADDR((int)setup_desc); }
static void handle_ep_int(int ep, bool out) { unsigned long sts = DEPINT(ep, out); struct ep_type *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN]; logf("%s(%d %s): sts = 0x%lx", __func__, ep, out?"OUT":"IN", sts); if(sts & DEPINT_ahberr) panicf("usb-drv: ahb error on EP%d %s", ep, out ? "OUT" : "IN"); if(sts & DEPINT_xfercompl) { discard_dma_buffer_cache(); if(endpoint->busy) { endpoint->busy = false; endpoint->status = 0; /* works even for EP0 */ int size = (DEPTSIZ(ep, out) & DEPTSIZ_xfersize_bits); int transfered = endpoint->size - size; if(ep == 0) { bool is_ack = endpoint->size == 0; switch(ep0_state) { case EP0_WAIT_SETUP: panicf("usb-drv: EP0 completion while waiting for SETUP"); case EP0_WAIT_DATA_ACK: ep0_state = is_ack ? EP0_WAIT_DATA : EP0_WAIT_ACK; break; case EP0_WAIT_ACK: case EP0_WAIT_DATA: if((!is_ack && ep0_state == EP0_WAIT_ACK) || (is_ack && ep0_state == EP0_WAIT_DATA)) panicf("usb-drv: bad EP0 state"); prepare_setup_ep0(); break; } } if (!out) endpoint->size = size; usb_core_transfer_complete(ep, out ? USB_DIR_OUT : USB_DIR_IN, 0, transfered); endpoint->done = true; semaphore_release(&endpoint->complete); } } if(!out && (sts & DIEPINT_timeout)) { if (endpoint->busy) { endpoint->busy = false; endpoint->status = 1; endpoint->done = true; semaphore_release(&endpoint->complete); } } if(out && (sts & DOEPINT_setup)) { discard_dma_buffer_cache(); if(ep != 0) panicf("usb-drv: setup not on EP0, this is impossible"); if((DEPTSIZ(ep, true) & DEPTSIZ_xfersize_bits) != 0) { logf("usb-drv: ignore spurious setup (xfersize=%ld)", DOEPTSIZ(ep) & DEPTSIZ_xfersize_bits); prepare_setup_ep0(); } else { if(ep0_state == EP0_WAIT_SETUP) { bool data_phase = ep0_setup_pkt->wLength != 0; ep0_state = data_phase ? EP0_WAIT_DATA_ACK : EP0_WAIT_ACK; } logf(" rt=%x r=%x", ep0_setup_pkt->bRequestType, ep0_setup_pkt->bRequest); if(ep0_setup_pkt->bRequestType == USB_TYPE_STANDARD && ep0_setup_pkt->bRequest == USB_REQ_SET_ADDRESS) DCFG = (DCFG & ~bitm(DCFG, devadr)) | (ep0_setup_pkt->wValue << DCFG_devadr_bitp); usb_core_control_request(ep0_setup_pkt); } } DEPINT(ep, out) = sts; }