Root/atusb/fw/usb/dfu.c

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

Archive Download this file



interactive