import initial dfu implementation (incomplete)

This commit is contained in:
Harald Welte 2012-01-01 23:42:02 +01:00
parent b2f95a3c7c
commit c7c3a3526b
4 changed files with 543 additions and 0 deletions

81
usb/common/dfu/usb_dfu.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef _USB_DFU_H
#define _USB_DFU_H
/* USB Device Firmware Update Implementation for OpenPCD
* (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
*
* Protocol definitions for USB DFU
*
* This ought to be compliant to the USB DFU Spec 1.0 as available from
* http://www.usb.org/developers/devclass_docs/usbdfu10.pdf
*
*/
#include <stdint.h>
#include <usb/common/core/USBGenericRequest.h>
#define USB_DT_DFU 0x21
struct usb_dfu_func_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bmAttributes;
#define USB_DFU_CAN_DOWNLOAD (1 << 0)
#define USB_DFU_CAN_UPLOAD (1 << 1)
#define USB_DFU_MANIFEST_TOL (1 << 2)
#define USB_DFU_WILL_DETACH (1 << 3)
uint16_t wDetachTimeOut;
uint16_t wTransferSize;
uint16_t bcdDFUVersion;
} __attribute__ ((packed));
#define USB_DT_DFU_SIZE 9
/* DFU class-specific requests (Section 3, DFU Rev 1.1) */
#define USB_REQ_DFU_DETACH 0x00
#define USB_REQ_DFU_DNLOAD 0x01
#define USB_REQ_DFU_UPLOAD 0x02
#define USB_REQ_DFU_GETSTATUS 0x03
#define USB_REQ_DFU_CLRSTATUS 0x04
#define USB_REQ_DFU_GETSTATE 0x05
#define USB_REQ_DFU_ABORT 0x06
struct dfu_status {
uint8_t bStatus;
uint8_t bwPollTimeout[3];
uint8_t bState;
uint8_t iString;
} __attribute__((packed));
#define DFU_STATUS_OK 0x00
#define DFU_STATUS_errTARGET 0x01
#define DFU_STATUS_errFILE 0x02
#define DFU_STATUS_errWRITE 0x03
#define DFU_STATUS_errERASE 0x04
#define DFU_STATUS_errCHECK_ERASED 0x05
#define DFU_STATUS_errPROG 0x06
#define DFU_STATUS_errVERIFY 0x07
#define DFU_STATUS_errADDRESS 0x08
#define DFU_STATUS_errNOTDONE 0x09
#define DFU_STATUS_errFIRMWARE 0x0a
#define DFU_STATUS_errVENDOR 0x0b
#define DFU_STATUS_errUSBR 0x0c
#define DFU_STATUS_errPOR 0x0d
#define DFU_STATUS_errUNKNOWN 0x0e
#define DFU_STATUS_errSTALLEDPKT 0x0f
enum dfu_state {
DFU_STATE_appIDLE = 0,
DFU_STATE_appDETACH = 1,
DFU_STATE_dfuIDLE = 2,
DFU_STATE_dfuDNLOAD_SYNC = 3,
DFU_STATE_dfuDNBUSY = 4,
DFU_STATE_dfuDNLOAD_IDLE = 5,
DFU_STATE_dfuMANIFEST_SYNC = 6,
DFU_STATE_dfuMANIFEST = 7,
DFU_STATE_dfuMANIFEST_WAIT_RST = 8,
DFU_STATE_dfuUPLOAD_IDLE = 9,
DFU_STATE_dfuERROR = 10,
};
#endif /* _USB_DFU_H */

50
usb/device/dfu/dfu.c Normal file
View File

