From 43d61cf07955679d364ed5805dab29bc37e6477a Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@6dc7ffe9-61d6-0310-9af1-9938baff3ed1> Date: Fri, 25 Aug 2006 20:28:51 +0000 Subject: - add unfinished support for DFU (up to now state machine switching works, including change of interrupt handlers, etc) git-svn-id: https://svn.openpcd.org:2342/trunk@136 6dc7ffe9-61d6-0310-9af1-9938baff3ed1 --- openpcd/firmware/Makefile | 2 +- openpcd/firmware/include/usb_dfu.h | 81 +++++ openpcd/firmware/src/dfu.c | 585 ++++++++++++++++++++++++++++++++--- openpcd/firmware/src/dfu.h | 90 ++++-- openpcd/firmware/src/pcd_enumerate.c | 249 ++++++++------- openpcd/firmware/src/pcd_enumerate.h | 50 ++- 6 files changed, 854 insertions(+), 203 deletions(-) create mode 100644 openpcd/firmware/include/usb_dfu.h (limited to 'openpcd') diff --git a/openpcd/firmware/Makefile b/openpcd/firmware/Makefile index 4312257..edbf63c 100644 --- a/openpcd/firmware/Makefile +++ b/openpcd/firmware/Makefile @@ -76,7 +76,7 @@ SRCARM = lib/lib_AT91SAM7.c SRCARM += src/pcd_enumerate.c src/fifo.c src/dbgu.c \ src/led.c src/rc632.c src/rc632_highlevel.c src/req_ctx.c \ src/trigger.c src/main.c src/syscalls.c src/pwm.c src/tc.c \ - src/usb_handler.c src/ssc.c src/usb_benchmark.c \ + src/usb_handler.c src/ssc.c src/usb_benchmark.c src/dfu.c \ src/$(TARGET).c src/start/Cstartup_SAM7.c ifdef DEBUG SRCARM += lib/vsprintf.c lib/ctype.c lib/string.c diff --git a/openpcd/firmware/include/usb_dfu.h b/openpcd/firmware/include/usb_dfu.h new file mode 100644 index 0000000..5000edc --- /dev/null +++ b/openpcd/firmware/include/usb_dfu.h @@ -0,0 +1,81 @@ +#ifndef _USB_DFU_H +#define _USB_DFU_H +/* USB Device Firmware Update Implementation for OpenPCD + * (C) 2006 by Harald Welte + * + * Protocol definitions for USB DFU + * + * This ought to be compliant to the USB DFU Spec 1.0 as available from + * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf + * + */ + +#include + +#define USB_DT_DFU 0x21 + +struct usb_dfu_func_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int8_t bmAttributes; +#define USB_DFU_CAN_DOWNLOAD (1 << 0) +#define USB_DFU_CAN_UPLOAD (1 << 1) +#define USB_DFU_MANIFEST_TOL (1 << 2) +#define USB_DFU_WILL_DETACH (1 << 3) + u_int16_t wDetachTimeOut; + u_int16_t wTransferSize; + u_int16_t bcdDFUVersion; +} __attribute__ ((packed)); + +#define USB_DT_DFU_SIZE 9 + +#define USB_TYPE_DFU (USB_TYPE_CLASS|USB_RECIP_INTERFACE) + +/* DFU class-specific requests (Section 3, DFU Rev 1.1) */ +#define USB_REQ_DFU_DETACH 0x00 +#define USB_REQ_DFU_DNLOAD 0x01 +#define USB_REQ_DFU_UPLOAD 0x02 +#define USB_REQ_DFU_GETSTATUS 0x03 +#define USB_REQ_DFU_CLRSTATUS 0x04 +#define USB_REQ_DFU_GETSTATE 0x05 +#define USB_REQ_DFU_ABORT 0x06 + +struct dfu_status { + u_int8_t bStatus; + u_int8_t bwPollTimeout[3]; + u_int8_t bState; + u_int8_t iString; +} __attribute__((packed)); + +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_errTARGET 0x01 +#define DFU_STATUS_errFILE 0x02 +#define DFU_STATUS_errWRITE 0x03 +#define DFU_STATUS_errERASE 0x04 +#define DFU_STATUS_errCHECK_ERASED 0x05 +#define DFU_STATUS_errPROG 0x06 +#define DFU_STATUS_errVERIFY 0x07 +#define DFU_STATUS_errADDRESS 0x08 +#define DFU_STATUS_errNOTDONE 0x09 +#define DFU_STATUS_errFIRMWARE 0x0a +#define DFU_STATUS_errVENDOR 0x0b +#define DFU_STATUS_errUSBR 0x0c +#define DFU_STATUS_errPOR 0x0d +#define DFU_STATUS_errUNKNOWN 0x0e +#define DFU_STATUS_errSTALLEDPKT 0x0f + +enum dfu_state { + DFU_STATE_appIDLE = 0, + DFU_STATE_appDETACH = 1, + DFU_STATE_dfuIDLE = 2, + DFU_STATE_dfuDNLOAD_SYNC = 3, + DFU_STATE_dfuDNBUSY = 4, + DFU_STATE_dfuDNLOAD_IDLE = 5, + DFU_STATE_dfuMANIFEST_SYNC = 6, + DFU_STATE_dfuMANIFEST = 7, + DFU_STATE_dfuMANIFEST_WAIT_RST = 8, + DFU_STATE_dfuUPLOAD_IDLE = 9, + DFU_STATE_dfuERROR = 10, +}; + +#endif /* _USB_DFU_H */ diff --git a/openpcd/firmware/src/dfu.c b/openpcd/firmware/src/dfu.c index 85f85ee..a1624e9 100644 --- a/openpcd/firmware/src/dfu.c +++ b/openpcd/firmware/src/dfu.c @@ -1,19 +1,296 @@ +/* USB Device Firmware Update Implementation for OpenPCD + * (C) 2006 by Harald Welte + * + * This ought to be compliant to the USB DFU Spec 1.0 as available from + * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf + * + */ -/* USB Interface descriptor in Runtime mode */ -struct usb_interface_descriptor desc_if_rt = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0x01, - .bAlternateSetting = 0x00, - .bNumEndpoints = 0x00, - .bInterfaceClass = 0xfe, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01, - .iInterface = FIXME, -}; +#include +#include +#include +#include + +#include "dfu.h" +#include "pcd_enumerate.h" +#include "openpcd.h" + +/* If debug is enabled, we need to access debug functions from flash + * and therefore have to omit flashing */ +#define DEBUG_DFU + +#ifdef DEBUG_DFU +#define DEBUGE DEBUGP +#define DEBUGI DEBUGP +#else +#define DEBUGE(x, args ...) +#define DEBUGI(x, args ...) +#endif + +static u_int8_t status; +static u_int8_t *ptr; +enum dfu_state dfu_state; + +static int __ramfunc handle_dnload(u_int16_t val, u_int16_t len) +{ + volatile u_int32_t *p = (volatile u_int32_t *)ptr; + + DEBUGE("download "); + + if (len > AT91C_IFLASH_PAGE_SIZE) { + /* Too big */ + dfu_state = DFU_STATE_dfuERROR; + status = DFU_STATUS_errADDRESS; + udp_ep0_send_stall(); + return -EINVAL; + } + if (len & 0x3) { + dfu_state = DFU_STATE_dfuERROR; + status = DFU_STATUS_errADDRESS; + udp_ep0_send_stall(); + return -EINVAL; + } + if (len == 0) { + dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + return 0; + } +#if 0 + for (i = 0; i < len/4; i++) + p[i] = +#endif + + /* FIXME: get data packet from FIFO, (erase+)write flash */ +#ifndef DEBUG_DFU + +#endif + + return 0; +} + +static __ramfunc int handle_upload(u_int16_t val, u_int16_t len) +{ + DEBUGE("upload "); + if (len > AT91C_IFLASH_PAGE_SIZE + || ptr > AT91C_IFLASH_SIZE) { + /* Too big */ + dfu_state = DFU_STATE_dfuERROR; + status = DFU_STATUS_errADDRESS; + udp_ep0_send_stall(); + return -EINVAL; + } + + if (ptr + len > AT91C_IFLASH_SIZE) + len = AT91C_IFLASH_SIZE - (u_int32_t) ptr; + + udp_ep0_send_data(ptr, len); + ptr+= len; + + return len; +} + +static __ramfunc void handle_getstatus(void) +{ + struct dfu_status dstat; + + DEBUGE("getstatus "); + + /* send status response */ + dstat.bStatus = status; + dstat.bState = dfu_state; + dstat.iString = 0; + udp_ep0_send_data(&dstat, sizeof(dstat)); +} + +static void __ramfunc handle_getstate(void) +{ + u_int8_t u8 = dfu_state; + DEBUGE("getstate "); + udp_ep0_send_data((char *)&u8, sizeof(u8)); +} + +/* callback function for DFU requests */ +int __ramfunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req, + u_int16_t val, u_int16_t len) +{ + int rc; + + DEBUGE("old_state = %u ", dfu_state); + + switch (dfu_state) { + case DFU_STATE_appIDLE: + if (req != USB_REQ_DFU_DETACH) + goto send_stall; + dfu_state = DFU_STATE_appDETACH; + goto send_zlp; + break; + case DFU_STATE_appDETACH: + switch (req) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + default: + dfu_state = DFU_STATE_appIDLE; + goto send_stall; + break; + } + /* FIXME: implement timer to return to appIDLE */ + break; + case DFU_STATE_dfuIDLE: + switch (req) { + case USB_REQ_DFU_DNLOAD: + if (len == 0) { + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + } + handle_dnload(val, len); + break; + case USB_REQ_DFU_UPLOAD: + ptr = 0; + dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + handle_upload(val, len); + break; + case USB_REQ_DFU_ABORT: + /* no zlp? */ + goto send_zlp; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + default: + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + break; + } + break; + case DFU_STATE_dfuDNLOAD_SYNC: + switch (req) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + /* FIXME: state transition depending on block completeness */ + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + default: + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + } + break; + case DFU_STATE_dfuDNBUSY: + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + break; + case DFU_STATE_dfuDNLOAD_IDLE: + switch (req) { + case USB_REQ_DFU_DNLOAD: + if (handle_dnload(val, len)) + /* FIXME: state transition */ + break; + case USB_REQ_DFU_ABORT: + dfu_state = DFU_STATE_dfuIDLE; + goto send_zlp; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + default: + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + break; + } + break; + case DFU_STATE_dfuMANIFEST_SYNC: + switch (req) { + case USB_REQ_DFU_GETSTATUS: + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + default: + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + break; + } + break; + case DFU_STATE_dfuMANIFEST: + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + break; + case DFU_STATE_dfuMANIFEST_WAIT_RST: + /* we should never go here */ + break; + case DFU_STATE_dfuUPLOAD_IDLE: + switch (req) { + case USB_REQ_DFU_UPLOAD: + /* state transition if less data then requested */ + rc = handle_upload(val, len); + if (rc >= 0 && rc < len) + dfu_state = DFU_STATE_dfuIDLE; + break; + case USB_REQ_DFU_ABORT: + dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + goto send_zlp; + break; + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + default: + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + break; + } + break; + case DFU_STATE_dfuERROR: + switch (req) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + case USB_REQ_DFU_CLRSTATUS: + dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + goto send_zlp; + break; + default: + dfu_state = DFU_STATE_dfuERROR; + goto send_stall; + break; + } + break; + } + + DEBUGE("OK new_state = %u\r\n", dfu_state); + return 0; + +send_stall: + udp_ep0_send_stall(); + DEBUGE("STALL new_state = %u\r\n", dfu_state); + return -EINVAL; + +send_zlp: + udp_ep0_send_zlp(); + DEBUGE("ZLP new_state = %u\r\n", dfu_state); + return 0; +} +static u_int8_t cur_config; /* USB DFU Device descriptor in DFU mode */ -struct usb_device_descriptor desc_dev_dfu = { +struct usb_device_descriptor dfu_dev_descriptor = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x0100, @@ -21,44 +298,264 @@ struct usb_device_descriptor desc_dev_dfu = { .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, .bMaxPacketSize0 = 8, - .idVendor = USB_VENDOR, - .idProtuct = USB_PRODUCT, + .idVendor = OPENPCD_VENDOR_ID, + .idProduct = OPENPCD_PRODUCT_ID, .bcdDevice = 0x0000, - .iManufacturer = FIXME, - .iProduct = FIXME, - .iSerialNumber = FIXME, + .iManufacturer = 0x00, + .iProduct = 0x00, + .iSerialNumber = 0x00, .bNumConfigurations = 0x01, }; -/* USB DFU Interface descriptor in DFU mode */ -struct usb_interface_descriptor desc_if_dfu = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0x00, - .bAlternateSetting = 0x00, - .bNumEndpoints = 0x00, - .bInterfaceClass = 0xfe, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x02, - .iInterface = FIXME, +/* USB DFU Config descriptor in DFU mode */ +struct _dfu_desc dfu_cfg_descriptor = { + .ucfg = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = USB_DT_CONFIG_SIZE + + 2* USB_DT_INTERFACE_SIZE + + USB_DT_DFU_SIZE, + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_ONE, + .bMaxPower = 100, + }, + .uif[0] = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0xfe, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x02, + .iInterface = 0, + }, + .uif[1] = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x01, + .bNumEndpoints = 0x00, + .bInterfaceClass = 0xfe, + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x02, + .iInterface = 0, + }, + + .func_dfu = DFU_FUNC_DESC, }; +/* minimal USB EP0 handler in DFU mode */ +static __ramfunc void dfu_udp_ep0_handler(void) { - switch () { - case USB_REQ_DFU_DETACH: - break; - case USB_REQ_DFU_DNLOAD: - break; - case USB_REQ_DFU_GETSTATUS: - break; - case USB_REQ_DFU_CLRSTATUS: - break; - case USB_REQ_DFU_ABORT: - break; - case USB_REQ_GETSTATE: - break; - case USB_REQ_DFU_UPLOAD: - break; + AT91PS_UDP pUDP = AT91C_BASE_UDP; + u_int8_t bmRequestType, bRequest; + u_int16_t wValue, wIndex, wLength, wStatus; + u_int32_t csr = pUDP->UDP_CSR[0]; + + DEBUGE("CSR=0x%04x ", csr); + + if (csr & AT91C_UDP_STALLSENT) { + DEBUGE("ACK_STALLSENT "); + pUDP->UDP_CSR[0] = ~AT91C_UDP_STALLSENT; + } + + if (csr & AT91C_UDP_RX_DATA_BK0) { + DEBUGE("ACK_BANK0 "); + pUDP->UDP_CSR[0] &= ~AT91C_UDP_RX_DATA_BK0; + } + + if (!(csr & AT91C_UDP_RXSETUP)) { + DEBUGE("no setup packet "); + return; + } + + DEBUGE("len=%d ", csr >> 16); + if (csr >> 16 == 0) { + DEBUGE("empty packet "); + return; + } + + bmRequestType = pUDP->UDP_FDR[0]; + bRequest = pUDP->UDP_FDR[0]; + wValue = (pUDP->UDP_FDR[0] & 0xFF); + wValue |= (pUDP->UDP_FDR[0] << 8); + wIndex = (pUDP->UDP_FDR[0] & 0xFF); + wIndex |= (pUDP->UDP_FDR[0] << 8); + wLength = (pUDP->UDP_FDR[0] & 0xFF); + wLength |= (pUDP->UDP_FDR[0] << 8); + + DEBUGE("bmRequestType=0x%2x ", bmRequestType); + + if (bmRequestType & 0x80) { + DEBUGE("DATA_IN=1 "); + pUDP->UDP_CSR[0] |= AT91C_UDP_DIR; + while (!(pUDP->UDP_CSR[0] & AT91C_UDP_DIR)) ; } + pUDP->UDP_CSR[0] &= ~AT91C_UDP_RXSETUP; + while ((pUDP->UDP_CSR[0] & AT91C_UDP_RXSETUP)) ; + + /* Handle supported standard device request Cf Table 9-3 in USB + * speciication Rev 1.1 */ + switch ((bRequest << 8) | bmRequestType) { + case STD_GET_DESCRIPTOR: + DEBUGE("GET_DESCRIPTOR "); + if (wValue == 0x100) { + /* Return Device Descriptor */ + udp_ep0_send_data((const char *) + &dfu_dev_descriptor, + MIN(sizeof(dfu_dev_descriptor), + wLength)); + } else if (wValue == 0x200) { + /* Return Configuration Descriptor */ + udp_ep0_send_data((const char *) + &dfu_cfg_descriptor, + MIN(sizeof(dfu_cfg_descriptor), + wLength)); +#if 0 + } else if (wValue == 0x400) { + /* 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)); +#endif + } else + udp_ep0_send_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; + break; + case STD_SET_CONFIGURATION: + DEBUGE("SET_CONFIG "); + if (wValue) + DEBUGE("VALUE!=0 "); + 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 *)&(cur_config), + sizeof(cur_config)); + break; + case STD_GET_STATUS_ZERO: + DEBUGE("GET_STATUS_ZERO "); + wStatus = 0; + udp_ep0_send_data((char *)&wStatus, sizeof(wStatus)); + break; + case STD_GET_STATUS_INTERFACE: + DEBUGE("GET_STATUS_INTERFACE "); + wStatus = 0; + udp_ep0_send_data((char *)&wStatus, sizeof(wStatus)); + break; + case STD_GET_STATUS_ENDPOINT: + DEBUGE("GET_STATUS_ENDPOINT(EPidx=%u) ", wIndex&0x0f); + wStatus = 0; + wIndex &= 0x0F; + if ((pUDP->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex == 0)) { + wStatus = + (pUDP->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; + udp_ep0_send_data((char *)&wStatus, + sizeof(wStatus)); + } else if ((pUDP->UDP_GLBSTATE & AT91C_UDP_FADDEN) + && (wIndex == 0)) { + wStatus = + (pUDP->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; + udp_ep0_send_data((char *)&wStatus, + sizeof(wStatus)); + } else + udp_ep0_send_stall(); + break; + case STD_SET_FEATURE_ZERO: + DEBUGE("SET_FEATURE_ZERO "); + udp_ep0_send_stall(); + break; + case STD_SET_FEATURE_INTERFACE: + DEBUGE("SET_FEATURE_INTERFACE "); + udp_ep0_send_zlp(); + break; + case STD_SET_FEATURE_ENDPOINT: + DEBUGE("SET_FEATURE_ENDPOINT "); + udp_ep0_send_stall(); + break; + case STD_CLEAR_FEATURE_ZERO: + DEBUGE("CLEAR_FEATURE_ZERO "); + udp_ep0_send_stall(); + break; + case STD_CLEAR_FEATURE_INTERFACE: + DEBUGE("CLEAR_FEATURE_INTERFACE "); + udp_ep0_send_zlp(); + break; + case STD_CLEAR_FEATURE_ENDPOINT: + DEBUGE("CLEAR_FEATURE_ENDPOINT(EPidx=%u) ", wIndex & 0x0f); + udp_ep0_send_stall(); + break; + case STD_SET_INTERFACE: + DEBUGE("SET INTERFACE "); + udp_ep0_send_stall(); + break; + default: + DEBUGE("DEFAULT(req=0x%02x, type=0x%02x) ", bRequest, bmRequestType); + if ((bmRequestType & 0x3f) == USB_TYPE_DFU) { + dfu_ep0_handler(bmRequestType, bRequest, wValue, wLength); + } else + udp_ep0_send_stall(); + break; + } +} + +/* minimal USB IRQ handler in DFU mode */ +static __ramfunc void dfu_udp_irq(void) +{ + AT91PS_UDP pUDP = AT91C_BASE_UDP; + AT91_REG isr = pUDP->UDP_ISR; + + if (isr & AT91C_UDP_ENDBUSRES) { + pUDP->UDP_IER = AT91C_UDP_EPINT0; + /* reset all endpoints */ + pUDP->UDP_RSTEP = (unsigned int)-1; + pUDP->UDP_RSTEP = 0; + /* Enable the function */ + pUDP->UDP_FADDR = AT91C_UDP_FEN; + /* Configure endpoint 0 */ + pUDP->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); + cur_config = 0; + } + + if (isr & AT91C_UDP_EPINT0) + dfu_udp_ep0_handler(); + + /* clear all interrupts */ + pUDP->UDP_ICR = isr; + + AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_UDP); +} + +void dfu_switch(void) +{ + AT91PS_AIC pAic = AT91C_BASE_AIC; + + DEBUGE("Switching to DFU mode "); + + pAic->AIC_SVR[AT91C_ID_UDP] = (unsigned int) &dfu_udp_irq; + dfu_state = DFU_STATE_dfuIDLE; } diff --git a/openpcd/firmware/src/dfu.h b/openpcd/firmware/src/dfu.h index 873bc0e..4372043 100644 --- a/openpcd/firmware/src/dfu.h +++ b/openpcd/firmware/src/dfu.h @@ -1,31 +1,59 @@ -#ifndef _USB_DFU_H -#define _USB_DFU_H - -#define USB_DT_DFU 0x21 - -struct usb_dfu_func_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bmAttributs; -#define USB_DFU_CAN_DOWNLOAD (1 << 0) -#define USB_DFU_CAN_UPLOAD (1 << 1) -#define USB_DFU_MANIFEST_TOL (1 << 2) -#define USB_DFU_WILL_DETACH (1 << 3) - __le16 wDetachTimeOut; - __le16 wTransferSize; - __le16 bcdDFUVersion; -} __attribute__ ((packed)); - -#define USB_DT_DFU_SIZE 9 - - -/* DFU class-specific requests (Section 3, DFU Rev 1.1) */ -#define USB_REQ_DFU_DETACH 0x00 -#define USB_REQ_DFU_DNLOAD 0x01 -#define USB_REQ_DFU_UPLOAD 0x02 -#define USB_REQ_DFU_GETSTATUS 0x03 -#define USB_REQ_DFU_CLRSTATUS 0x04 -#define USB_REQ_DFU_GETSTATE 0x05 -#define USB_REQ_DFU_ABORT 0x06 - -#endif /* _USB_DFU_H +#ifndef _DFU_H +#define _DFU_H + +/* USB Device Firmware Update Implementation for OpenPCD + * (C) 2006 by Harald Welte + * + * This ought to be compliant to the USB DFU Spec 1.0 as available from + * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf + * + */ + +#include +#include +#include + +#include "dbgu.h" + +/* USB DFU functional descriptor */ +#define DFU_FUNC_DESC { \ + .bLength = USB_DT_DFU_SIZE, \ + .bDescriptorType = USB_DT_DFU, \ + .bmAttributes = USB_DFU_CAN_UPLOAD | USB_DFU_CAN_DOWNLOAD, \ + .wDetachTimeOut = 0xff00, \ + .wTransferSize = AT91C_IFLASH_PAGE_SIZE, \ + .bcdDFUVersion = 0x0100, \ +} + +/* USB Interface descriptor in Runtime mode */ +#define DFU_RT_IF_DESC { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = 0x01, \ + .bAlternateSetting = 0x00, \ + .bNumEndpoints = 0x00, \ + .bInterfaceClass = 0xfe, \ + .bInterfaceSubClass = 0x01, \ + .bInterfaceProtocol = 0x01, \ + .iInterface = 1, \ +} + +struct udp_pcd; + +extern struct usb_device_descriptor dfu_dev_descriptor; + +struct _dfu_desc { + struct usb_config_descriptor ucfg; + struct usb_interface_descriptor uif[2]; + struct usb_dfu_func_descriptor func_dfu; +}; + +extern struct _dfu_desc dfu_cfg_descriptor; + +extern void dfu_switch(void); +extern int __ramfunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req, + u_int16_t val, u_int16_t len); + +extern enum dfu_state dfu_state; + +#endif /* _DFU_H */ diff --git a/openpcd/firmware/src/pcd_enumerate.c b/openpcd/firmware/src/pcd_enumerate.c index 3ac03fe..75c0279 100644 --- a/openpcd/firmware/src/pcd_enumerate.c +++ b/openpcd/firmware/src/pcd_enumerate.c @@ -22,11 +22,14 @@ #include #include "pcd_enumerate.h" +#include "dfu.h" #include "openpcd.h" #include "dbgu.h" -//#define DEBUG_UDP_IRQ -//#ifdef DEBUG_UDP_EP0 +#define DEBUG_UDP_IRQ +#define DEBUG_UDP_EP0 + +#define CONFIG_DFU #define AT91C_EP_OUT 1 #define AT91C_EP_OUT_SIZE 0x40 @@ -34,24 +37,10 @@ #define AT91C_EP_IN_SIZE 0x40 #define AT91C_EP_INT 3 -struct ep_ctx { - atomic_t pkts_in_transit; - void *ctx; -}; - -struct udp_pcd { - AT91PS_UDP pUdp; - unsigned char cur_config; - unsigned int cur_rcv_bank; - struct ep_ctx ep[4]; -}; - static struct udp_pcd upcd; -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) - -struct usb_device_descriptor dev_descriptor = { +const struct usb_device_descriptor dev_descriptor = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = 0x0200, @@ -72,6 +61,10 @@ struct _desc { struct usb_config_descriptor ucfg; struct usb_interface_descriptor uif; struct usb_endpoint_descriptor ep[3]; +#ifdef CONFIG_DFU + struct usb_interface_descriptor uif_dfu; + struct usb_dfu_func_descriptor func_dfu; +#endif }; const struct _desc cfg_descriptor = { @@ -79,8 +72,16 @@ const struct _desc cfg_descriptor = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, .wTotalLength = USB_DT_CONFIG_SIZE + - USB_DT_INTERFACE_SIZE + 3 * USB_DT_ENDPOINT_SIZE, +#ifdef CONFIG_DFU + 2 * USB_DT_INTERFACE_SIZE + + 3 * USB_DT_ENDPOINT_SIZE + + USB_DT_DFU_SIZE, + .bNumInterfaces = 2, +#else + 1 * USB_DT_INTERFACE_SIZE + + 3 * USB_DT_ENDPOINT_SIZE, .bNumInterfaces = 1, +#endif .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = USB_CONFIG_ATT_ONE, @@ -121,30 +122,18 @@ const struct _desc cfg_descriptor = { .bInterval = 0xff, /* FIXME */ }, }, +#ifdef CONFIG_DFU + .uif_dfu = DFU_RT_IF_DESC, + .func_dfu = DFU_FUNC_DESC, +#endif }; -/* USB standard request code */ - -#define STD_GET_STATUS_ZERO 0x0080 -#define STD_GET_STATUS_INTERFACE 0x0081 -#define STD_GET_STATUS_ENDPOINT 0x0082 - -#define STD_CLEAR_FEATURE_ZERO 0x0100 -#define STD_CLEAR_FEATURE_INTERFACE 0x0101 -#define STD_CLEAR_FEATURE_ENDPOINT 0x0102 - -#define STD_SET_FEATURE_ZERO 0x0300 -#define STD_SET_FEATURE_INTERFACE 0x0301 -#define STD_SET_FEATURE_ENDPOINT 0x0302 +static struct usb_string_descriptor string0 = { + .bLength = sizeof(string0), + .bDescriptorType = USB_DT_STRING, + .wData[0] = 0x0409, /* English */ +}; -#define STD_SET_ADDRESS 0x0500 -#define STD_GET_DESCRIPTOR 0x0680 -#define STD_SET_DESCRIPTOR 0x0700 -#define STD_GET_CONFIGURATION 0x0880 -#define STD_SET_CONFIGURATION 0x0900 -#define STD_GET_INTERFACE 0x0A81 -#define STD_SET_INTERFACE 0x0B01 -#define STD_SYNCH_FRAME 0x0C82 static void udp_ep0_handler(void); @@ -212,6 +201,11 @@ static void udp_irq(void) /* Configure endpoint 0 */ pUDP->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); upcd.cur_config = 0; + + if (dfu_state == DFU_STATE_appDETACH) { + /* now we need to switch to DFU mode */ + dfu_switch(); + } } if (isr & AT91C_UDP_EPINT0) { @@ -325,7 +319,7 @@ static int udp_open(AT91PS_UDP pUdp) upcd.pUdp = pUdp; upcd.cur_config = 0; upcd.cur_rcv_bank = AT91C_UDP_RX_DATA_BK0; - + AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_UDP, OPENPCD_IRQ_PRIO_UDP, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, udp_irq); @@ -355,67 +349,15 @@ void udp_init(void) udp_open(AT91C_BASE_UDP); } -#if 0 -/* Test if the device is configured and handle enumeration */ -u_int8_t udp_is_configured(void) -{ - return upcd.cur_config; -} - -/* Send data through endpoint 2/3 */ -u_int32_t AT91F_UDP_Write(u_int8_t irq, const unsigned char *pData, u_int32_t length) +void udp_reset(void) { - AT91PS_UDP pUdp = upcd.pUdp; - u_int32_t cpt = 0; - u_int8_t ep; - - DEBUGPCRF("enter(irq=%u, len=%u)", irq, length); - - if (irq) - ep = 3; - else - ep = 2; - - /* Send the first packet */ - cpt = MIN(length, AT91C_EP_IN_SIZE); - length -= cpt; - while (cpt--) - pUdp->UDP_FDR[ep] = *pData++; - DEBUGPCRF("sending first packet"); - pUdp->UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY; - - while (length) { - DEBUGPCRF("sending further packet"); - /* Fill the second bank */ - cpt = MIN(length, AT91C_EP_IN_SIZE); - length -= cpt; - while (cpt--) - pUdp->UDP_FDR[ep] = *pData++; - DEBUGPCRF("waiting for end of further packet"); - /* Wait for the the first bank to be sent */ - while (!(pUdp->UDP_CSR[ep] & AT91C_UDP_TXCOMP)) - if (!udp_is_configured()) { - DEBUGPCRF("return(!configured)"); - return length; - } - pUdp->UDP_CSR[ep] &= ~(AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[ep] & AT91C_UDP_TXCOMP) ; - pUdp->UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY; - } - /* Wait for the end of transfer */ - DEBUGPCRF("waiting for end of transfer"); - while (!(pUdp->UDP_CSR[ep] & AT91C_UDP_TXCOMP)) - if (!udp_is_configured()) { - DEBUGPCRF("return(!configured)"); - return length; - } - pUdp->UDP_CSR[ep] &= ~(AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[ep] & AT91C_UDP_TXCOMP) ; + volatile int i; - DEBUGPCRF("return(normal, len=%u)", length); - return length; + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); + for (i = 0; i < 0xffff; i++) + ; + AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); } -#endif #ifdef DEBUG_UDP_EP0 #define DEBUGE(x, args ...) DEBUGP(x, ## args) @@ -424,8 +366,9 @@ u_int32_t AT91F_UDP_Write(u_int8_t irq, const unsigned char *pData, u_int32_t le #endif /* Send Data through the control endpoint */ -static void udp_ep0_send_data(AT91PS_UDP pUdp, const char *pData, u_int32_t length) +void __ramfunc udp_ep0_send_data(const char *pData, u_int32_t length) { + AT91PS_UDP pUdp = AT91C_BASE_UDP; u_int32_t cpt = 0; AT91_REG csr; @@ -464,8 +407,9 @@ static void udp_ep0_send_data(AT91PS_UDP pUdp, const char *pData, u_int32_t leng } /* Send zero length packet through the control endpoint */ -static void udp_ep0_send_zlp(AT91PS_UDP pUdp) +void __ramfunc udp_ep0_send_zlp(void) { + AT91PS_UDP pUdp = AT91C_BASE_UDP; pUdp->UDP_CSR[0] |= AT91C_UDP_TXPKTRDY; while (!(pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP)) ; pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); @@ -473,8 +417,9 @@ static void udp_ep0_send_zlp(AT91PS_UDP pUdp) } /* Stall the control endpoint */ -static void udp_ep0_send_stall(AT91PS_UDP pUdp) +void __ramfunc udp_ep0_send_stall(void) { + AT91PS_UDP pUdp = AT91C_BASE_UDP; pUdp->UDP_CSR[0] |= AT91C_UDP_FORCESTALL; while (!(pUdp->UDP_CSR[0] & AT91C_UDP_ISOERROR)) ; pUdp->UDP_CSR[0] &= ~(AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR); @@ -536,18 +481,58 @@ static void udp_ep0_handler(void) switch ((bRequest << 8) | bmRequestType) { case STD_GET_DESCRIPTOR: DEBUGE("GET_DESCRIPTOR "); - if (wValue == 0x100) /* Return Device Descriptor */ - udp_ep0_send_data(pUDP, (const char *) &dev_descriptor, + if (wValue == 0x100) { + /* Return Device Descriptor */ +#ifdef CONFIG_DFU + if (dfu_state != DFU_STATE_appIDLE) + udp_ep0_send_data((const char *) + &dfu_dev_descriptor, + MIN(sizeof(dfu_dev_descriptor), + wLength)); + else +#endif + udp_ep0_send_data((const char *) &dev_descriptor, MIN(sizeof(dev_descriptor), wLength)); - else if (wValue == 0x200) /* Return Configuration Descriptor */ - udp_ep0_send_data(pUDP, (const char *) &cfg_descriptor, + } else if (wValue == 0x200) { + /* Return Configuration Descriptor */ +#ifdef CONFIG_DFU + if (dfu_state != DFU_STATE_appIDLE) + udp_ep0_send_data((const char *) + &dfu_cfg_descriptor, + MIN(sizeof(dfu_cfg_descriptor), + wLength)); + else +#endif + udp_ep0_send_data((const char *) &cfg_descriptor, MIN(sizeof(cfg_descriptor), wLength)); - else - udp_ep0_send_stall(pUDP); + } else if (wValue == 0x300) { + /* Return String descriptor */ + switch (wIndex) { + case 0: + udp_ep0_send_data((const char *) &string0, + MIN(sizeof(string0), wLength)); + break; + default: + /* FIXME: implement this */ + udp_ep0_send_stall(); + break; + } +#if 0 + } else if (wValue == 0x400) { + /* 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)); +#endif + } else + udp_ep0_send_stall(); break; case STD_SET_ADDRESS: DEBUGE("SET_ADDRESS "); - udp_ep0_send_zlp(pUDP); + udp_ep0_send_zlp(); pUDP->UDP_FADDR = (AT91C_UDP_FEN | wValue); pUDP->UDP_GLBSTATE = (wValue) ? AT91C_UDP_FADDEN : 0; break; @@ -556,7 +541,7 @@ static void udp_ep0_handler(void) if (wValue) DEBUGE("VALUE!=0 "); upcd.cur_config = wValue; - udp_ep0_send_zlp(pUDP); + udp_ep0_send_zlp(); pUDP->UDP_GLBSTATE = (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN; pUDP->UDP_CSR[1] = @@ -571,18 +556,18 @@ static void udp_ep0_handler(void) break; case STD_GET_CONFIGURATION: DEBUGE("GET_CONFIG "); - udp_ep0_send_data(pUDP, (char *)&(upcd.cur_config), + udp_ep0_send_data((char *)&(upcd.cur_config), sizeof(upcd.cur_config)); break; case STD_GET_STATUS_ZERO: DEBUGE("GET_STATUS_ZERO "); wStatus = 0; - udp_ep0_send_data(pUDP, (char *)&wStatus, sizeof(wStatus)); + udp_ep0_send_data((char *)&wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_INTERFACE: DEBUGE("GET_STATUS_INTERFACE "); wStatus = 0; - udp_ep0_send_data(pUDP, (char *)&wStatus, sizeof(wStatus)); + udp_ep0_send_data((char *)&wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_ENDPOINT: DEBUGE("GET_STATUS_ENDPOINT(EPidx=%u) ", wIndex&0x0f); @@ -591,42 +576,42 @@ static void udp_ep0_handler(void) if ((pUDP->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= 3)) { wStatus = (pUDP->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; - udp_ep0_send_data(pUDP, (char *)&wStatus, + udp_ep0_send_data((char *)&wStatus, sizeof(wStatus)); } else if ((pUDP->UDP_GLBSTATE & AT91C_UDP_FADDEN) && (wIndex == 0)) { wStatus = (pUDP->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; - udp_ep0_send_data(pUDP, (char *)&wStatus, + udp_ep0_send_data((char *)&wStatus, sizeof(wStatus)); } else - udp_ep0_send_stall(pUDP); + udp_ep0_send_stall(); break; case STD_SET_FEATURE_ZERO: DEBUGE("SET_FEATURE_ZERO "); - udp_ep0_send_stall(pUDP); + udp_ep0_send_stall(); break; case STD_SET_FEATURE_INTERFACE: DEBUGE("SET_FEATURE_INTERFACE "); - udp_ep0_send_zlp(pUDP); + udp_ep0_send_zlp(); break; case STD_SET_FEATURE_ENDPOINT: DEBUGE("SET_FEATURE_ENDPOINT "); - udp_ep0_send_zlp(pUDP); + udp_ep0_send_zlp(); wIndex &= 0x0F; if ((wValue == 0) && wIndex && (wIndex <= 3)) { pUDP->UDP_CSR[wIndex] = 0; - udp_ep0_send_zlp(pUDP); + udp_ep0_send_zlp(); } else - udp_ep0_send_stall(pUDP); + udp_ep0_send_stall(); break; case STD_CLEAR_FEATURE_ZERO: DEBUGE("CLEAR_FEATURE_ZERO "); - udp_ep0_send_stall(pUDP); + udp_ep0_send_stall(); break; case STD_CLEAR_FEATURE_INTERFACE: DEBUGP("CLEAR_FEATURE_INTERFACE "); - udp_ep0_send_zlp(pUDP); + udp_ep0_send_zlp(); break; case STD_CLEAR_FEATURE_ENDPOINT: DEBUGE("CLEAR_FEATURE_ENDPOINT(EPidx=%u) ", wIndex & 0x0f); @@ -664,17 +649,29 @@ static void udp_ep0_handler(void) RCTX_STATE_FREE)) {} atomic_set(&upcd.ep[wIndex].pkts_in_transit, 0); } - udp_ep0_send_zlp(pUDP); + udp_ep0_send_zlp(); } else - udp_ep0_send_stall(pUDP); + udp_ep0_send_stall(); break; case STD_SET_INTERFACE: DEBUGE("SET INTERFACE "); - udp_ep0_send_stall(pUDP); + udp_ep0_send_stall(); break; +#ifdef CONFIG_DFU + case USB_TYPE_DFU<<8|USB_REQ_DFU_DETACH: + DEBUGE("DFU_DETACH "); + /* if USB reset occurs within wValue milliseconds, switch to DFU */ + break; +#endif default: DEBUGE("DEFAULT(req=0x%02x, type=0x%02x) ", bRequest, bmRequestType); - udp_ep0_send_stall(pUDP); +#ifdef CONFIG_DFU + if ((bmRequestType & 0x3f) == USB_TYPE_DFU) { + dfu_ep0_handler(bmRequestType, bRequest, wValue, wLength); + } else +#endif + udp_ep0_send_stall(); break; } } + diff --git a/openpcd/firmware/src/pcd_enumerate.h b/openpcd/firmware/src/pcd_enumerate.h index aa007de..e4ad57d 100644 --- a/openpcd/firmware/src/pcd_enumerate.h +++ b/openpcd/firmware/src/pcd_enumerate.h @@ -1,12 +1,60 @@ #ifndef _OPCD_USB_H #define _OPCD_USB_H +#include #include -#include "src/openpcd.h" +#include +#include "openpcd.h" +#include "dfu.h" + +struct req_ctx; + +extern void __ramfunc udp_ep0_send_data(const char *data, u_int32_t length); +extern void __ramfunc udp_ep0_send_zlp(void); +extern void __ramfunc udp_ep0_send_stall(void); extern void udp_init(void); extern int udp_refill_ep(int ep, struct req_ctx *rctx); extern void udp_unthrottle(void); +extern void udp_reset(void); + +struct ep_ctx { + atomic_t pkts_in_transit; + void *ctx; +}; + +struct udp_pcd { + AT91PS_UDP pUdp; + unsigned char cur_config; + unsigned int cur_rcv_bank; + struct ep_ctx ep[4]; +}; + +/* USB standard request code */ + +#define STD_GET_STATUS_ZERO 0x0080 +#define STD_GET_STATUS_INTERFACE 0x0081 +#define STD_GET_STATUS_ENDPOINT 0x0082 + +#define STD_CLEAR_FEATURE_ZERO 0x0100 +#define STD_CLEAR_FEATURE_INTERFACE 0x0101 +#define STD_CLEAR_FEATURE_ENDPOINT 0x0102 + +#define STD_SET_FEATURE_ZERO 0x0300 +#define STD_SET_FEATURE_INTERFACE 0x0301 +#define STD_SET_FEATURE_ENDPOINT 0x0302 + +#define STD_SET_ADDRESS 0x0500 +#define STD_GET_DESCRIPTOR 0x0680 +#define STD_SET_DESCRIPTOR 0x0700 +#define STD_GET_CONFIGURATION 0x0880 +#define STD_SET_CONFIGURATION 0x0900 +#define STD_GET_INTERFACE 0x0A81 +#define STD_SET_INTERFACE 0x0B01 +#define STD_SYNCH_FRAME 0x0C82 + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + #endif -- cgit v1.2.3