From a97e460bea62375c9c870fb3e3650fbff20c5ad1 Mon Sep 17 00:00:00 2001 From: laforge Date: Wed, 13 Sep 2006 16:02:52 +0000 Subject: Completely separate DFU from application program, we now have a real bootloader. DFU occupies 0x00100000 - 0x00100fff in flash, application starts at 0x00101000. DFU also occupies the first couple of bytes in SRAM, application starts at 0x00200024. In order to produce a samba-flashable image, first build dfu.bin by typing 'make -f Makefule.dfu', succeeded by 'make TARGET=... DEBUG=... BOARD=...' and then concatenating the two files together, e.g. cat dfu.bin main_reqa.bin > flash.bin. Actual flashing via DFU is still not operational, but will be implemented next git-svn-id: https://svn.openpcd.org:2342/trunk@194 6dc7ffe9-61d6-0310-9af1-9938baff3ed1 --- firmware/src/dfu/dfu.c | 681 ++++++++++++++++++++++++++++++++++++++ firmware/src/dfu/dfu.h | 77 +++++ firmware/src/os/dfu.c | 681 -------------------------------------- firmware/src/os/dfu.h | 80 ----- firmware/src/os/main.c | 2 - firmware/src/os/pcd_enumerate.c | 5 +- firmware/src/os/pcd_enumerate.h | 2 +- firmware/src/start/Cstartup.S | 348 ++++++++++--------- firmware/src/start/Cstartup_app.S | 81 +++++ 9 files changed, 1016 insertions(+), 941 deletions(-) create mode 100644 firmware/src/dfu/dfu.c create mode 100644 firmware/src/dfu/dfu.h delete mode 100644 firmware/src/os/dfu.c delete mode 100644 firmware/src/os/dfu.h create mode 100644 firmware/src/start/Cstartup_app.S (limited to 'firmware/src') diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c new file mode 100644 index 0000000..f50b564 --- /dev/null +++ b/firmware/src/dfu/dfu.c @@ -0,0 +1,681 @@ +/* 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 + +#include +#include +#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 ...) do { } while (0) +#define DEBUGI(x, args ...) do { } while (0) +#endif + + +static void __dfufunc udp_init(void) +{ + /* Set the PLL USB Divider */ + AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1; + + /* Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock */ + AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP; + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP); + + /* Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO + * Set in PIO mode and Configure in Output */ + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); +} + +/* Send Data through the control endpoint */ +static void __dfufunc 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; + + DEBUGE("send_data: %u bytes ", length); + + do { + cpt = MIN(length, 8); + length -= cpt; + + while (cpt--) + pUdp->UDP_FDR[0] = *pData++; + + if (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) { + pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ; + } + + pUdp->UDP_CSR[0] |= AT91C_UDP_TXPKTRDY; + do { + csr = pUdp->UDP_CSR[0]; + + /* Data IN stage has been stopped by a status OUT */ + if (csr & AT91C_UDP_RX_DATA_BK0) { + pUdp->UDP_CSR[0] &= ~(AT91C_UDP_RX_DATA_BK0); + DEBUGE("stopped by status out "); + return; + } + } while (!(csr & AT91C_UDP_TXCOMP)); + + } while (length); + + if (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) { + pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ; + } +} + +/* Send zero length packet through the control endpoint */ +static void __dfufunc 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); + while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ; +} + +/* Stall the control endpoint */ +static void __dfufunc 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); + while (pUdp->UDP_CSR[0] & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)) ; +} + + +static u_int8_t status; +static u_int8_t *ptr; +static __dfudata u_int32_t dfu_state; + +static int __dfufunc 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 __dfufunc int handle_upload(u_int16_t val, u_int16_t len) +{ + DEBUGE("upload "); + if (len > AT91C_IFLASH_PAGE_SIZE + || ptr > AT91C_IFLASH_SIZE - 0x1000) { + /* 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((char *)ptr, len); + ptr+= len; + + return len; +} + +static __dfufunc 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((char *)&dstat, sizeof(dstat)); +} + +static void __dfufunc 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 __dfufunc 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 = 0x00101000; /* Flash base address for app */ + 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 */ +__dfustruct const struct usb_device_descriptor dfu_dev_descriptor = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0100, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 8, + .idVendor = OPENPCD_VENDOR_ID, + .idProduct = OPENPCD_PRODUCT_ID, + .bcdDevice = 0x0000, + .iManufacturer = 0x00, + .iProduct = 0x00, + .iSerialNumber = 0x00, + .bNumConfigurations = 0x01, +}; + +/* USB DFU Config descriptor in DFU mode */ +__dfustruct const 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 __dfufunc void dfu_udp_ep0_handler(void) +{ + 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 __dfufunc 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); +} + +/* this is only called once before DFU mode, no __dfufunc required */ +static 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; +} + +void __dfufunc dfu_main(void) +{ + udp_init(); + + /* This implements + AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_UDP, + OPENPCD_IRQ_PRIO_UDP, + AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, dfu_udp_irq); + */ + AT91PS_AIC pAic = AT91C_BASE_AIC; + pAic->AIC_IDCR = 1 << AT91C_ID_UDP; + pAic->AIC_SVR[AT91C_ID_UDP] = (unsigned int) &dfu_udp_irq; + pAic->AIC_SMR[AT91C_ID_UDP] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | + OPENPCD_IRQ_PRIO_UDP; + pAic->AIC_ICCR = 1 << AT91C_ID_UDP; + + AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_UDP); + + /* End-of-Bus-Reset is always enabled */ + + /* Clear for set the Pull up resistor */ + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); + + /* do nothing, since all of DFU is interrupt driven */ + while (1) ; +} + +const struct dfuapi __dfufunctab dfu_api = { + .udp_init = &udp_init, + .ep0_send_data = &udp_ep0_send_data, + .ep0_send_zlp = &udp_ep0_send_zlp, + .ep0_send_stall = &udp_ep0_send_stall, + .dfu_ep0_handler = &dfu_ep0_handler, + .dfu_switch = &dfu_switch, + .dfu_state = &dfu_state, + .dfu_dev_descriptor = &dfu_dev_descriptor, + .dfu_cfg_descriptor = &dfu_cfg_descriptor, +}; + +/* just for testing */ +int foo = 12345; diff --git a/firmware/src/dfu/dfu.h b/firmware/src/dfu/dfu.h new file mode 100644 index 0000000..992a4f2 --- /dev/null +++ b/firmware/src/dfu/dfu.h @@ -0,0 +1,77 @@ +#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 + +/* 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, \ +} + +#define __dfufunctab __attribute__ ((section (".dfu.functab"))) +#define __dfudata __attribute__ ((section (".data.shared"))) +#define __dfufunc +#define __dfustruct const + +#if 0 +extern void __dfufunc udp_ep0_send_data(const char *data, u_int32_t length); +extern void __dfufunc udp_ep0_send_zlp(void); +extern void __dfufunc udp_ep0_send_stall(void); +extern __dfustruct struct usb_device_descriptor dfu_dev_descriptor; +extern __dfustruct struct _dfu_desc dfu_cfg_descriptor; +extern void dfu_switch(void); +extern int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req, + u_int16_t val, u_int16_t len); +extern static u_int8_t dfu_state; +struct udp_pcd; +#endif + +struct _dfu_desc { + struct usb_config_descriptor ucfg; + struct usb_interface_descriptor uif[2]; + struct usb_dfu_func_descriptor func_dfu; +}; + +struct dfuapi { + void (*udp_init)(void); + void (*ep0_send_data)(const char *data, u_int32_t len); + void (*ep0_send_zlp)(void); + void (*ep0_send_stall)(void); + int (*dfu_ep0_handler)(u_int8_t req_type, u_int8_t req, + u_int16_t val, u_int16_t len); + void (*dfu_switch)(void); + u_int8_t *dfu_state; + const struct usb_device_descriptor *dfu_dev_descriptor; + const struct _dfu_desc *dfu_cfg_descriptor; +}; + + +#endif /* _DFU_H */ diff --git a/firmware/src/os/dfu.c b/firmware/src/os/dfu.c deleted file mode 100644 index f2bc0ca..0000000 --- a/firmware/src/os/dfu.c +++ /dev/null @@ -1,681 +0,0 @@ -/* 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 - -#include -#include -#include -#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 ...) do { } while (0) -#define DEBUGI(x, args ...) do { } while (0) -#endif - - -void __dfufunc udp_init(void) -{ - /* Set the PLL USB Divider */ - AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1; - - /* Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock */ - AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP; - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP); - - /* Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO - * Set in PIO mode and Configure in Output */ - AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); -} - -/* Send Data through the control endpoint */ -static void __dfufunc 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; - - DEBUGE("send_data: %u bytes ", length); - - do { - cpt = MIN(length, 8); - length -= cpt; - - while (cpt--) - pUdp->UDP_FDR[0] = *pData++; - - if (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) { - pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ; - } - - pUdp->UDP_CSR[0] |= AT91C_UDP_TXPKTRDY; - do { - csr = pUdp->UDP_CSR[0]; - - /* Data IN stage has been stopped by a status OUT */ - if (csr & AT91C_UDP_RX_DATA_BK0) { - pUdp->UDP_CSR[0] &= ~(AT91C_UDP_RX_DATA_BK0); - DEBUGE("stopped by status out "); - return; - } - } while (!(csr & AT91C_UDP_TXCOMP)); - - } while (length); - - if (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) { - pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ; - } -} - -/* Send zero length packet through the control endpoint */ -static void __dfufunc 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); - while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ; -} - -/* Stall the control endpoint */ -static void __dfufunc 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); - while (pUdp->UDP_CSR[0] & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)) ; -} - - -static u_int8_t status; -static u_int8_t *ptr; -static u_int8_t dfu_state; - -static int __dfufunc 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 __dfufunc 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((char *)ptr, len); - ptr+= len; - - return len; -} - -static __dfufunc 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((char *)&dstat, sizeof(dstat)); -} - -static void __dfufunc 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 __dfufunc 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 */ -__dfustruct const struct usb_device_descriptor dfu_dev_descriptor = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0100, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = 8, - .idVendor = OPENPCD_VENDOR_ID, - .idProduct = OPENPCD_PRODUCT_ID, - .bcdDevice = 0x0000, - .iManufacturer = 0x00, - .iProduct = 0x00, - .iSerialNumber = 0x00, - .bNumConfigurations = 0x01, -}; - -/* USB DFU Config descriptor in DFU mode */ -__dfustruct const 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 __dfufunc void dfu_udp_ep0_handler(void) -{ - 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 __dfufunc 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); -} - -/* this is only called once before DFU mode, no __dfufunc required */ -static 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; -} - -void __dfufunc dfu_main(void) -{ - udp_init(); - - /* This implements - AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_UDP, - OPENPCD_IRQ_PRIO_UDP, - AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, dfu_udp_irq); - */ - AT91PS_AIC pAic = AT91C_BASE_AIC; - pAic->AIC_IDCR = 1 << AT91C_ID_UDP; - pAic->AIC_SVR[AT91C_ID_UDP] = (unsigned int) &dfu_udp_irq; - pAic->AIC_SMR[AT91C_ID_UDP] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | - OPENPCD_IRQ_PRIO_UDP; - pAic->AIC_ICCR = 1 << AT91C_ID_UDP; - - AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_UDP); - - /* End-of-Bus-Reset is always enabled */ - - /* Clear for set the Pull up resistor */ - AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); - - /* do nothing, since all of DFU is interrupt driven */ - while (1) ; -} - -const struct dfuapi __dfufunctab dfu_api = { - .ep0_send_data = &udp_ep0_send_data, - .ep0_send_zlp = &udp_ep0_send_zlp, - .ep0_send_stall = &udp_ep0_send_stall, - .dfu_ep0_handler = &dfu_ep0_handler, - .dfu_switch = &dfu_switch, - .dfu_state = &dfu_state, - .dfu_dev_descriptor = &dfu_dev_descriptor, - .dfu_cfg_descriptor = &dfu_cfg_descriptor, -}; - -/* just for testing */ -int foo = 12345; diff --git a/firmware/src/os/dfu.h b/firmware/src/os/dfu.h deleted file mode 100644 index 8786044..0000000 --- a/firmware/src/os/dfu.h +++ /dev/null @@ -1,80 +0,0 @@ -#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, \ -} - -#define __dfufunc __attribute__ ((long_call, section (".dfu.func"))) -#define __dfustruct __attribute__ ((section (".dfu.struct"))) const -#define __dfufunctab __attribute__ ((section (".dfu.functab"))) - -#if 0 -extern void __dfufunc udp_ep0_send_data(const char *data, u_int32_t length); -extern void __dfufunc udp_ep0_send_zlp(void); -extern void __dfufunc udp_ep0_send_stall(void); -extern __dfustruct struct usb_device_descriptor dfu_dev_descriptor; -extern __dfustruct struct _dfu_desc dfu_cfg_descriptor; -extern void dfu_switch(void); -extern int __dfufunc dfu_ep0_handler(u_int8_t req_type, u_int8_t req, - u_int16_t val, u_int16_t len); -extern static u_int8_t dfu_state; -struct udp_pcd; -#endif - - -extern void __dfufunc udp_init(void); - -struct _dfu_desc { - struct usb_config_descriptor ucfg; - struct usb_interface_descriptor uif[2]; - struct usb_dfu_func_descriptor func_dfu; -}; - -struct dfuapi { - void (*ep0_send_data)(const char *data, u_int32_t len); - void (*ep0_send_zlp)(void); - void (*ep0_send_stall)(void); - int (*dfu_ep0_handler)(u_int8_t req_type, u_int8_t req, - u_int16_t val, u_int16_t len); - void (*dfu_switch)(void); - u_int8_t *dfu_state; - const struct usb_device_descriptor *dfu_dev_descriptor; - const struct _dfu_desc *dfu_cfg_descriptor; -}; - - -#endif /* _DFU_H */ diff --git a/firmware/src/os/main.c b/firmware/src/os/main.c index 4b7a7a2..878fdbf 100644 --- a/firmware/src/os/main.c +++ b/firmware/src/os/main.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -20,7 +19,6 @@ int main(void) _init_func(); /* initialize USB */ - udp_init(); udp_open(); // Enable User Reset and set its minimal assertion to 960 us diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c index cded8c8..40bd88e 100644 --- a/firmware/src/os/pcd_enumerate.c +++ b/firmware/src/os/pcd_enumerate.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include "../openpcd.h" #include @@ -41,6 +41,7 @@ #ifdef CONFIG_DFU #define DFU_API_LOCATION ((const struct dfuapi *) 0x00102100) static const struct dfuapi *dfu = DFU_API_LOCATION; +#define udp_init dfu->udp_init #define udp_ep0_send_data dfu->ep0_send_data #define udp_ep0_send_zlp dfu->ep0_send_zlp #define udp_ep0_send_stall dfu->ep0_send_stall @@ -328,6 +329,7 @@ static void udp_irq(void) void udp_open(void) { DEBUGPCRF("entering"); + udp_init(); upcd.pUdp = AT91C_BASE_UDP; upcd.cur_config = 0; upcd.cur_rcv_bank = AT91C_UDP_RX_DATA_BK0; @@ -340,7 +342,6 @@ void udp_open(void) /* End-of-Bus-Reset is always enabled */ /* Set the Pull up resistor */ - AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUP); } diff --git a/firmware/src/os/pcd_enumerate.h b/firmware/src/os/pcd_enumerate.h index 57ff88c..813243e 100644 --- a/firmware/src/os/pcd_enumerate.h +++ b/firmware/src/os/pcd_enumerate.h @@ -5,7 +5,7 @@ #include #include #include "openpcd.h" -#include "dfu.h" +#include struct req_ctx; diff --git a/firmware/src/start/Cstartup.S b/firmware/src/start/Cstartup.S index e91fd86..e7d5714 100644 --- a/firmware/src/start/Cstartup.S +++ b/firmware/src/start/Cstartup.S @@ -76,165 +76,22 @@ .text .arm .section .vectram, "ax" -resetvecR: B resetvecR -undefvecR: B undefvecR -swivecR: B swivecR -pabtvecR: B pabtvecR -dabtvecR: B dabtvecR -rsvdvecR: B rsvdvecR -irqvecR: B IRQ_Handler_EntryR -fiqvecR: -FIQ_Handler_EntryR: - -/*- Switch in SVC/User Mode to allow User Stack access for C code */ -/* because the FIQ is not yet acknowledged*/ - -/*- Save and r0 in FIQ_Register */ - mov r9,r0 - ldr r0 , [r8, #AIC_FVR] - msr CPSR_c,#I_BIT | F_BIT | ARM_MODE_SVC - -/*- Save scratch/used registers and LR in User Stack */ - stmfd sp!, { r1-r3, r12, lr} - -/*- Branch to the routine pointed by the AIC_FVR */ - mov r14, pc - bx r0 - -/*- Restore scratch/used registers and LR from User Stack */ - ldmia sp!, { r1-r3, r12, lr} - -/*- Leave Interrupts disabled and switch back in FIQ mode */ - msr CPSR_c, #I_BIT | F_BIT | ARM_MODE_FIQ - -/*- Restore the R0 ARM_MODE_SVC register */ - mov r0,r9 - -/*- Restore the Program Counter using the LR_fiq directly in the PC */ - subs pc,lr,#4 - - .global IRQ_Handler_EntryR - .func IRQ_Handler_EntryR - -IRQ_Handler_EntryR: - -/*- Manage Exception Entry */ -/*- Adjust and save LR_irq in IRQ stack */ - sub lr, lr, #4 - stmfd sp!, {lr} - -/*- Save SPSR need to be saved for nested interrupt */ - mrs r14, SPSR - stmfd sp!, {r14} - -/*- Save and r0 in IRQ stack */ - stmfd sp!, {r0} - -/*- Write in the IVR to support Protect Mode */ -/*- No effect in Normal Mode */ -/*- De-assert the NIRQ and clear the source in Protect Mode */ - ldr r14, =AT91C_BASE_AIC - ldr r0 , [r14, #AIC_IVR] - str r14, [r14, #AIC_IVR] - -/*- Enable Interrupt and Switch in Supervisor Mode */ - msr CPSR_c, #ARM_MODE_SVC - -/*- Save scratch/used registers and LR in User Stack */ - stmfd sp!, { r1-r3, r12, r14} - -/*- Branch to the routine pointed by the AIC_IVR */ - mov r14, pc - bx r0 -/*- Restore scratch/used registers and LR from User Stack*/ - ldmia sp!, { r1-r3, r12, r14} - -/*- Disable Interrupt and switch back in IRQ mode */ - msr CPSR_c, #I_BIT | ARM_MODE_IRQ - -/*- Mark the End of Interrupt on the AIC */ - ldr r14, =AT91C_BASE_AIC - str r14, [r14, #AIC_EOICR] - -/*- Restore SPSR_irq and r0 from IRQ stack */ - ldmia sp!, {r0} - -/*- Restore SPSR_irq and r0 from IRQ stack */ - ldmia sp!, {r14} - msr SPSR_cxsf, r14 - -/*- Restore adjusted LR_irq from IRQ stack directly in the PC */ - ldmia sp!, {pc}^ - - .size IRQ_Handler_EntryR, . - IRQ_Handler_EntryR - .endfunc - - .global remap - .func remap -_remap: -# led1on -# Remap RAM to 0x00000000 for DFU + .global _remap_call_dfu + .func _remap_call_dfu +_remap_call_dfu: + led1on + /* Remap RAM to 0x00000000 for DFU */ ldr r1, =AT91C_BASE_AIC mov r2, #0x01 str r2, [r1, #AIC_MCR_RCR] + + ldr r4, =dfu_main + bx r4 - /* prepare c function call to main */ - mov r0, #0 /* argc = 0 */ - ldr lr,=exit - ldr r10,=main - -#ifdef CONFIG_DFU_SWITCH - /* check whether bootloader button is pressed */ - ldr r1, =AT91C_PMC_PCER - mov r2, #(1 << AT91C_ID_PIOA) - str r2, [r1] - - ldr r1, =AT91C_BASE_PIOA - ldr r2, [r1, #PIOA_PDSR] - tst r2, #PIO_BOOTLDR - ldrne r10,=dfu_main -#endif - - bx r10 - .size remap, . - remap + .size _remap_call_dfu, . - _remap_call_dfu .endfunc -/* "exit" dummy added by mthomas to avoid sbrk write read etc. needed - by the newlib default "exit" */ - .global exit - .func exit -exit: - b . - .size exit, . - exit - .endfunc - -/*--------------------------------------------------------------- -//* ?EXEPTION_VECTOR -//* This module is only linked if needed for closing files. -//*---------------------------------------------------------------*/ - .global AT91F_Default_FIQ_handler - .func AT91F_Default_FIQ_handler -AT91F_Default_FIQ_handler: - b AT91F_Default_FIQ_handler - .size AT91F_Default_FIQ_handler, . - AT91F_Default_FIQ_handler - .endfunc - - .global AT91F_Default_IRQ_handler - .func AT91F_Default_IRQ_handler -AT91F_Default_IRQ_handler: - b AT91F_Default_IRQ_handler - .size AT91F_Default_IRQ_handler, . - AT91F_Default_IRQ_handler - .endfunc - - .global AT91F_Spurious_handler - .func AT91F_Spurious_handler -AT91F_Spurious_handler: - b AT91F_Spurious_handler - .size AT91F_Spurious_handler, . - AT91F_Spurious_handler - .endfunc - - #;------------------------------------------------------------------------------ #;- Section Definition @@ -255,9 +112,6 @@ Top_Stack: * .text is used instead of .section .text so it works with arm-aout too. */ .section .reset .text - .global _startup - .func _startup -_startup: reset: /*------------------------------------------------------------------------------ //*- Exception vectors @@ -277,17 +131,108 @@ swivec: pabtvec: B pabtvec /* 0x0C Prefetch Abort */ dabtvec: - B dabtvec /* 0x10 Data Abort */ + b dabtvec /* 0x10 Data Abort */ rsvdvec: - B rsvdvec /* 0x14 reserved */ + b rsvdvec /* 0x14 reserved */ irqvec: - B irqvec /* 0x18 IRQ */ -fiqvec: - B fiqvec /* 0x1c FIQ */ + b IRQ_Handler_Entry /* 0x18 IRQ */ +fiqvec: + b FIQ_Handler_Entry + /* 0x1c FIQ */ +dfu_state_dummy: + .word 0 + +FIQ_Handler_Entry: + +/*- Switch in SVC/User Mode to allow User Stack access for C code */ +/* because the FIQ is not yet acknowledged*/ + +/*- Save and r0 in FIQ_Register */ + mov r9, r0 + ldr r0, [r8, #AIC_FVR] + msr CPSR_c, #I_BIT | F_BIT | ARM_MODE_SVC + + /*- Save scratch/used registers and LR in User Stack */ + stmfd sp!, { r1-r3, r12, lr} + + /*- Branch to the routine pointed by the AIC_FVR */ + mov r14, pc + bx r0 + + /*- Restore scratch/used registers and LR from User Stack */ + ldmia sp!, { r1-r3, r12, lr} + + /*- Leave Interrupts disabled and switch back in FIQ mode */ + msr CPSR_c, #I_BIT | F_BIT | ARM_MODE_FIQ + + /*- Restore the R0 ARM_MODE_SVC register */ + mov r0,r9 + + /*- Restore the Program Counter using the LR_fiq directly in the PC */ + subs pc, lr, #4 + + .global IRQ_Handler_Entry + .func IRQ_Handler_Entry + +IRQ_Handler_Entry: + + /*- Manage Exception Entry */ + /*- Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /*- Save SPSR need to be saved for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /*- Save and r0 in IRQ stack */ + stmfd sp!, {r0} + + /*- Write in the IVR to support Protect Mode */ + /*- No effect in Normal Mode */ + /*- De-assert the NIRQ and clear the source in Protect Mode */ + ldr r14, =AT91C_BASE_AIC + ldr r0 , [r14, #AIC_IVR] + str r14, [r14, #AIC_IVR] + + /*- Enable Interrupt and Switch in Supervisor Mode */ + msr CPSR_c, #ARM_MODE_SVC + + /*- Save scratch/used registers and LR in User Stack */ + stmfd sp!, { r1-r3, r12, r14} + + /*- Branch to the routine pointed by the AIC_IVR */ + mov r14, pc + bx r0 + + /*- Restore scratch/used registers and LR from User Stack*/ + ldmia sp!, { r1-r3, r12, r14} + + /*- Disable Interrupt and switch back in IRQ mode */ + msr CPSR_c, #I_BIT | ARM_MODE_IRQ + + /*- Mark the End of Interrupt on the AIC */ + ldr r14, =AT91C_BASE_AIC + str r14, [r14, #AIC_EOICR] + + /*- Restore SPSR_irq and r0 from IRQ stack */ + ldmia sp!, {r0} + + /*- Restore SPSR_irq and r0 from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /*- Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + + .size IRQ_Handler_Entry, . - IRQ_Handler_Entry + .endfunc .align 0 .RAM_TOP: .word Top_Stack + .global _startup + .func _startup InitReset: /*------------------------------------------------------------------------------ /*- Low level Init (PMC, AIC, ? ....) by C function AT91F_LowLevelInit @@ -348,16 +293,7 @@ InitReset: msr CPSR_c, #ARM_MODE_SVC mov r13, r0 /* Init stack Sup */ -/*- Relocate DFU .data section (Copy from ROM to RAM)*/ - ldr r1, =_etext_dfu - ldr r2, =_data_dfu - ldr r3, =_edata_dfu -LoopRelDFU: cmp r2, r3 - ldrlo r0, [r1], #4 - strlo r0, [r2], #4 - blo LoopRelDFU - -# Relocate .data section (Copy from ROM to RAM) +# Relocate DFU .data section (Copy from ROM to RAM) LDR R1, =_etext LDR R2, =_data LDR R3, =_edata @@ -366,7 +302,7 @@ LoopRel: CMP R2, R3 STRLO R0, [R2], #4 BLO LoopRel -# Clear .bss section (Zero init) +# Clear DFU .bss section (Zero init) MOV R0, #0 LDR R1, =__bss_start__ LDR R2, =__bss_end__ @@ -374,13 +310,75 @@ LoopZI: CMP R1, R2 STRLO R0, [R1], #4 BLO LoopZI - led1on + /* prepare c function call to main */ + mov r0, #0 /* argc = 0 */ + ldr lr, =exit + ldr r10, =0x00101000 - ldr r0, =_remap - bx r0 +#ifdef CONFIG_DFU_SWITCH + /* check whether bootloader button is pressed */ + ldr r1, =AT91C_PMC_PCER + mov r2, #(1 << AT91C_ID_PIOA) + str r2, [r1] + ldr r1, =AT91C_BASE_PIOA + ldr r2, [r1, #PIOA_PDSR] + tst r2, #PIO_BOOTLDR + bne _reloc_dfu_text +#endif + + bx r10 + +_reloc_dfu_text: + + ldr r1, =0x00100000 + ldr r2, =0x00200000 + ldr r3, =_etext + add r3, r3, r2 +loop_rel_t: cmp r2, r3 + ldrlo r4, [r1], #4 + strlo r4, [r2], #4 + blo loop_rel_t + ldr r4, =_remap_call_dfu + bx r4 + .size _startup, . - _startup .endfunc - + +/* "exit" dummy to avoid sbrk write read etc. needed by the newlib default "exit" */ + .global exit + .func exit +exit: + b . + .size exit, . - exit + .endfunc + +/*--------------------------------------------------------------- +//* ?EXEPTION_VECTOR +//* This module is only linked if needed for closing files. +//*---------------------------------------------------------------*/ + .global AT91F_Default_FIQ_handler + .func AT91F_Default_FIQ_handler +AT91F_Default_FIQ_handler: + b AT91F_Default_FIQ_handler + .size AT91F_Default_FIQ_handler, . - AT91F_Default_FIQ_handler + .endfunc + + .global AT91F_Default_IRQ_handler + .func AT91F_Default_IRQ_handler +AT91F_Default_IRQ_handler: + b AT91F_Default_IRQ_handler + .size AT91F_Default_IRQ_handler, . - AT91F_Default_IRQ_handler + .endfunc + + .global AT91F_Spurious_handler + .func AT91F_Spurious_handler +AT91F_Spurious_handler: + b AT91F_Spurious_handler + .size AT91F_Spurious_handler, . - AT91F_Spurious_handler + .endfunc + + + .end diff --git a/firmware/src/start/Cstartup_app.S b/firmware/src/start/Cstartup_app.S new file mode 100644 index 0000000..4a2187a --- /dev/null +++ b/firmware/src/start/Cstartup_app.S @@ -0,0 +1,81 @@ +/* Cstartup header for the application to be started by at91dfu + * (C) 2006 by Harald Welte */ + +//#define DEBUG_LL + +#ifdef DEBUG_LL +/* Debugging macros for switching on/off LED1 (green) */ +#define PIOA_PER 0xFFFFF400 +#define PIOA_OER 0xFFFFF410 +#define PIOA_SODR 0xFFFFF430 +#define PIOA_CODR 0xFFFFF434 +#define LED1 25 /* this only works on OpenPICC, not Olimex */ + .macro led1on + ldr r2, =PIOA_CODR + mov r1, #(1 << LED1) + str r1, [r2] + .endm + .macro led1off + ldr r2, =PIOA_SODR + mov r1, #(1 << LED1) + str r1, [r2] + .endm + .macro ledinit + ldr r2, =PIOA_PER + mov r1, #(1 << LED1) + str r1, [r2] + ldr r2, =PIOA_OER + str r1, [r2] + led1off + .endm +#else + .macro ledinit + .endm + .macro led1on + .endm + .macro led1off + .endm +#endif + + .global _startup + .func _startup +_startup: + /* Relocate .data section (copy from Flash to RAM) */ + ldr r1, =_etext + ldr r2, =_data + ldr r3, =_edata +loop_r: cmp r2, r3 + ldrlo r0, [r1], #4 + strlo r0, [r2], #4 + blo loop_r + + /* Clear .bss section (Zero init) */ + mov r0, #0 + ldr r1, =__bss_start__ + ldr r2, =__bss_end__ +loop_z: cmp r1, r2 + strlo r0, [r1], #4 + blo loop_z + + led1on + + /* prepare C function call to main */ + mov r0, #0 /* argc = 0 */ + ldr lr, =exit + ldr r10, =main + + bx r10 + + .size _startup, . - _startup + .endfunc + +/* "exit" dummy to avoid sbrk write read etc. needed by the newlib default "exit" */ + .global exit + .func exit +exit: + b . + .size exit, . - exit + .endfunc + + .end + -- cgit v1.2.3