@ -0,0 +1,50 @@
#include <usb/common/core/USBInterfaceDescriptor.h>
#include <usb/common/core/USBGenericDescriptor.h>
#include <usb/device/dfu/dfu.h>
/* String 1 "SimTrace DFU Interface - Application Partition" */
const struct USBStringDescriptor USBDFU_string1 = {
.hdr = {
.bLength = sizeof(USBGenericDescriptor) + 46 * sizeof(unsigned short),
.bDescriptorType = USBGenericDescriptor_STRING,
},
.wData = { 0x0053, 0x0069, 0x006d, 0x0054, 0x0072, 0x0061,
0x0063, 0x0065, 0x0020, 0x0044, 0x0046, 0x0055,
0x0020, 0x0049, 0x006e, 0x0074, 0x0065, 0x0072,
0x0066, 0x0061, 0x0063, 0x0065, 0x0020, 0x002d,
0x0020, 0x0041, 0x0070, 0x0070, 0x006c, 0x0069,
0x0063, 0x0061, 0x0074, 0x0069, 0x006f, 0x006e,
0x0020, 0x0050, 0x0061, 0x0072, 0x0074, 0x0069,
0x0074, 0x0069, 0x006f, 0x006e, },
};
/* String 2 "SimTrace DFU Interface - Bootloader Partition" */
const struct USBStringDescriptor USBDFU_string2 = {
.hdr = {
.bLength = sizeof(USBGenericDescriptor) + 45 * sizeof(unsigned short),
.bDescriptorType = USBGenericDescriptor_STRING,
},
.wData = { 0x0053, 0x0069, 0x006d, 0x0054, 0x0072, 0x0061,
0x0063, 0x0065, 0x0020, 0x0044, 0x0046, 0x0055,
0x0020, 0x0049, 0x006e, 0x0074, 0x0065, 0x0072,
0x0066, 0x0061, 0x0063, 0x0065, 0x0020, 0x002d,
0x0020, 0x0042, 0x006f, 0x006f, 0x0074, 0x006c,
0x006f, 0x0061, 0x0064, 0x0065, 0x0072, 0x0020,
0x0050, 0x0061, 0x0072, 0x0074, 0x0069, 0x0074,
0x0069, 0x006f, 0x006e, },
};
/* String 3 "SimTrace DFU Interface - RAM" */
const struct USBStringDescriptor USBDFU_string3 = {
.hdr = {
.bLength = sizeof(USBGenericDescriptor) + 28 * sizeof(unsigned short),
.bDescriptorType = USBGenericDescriptor_STRING,
},
.wData = { 0x0053, 0x0069, 0x006d, 0x0054, 0x0072, 0x0061,
0x0063, 0x0065, 0x0020, 0x0044, 0x0046, 0x0055,
0x0020, 0x0049, 0x006e, 0x0074, 0x0065, 0x0072,
0x0066, 0x0061, 0x0063, 0x0065, 0x0020, 0x002d,
0x0020, 0x0052, 0x0041, 0x004d, },
};

110
usb/device/dfu/dfu.h Normal file
View File

