working dfu framework

This commit is contained in:
Olaf Rempel 2008-03-01 22:17:33 +01:00
parent 588cfbc2c2
commit fded24a803
6 changed files with 505 additions and 219 deletions

View File

@ -1,6 +1,20 @@
#ifndef AT91_UDP_H_
#define AT91_UDP_H_
#include <stdint.h>
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_*/

4
main.c
View File

@ -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();

View File

@ -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;

View File

@ -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 <stdint.h>
#include <stdio.h>
#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);
}

View File

@ -18,14 +18,16 @@
***************************************************************************/
#include <stdio.h>
#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<<i))
udp_handle_ep(i);
}
if (isr & AT91C_UDP_EPINT0)
udp_handle_ep(0);
/* clear all unhandled interrupts */
*AT91C_UDP_ICR = isr & (AT91C_UDP_RXSUSP | AT91C_UDP_RXRSM |
@ -583,8 +603,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;
@ -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);

338
src/dfu.c Normal file
View File

@ -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 <stdio.h>
#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;
}
}