Root/atusb/fw/usb/dfu.c

Source at commit d233c04c86c2fde39b5e069bb6750caef3393f32 created 8 years 7 months ago.
By Werner Almesberger, atusb/fw: implemented USB bus reset (to host) and polling of reset from host
1/*
2 * boot/dfu.c - DFU protocol engine
3 *
4 * Written 2008-2011 by Werner Almesberger
5 * Copyright 2008-2011 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 "usb.h"
31#include "dfu.h"
32
33#include "../board.h"
34
35
36#ifndef NULL
37#define NULL 0
38#endif
39
40#define debug(...)
41#define error(...)
42
43
44const uint8_t device_descriptor[] = {
45    18, /* bLength */
46    USB_DT_DEVICE, /* bDescriptorType */
47    LE(0x100), /* bcdUSB */
48    USB_CLASS_PER_INTERFACE,/* bDeviceClass */
49    0x00, /* bDeviceSubClass (per interface) */
50    0x00, /* bDeviceProtocol (per interface) */
51    EP0_SIZE, /* bMaxPacketSize */
52    LE(DFU_USB_VENDOR), /* idVendor */
53    LE(DFU_USB_PRODUCT), /* idProduct */
54    LE(0x0001), /* bcdDevice */
55    0, /* iManufacturer */
56    0, /* iProduct */
57    0, /* iSerialNumber */
58    1 /* bNumConfigurations */
59};
60
61
62const uint8_t config_descriptor[] = {
63    9, /* bLength */
64    USB_DT_CONFIG, /* bDescriptorType */
65    LE(9+9), /* wTotalLength */
66    1, /* bNumInterfaces */
67    1, /* bConfigurationValue (> 0 !) */
68    0, /* iConfiguration */
69// USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED,
70    USB_ATTR_BUS_POWERED, /* bmAttributes */
71    15, /* bMaxPower */
72
73    /* Interface #0 */
74
75    9, /* bLength */
76    USB_DT_INTERFACE, /* bDescriptorType */
77    0, /* bInterfaceNumber */
78    0, /* bAlternateSetting */
79    0, /* bNumEndpoints */
80    0xfe, /* bInterfaceClass (application specific) */
81    0x01, /* bInterfaceSubClass (device fw upgrade) */
82    0x02, /* bInterfaceProtocol (DFU mode protocol) */
83    0, /* iInterface */
84};
85
86
87static const uint8_t functional_descriptor[] = {
88    9, /* bLength */
89    DFU_DT_FUNCTIONAL, /* bDescriptorType */
90    0xf, /* bmAttributes (claim omnipotence :-) */
91    LE(0xffff), /* wDetachTimeOut (we're very patient) */
92    LE(EP0_SIZE), /* wTransferSize */
93    LE(0x101), /* bcdDFUVersion */
94};
95
96
97struct dfu dfu = {
98    OK,
99    LE(1000), 0,
100    dfuIDLE,
101    0,
102};
103
104
105static uint16_t next_block = 0;
106static int did_download;
107
108
109static uint8_t buf[EP0_SIZE];
110
111
112static void block_write(void *user)
113{
114    uint16_t *size = user;
115
116    flash_write(buf, *size);
117}
118
119
120static int block_receive(uint16_t length)
121{
122    static uint16_t size;
123
124    if (!flash_can_write(length)) {
125        dfu.state = dfuERROR;
126        dfu.status = errADDRESS;
127        return 0;
128    }
129    if (length > EP0_SIZE) {
130        dfu.state = dfuERROR;
131        dfu.status = errUNKNOWN;
132        return 0;
133    }
134    size = length;
135    usb_recv(&eps[0], buf, size, block_write, &size);
136    return 1;
137}
138
139
140static int block_transmit(uint16_t length)
141{
142    uint16_t got;
143
144    if (length > EP0_SIZE) {
145        dfu.state = dfuERROR;
146        dfu.status = errUNKNOWN;
147        return 1;
148    }
149    got = flash_read(buf, length);
150    if (got < length) {
151        length = got;
152        dfu.state = dfuIDLE;
153    }
154    usb_send(&eps[0], buf, length, NULL, NULL);
155    return 1;
156}
157
158
159static int my_setup(const struct setup_request *setup)
160{
161    int ok;
162
163    switch (setup->bmRequestType | setup->bRequest << 8) {
164    case DFU_TO_DEV(DFU_DETACH):
165        debug("DFU_DETACH\n");
166        /*
167         * The DFU spec says thay this is sent in protocol 1 only.
168         * However, dfu-util also sends it to get out of DFU mode,
169         * so we just don't make a fuss and ignore it.
170         */
171        return 1;
172    case DFU_TO_DEV(DFU_DNLOAD):
173        debug("DFU_DNLOAD\n");
174        if (dfu.state == dfuIDLE) {
175            next_block = setup->wValue;
176            flash_start();
177        }
178        else if (dfu.state != dfuDNLOAD_IDLE) {
179            error("bad state\n");
180            return 0;
181        }
182        if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
183            debug("retransmisson\n");
184            return 1;
185        }
186        if (setup->wValue != next_block) {
187            debug("bad block (%d vs. %d)\n",
188                setup->wValue, next_block);
189            dfu.state = dfuERROR;
190            dfu.status = errUNKNOWN;
191            return 1;
192        }
193        if (!setup->wLength) {
194            debug("DONE\n");
195            flash_end_write();
196            dfu.state = dfuIDLE;
197            did_download = 1;
198            return 1;
199        }
200        ok = block_receive(setup->wLength);
201        next_block++;
202        dfu.state = dfuDNLOAD_IDLE;
203        return ok;
204    case DFU_FROM_DEV(DFU_UPLOAD):
205        debug("DFU_UPLOAD\n");
206        if (dfu.state == dfuIDLE) {
207            next_block = setup->wValue;
208            flash_start();
209        }
210        else if (dfu.state != dfuUPLOAD_IDLE)
211            return 0;
212        if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
213            debug("retransmisson\n");
214            /* @@@ try harder */
215            dfu.state = dfuERROR;
216            dfu.status = errUNKNOWN;
217            return 1;
218        }
219        if (setup->wValue != next_block) {
220            debug("bad block (%d vs. %d)\n",
221                setup->wValue, next_block);
222            dfu.state = dfuERROR;
223            dfu.status = errUNKNOWN;
224            return 1;
225        }
226        ok = block_transmit(setup->wLength);
227        next_block++;
228        dfu.state = dfuUPLOAD_IDLE;
229        return ok;
230    case DFU_FROM_DEV(DFU_GETSTATUS):
231        debug("DFU_GETSTATUS\n");
232        usb_send(&eps[0], (uint8_t *) &dfu, sizeof(dfu), NULL, NULL);
233        return 1;
234    case DFU_TO_DEV(DFU_CLRSTATUS):
235        debug("DFU_CLRSTATUS\n");
236        dfu.state = dfuIDLE;
237        dfu.status = OK;
238        return 1;
239    case DFU_FROM_DEV(DFU_GETSTATE):
240        debug("DFU_GETSTATE\n");
241        usb_send(&eps[0], &dfu.state, 1, NULL, NULL);
242        return 1;
243    case DFU_TO_DEV(DFU_ABORT):
244        debug("DFU_ABORT\n");
245        dfu.state = dfuIDLE;
246        dfu.status = OK;
247        return 1;
248    default:
249        error("DFU rt %x, rq%x ?\n",
250            setup->bmRequestType, setup->bRequest);
251        return 0;
252    }
253}
254
255
256static int my_descr(uint8_t type, uint8_t index, const uint8_t **reply,
257    uint8_t *size)
258{
259    if (type != DFU_DT_FUNCTIONAL)
260        return 0;
261    *reply = functional_descriptor;
262    *size = sizeof(functional_descriptor);
263    return 1;
264}
265
266
267static void my_reset(void)
268{
269#if 0
270    /* @@@ not nice -- think about where this should go */
271    extern void run_payload(void);
272
273    if (did_download)
274        run_payload();
275#endif
276}
277
278
279void dfu_init(void)
280{
281    user_setup = my_setup;
282    user_get_descriptor = my_descr;
283    user_reset = my_reset;
284}
285

Archive Download this file



interactive