@ -0,0 +1,110 @@
#ifndef _USB_DFU_DESC_H
#define _USB_DFU_DESC_H
#include <stdint.h>
#include <board.h>
#include <usb/common/core/USBGenericDescriptor.h>
#include <usb/common/core/USBGenericDescriptor.h>
#include <usb/device/core/USBDDriver.h>
#if 0
/* This is valid for CCID */
#define CONFIG_DFU_NUM_APP_IF 1
#define CONFIG_DFU_NUM_APP_STR 4
#else
/* This is valid for CDC-Serial */
#define CONFIG_DFU_NUM_APP_IF 2
#define CONFIG_DFU_NUM_APP_STR 2
#endif
struct USBStringDescriptor {
USBGenericDescriptor hdr;
unsigned short wData[];
} __attribute__((packed));
#ifdef BOARD_USB_DFU
#define DFU_NUM_IF 3
#define DFU_IF_DESCRIPTORS_STRUCT \
USBInterfaceDescriptor dfu_interface[DFU_NUM_IF];
#define DFU_IF_DESCRIPTORS { \
{ \
.bLength = sizeof(USBInterfaceDescriptor), \
.bDescriptorType = USBGenericDescriptor_INTERFACE, \
.bInterfaceNumber = CONFIG_DFU_NUM_APP_IF, \
.bAlternateSetting = 0, \
.bNumEndpoints = 0, \
.bInterfaceClass = 0xFE, \
.bInterfaceSubClass = 0x01, \
.bInterfaceProtocol = 0x01, \
.iInterface = CONFIG_DFU_NUM_APP_STR, \
}, \
{ \
.bLength = sizeof(USBInterfaceDescriptor), \
.bDescriptorType = USBGenericDescriptor_INTERFACE, \
.bInterfaceNumber = CONFIG_DFU_NUM_APP_IF+1, \
.bAlternateSetting = 0, \
.bNumEndpoints = 0, \
.bInterfaceClass = 0xFE, \
.bInterfaceSubClass = 0x01, \
.bInterfaceProtocol = 0x01, \
.iInterface = CONFIG_DFU_NUM_APP_STR+1, \
}, \
{ \
.bLength = sizeof(USBInterfaceDescriptor), \
.bDescriptorType = USBGenericDescriptor_INTERFACE, \
.bInterfaceNumber = CONFIG_DFU_NUM_APP_IF+2, \
.bAlternateSetting = 0, \
.bNumEndpoints = 0, \
.bInterfaceClass = 0xFE, \
.bInterfaceSubClass = 0x01, \
.bInterfaceProtocol = 0x01, \
.iInterface = CONFIG_DFU_NUM_APP_STR+2, \
}, \
}
extern const struct USBStringDescriptor USBDFU_string1;
extern const struct USBStringDescriptor USBDFU_string2;
extern const struct USBStringDescriptor USBDFU_string3;
#define DFU_NUM_STRINGS 3
#define DFU_STRING_DESCRIPTORS \
(const unsigned char *) &USBDFU_string1, \
(const unsigned char *) &USBDFU_string2, \
(const unsigned char *) &USBDFU_string3,
#else /* BOARD_USB_DFU */
/* no DFU bootloader is being used */
#define DFU_NUM_IF 0
#define DFU_IF_DESCRIPTORS_STRUCT
#define DFU_IF_DESCRIPTORS
#define DFU_NUM_STRINGS 0
#define DFU_STRING_DESCRIPTORS
#endif /* BOARD_USB_DFU */
/* The API between the core DFU handler and the board/soc specific code */
struct dfu {
uint8_t status;
uint32_t state;
int past_manifest;
};
extern struct dfu dfu;
/* call-backs by the board/SOC */
extern int USBDFU_handle_dnload(uint16_t val, uint16_t len, int first);
static int USBDFU_handle_upload(uint16_t val, uint16_t len, int first);
void USBDFU_Runtime_RequestHandler(USBDDriver *pDriver, const USBGenericRequest *request);
void USBDFU_DFU_RequestHandler(USBDDriver *pDriver, const USBGenericRequest *request);
void USBDFU_Initialize(const USBDDriverDescriptors *pDescriptors, unsigned char *pInterfaces);
#endif

302
usb/device/dfu/dfu_driver.c Normal file
View File

