From fded24a803b6355bec88e5d8febaf3b8b3399266 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Sat, 1 Mar 2008 22:17:33 +0100 Subject: [PATCH] working dfu framework --- include/at91_udp.h | 14 ++ main.c | 4 - src/at91_dbgu.c | 4 +- src/at91_pio.c | 69 --------- src/at91_udp.c | 295 ++++++++++++++++++++------------------- src/dfu.c | 338 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 505 insertions(+), 219 deletions(-) delete mode 100644 src/at91_pio.c create mode 100644 src/dfu.c diff --git a/include/at91_udp.h b/include/at91_udp.h index e667f91..235da43 100644 --- a/include/at91_udp.h +++ b/include/at91_udp.h @@ -1,6 +1,20 @@ #ifndef AT91_UDP_H_ #define AT91_UDP_H_ +#include + +void ep_transfer_send(uint32_t ep, + char *data, + uint32_t length, + void (*complete_cb)(void)); + +void ep_transfer_receive(uint32_t ep, + char *data, + uint32_t length, + void (*complete_cb)(void)); + +void ep_send_stall(uint32_t ep); + void at91_udp_init(void); #endif /*AT91_UDP_H_*/ diff --git a/main.c b/main.c index 003cdf6..89c9438 100644 --- a/main.c +++ b/main.c @@ -21,7 +21,6 @@ #include "at91_dbgu.h" #include "at91_pitc.h" #include "at91_udp.h" -#include "at91_pio.h" #include "at91_twi.h" #include "memalloc.h" @@ -57,9 +56,6 @@ int main(void) at91_dbgu_init(); at91_dbgu_puts("==========================================================\n\rGood morning Dave\n\r"); - /* triggers pinchange-isrs */ - at91_pio_init(); - /* timer */ at91_pitc_init(); diff --git a/src/at91_dbgu.c b/src/at91_dbgu.c index 739c53c..de2150c 100644 --- a/src/at91_dbgu.c +++ b/src/at91_dbgu.c @@ -44,7 +44,7 @@ static void dbgu_isr(uint32_t status) } */ if (status & AT91C_US_TXBUFE) - if (fifo_txpdc(txfifo, AT91C_BASE_PDC_DBGU, 16) == 0) + if (fifo_txpdc(txfifo, AT91C_BASE_PDC_DBGU, 32) == 0) *AT91C_DBGU_IDR = AT91C_US_TXBUFE; } @@ -59,7 +59,7 @@ void at91_dbgu_init(void) dbgu->DBGU_MR = AT91C_US_PAR_NONE | AT91C_US_CHMODE_NORMAL; dbgu->DBGU_CR = AT91C_US_RXEN | AT91C_US_TXEN | AT91C_US_RSTSTA; - txfifo = fifo_alloc(1024); + txfifo = fifo_alloc(16384); /* enable TX PDC */ dbgu->DBGU_PTCR = AT91C_PDC_TXTEN; diff --git a/src/at91_pio.c b/src/at91_pio.c deleted file mode 100644 index 6a990d4..0000000 --- a/src/at91_pio.c +++ /dev/null @@ -1,69 +0,0 @@ -/*************************************************************************** - * Copyright (C) 01/2008 by Olaf Rempel * - * razzor@kopf-tisch.de * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; version 2 of the License * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#include -#include -#include "AT91SAM7S256.h" -#include "board.h" -#include "at91_pio.h" - -/* extern symbols, defined in ldscript */ -extern struct pio_pinchange_isr _pio_isr_table; -extern struct pio_pinchange_isr _pio_isr_table_end; - -static void pio_isr(void) -{ - uint32_t status = *AT91C_PIOA_ISR; - uint32_t input = *AT91C_PIOA_PDSR; - - struct pio_pinchange_isr *isr; - for (isr = &_pio_isr_table; isr < &_pio_isr_table_end; isr++) - if (isr->mask & status) - isr->func(status, input); -} - -void pio_trigger_isr(uint32_t mask) -{ - uint32_t input = *AT91C_PIOA_PDSR; - - struct pio_pinchange_isr *isr; - for (isr = &_pio_isr_table; isr < &_pio_isr_table_end; isr++) - if (isr->mask & mask) - isr->func(mask, input); -} - -void at91_pio_init(void) -{ - /* enable PIO clock */ - *AT91C_PMC_PCER = (1 << AT91C_ID_PIOA); - - /* enable pinchange interrupts */ - struct pio_pinchange_isr *isr; - for (isr = &_pio_isr_table; isr < &_pio_isr_table_end; isr++) - *AT91C_PIOA_IER = isr->mask; - - /* dummy read to clear interrupts */ - uint32_t dummy = *AT91C_PIOA_ISR; - dummy = dummy; - - /* low priority, level triggered, own vector */ - AT91S_AIC *aic = AT91C_BASE_AIC; - aic->AIC_SMR[AT91C_ID_PIOA] = IRQPRIO_PIOA | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL; - aic->AIC_SVR[AT91C_ID_PIOA] = (uint32_t)pio_isr; - aic->AIC_IECR = (1 << AT91C_ID_PIOA); -} diff --git a/src/at91_udp.c b/src/at91_udp.c index 0fde2ba..c206d4d 100644 --- a/src/at91_udp.c +++ b/src/at91_udp.c @@ -18,14 +18,16 @@ ***************************************************************************/ #include #include "AT91SAM7S256.h" -#include "at91_pio.h" #include "board.h" #include "fifo.h" #include "usb_ch9.h" -#include "usb_cdc.h" #include "usb_dfu.h" +/* from dfu.c */ +extern void ep0_handle_dfu(struct usb_ctrlrequest *req); + + #define csr_clear_flags(csr, flags) \ while ((csr) & (flags)) \ (csr) &= ~(flags); @@ -74,71 +76,9 @@ static const struct usb_device_descriptor dev_descriptor = { .idVendor = USB_VENDOR_ID, .idProduct = USB_PRODUCT_ID +1, .bcdDevice = 0x0001, + .iProduct = 0x01, .bNumConfigurations = 1, }; - -static const struct usb_string_descriptor usb_string0 = { - /* String 0 - Language */ - .bLength = sizeof(struct usb_string_descriptor) + 1 * sizeof(uint16_t), - .bDescriptorType = USB_DT_STRING, - .wData = { 0x0409 /* English */ }, -}; - -static const struct usb_string_descriptor usb_string1 = { - /* String 1 "sam7fc-flash" */ - .bLength = sizeof(struct usb_string_descriptor) + 12 * sizeof(uint16_t), - .bDescriptorType = USB_DT_STRING, - .wData = { - 0x0073, 0x0061, 0x006d, 0x0037, 0x0066, 0x0063, 0x002d, 0x0066, - 0x006c, 0x0061, 0x0073, 0x0068, - }, -}; - -static const struct usb_string_descriptor usb_string2 = { - /* String 2 "blctrl1-flash" */ - .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), - .bDescriptorType = USB_DT_STRING, - .wData = { - 0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0031, 0x002d, - 0x0066, 0x006c, 0x0061, 0x0073, 0x0068, - }, -}; - -static const struct usb_string_descriptor usb_string3 = { - /* String 3 "blctrl2-flash" */ - .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), - .bDescriptorType = USB_DT_STRING, - .wData = { - 0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0032, 0x002d, - 0x0066, 0x006c, 0x0061, 0x0073, 0x0068, - }, -}; - -static const struct usb_string_descriptor usb_string4 = { - /* String 4 "blctrl3-flash" */ - .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), - .bDescriptorType = USB_DT_STRING, - .wData = { - 0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0033, 0x002d, - 0x0066, 0x006c, 0x0061, 0x0073, 0x0068, - }, -}; - -static const struct usb_string_descriptor usb_string5 = { - /* String 5 "blctrl4-flash" */ - .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), - .bDescriptorType = USB_DT_STRING, - .wData = { - 0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0034, 0x002d, - 0x0066, 0x006c, 0x0061, 0x0073, 0x0068, - }, -}; - -static const struct usb_string_descriptor *usb_strings[] = { - &usb_string0, &usb_string1, &usb_string2, &usb_string3, - &usb_string4, &usb_string5, -}; - struct my_config { struct usb_config_descriptor cfg; struct usb_interface_descriptor iface0; @@ -146,6 +86,7 @@ struct my_config { struct usb_interface_descriptor iface2; struct usb_interface_descriptor iface3; struct usb_interface_descriptor iface4; + struct usb_interface_descriptor iface5; struct usb_dfu_descriptor dfu; } __attribute__ ((packed)); @@ -154,8 +95,9 @@ static const struct my_config cfg_descriptor = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = USB_DT_CONFIG, .wTotalLength = sizeof(struct my_config), - .bNumInterfaces = 1, + .bNumInterfaces = 6, .bConfigurationValue = 1, + .iConfiguration = 0x02, .bmAttributes = USB_CONFIG_ATT_SELFPOWER | USB_CONFIG_ATT_WAKEUP, .bMaxPower = 50, }, @@ -166,69 +108,161 @@ static const struct my_config cfg_descriptor = { .bInterfaceClass = USB_CLASS_APP_SPEC, .bInterfaceSubClass = 0x01, /* DFU */ .bInterfaceProtocol = 0x02, - .iInterface = 0x01, + .iInterface = 0x03, }, .iface1 = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 1, - .bInterfaceClass = USB_CLASS_APP_SPEC, - .bInterfaceSubClass = 0x01, /* DFU */ - .bInterfaceProtocol = 0x02, - .iInterface = 0x02, -}, -.iface2 = { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 2, - .bInterfaceClass = USB_CLASS_APP_SPEC, - .bInterfaceSubClass = 0x01, /* DFU */ - .bInterfaceProtocol = 0x02, - .iInterface = 0x03, -}, -.iface3 = { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 3, + .bInterfaceNumber = 1, .bInterfaceClass = USB_CLASS_APP_SPEC, .bInterfaceSubClass = 0x01, /* DFU */ .bInterfaceProtocol = 0x02, .iInterface = 0x04, }, -.iface4 = { +.iface2 = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 4, + .bInterfaceNumber = 2, .bInterfaceClass = USB_CLASS_APP_SPEC, .bInterfaceSubClass = 0x01, /* DFU */ .bInterfaceProtocol = 0x02, .iInterface = 0x05, }, +.iface3 = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 3, + .bInterfaceClass = USB_CLASS_APP_SPEC, + .bInterfaceSubClass = 0x01, /* DFU */ + .bInterfaceProtocol = 0x02, + .iInterface = 0x06, +}, +.iface4 = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 4, + .bInterfaceClass = USB_CLASS_APP_SPEC, + .bInterfaceSubClass = 0x01, /* DFU */ + .bInterfaceProtocol = 0x02, + .iInterface = 0x07, +}, +.iface5 = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 5, + .bInterfaceClass = USB_CLASS_APP_SPEC, + .bInterfaceSubClass = 0x01, /* DFU */ + .bInterfaceProtocol = 0x02, + .iInterface = 0x08, +}, .dfu = { .bLength = sizeof(struct usb_dfu_descriptor), .bDescriptorType = USB_TYPE_DFU, - .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_CAN_UPLOAD, + .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_CAN_UPLOAD | USB_DFU_MANIFEST_TOL | USB_DFU_WILL_DETACH, .wDetachTimeOut = 0xff00, .wTransferSize = AT91C_IFLASH_PAGE_SIZE, .bcdDFUVersion = 0x0101, }, }; -static struct dfu_status dfu_status = { - .bStatus = DFU_STATUS_OK, - .bwPollTimeout = {0x00, 0x04, 0x00}, - .bState = DFU_STATE_dfuIDLE, +static const struct usb_string_descriptor usb_string0 = { + /* String 0 - Language */ + .bLength = sizeof(struct usb_string_descriptor) + 1 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { 0x0409 /* English */ }, }; -static void ep_transfer_send(uint32_t ep, char *data, uint32_t length, +static const struct usb_string_descriptor usb_string1 = { + /* String 1 "sam7fc" */ + .bLength = sizeof(struct usb_string_descriptor) + 6 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { + 0x0073, 0x0061, 0x006d, 0x0037, 0x0066, 0x0063, + }, +}; + +static const struct usb_string_descriptor usb_string2 = { + /* String 2 "device firmware upgrade" */ + .bLength = sizeof(struct usb_string_descriptor) + 23 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { + 0x0064, 0x0065, 0x0076, 0x0069, 0x0063, 0x0065, 0x0020, 0x0066, + 0x0069, 0x0072, 0x006d, 0x0077, 0x0061, 0x0072, 0x0065, 0x0020, + 0x0075, 0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, + }, +}; + +static const struct usb_string_descriptor usb_string3 = { + /* String 3 "sam7fc-flash" */ + .bLength = sizeof(struct usb_string_descriptor) + 12 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { + 0x0073, 0x0061, 0x006d, 0x0037, 0x0066, 0x0063, 0x002d, 0x0066, + 0x006c, 0x0061, 0x0073, 0x0068, + }, +}; + +static const struct usb_string_descriptor usb_string4 = { + /* String 4 "sam7fc-eeprom" */ + .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { + 0x0073, 0x0061, 0x006d, 0x0037, 0x0066, 0x0063, 0x002d, 0x0065, + 0x0065, 0x0070, 0x0072, 0x006f, 0x006d, + }, +}; + +static const struct usb_string_descriptor usb_string5 = { + /* String 5 "blctrl1-flash" */ + .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { + 0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0031, 0x002d, + 0x0066, 0x006c, 0x0061, 0x0073, 0x0068, + }, +}; + +static const struct usb_string_descriptor usb_string6 = { + /* String 6 "blctrl2-flash" */ + .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { + 0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0032, 0x002d, + 0x0066, 0x006c, 0x0061, 0x0073, 0x0068, + }, +}; + +static const struct usb_string_descriptor usb_string7 = { + /* String 7 "blctrl3-flash" */ + .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { + 0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0033, 0x002d, + 0x0066, 0x006c, 0x0061, 0x0073, 0x0068, + }, +}; + +static const struct usb_string_descriptor usb_string8 = { + /* String 8 "blctrl4-flash" */ + .bLength = sizeof(struct usb_string_descriptor) + 13 * sizeof(uint16_t), + .bDescriptorType = USB_DT_STRING, + .wData = { + 0x0062, 0x006c, 0x0063, 0x0074, 0x0072, 0x006c, 0x0034, 0x002d, + 0x0066, 0x006c, 0x0061, 0x0073, 0x0068, + }, +}; + +static const struct usb_string_descriptor *usb_strings[] = { + &usb_string0, &usb_string1, &usb_string2, &usb_string3, + &usb_string4, &usb_string5, &usb_string6, &usb_string7, + &usb_string8, +}; + +void ep_transfer_send(uint32_t ep, char *data, uint32_t length, void (*complete_cb)(void)) { struct ep_ctx *ctx = &ep_ctx[ep]; - printf("ep_transfer_send(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags); +// printf("ep_transfer_send(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags); if (!(ctx->flags & CTX_TRANSFER) || (ctx->flags & (CTX_IN | CTX_OUT))) return; @@ -251,11 +285,11 @@ static void ep_transfer_send(uint32_t ep, char *data, uint32_t length, AT91C_UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY; } -static void ep_transfer_receive(uint32_t ep, char *data, uint32_t length, +void ep_transfer_receive(uint32_t ep, char *data, uint32_t length, void (*complete_cb)(void)) { struct ep_ctx *ctx = &ep_ctx[ep]; - printf("ep_transfer_receive(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags); +// printf("ep_transfer_receive(%ld) size=%ld flags=0x%x\n\r", ep, length, ctx->flags); if (!(ctx->flags & CTX_TRANSFER) || (ctx->flags & (CTX_IN | CTX_OUT))) return; @@ -270,7 +304,7 @@ static void ep_transfer_receive(uint32_t ep, char *data, uint32_t length, } /* stalls the endpoint */ -static void ep_send_stall(uint32_t ep) +void ep_send_stall(uint32_t ep) { printf("stall\n\r"); AT91C_UDP_CSR[ep] |= AT91C_UDP_FORCESTALL; @@ -324,8 +358,8 @@ static void udp_txcb_setinterface(void) static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req) { - printf("typ:0x%02x req:0x%02x val:0x%04x idx:0x%04x len:0x%04x\n\r", - req->bRequestType, req->bRequest, req->wValue, req->wIndex, req->wLength); +// printf("typ:0x%02x req:0x%02x val:0x%04x idx:0x%04x len:0x%04x\n\r", +// req->bRequestType, req->bRequest, req->wValue, req->wIndex, req->wLength); switch (req->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) { case (USB_TYPE_STANDARD | USB_RECIP_DEVICE): /* 0x00/0x80 */ @@ -353,7 +387,6 @@ static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req) ; uint8_t index = req->wValue & 0xFF; if (index < ARRAY_SIZE(usb_strings)) { - printf("get string %d: %p\n\r", index, usb_strings[index]); ep_transfer_send(0, (char *)usb_strings[index], MIN(usb_strings[index]->bLength, req->wLength), NULL); @@ -362,6 +395,12 @@ static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req) } break; + case USB_DT_CS_DEVICE: /* 0x21 */ + ep_transfer_send(0, (char *)&cfg_descriptor.dfu, + MIN(sizeof(cfg_descriptor.dfu), req->wLength), + NULL); + break; + default: ep_send_stall(0); break; @@ -380,7 +419,6 @@ static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req) break; case (USB_TYPE_STANDARD | USB_RECIP_INTERFACE): /* 0x01/0x81 */ - // TODO: follow current_interface switch (req->bRequest) { case USB_REQ_SET_INTERFACE: /* 0x0b */ current_interface = req->wValue; @@ -394,22 +432,7 @@ static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req) break; case (USB_TYPE_CLASS | USB_RECIP_INTERFACE): /* 0x21/0xA1 */ - // TODO: follow current_interface - switch (req->bRequest) { - case USB_REQ_DFU_DETACH: /* 0x00 */ - ep_transfer_send(0, NULL, 0, NULL); - break; - - case USB_REQ_DFU_GETSTATUS: /* 0x03 */ - ep_transfer_send(0, (char *)&dfu_status, - MIN(sizeof(struct dfu_status), req->wLength), - NULL); - break; - - default: - ep_send_stall(0); - break; - } + ep0_handle_dfu(req); break; default: @@ -468,8 +491,6 @@ static void udp_handle_ep(uint32_t ep) } else { ctx->flags &= ~CTX_IN; - printf("txcomp\n\r"); - if (transfer->complete_cb) transfer->complete_cb(); } @@ -539,6 +560,8 @@ static void udp_isr(void) { uint32_t isr = *AT91C_UDP_ISR; if (isr & AT91C_UDP_ENDBUSRES) { + printf("USB reset\n\r"); + AT91S_UDP *udp = AT91C_BASE_UDP; /* reset all endpoints */ @@ -565,11 +588,8 @@ static void udp_isr(void) } /* Handle Endpoint Interrupts */ - uint32_t i; - for (i = 0; i < 4; i++) { - if (isr & *AT91C_UDP_IMR & (1<PIO_CODR = UDP_PULLUP; pio->PIO_PER = UDP_PULLUP; pio->PIO_OER = UDP_PULLUP; - // TODO: needed? - pio->PIO_PPUDR = UDP_VBUS_MON; /* UDPCK (48MHz) = PLLCK / 2 */ *AT91C_CKGR_PLLR |= AT91C_CKGR_USBDIV_1; @@ -610,17 +628,6 @@ void at91_udp_init(void) aic->AIC_SVR[AT91C_ID_UDP] = (uint32_t)udp_isr; aic->AIC_IECR = (1 << AT91C_ID_UDP); - pio_trigger_isr(UDP_VBUS_MON); + /* usb connected -> enable pullup */ + *AT91C_PIOA_CODR = UDP_PULLUP; } - -static void udp_vbus_monitor(uint32_t status, uint32_t input) -{ - if (input & UDP_VBUS_MON) - /* usb connected -> enable pullup */ - *AT91C_PIOA_CODR = UDP_PULLUP; - else - /* usb got diconnected -> disable pullup */ - *AT91C_PIOA_SODR = UDP_PULLUP; -} - -PIO_PINCHANGE_ISR(UDP_VBUS_MON, udp_vbus_monitor); diff --git a/src/dfu.c b/src/dfu.c new file mode 100644 index 0000000..f208d14 --- /dev/null +++ b/src/dfu.c @@ -0,0 +1,338 @@ +/*************************************************************************** + * Copyright (C) 01/2008 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include "AT91SAM7S256.h" +#include "board.h" + +#include "at91_udp.h" + +#include "usb_ch9.h" +#include "usb_dfu.h" + +static uint8_t dfu_state = DFU_STATE_dfuIDLE; +static uint8_t dfu_status = DFU_STATUS_OK; + +#define RET_NOTHING 0 +#define RET_STALL 1 +#define RET_ZLP 2 + +static void handle_getstatus(void) +{ + struct dfu_status dstat; + + uint32_t fmr = *AT91C_MC_FMR; + + switch (dfu_state) { + case DFU_STATE_dfuDNLOAD_SYNC: + case DFU_STATE_dfuDNBUSY: + if (fmr & AT91C_MC_PROGE) { + dfu_status = DFU_STATUS_errPROG; + dfu_state = DFU_STATE_dfuERROR; + + } else if (fmr & AT91C_MC_LOCKE) { + dfu_status = DFU_STATUS_errWRITE; + dfu_state = DFU_STATE_dfuERROR; + + } else if (fmr & AT91C_MC_FRDY) { + dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + + } else { +// dfu_state = DFU_STATE_dfuDNBUSY; + dfu_state = DFU_STATE_dfuDNLOAD_IDLE; + } + break; + } + + /* send status response */ + dstat.bStatus = dfu_status; + dstat.bState = dfu_state; + dstat.iString = 0; + /* FIXME: set dstat.bwPollTimeout */ + + ep_transfer_send(0, (char *)&dstat, sizeof(dstat), NULL); +} + +static void handle_getstate(void) +{ + ep_transfer_send(0, (char *)&dfu_state, sizeof(dfu_state), NULL); +} + +static uint8_t buf[256]; + +static void handle_dnload_cb(void) +{ + ep_transfer_send(0, NULL, 0, NULL); +} + +static uint32_t handle_dnload(uint16_t index, uint16_t value, uint16_t length) +{ + printf("down:%x-%x-%x\n\r", index, value, length); + + if (length == 0) { + dfu_state = DFU_STATE_dfuMANIFEST_SYNC; + return RET_ZLP; + } + + ep_transfer_receive(0, (char *)&buf, length, handle_dnload_cb); + return RET_NOTHING; +} + +static uint32_t handle_upload(uint16_t index, uint16_t value, uint16_t length) +{ + printf("up:%x-%x-%x\n\r", index, value, length); + return 0; +} + +void ep0_handle_dfu(struct usb_ctrlrequest *req) +{ + uint32_t rc, ret = RET_NOTHING; +// printf("state:%x\n\r", dfu_state); + + switch (dfu_state) { + case DFU_STATE_appIDLE: + switch (req->bRequest) { + case USB_REQ_DFU_DETACH: + dfu_state = DFU_STATE_appDETACH; + ret = RET_ZLP; + break; + + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + + default: + ret = RET_STALL; + break; + } + break; + + case DFU_STATE_appDETACH: + switch (req->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + + default: + dfu_state = DFU_STATE_appIDLE; + ret = RET_STALL; + break; + } + /* FIXME: implement timer to return to appIDLE */ + break; + + case DFU_STATE_dfuIDLE: + switch (req->bRequest) { + case USB_REQ_DFU_DNLOAD: + if (req->wLength == 0) { + dfu_state = DFU_STATE_dfuERROR; + ret = RET_STALL; + break; + } + dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + ret = handle_dnload(req->wIndex, req->wValue, req->wLength); + break; + + case USB_REQ_DFU_UPLOAD: + dfu_state = DFU_STATE_dfuUPLOAD_IDLE; + handle_upload(req->wIndex, req->wValue, req->wLength); + break; + + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + + case USB_REQ_DFU_ABORT: + /* no zlp? */ + ret = RET_ZLP; + break; + + default: + dfu_state = DFU_STATE_dfuERROR; + ret = RET_STALL; + break; + } + break; + + case DFU_STATE_dfuDNLOAD_SYNC: + switch (req->bRequest) { + 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; + ret = RET_STALL; + break; + } + break; + + case DFU_STATE_dfuDNBUSY: + switch (req->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* FIXME: only accept getstatus if bwPollTimeout + * has elapsed */ + handle_getstatus(); + break; + + default: + dfu_state = DFU_STATE_dfuERROR; + ret = RET_STALL; + break; + } + break; + + case DFU_STATE_dfuDNLOAD_IDLE: + switch (req->bRequest) { + case USB_REQ_DFU_DNLOAD: + dfu_state = DFU_STATE_dfuDNLOAD_SYNC; + ret = handle_dnload(req->wIndex, req->wValue, req->wLength); + break; + + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + + case USB_REQ_DFU_ABORT: + dfu_state = DFU_STATE_dfuIDLE; + ret = RET_ZLP; + break; + + default: + dfu_state = DFU_STATE_dfuERROR; + ret = RET_STALL; + break; + } + break; + + case DFU_STATE_dfuMANIFEST_SYNC: + switch (req->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + dfu_state = DFU_STATE_dfuIDLE; + break; + + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + + default: + dfu_state = DFU_STATE_dfuERROR; + ret = RET_STALL; + break; + } + break; + + case DFU_STATE_dfuMANIFEST: + dfu_state = DFU_STATE_dfuERROR; + ret = RET_STALL; + break; + + case DFU_STATE_dfuMANIFEST_WAIT_RST: + /* we should never go here */ + break; + + case DFU_STATE_dfuUPLOAD_IDLE: + switch (req->bRequest) { + case USB_REQ_DFU_UPLOAD: + /* state transition if less data then requested */ + rc = handle_upload(req->wIndex, req->wValue, req->wLength); + if (rc >= 0 && rc < req->wLength) + dfu_state = DFU_STATE_dfuIDLE; + break; + + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + + case USB_REQ_DFU_ABORT: + dfu_state = DFU_STATE_dfuIDLE; + /* no zlp? */ + ret = RET_ZLP; + break; + + default: + dfu_state = DFU_STATE_dfuERROR; + ret = RET_STALL; + break; + } + break; + + case DFU_STATE_dfuERROR: + switch (req->bRequest) { + case USB_REQ_DFU_GETSTATUS: + handle_getstatus(); + break; + + case USB_REQ_DFU_CLRSTATUS: + dfu_state = DFU_STATE_dfuIDLE; + dfu_status = DFU_STATUS_OK; + /* no zlp? */ + ret = RET_ZLP; + break; + + case USB_REQ_DFU_GETSTATE: + handle_getstate(); + break; + + default: + dfu_state = DFU_STATE_dfuERROR; + ret = RET_STALL; + break; + } + break; + } + + switch (ret) { + case RET_NOTHING: + break; + + case RET_ZLP: + ep_transfer_send(0, NULL, 0, NULL); + break; + + case RET_STALL: + ep_send_stall(0); + break; + } +}