diff --git a/include/at91_twi.h b/include/at91_twi.h index 0e23314..dc69ee2 100644 --- a/include/at91_twi.h +++ b/include/at91_twi.h @@ -1,7 +1,7 @@ #ifndef AT91TWI_H_ #define AT91TWI_H_ -#include +#include /* TWI slave addresses */ #define TWI_ADDR_BL1 0x21 @@ -10,7 +10,7 @@ #define TWI_ADDR_BL4 0x24 #define TWI_ADDR_EEPROM 0x40 -/* TWIBOOT commands */ +/* TWIBOOT commands */ #define CMD_WAIT 0x00 #define CMD_GET_INFO 0x10 #define CMD_GET_SIGNATURE 0x11 @@ -33,7 +33,7 @@ struct blmc_cmd { uint32_t cmd; /* cmd byte(s) */ uint8_t mode; /* read/write, cmdlen (1-3 bytes) */ - uint8_t size; /* data size */ + uint16_t size; /* data size */ uint8_t *data; /* read/write data */ }; diff --git a/src/at91_udp.c b/src/at91_udp.c index 2fec7ee..81a7e80 100644 --- a/src/at91_udp.c +++ b/src/at91_udp.c @@ -24,6 +24,7 @@ #include "usb_ch9.h" #include "usb_cdc.h" +#include "usb_dfu.h" #define csr_clear_flags(csr, flags) \ while ((csr) & (flags)) \ @@ -73,6 +74,7 @@ static const struct usb_device_descriptor dev_descriptor = { .idVendor = USB_VENDOR_ID, .idProduct = USB_PRODUCT_ID +1, .bcdDevice = 0x0001, + .iProduct = 0x01, .bNumConfigurations = 1, }; @@ -87,6 +89,8 @@ struct my_config { struct usb_interface_descriptor data_iface; struct usb_endpoint_descriptor dataout_ep; struct usb_endpoint_descriptor datain_ep; + struct usb_interface_descriptor dfu_iface; + struct usb_dfu_descriptor dfu; } __attribute__ ((packed)); static const struct my_config cfg_descriptor = { @@ -94,7 +98,7 @@ static const struct my_config cfg_descriptor = { .bLength = sizeof(struct usb_config_descriptor), .bDescriptorType = USB_DT_CONFIG, .wTotalLength = sizeof(struct my_config), - .bNumInterfaces = 2, + .bNumInterfaces = 3, .bConfigurationValue = 1, .bmAttributes = USB_CONFIG_ATT_SELFPOWER | USB_CONFIG_ATT_WAKEUP, .bMaxPower = 50, @@ -163,9 +167,50 @@ static const struct my_config cfg_descriptor = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = 64, }, +.dfu_iface = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 2, + .bInterfaceClass = USB_CLASS_APP_SPEC, + .bInterfaceSubClass = 0x01, /* DFU */ + .bInterfaceProtocol = 0x01, +}, +.dfu = { + .bLength = sizeof(struct usb_dfu_descriptor), + .bDescriptorType = USB_TYPE_DFU, + .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 void ep_transfer_send(uint32_t ep, char *data, uint32_t length, +static const struct dfu_status dfu_status = { + .bStatus = DFU_STATUS_OK, + .bState = DFU_STATE_appIDLE, +}; + +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" */ + .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_strings[] = { + &usb_string0, &usb_string1, +}; + +void ep_transfer_send(uint32_t ep, char *data, uint32_t length, void (*complete_cb)(void)) { struct ep_ctx *ctx = &ep_ctx[ep]; @@ -192,7 +237,7 @@ 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]; @@ -292,6 +337,24 @@ static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req) NULL); break; + case USB_DT_STRING: /* 0x03 */ + ; + uint8_t index = req->wValue & 0xFF; + if (index < ARRAY_SIZE(usb_strings)) { + ep_transfer_send(0, (char *)usb_strings[index], + MIN(usb_strings[index]->bLength, req->wLength), + NULL); + } else { + ep_send_stall(0); + } + 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; @@ -310,7 +373,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; @@ -326,8 +388,16 @@ static void ep_handle_ctrlrequest(struct usb_ctrlrequest *req) 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, sizeof(dfu_status), NULL); + break; + case USB_CDC_REQ_SET_LINE_CODING: /* 0x20 */ - /* read 7 bytes */ + /* TODO: read 7 bytes to dummy buffer */ break; case USB_CDC_REQ_SET_CONTROL_LINE_STATE: /* 0x22 */ @@ -353,6 +423,10 @@ static void udp_handle_ep(uint32_t ep) if (!(*csr & AT91C_UDP_EPEDS)) return; + /* clear STALLSENT interrupt */ + if (*csr & AT91C_UDP_STALLSENT) + csr_clear_flags(*csr, (AT91C_UDP_STALLSENT | AT91C_UDP_FORCESTALL)); + /* ctrl request packet? */ if (*csr & AT91C_UDP_RXSETUP) { struct usb_ctrlrequest req; @@ -360,6 +434,10 @@ static void udp_handle_ep(uint32_t ep) for (p = (uint8_t *)&req; p < (uint8_t *)(&req +1); p++) *p = AT91C_UDP_FDR[ep]; + /* ack bank0 *now */ + if (*csr & AT91C_UDP_RX_DATA_BK0) + csr_clear_flags(*csr, AT91C_UDP_RX_DATA_BK0); + /* set data phase transfer direction */ if (req.bRequestType & USB_DIR_IN) *csr |= AT91C_UDP_DIR; @@ -370,6 +448,8 @@ static void udp_handle_ep(uint32_t ep) ep_handle_ctrlrequest(&req); } + void (* transfer_cb)(void) = NULL; + /* transmit complete? */ if (*csr & AT91C_UDP_TXCOMP) { struct ep_ctx *ctx = &ep_ctx[ep]; @@ -395,9 +475,7 @@ static void udp_handle_ep(uint32_t ep) /* transfer complete, execute callback */ } else { ctx->flags &= ~CTX_IN; - - if (transfer->complete_cb) - transfer->complete_cb(); + transfer_cb = transfer->complete_cb; } } @@ -405,14 +483,9 @@ static void udp_handle_ep(uint32_t ep) *csr &= ~(AT91C_UDP_TXCOMP); } - /* clear STALLSENT interrupt */ - if (*csr & AT91C_UDP_STALLSENT) - csr_clear_flags(*csr, (AT91C_UDP_STALLSENT | AT91C_UDP_FORCESTALL)); - /* data ready to read? */ if (*csr & (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1)) { struct ep_ctx *ctx = &ep_ctx[ep]; - uint16_t len = (*csr & AT91C_UDP_RXBYTECNT) >> 16; // TODO: only ep0 status OUT? @@ -459,6 +532,9 @@ static void udp_handle_ep(uint32_t ep) ctx->flags = (ctx->flags & ~CTX_RXBANK1) | CTX_RXBANK0; } } + + if (transfer_cb) + transfer_cb(); } static void udp_isr(void) @@ -509,8 +585,6 @@ void at91_udp_init(void) pio->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;