@ -0,0 +1,302 @@
/* USB Device Firmware Update Implementation for OpenPCD
* (C) 2006-2011 by Harald Welte <hwelte@hmw-consulting.de>
*
* This ought to be compliant to the USB DFU Spec 1.0 as available from
* http://www.usb.org/developers/devclass_docs/usbdfu10.pdf
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 <unistd.h>
#include <usb/common/core/USBInterfaceDescriptor.h>
#include <usb/common/core/USBGenericDescriptor.h>
#include <usb/common/dfu/usb_dfu.h>
#include <usb/device/dfu/dfu.h>
/* FIXME */
#define __dfudata
#define __dfufunc
/// Standard device driver instance.
static USBDDriver usbdDriver;
#define RET_NOTHING 0
#define RET_ZLP 1
#define RET_STALL 2
__dfudata struct dfu dfu = {
.state = DFU_STATE_appIDLE,
.past_manifest = 0,
};
static __dfufunc void handle_getstatus(void)
{
struct dfu_status dstat;
dfu_drv_updstatus();
/* send status response */
dstat.bStatus = dfu.status;
dstat.bState = dfu.state;
dstat.iString = 0;
/* FIXME: set dstat.bwPollTimeout */
USBD_Write(0, (char *)&dstat, sizeof(dstat), NULL, 0);
}
static void __dfufunc handle_getstate(void)
{
uint8_t u8 = dfu.state;
USBD_Write(0, (char *)&u8, sizeof(u8), NULL, 0);
}
/* this function gets daisy-chained into processing EP0 requests */
void USBDFU_DFU_RequestHandler(USBDDriver *pDriver, const USBGenericRequest *request)
{
uint8_t req = USBGenericRequest_GetRequest(request);
uint16_t len = USBGenericRequest_GetLength(request);
uint16_t val = USBGenericRequest_GetValue(request);
int rc, ret;
/* only process actual DFU specific messages */
if (USBGenericRequest_GetType(request) != USBGenericRequest_CLASS ||
USBGenericRequest_GetRecipient(request) != USBGenericRequest_INTERFACE) {
USBDDriver_RequestHandler(pDriver, request);
}
switch (dfu.state) {
case DFU_STATE_appIDLE:
switch (req) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus();
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate();
break;
case USB_REQ_DFU_DETACH:
dfu.state = DFU_STATE_appDETACH;
ret = RET_ZLP;
goto out;
break;
default:
ret = RET_STALL;
}
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;
ret = RET_STALL;
goto out;
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;
ret = RET_STALL;
goto out;
}
dfu.state = DFU_STATE_dfuDNLOAD_SYNC;
ret = USBDFU_handle_dnload(val, len, 1);
break;
case USB_REQ_DFU_UPLOAD:
dfu.state = DFU_STATE_dfuUPLOAD_IDLE;
USBDFU_handle_upload(val, len, 1);
break;
case USB_REQ_DFU_ABORT:
/* no zlp? */
ret = RET_ZLP;
break;
case USB_REQ_DFU_GETSTATUS:
handle_getstatus();
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate();
break;
default:
dfu.state = DFU_STATE_dfuERROR;
ret = RET_STALL;
goto out;
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;
ret = RET_STALL;
goto out;
}
break;
case DFU_STATE_dfuDNBUSY:
switch (req) {
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;
goto out;
}
break;
case DFU_STATE_dfuDNLOAD_IDLE:
switch (req) {
case USB_REQ_DFU_DNLOAD:
dfu.state = DFU_STATE_dfuDNLOAD_SYNC;
ret = USBDFU_handle_dnload(val, len, 0);
break;
case USB_REQ_DFU_ABORT:
dfu.state = DFU_STATE_dfuIDLE;
ret = RET_ZLP;
break;
case USB_REQ_DFU_GETSTATUS:
handle_getstatus();
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate();
break;
default:
dfu.state = DFU_STATE_dfuERROR;
ret = RET_STALL;
break;
}
break;
case DFU_STATE_dfuMANIFEST_SYNC:
switch (req) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus();
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate();
break;
default:
dfu.state = DFU_STATE_dfuERROR;
ret = RET_STALL;
break;
}
break;
case DFU_STATE_dfuMANIFEST:
switch (req) {
case USB_REQ_DFU_GETSTATUS:
/* we don't want to change to WAIT_RST, as it
* would mean that we can not support another
* DFU transaction before doing the actual
* reset. Instead, we switch to idle and note
* that we've already been through MANIFST in
* the global variable 'past_manifest'.
*/
//dfu.state = DFU_STATE_dfuMANIFEST_WAIT_RST;
dfu.state = DFU_STATE_dfuIDLE;
dfu.past_manifest = 1;
handle_getstatus();
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate();
break;
default:
dfu.state = DFU_STATE_dfuERROR;
ret = RET_STALL;
break;
}
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 = USBDFU_handle_upload(val, len, 0);
if (rc >= 0 && rc < len)
dfu.state = DFU_STATE_dfuIDLE;
break;
case USB_REQ_DFU_ABORT:
dfu.state = DFU_STATE_dfuIDLE;
/* no zlp? */
ret = RET_ZLP;
break;
case USB_REQ_DFU_GETSTATUS:
handle_getstatus();
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate();
break;
default:
dfu.state = DFU_STATE_dfuERROR;
ret = RET_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;
dfu.status = DFU_STATUS_OK;
/* no zlp? */
ret = RET_ZLP;
break;
default:
dfu.state = DFU_STATE_dfuERROR;
ret = RET_STALL;
break;
}
break;
}
out:
switch (ret) {
case RET_NOTHING:
break;
case RET_ZLP:
USBD_Write(0, 0, 0, 0, 0);
break;
case RET_STALL:
USBD_Stall(0);
break;
}
}
void USBDFU_Initialize(const USBDDriverDescriptors *pDescriptors, unsigned char *pInterfaces)
{
USBDDriver_Initialize(&usbdDriver, pDescriptors, pInterfaces);
}