Root/atusb/fw/usb/dfu.c

Source at commit 8f2f13c30a2b9b4305ba656e34c28ab35fb49212 created 5 years 8 months ago.
By Werner Almesberger, atusb/fw/: DFU: support multiple sets of flash operations; support multiple alt settings
1/*
2 * boot/dfu.c - DFU protocol engine
3 *
4 * Written 2008-2011, 2013-2014 by Werner Almesberger
5 * Copyright 2008-2011, 2013-2014 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 <stdbool.h>
29#include <stdint.h>
30
31#include "usb.h"
32#include "dfu.h"
33
34#include "board.h"
35
36
37#ifndef NULL
38#define NULL 0
39#endif
40
41#define debug(...)
42#define error(...)
43
44
45#ifndef DFU_ALT_SETTINGS
46#define DFU_ALT_SETTINGS 1
47#endif
48
49
50const uint8_t device_descriptor[] = {
51    18, /* bLength */
52    USB_DT_DEVICE, /* bDescriptorType */
53    LE(0x100), /* bcdUSB */
54    USB_CLASS_PER_INTERFACE,/* bDeviceClass */
55    0x00, /* bDeviceSubClass (per interface) */
56    0x00, /* bDeviceProtocol (per interface) */
57    EP0_SIZE, /* bMaxPacketSize */
58    LE(DFU_USB_VENDOR), /* idVendor */
59    LE(DFU_USB_PRODUCT), /* idProduct */
60    LE(0x0001), /* bcdDevice */
61    0, /* iManufacturer */
62    0, /* iProduct */
63#ifdef HAS_BOARD_SERNUM
64    1, /* iSerialNumber */
65#else
66    0, /* iSerialNumber */
67#endif
68    1 /* bNumConfigurations */
69};
70
71
72const uint8_t config_descriptor[] = {
73    9, /* bLength */
74    USB_DT_CONFIG, /* bDescriptorType */
75    LE(9+9*DFU_ALT_SETTINGS), /* wTotalLength */
76    1, /* bNumInterfaces */
77    1, /* bConfigurationValue (> 0 !) */
78    0, /* iConfiguration */
79// USB_ATTR_SELF_POWERED | USB_ATTR_BUS_POWERED,
80    USB_ATTR_BUS_POWERED, /* bmAttributes */
81    ((BOARD_MAX_mA)+1)/2, /* bMaxPower */
82
83    /* Interface #0 */
84
85    DFU_ITF_DESCR(0, 0, dfu_proto_dfu)
86#if DFU_ALT_SETTINGS >= 1
87    DFU_ITF_DESCR(0, 1, dfu_proto_dfu)
88#endif
89#if DFU_ALT_SETTINGS >= 2
90    DFU_ITF_DESCR(0, 2, dfu_proto_dfu)
91#endif
92};
93
94
95static uint16_t next_block = 0;
96static bool did_download;
97
98
99static uint8_t buf[EP0_SIZE];
100
101
102static void block_write(void *user)
103{
104    uint16_t *size = user;
105
106    dfu_flash_ops->write(buf, *size);
107}
108
109
110static bool block_receive(uint16_t length)
111{
112    static uint16_t size;
113
114    if (!dfu_flash_ops->can_write(length)) {
115        dfu.state = dfuERROR;
116        dfu.status = errADDRESS;
117        return 0;
118    }
119    if (length > EP0_SIZE) {
120        dfu.state = dfuERROR;
121        dfu.status = errUNKNOWN;
122        return 0;
123    }
124    size = length;
125    usb_recv(&eps[0], buf, size, block_write, &size);
126    return 1;
127}
128
129
130static bool block_transmit(uint16_t length)
131{
132    uint16_t got;
133
134    if (length > EP0_SIZE) {
135        dfu.state = dfuERROR;
136        dfu.status = errUNKNOWN;
137        return 1;
138    }
139    got = dfu_flash_ops->read(buf, length);
140    if (got < length) {
141        length = got;
142        dfu.state = dfuIDLE;
143    }
144    usb_send(&eps[0], buf, length, NULL, NULL);
145    return 1;
146}
147
148
149static bool my_setup(const struct setup_request *setup)
150{
151    bool ok;
152
153    switch (setup->bmRequestType | setup->bRequest << 8) {
154    case DFU_TO_DEV(DFU_DETACH):
155        debug("DFU_DETACH\n");
156        /*
157         * The DFU spec says thay this is sent in protocol 1 only.
158         * However, dfu-util also sends it to get out of DFU mode,
159         * so we just don't make a fuss and ignore it.
160         */
161        return 1;
162    case DFU_TO_DEV(DFU_DNLOAD):
163        debug("DFU_DNLOAD\n");
164        if (dfu.state == dfuIDLE) {
165            next_block = setup->wValue;
166            dfu_flash_ops->start();
167        }
168        else if (dfu.state != dfuDNLOAD_IDLE) {
169            error("bad state\n");
170            return 0;
171        }
172        if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
173            debug("retransmisson\n");
174            return 1;
175        }
176        if (setup->wValue != next_block) {
177            debug("bad block (%d vs. %d)\n",
178                setup->wValue, next_block);
179            dfu.state = dfuERROR;
180            dfu.status = errUNKNOWN;
181            return 1;
182        }
183        if (!setup->wLength) {
184            debug("DONE\n");
185            dfu_flash_ops->end_write();
186            dfu.state = dfuIDLE;
187            did_download = 1;
188            return 1;
189        }
190        ok = block_receive(setup->wLength);
191        next_block++;
192        dfu.state = dfuDNLOAD_IDLE;
193        return ok;
194    case DFU_FROM_DEV(DFU_UPLOAD):
195        debug("DFU_UPLOAD\n");
196        if (dfu.state == dfuIDLE) {
197            next_block = setup->wValue;
198            dfu_flash_ops->start();
199        }
200        else if (dfu.state != dfuUPLOAD_IDLE)
201            return 0;
202        if (dfu.state != dfuIDLE && setup->wValue == next_block-1) {
203            debug("retransmisson\n");
204            /* @@@ try harder */
205            dfu.state = dfuERROR;
206            dfu.status = errUNKNOWN;
207            return 1;
208        }
209        if (setup->wValue != next_block) {
210            debug("bad block (%d vs. %d)\n",
211                setup->wValue, next_block);
212            dfu.state = dfuERROR;
213            dfu.status = errUNKNOWN;
214            return 1;
215        }
216        ok = block_transmit(setup->wLength);
217        next_block++;
218        dfu.state = dfuUPLOAD_IDLE;
219        return ok;
220    case DFU_TO_DEV(DFU_ABORT):
221        debug("DFU_ABORT\n");
222        dfu.state = dfuIDLE;
223        dfu.status = OK;
224        return 1;
225    default:
226        return dfu_setup_common(setup);
227    }
228}
229
230
231static void my_reset(void)
232{
233#if 0
234    /* @@@ not nice -- think about where this should go */
235    extern void run_payload(void);
236
237    if (did_download)
238        run_payload();
239#endif
240}
241
242
243void dfu_init(void)
244{
245    user_setup = my_setup;
246    user_get_descriptor = dfu_my_descr;
247    user_reset = my_reset;
248}
249

Archive Download this file



interactive