Date:2011-03-08 23:05:26 (13 years 20 days ago)
Author:Werner Almesberger
Commit:fb2204ad8435e09d8a9459862808fd4b579c155f
Message:usb/: added DFU implementation from f386base/fw/boot/ (unported)

Files: atusb/fw/usb/dfu.c (1 diff)
atusb/fw/usb/dfu.h (1 diff)

Change Details

atusb/fw/usb/dfu.c
1/*
2 * boot/dfu.c - DFU protocol engine
3 *
4 * Written 2008-2010 by Werner Almesberger
5 * Copyright 2008-2010 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13/*
14 * http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
15 */
16
17/*
18 * A few, erm, shortcuts:
19 *
20 * - we don't bother with the app* states since DFU is all this firmware does
21 * - after DFU_DNLOAD, we just block until things are written, so we never
22 * enter dfuDNLOAD_SYNC or dfuDNBUSY
23 * - no dfuMANIFEST_SYNC, dfuMANIFEST, or dfuMANIFEST_WAIT_RESET
24 * - to keep our buffers small, we only accept EP0-sized blocks
25 */
26
27
28#include <stdint.h>
29
30#include "regs.h"
31#include "uart.h"
32#include "usb.h"
33#include "dfu.h"
34
35#include "config.h"
36
37
38#ifndef NULL
39#define NULL 0
40#endif
41
42
43#define PAYLOAD_END (PAYLOAD_START+PAYLOAD_SIZE)
44
45
46const uint8_t device_descriptor[] = {
47    18, /* bLength */
48    USB_DT_DEVICE, /* bDescriptorType */
49    LE(0x100), /* bcdUSB */
50    USB_CLASS_PER_INTERFACE,/* bDeviceClass */
51    0x00, /* bDeviceSubClass (per interface) */
52    0x00, /* bDeviceProtocol (per interface) */
53    EP0_SIZE, /* bMaxPacketSize */
54    LE(USB_VENDOR), /* idVendor */
55    LE(USB_PRODUCT), /* idProduct */
56    LE(0x0001), /* bcdDevice */
57    0, /* iManufacturer */
58    0, /* iProduct */
59    0, /* iSerialNumber */
60    1 /* bNumConfigurations */
61};
62
63
64const uint8_t config_descriptor[] = {
65    9, /* bLength */
66    USB_DT_CONFIG, /* bDescriptorType */
67    LE(9+9), /* wTotalLength */
68    1, /* bNumInterfaces */
69    1, /* bConfigurationValue (> 0 !) */
70    0, /* iConfiguration */
71// USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED,
72    USB_ATTR_BUS_POWERED, /* bmAttributes */
73    15, /* bMaxPower */
74
75    /* Interface #0 */
76
77    9, /* bLength */
78    USB_DT_INTERFACE, /* bDescriptorType */
79    0, /* bInterfaceNumber */
80    0, /* bAlternateSetting */
81    0, /* bNumEndpoints */
82    0xfe, /* bInterfaceClass (application specific) */
83    0x01, /* bInterfaceSubClass (device fw upgrade) */
84    0x02, /* bInterfaceProtocol (DFU mode protocol) */
85    0, /* iInterface */
86};
87
88
89static const uint8_t functional_descriptor[] = {
90    9, /* bLength */
91    DFU_DT_FUNCTIONAL, /* bDescriptorType */
92    0xf, /* bmAttributes (claim omnipotence :-) */
93    LE(0xffff), /* wDetachTimeOut (we're very patient) */
94    LE(EP0_SIZE), /* wTransferSize */
95    LE(0x101), /* bcdDFUVersion */
96};
97
98
99struct dfu dfu = {
100    OK,
101    LE(1000), 0,
102    dfuIDLE,
103    0,
104};
105
106
107static uint16_t next_block = 0;
108static uint16_t payload;
109static __bit did_download;
110
111
112static __xdata uint8_t buf[EP0_SIZE];
113
114
115static void flash_erase_page(uint16_t addr)
116{
117    FLKEY = 0xa5;
118    FLKEY = 0xf1;
119    PSCTL |= PSEE;
120    PSCTL |= PSWE;
121    *(__xdata uint8_t *) addr = 0;
122    PSCTL &= ~PSWE;
123    PSCTL &= ~PSEE;
124}
125
126
127static void flash_write_byte(uint16_t addr, uint8_t value)
128{
129    FLKEY = 0xa5;
130    FLKEY = 0xf1;
131    PSCTL |= PSWE;
132    PSCTL &= ~PSEE;
133    *(__xdata uint8_t *) addr = value;
134    PSCTL &= ~PSWE;
135}
136
137
138static void block_write(void *user)
139{
140    uint16_t *size = user;
141    uint8_t *p;
142
143    for (p = buf; p != buf+*size; p++) {
144        if (!(payload & 511))
145            flash_erase_page(payload);
146        flash_write_byte(payload, *p);
147        payload++;
148    }
149}
150
151
152static __bit block_receive(uint16_t length)
153{
154    static uint16_t size;
155
156    if (payload < PAYLOAD_START || payload+length > PAYLOAD_END) {
157        dfu.state = dfuERROR;
158        dfu.status = errADDRESS;
159        return 0;
160    }
161    if (length > EP0_SIZE) {
162        dfu.state = dfuERROR;
163        dfu.status = errUNKNOWN;
164        return 0;
165    }
166    size = length;
167    usb_recv(&ep0, buf, size, block_write, &size);
168    return 1;
169}
170
171
172static __bit block_transmit(uint16_t length)
173{
174    uint16_t left;
175
176    if (payload < PAYLOAD_START || payload > PAYLOAD_END) {
177        dfu.state = dfuERROR;
178        dfu.status = errADDRESS;
179        return 1;
180    }
181    if (length > EP0_SIZE) {
182        dfu.state = dfuERROR;
183        dfu.status = errUNKNOWN;
184        return 1;
185    }
186    left = PAYLOAD_END-payload;
187    if (left < length) {
188        length = left;
189        dfu.state = dfuIDLE;
190    }
191    usb_send(&ep0, (__code uint8_t *) payload, length, NULL, NULL);
192    payload += length;
193    return 1;
194}
195
196
197static __bit my_setup(struct setup_request *setup) __reentrant
198{
199    __bit ok;
200
201    switch (setup->bmRequestType | setup->bRequest << 8) {
202    case DFU_TO_DEV(DFU_DETACH):
203        debug("DFU_DETACH\n");
204        /*
205         * The DFU spec says thay this is sent in protocol 1 only.
206         * However, dfu-util also sends it to get out of DFU mode,
207         * so we just don't make a fuss and ignore it.
208         */
209        return 1;
210    case DFU_TO_DEV(DFU_DNLOAD):
211        debug("DFU_DNLOAD\n");
212        if (dfu.state == dfuIDLE) {
213            next_block = setup->wValue;
214            payload = PAYLOAD_START;
215        }
216        else if (dfu.state != dfuDNLOAD_IDLE) {
217            error("bad state\n");
218            return 0;
219        }
220        if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
221            debug("retransmisson\n");
222            return 1;
223        }
224        if (setup->wValue != next_block) {
225            debug("bad block (%d vs. %d)\n",
226                setup->wValue, next_block);
227            dfu.state = dfuERROR;
228            dfu.status = errUNKNOWN;
229            return 1;
230        }
231        if (!setup->wLength) {
232            debug("DONE\n");
233            dfu.state = dfuIDLE;
234            did_download = 1;
235            return 1;
236        }
237        ok = block_receive(setup->wLength);
238        next_block++;
239        dfu.state = dfuDNLOAD_IDLE;
240        return ok;
241    case DFU_FROM_DEV(DFU_UPLOAD):
242        debug("DFU_UPLOAD\n");
243        if (dfu.state == dfuIDLE) {
244            next_block = setup->wValue;
245            payload = PAYLOAD_START;
246        }
247        else if (dfu.state != dfuUPLOAD_IDLE)
248            return 0;
249        if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
250            debug("retransmisson\n");
251            /* @@@ try harder */
252            dfu.state = dfuERROR;
253            dfu.status = errUNKNOWN;
254            return 1;
255        }
256        if (setup->wValue != next_block) {
257            debug("bad block (%d vs. %d)\n",
258                setup->wValue, next_block);
259            dfu.state = dfuERROR;
260            dfu.status = errUNKNOWN;
261            return 1;
262        }
263        ok = block_transmit(setup->wLength);
264        next_block++;
265        dfu.state = dfuUPLOAD_IDLE;
266        return ok;
267    case DFU_FROM_DEV(DFU_GETSTATUS):
268        debug("DFU_GETSTATUS\n");
269        usb_send(&ep0, (uint8_t *) &dfu, sizeof(dfu), NULL, NULL);
270        return 1;
271    case DFU_TO_DEV(DFU_CLRSTATUS):
272        debug("DFU_CLRSTATUS\n");
273        dfu.state = dfuIDLE;
274        dfu.status = OK;
275        return 1;
276    case DFU_FROM_DEV(DFU_GETSTATE):
277        debug("DFU_GETSTATE\n");
278        usb_send(&ep0, &dfu.state, 1, NULL, NULL);
279        return 1;
280    case DFU_TO_DEV(DFU_ABORT):
281        debug("DFU_ABORT\n");
282        dfu.state = dfuIDLE;
283        dfu.status = OK;
284        return 1;
285    default:
286#ifdef CONFIG_PRINTK
287        printk("DFU rt %x, rq%x ?\n",
288            setup->bmRequestType, setup->bRequest);
289#else
290        /*
291         * @@@ SDCC 2.7.0 ends up OR'in setup->bmRequestType with
292         * setup->bRequest unshifted if we don't use at least one of
293         * them here.
294         */
295        {
296            static volatile uint8_t foo;
297            foo = setup->bRequest;
298        }
299#endif
300        return 0;
301    }
302}
303
304
305static __bit my_descr(uint8_t type, uint8_t index, const uint8_t **reply,
306    uint8_t *size) __reentrant
307{
308    index; /* suppress warning */
309    if (type != DFU_DT_FUNCTIONAL)
310        return 0;
311    *reply = functional_descriptor;
312    *size = sizeof(functional_descriptor);
313    return 1;
314}
315
316
317static void my_reset(void) __reentrant
318{
319    /* @@@ not nice -- think about where this should go */
320    extern void run_payload(void);
321
322    if (did_download)
323        run_payload();
324}
325
326
327void dfu_init(void)
328{
329    user_setup = my_setup;
330    user_get_descriptor = my_descr;
331    user_reset = my_reset;
332}
atusb/fw/usb/dfu.h
1/*
2 * boot/dfu.h - DFU protocol constants and data structures
3 *
4 * Written 2008 by Werner Almesberger
5 * Copyright 2008 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#ifndef DFU_H
15#define DFU_H
16
17#include <stdint.h>
18
19
20enum dfu_request {
21    DFU_DETACH,
22    DFU_DNLOAD,
23    DFU_UPLOAD,
24    DFU_GETSTATUS,
25    DFU_CLRSTATUS,
26    DFU_GETSTATE,
27    DFU_ABORT,
28};
29
30
31enum dfu_status {
32    OK,
33    errTARGET,
34    errFILE,
35    errWRITE,
36    errERASE,
37    errCHECK_ERASED,
38    errPROG,
39    errVERIFY,
40    errADDRESS,
41    errNOTDONE,
42    errFIRMWARE,
43    errVENDOR,
44    errUSBR,
45    errPOR,
46    errUNKNOWN,
47    errSTALLEDPKT,
48};
49
50
51enum dfu_state {
52    appIDLE,
53    appDETACH,
54    dfuIDLE,
55    dfuDNLOAD_SYNC,
56    dfuDNBUSY,
57    dfuDNLOAD_IDLE,
58    dfuMANIFEST_SYNC,
59    dfuMANIFEST,
60    dfuMANIFEST_WAIT_RESET,
61    dfuUPLOAD_IDLE,
62    dfuERROR
63};
64
65
66#define DFU_DT_FUNCTIONAL 0x21 /* DFU FUNCTIONAL descriptor type */
67
68
69#define DFU_TO_DEV(req) (0x21 | (req) << 8)
70#define DFU_FROM_DEV(req) (0xa1 | (req) << 8)
71
72
73struct dfu {
74    uint8_t status; /* bStatus */
75    uint8_t toL, toM, toH; /* bwPollTimeout */
76    uint8_t state; /* bState */
77    uint8_t iString;
78};
79
80
81extern struct dfu dfu;
82
83
84void dfu_init(void);
85
86#endif /* !DFU_H */

Archive Download the corresponding diff file



interactive