summaryrefslogtreecommitdiff
path: root/usb
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2012-01-01 23:42:02 +0100
committerHarald Welte <laforge@gnumonks.org>2012-01-01 23:42:02 +0100
commitc7c3a3526b723b7793f0d4dcbf49f69ea73f3794 (patch)
tree48d22affb574904fcbb1c6bef5ad3eeaa0efb828 /usb
parentb2f95a3c7c8661818a606667f1d44b7086ce26b1 (diff)
import initial dfu implementation (incomplete)
Diffstat (limited to 'usb')
-rw-r--r--usb/common/dfu/usb_dfu.h81
-rw-r--r--usb/device/dfu/dfu.c50
-rw-r--r--usb/device/dfu/dfu.h110
-rw-r--r--usb/device/dfu/dfu_driver.c302
4 files changed, 543 insertions, 0 deletions
diff --git a/usb/common/dfu/usb_dfu.h b/usb/common/dfu/usb_dfu.h
new file mode 100644
index 0000000..d423840
--- /dev/null
+++ b/usb/common/dfu/usb_dfu.h
@@ -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 */
diff --git a/usb/device/dfu/dfu.c b/usb/device/dfu/dfu.c
new file mode 100644
index 0000000..89b5f3a
--- /dev/null
+++ b/usb/device/dfu/dfu.c
@@ -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, },
+};
diff --git a/usb/device/dfu/dfu.h b/usb/device/dfu/dfu.h
new file mode 100644
index 0000000..00a0622
--- /dev/null
+++ b/usb/device/dfu/dfu.h
@@ -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
diff --git a/usb/device/dfu/dfu_driver.c b/usb/device/dfu/dfu_driver.c
new file mode 100644
index 0000000..df6fb4f
--- /dev/null
+++ b/usb/device/dfu/dfu_driver.c
@@ -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);
+}
personal git repositories of Harald Welte. Your mileage may vary