diff options
Diffstat (limited to 'firmware/src/os')
-rw-r--r-- | firmware/src/os/pcd_enumerate.c | 201 | ||||
-rw-r--r-- | firmware/src/os/pcd_enumerate.h | 5 |
2 files changed, 161 insertions, 45 deletions
diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c index 3c7c2c3..2ca8c77 100644 --- a/firmware/src/os/pcd_enumerate.c +++ b/firmware/src/os/pcd_enumerate.c @@ -123,7 +123,7 @@ const struct _desc cfg_descriptor = { .bConfigurationValue = 1, .iConfiguration = 5, .bmAttributes = USB_CONFIG_ATT_ONE, - .bMaxPower = 100, /* 200mA */ + .bMaxPower = 250, /* 500mA */ }, .uif = { .bLength = USB_DT_INTERFACE_SIZE, @@ -189,11 +189,11 @@ static void reset_ep(unsigned int ep) atomic_set(&upcd.ep[ep].pkts_in_transit, 0); /* free all currently transmitting contexts */ - while (rctx = req_ctx_find_get(0, epstate[ep].state_busy, - RCTX_STATE_FREE)) {} + while ((rctx = req_ctx_find_get(0, epstate[ep].state_busy, + RCTX_STATE_FREE))) {} /* free all currently pending contexts */ - while (rctx = req_ctx_find_get(0, epstate[ep].state_pending, - RCTX_STATE_FREE)) {} + while ((rctx = req_ctx_find_get(0, epstate[ep].state_pending, + RCTX_STATE_FREE))) {} pUDP->UDP_RSTEP |= (1 << ep); pUDP->UDP_RSTEP &= ~(1 << ep); @@ -293,6 +293,8 @@ int __ramfunc udp_refill_ep(int ep) local_irq_save(flags); ret = __udp_refill_ep(ep); local_irq_restore(flags); + + return ret; } static void __ramfunc udp_irq(void) @@ -301,7 +303,8 @@ static void __ramfunc udp_irq(void) AT91PS_UDP pUDP = upcd.pUdp; AT91_REG isr = pUDP->UDP_ISR; - DEBUGI("udp_irq(imr=0x%04x, isr=0x%04x): ", pUDP->UDP_IMR, isr); + DEBUGI("udp_irq(imr=0x%04x, isr=0x%04x, state=%d): ", + pUDP->UDP_IMR, isr, upcd.state); if (isr & AT91C_UDP_ENDBUSRES) { DEBUGI("ENDBUSRES "); @@ -315,6 +318,7 @@ static void __ramfunc udp_irq(void) /* Configure endpoint 0 */ pUDP->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); upcd.cur_config = 0; + upcd.state = USB_STATE_DEFAULT; reset_ep(1); reset_ep(2); @@ -322,6 +326,7 @@ static void __ramfunc udp_irq(void) #ifdef CONFIG_DFU if (*dfu->dfu_state == DFU_STATE_appDETACH) { + DEBUGI("DFU_SWITCH "); /* now we need to switch to DFU mode */ dfu->dfu_switch(); goto out; @@ -481,10 +486,13 @@ void udp_open(void) upcd.pUdp = AT91C_BASE_UDP; upcd.cur_config = 0; upcd.cur_rcv_bank = AT91C_UDP_RX_DATA_BK0; + /* This should start with USB_STATE_NOTATTACHED, but we're a pure + * bus powered device and thus start with powered */ + upcd.state = USB_STATE_POWERED; AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_UDP, OPENPCD_IRQ_PRIO_UDP, - AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, udp_irq); + AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &udp_irq); AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_UDP); /* End-of-Bus-Reset is always enabled */ @@ -565,7 +573,8 @@ static void udp_ep0_handler(void) switch ((bRequest << 8) | bmRequestType) { u_int8_t desc_type, desc_index; case STD_GET_DESCRIPTOR: - DEBUGE("GET_DESCRIPTOR "); + DEBUGE("GET_DESCRIPTOR(wValue=0x%04x, wIndex=0x%04x) ", + wValue, wIndex); desc_type = wValue >> 8; desc_index = wValue & 0xff; switch (desc_type) { @@ -597,11 +606,9 @@ static void udp_ep0_handler(void) break; case USB_DT_STRING: /* Return String descriptor */ - if (desc_index > ARRAY_SIZE(usb_strings)) { - udp_ep0_send_stall(); - break; - } - DEBUGP("bLength=%u, wLength=%u\n", + if (desc_index > ARRAY_SIZE(usb_strings)) + goto out_stall; + DEBUGE("bLength=%u, wLength=%u\n", usb_strings[desc_index]->bLength, wLength); udp_ep0_send_data((const char *) usb_strings[desc_index], MIN(usb_strings[desc_index]->bLength, @@ -611,49 +618,125 @@ static void udp_ep0_handler(void) /* Return Function descriptor */ udp_ep0_send_data((const char *) &dfu->dfu_cfg_descriptor->func_dfu, MIN(sizeof(dfu->dfu_cfg_descriptor->func_dfu), wLength)); -#if 0 - } else if (wValue == 0x400) { + case USB_DT_INTERFACE: /* Return Interface descriptor */ - if (wIndex != 0x01) - udp_ep0_send_stall(); - udp_ep0_send_data((const char *) - &dfu_if_descriptor, - MIN(sizeof(dfu_if_descriptor), - wLength)); + if (desc_index > cfg_descriptor.ucfg.bNumInterfaces) + goto out_stall; + switch (desc_index) { + case 0: + udp_ep0_send_data((const char *) + &cfg_descriptor.uif, + MIN(sizeof(cfg_descriptor.uif), + wLength)); + break; +#ifdef CONFIG_DFU + case 1: + udp_ep0_send_data((const char *) + &cfg_descriptor.uif_dfu[0], + MIN(sizeof(cfg_descriptor.uif_dfu[0]), + wLength)); + break; + case 2: + udp_ep0_send_data((const char *) + &cfg_descriptor.uif_dfu[1], + MIN(sizeof(cfg_descriptor.uif_dfu[1]), + wLength)); + break; + #endif + default: + goto out_stall; + break; + } break; default: - udp_ep0_send_stall(); + goto out_stall; } break; case STD_SET_ADDRESS: DEBUGE("SET_ADDRESS "); - udp_ep0_send_zlp(); - pUDP->UDP_FADDR = (AT91C_UDP_FEN | wValue); - pUDP->UDP_GLBSTATE = (wValue) ? AT91C_UDP_FADDEN : 0; + if (wValue > 127) + goto out_stall; + + switch (upcd.state) { + case USB_STATE_DEFAULT: + if (wValue == 0) { + udp_ep0_send_zlp(); + /* do nothing */ + } else { + udp_ep0_send_zlp(); + pUDP->UDP_FADDR = (AT91C_UDP_FEN | wValue); + pUDP->UDP_GLBSTATE = AT91C_UDP_FADDEN; + upcd.state = USB_STATE_ADDRESS; + } + break; + case USB_STATE_ADDRESS: + if (wValue == 0) { + udp_ep0_send_zlp(); + upcd.state = USB_STATE_DEFAULT; + } else { + udp_ep0_send_zlp(); + pUDP->UDP_FADDR = (AT91C_UDP_FEN | wValue); + } + break; + default: + goto out_stall; + break; + } break; case STD_SET_CONFIGURATION: DEBUGE("SET_CONFIG "); - if (wValue) + if (upcd.state != USB_STATE_ADDRESS && + upcd.state != USB_STATE_CONFIGURED) { + goto out_stall; + } + if ((wValue & 0xff) == 0) { + DEBUGE("VALUE==0 "); + upcd.state = USB_STATE_ADDRESS; + pUDP->UDP_GLBSTATE = AT91C_UDP_FADDEN; + pUDP->UDP_CSR[1] = 0; + pUDP->UDP_CSR[2] = 0; + pUDP->UDP_CSR[3] = 0; + } else if ((wValue & 0xff) <= + dev_descriptor.bNumConfigurations) { DEBUGE("VALUE!=0 "); + upcd.state = USB_STATE_CONFIGURED; + pUDP->UDP_GLBSTATE = AT91C_UDP_CONFG; + pUDP->UDP_CSR[1] = AT91C_UDP_EPEDS | + AT91C_UDP_EPTYPE_BULK_OUT; + pUDP->UDP_CSR[2] = AT91C_UDP_EPEDS | + AT91C_UDP_EPTYPE_BULK_IN; + pUDP->UDP_CSR[3] = AT91C_UDP_EPEDS | + AT91C_UDP_EPTYPE_INT_IN; + } else { + /* invalid configuration */ + goto out_stall; + break; + } upcd.cur_config = wValue; udp_ep0_send_zlp(); - pUDP->UDP_GLBSTATE = - (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN; - pUDP->UDP_CSR[1] = - (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) : - 0; - pUDP->UDP_CSR[2] = - (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0; - pUDP->UDP_CSR[3] = - (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0; pUDP->UDP_IER = (AT91C_UDP_EPINT0|AT91C_UDP_EPINT1| AT91C_UDP_EPINT2|AT91C_UDP_EPINT3); break; case STD_GET_CONFIGURATION: DEBUGE("GET_CONFIG "); - udp_ep0_send_data((char *)&(upcd.cur_config), - sizeof(upcd.cur_config)); + switch (upcd.state) { + case USB_STATE_ADDRESS: + case USB_STATE_CONFIGURED: + udp_ep0_send_data((char *)&(upcd.cur_config), + sizeof(upcd.cur_config)); + break; + default: + goto out_stall; + break; + } + break; + case STD_GET_INTERFACE: + DEBUGE("GET_INTERFACE "); + if (upcd.state != USB_STATE_CONFIGURED) + goto out_stall; + udp_ep0_send_data((char *)&(upcd.cur_altsett), + sizeof(upcd.cur_altsett)); break; case STD_GET_STATUS_ZERO: DEBUGE("GET_STATUS_ZERO "); @@ -662,11 +745,17 @@ static void udp_ep0_handler(void) break; case STD_GET_STATUS_INTERFACE: DEBUGE("GET_STATUS_INTERFACE "); + if (upcd.state == USB_STATE_DEFAULT || + (upcd.state == USB_STATE_ADDRESS && wIndex != 0)) + goto out_stall; wStatus = 0; udp_ep0_send_data((char *)&wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_ENDPOINT: DEBUGE("GET_STATUS_ENDPOINT(EPidx=%u) ", wIndex&0x0f); + if (upcd.state == USB_STATE_DEFAULT || + (upcd.state == USB_STATE_ADDRESS && wIndex != 0)) + goto out_stall; wStatus = 0; wIndex &= 0x0F; if ((pUDP->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= 3)) { @@ -681,29 +770,41 @@ static void udp_ep0_handler(void) udp_ep0_send_data((char *)&wStatus, sizeof(wStatus)); } else - udp_ep0_send_stall(); + goto out_stall; break; case STD_SET_FEATURE_ZERO: DEBUGE("SET_FEATURE_ZERO "); - udp_ep0_send_stall(); + if (upcd.state == USB_STATE_ADDRESS && + (wIndex & 0xff) != 0) + goto out_stall; + /* FIXME: implement this */ + goto out_stall; break; case STD_SET_FEATURE_INTERFACE: DEBUGE("SET_FEATURE_INTERFACE "); + if (upcd.state == USB_STATE_ADDRESS && + (wIndex & 0xff) != 0) + goto out_stall; udp_ep0_send_zlp(); break; case STD_SET_FEATURE_ENDPOINT: DEBUGE("SET_FEATURE_ENDPOINT "); + if (upcd.state == USB_STATE_ADDRESS && + (wIndex & 0xff) != 0) + goto out_stall; + if (wValue != USB_ENDPOINT_HALT) + goto out_stall; udp_ep0_send_zlp(); wIndex &= 0x0F; if ((wValue == 0) && wIndex && (wIndex <= 3)) { pUDP->UDP_CSR[wIndex] = 0; udp_ep0_send_zlp(); } else - udp_ep0_send_stall(); + goto out_stall; break; case STD_CLEAR_FEATURE_ZERO: DEBUGE("CLEAR_FEATURE_ZERO "); - udp_ep0_send_stall(); + goto out_stall; break; case STD_CLEAR_FEATURE_INTERFACE: DEBUGP("CLEAR_FEATURE_INTERFACE "); @@ -711,16 +812,24 @@ static void udp_ep0_handler(void) break; case STD_CLEAR_FEATURE_ENDPOINT: DEBUGE("CLEAR_FEATURE_ENDPOINT(EPidx=%u) ", wIndex & 0x0f); + if (wValue != USB_ENDPOINT_HALT) + goto out_stall; wIndex &= 0x0F; if ((wValue == 0) && wIndex && (wIndex <= 3)) { reset_ep(wIndex); udp_ep0_send_zlp(); } else - udp_ep0_send_stall(); + goto out_stall; break; case STD_SET_INTERFACE: DEBUGE("SET INTERFACE "); - udp_ep0_send_stall(); + if (upcd.state != USB_STATE_CONFIGURED) + goto out_stall; + if (wIndex > cfg_descriptor.ucfg.bNumInterfaces) + goto out_stall; + upcd.cur_interface = wIndex; + upcd.cur_altsett = wValue; + udp_ep0_send_zlp(); break; default: DEBUGE("DEFAULT(req=0x%02x, type=0x%02x) ", @@ -731,8 +840,12 @@ static void udp_ep0_handler(void) wLength); } else #endif - udp_ep0_send_stall(); + goto out_stall; break; } + return; +out_stall: + DEBUGE("STALL!! "); + udp_ep0_send_stall(); } diff --git a/firmware/src/os/pcd_enumerate.h b/firmware/src/os/pcd_enumerate.h index acec89c..08d26de 100644 --- a/firmware/src/os/pcd_enumerate.h +++ b/firmware/src/os/pcd_enumerate.h @@ -17,7 +17,7 @@ struct req_ctx; extern void udp_open(void); -extern int udp_refill_ep(int ep); +extern int __ramfunc udp_refill_ep(int ep); extern void udp_unthrottle(void); extern void udp_reset(void); @@ -31,7 +31,10 @@ struct ep_ctx { struct udp_pcd { AT91PS_UDP pUdp; + enum usb_device_state state; unsigned char cur_config; + unsigned char cur_interface; + unsigned char cur_altsett; unsigned int cur_rcv_bank; struct ep_ctx ep[4]; }; |