Root/atusb/fw/usb/dfu.c

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

Archive Download this file



interactive