IEEE 802.15.4 subsystem
Sign in or create your account | Project List | Help
IEEE 802.15.4 subsystem Git Source Tree
Root/
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 | |
62 | const 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 | |
84 | const 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 | |
107 | static uint16_t next_block = 0; |
108 | static bool did_download; |
109 | |
110 | |
111 | static uint8_t buf[EP0_SIZE]; |
112 | |
113 | |
114 | static void block_write(void *user) |
115 | { |
116 | uint16_t *size = user; |
117 | |
118 | dfu_flash_ops->write(buf, *size); |
119 | } |
120 | |
121 | |
122 | static 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 | |
142 | static 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 | |
161 | static 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 | |
243 | static 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 | |
255 | void dfu_init(void) |
256 | { |
257 | user_setup = my_setup; |
258 | user_get_descriptor = dfu_my_descr; |
259 | user_reset = my_reset; |
260 | } |
261 |