1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "usbd_msg.h"
8
9 #include <zephyr/init.h>
10 #include <zephyr/usb/usbd.h>
11 #include <zephyr/usb/class/usbd_dfu.h>
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(usbd_dfu, CONFIG_USBD_DFU_LOG_LEVEL);
15
16 /*
17 * It is very unlikely that anyone would need more than one instance of the DFU
18 * class. Therefore, we make an exception here and do not support multiple
19 * instances, which allows us to have a much simpler implementation.
20 *
21 * This implementation provides two class instances, one with a single
22 * interface for the run-time mode, and the other with a number of user-defined
23 * interfaces for the DFU mode. The DFU mode instance can have up to 256
24 * (0...255) image (memory) segments, limited by the
25 * CONFIG_USBD_DFU_NUMOF_IMAGES and maximum value of bAlternateSetting.
26 *
27 * The implementation implicitly sets the bitWillDetach flag and expects the
28 * user to disable the device with run-time mode and enable a device with DFU
29 * mode.
30 */
31
32 #if defined(CONFIG_USBD_DFU_ENABLE_UPLOAD)
33 #define ATTR_CAN_UPLOAD USB_DFU_ATTR_CAN_UPLOAD
34 #else
35 #define ATTR_CAN_UPLOAD 0
36 #endif
37
38 #if defined(CONFIG_USBD_DFU_MANIFESTATION_TOLERANT)
39 #define ATTR_MANIFESTATION_TOLERANT USB_DFU_ATTR_MANIFESTATION_TOLERANT
40 #else
41 #define ATTR_MANIFESTATION_TOLERANT 0
42 #endif
43
44 /* DFU Functional Descriptor used for Run-Time und DFU mode */
45 static const struct usb_dfu_descriptor dfu_desc = {
46 .bLength = sizeof(struct usb_dfu_descriptor),
47 .bDescriptorType = USB_DESC_DFU_FUNCTIONAL,
48 .bmAttributes = USB_DFU_ATTR_CAN_DNLOAD |
49 ATTR_CAN_UPLOAD | ATTR_MANIFESTATION_TOLERANT |
50 USB_DFU_ATTR_WILL_DETACH,
51 .wDetachTimeOut = 0,
52 .wTransferSize = sys_cpu_to_le16(CONFIG_USBD_DFU_TRANSFER_SIZE),
53 .bcdDFUVersion = sys_cpu_to_le16(USB_DFU_VERSION),
54 };
55
56 /* Common class data for both run-time and DFU instances. */
57 struct usbd_dfu_data {
58 struct usb_desc_header **const runtime_mode_descs;
59 struct usb_desc_header **const dfu_mode_descs;
60 enum usb_dfu_state state;
61 enum usb_dfu_state next;
62 enum usb_dfu_status status;
63 struct k_work_delayable dwork;
64 struct usbd_context *ctx;
65 bool dfu_mode;
66 struct usbd_dfu_image *image;
67 uint8_t alternate;
68 };
69
70 /* Run-Time mode interface descriptor */
71 static __noinit struct usb_if_descriptor runtime_if0_desc;
72
73 /* Run-Time mode descriptors. No endpoints, identical for high and full speed. */
74 static struct usb_desc_header *runtime_mode_descs[] = {
75 (struct usb_desc_header *) &runtime_if0_desc,
76 (struct usb_desc_header *) &dfu_desc,
77 NULL,
78 };
79
80 /*
81 * DFU mode descriptors with two reserved indices for functional descriptor and
82 * at least one for NULL. No endpoints, identical for high and full speed.
83 */
84 static struct usb_desc_header *dfu_mode_descs[CONFIG_USBD_DFU_NUMOF_IMAGES + 2];
85
86 static struct usbd_dfu_data dfu_data = {
87 .runtime_mode_descs = runtime_mode_descs,
88 .dfu_mode_descs = dfu_mode_descs,
89 };
90
91 static const char *const dfu_state_list[] = {
92 "APP_IDLE",
93 "APP_DETACH",
94 "DFU_IDLE",
95 "DNLOAD_SYNC",
96 "DNBUSY",
97 "DNLOAD_IDLE",
98 "MANIFEST_SYNC",
99 "MANIFEST",
100 "MANIFEST_WAIT_RST",
101 "UPLOAD_IDLE",
102 "ERROR",
103 };
104
105 static const char *const dfu_req_list[] = {
106 "DETACH",
107 "DNLOAD",
108 "UPLOAD",
109 "GETSTATUS",
110 "CLRSTATUS",
111 "GETSTATE",
112 "ABORT",
113 };
114
115 BUILD_ASSERT(ARRAY_SIZE(dfu_state_list) == DFU_STATE_MAX,
116 "Number of entries in dfu_state_list is not equal to DFU_STATE_MAX");
117
118 BUILD_ASSERT(ARRAY_SIZE(dfu_req_list) == USB_DFU_REQ_ABORT + 1,
119 "Number of entries in dfu_req_list is not equal to USB_DFU_REQ_ABORT + 1");
120
dfu_state_string(const enum usb_dfu_state state)121 static const char *dfu_state_string(const enum usb_dfu_state state)
122 {
123 if (state >= 0 && state < DFU_STATE_MAX) {
124 return dfu_state_list[state];
125 }
126
127 return "?";
128 }
129
dfu_req_string(const enum usb_dfu_state state)130 static const char *dfu_req_string(const enum usb_dfu_state state)
131 {
132 if (state >= 0 && state <= USB_DFU_REQ_ABORT) {
133 return dfu_req_list[state];
134 }
135
136 return "?";
137 }
138
runtime_detach_work(struct k_work * work)139 static void runtime_detach_work(struct k_work *work)
140 {
141 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
142 struct usbd_dfu_data *data = CONTAINER_OF(dwork, struct usbd_dfu_data, dwork);
143
144 usbd_msg_pub_simple(data->ctx, USBD_MSG_DFU_APP_DETACH, 0);
145 }
146
init_if_desc(struct usb_if_descriptor * const desc,const uint8_t alternate,const uint8_t protocol)147 static void init_if_desc(struct usb_if_descriptor *const desc,
148 const uint8_t alternate, const uint8_t protocol)
149 {
150 desc->bLength = sizeof(struct usb_if_descriptor);
151 desc->bDescriptorType = USB_DESC_INTERFACE;
152 desc->bInterfaceNumber = 0;
153 desc->bAlternateSetting = alternate;
154 desc->bNumEndpoints = 0;
155 desc->bInterfaceClass = USB_BCC_APPLICATION;
156 desc->bInterfaceSubClass = USB_DFU_SUBCLASS;
157 desc->bInterfaceProtocol = protocol;
158 desc->iInterface = 0;
159 }
160
usbd_dfu_preinit(void)161 static int usbd_dfu_preinit(void)
162 {
163 struct usb_if_descriptor *if_desc;
164 int n = 0;
165
166 init_if_desc(&runtime_if0_desc, 0, USB_DFU_PROTOCOL_RUNTIME);
167
168 STRUCT_SECTION_FOREACH(usbd_dfu_image, image) {
169 if (n >= CONFIG_USBD_DFU_NUMOF_IMAGES) {
170 LOG_ERR("Cannot register USB DFU image %s", image->name);
171 return -ENOMEM;
172 }
173
174 if_desc = image->if_desc;
175 init_if_desc(if_desc, n, USB_DFU_PROTOCOL_DFU);
176 dfu_mode_descs[n] = (struct usb_desc_header *)if_desc;
177 n++;
178 }
179
180 dfu_mode_descs[n] = (struct usb_desc_header *)&dfu_desc;
181
182 k_work_init_delayable(&dfu_data.dwork, runtime_detach_work);
183
184 return 0;
185 }
186
187 /*
188 * Perhaps it makes sense to implement an on_registration class interface
189 * callback and not use SYS_INIT().
190 */
191 SYS_INIT(usbd_dfu_preinit, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
192
193 /*
194 * This function is used for two purposes, to inform the image backend about
195 * the next step and in some cases to get feedback if the next step is possible
196 * from the image perspective.
197 */
usbd_dfu_image_next(struct usbd_class_data * const c_data,const enum usb_dfu_state next)198 static inline bool usbd_dfu_image_next(struct usbd_class_data *const c_data,
199 const enum usb_dfu_state next)
200 {
201 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
202 struct usbd_dfu_image *const image = data->image;
203
204 if (image->next_cb != NULL) {
205 return image->next_cb(image->priv, data->state, next);
206 }
207
208 return true;
209 }
210
dfu_error(struct usbd_class_data * const c_data,const enum usb_dfu_state next,const enum usb_dfu_status status)211 static ALWAYS_INLINE void dfu_error(struct usbd_class_data *const c_data,
212 const enum usb_dfu_state next,
213 const enum usb_dfu_status status)
214 {
215 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
216
217 data->next = next;
218 data->status = status;
219 }
220
221 /*
222 * Because some states (e.g. APP_IDLE, APP_DETACH) require a stall handshake to
223 * be sent, but the state does not change to DFU_ERROR, there are some "return
224 * -ENOTSUP" without state change to indicate a protocol error.
225 */
226
app_idle_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)227 static int app_idle_next(struct usbd_class_data *const c_data,
228 const struct usb_setup_packet *const setup)
229 {
230 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
231
232 switch (setup->bRequest) {
233 case USB_DFU_REQ_DETACH:
234 data->next = APP_DETACH;
235 return 0;
236 case USB_DFU_REQ_GETSTATUS:
237 __fallthrough;
238 case USB_DFU_REQ_GETSTATE:
239 return 0;
240 default:
241 return -ENOTSUP;
242 }
243 }
244
app_detach_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)245 static int app_detach_next(struct usbd_class_data *const c_data,
246 const struct usb_setup_packet *const setup)
247 {
248 switch (setup->bRequest) {
249 case USB_DFU_REQ_GETSTATUS:
250 __fallthrough;
251 case USB_DFU_REQ_GETSTATE:
252 return 0;
253 default:
254 return -ENOTSUP;
255 }
256 }
257
dfu_idle_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)258 static int dfu_idle_next(struct usbd_class_data *const c_data,
259 const struct usb_setup_packet *const setup)
260 {
261 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
262
263 switch (setup->bRequest) {
264 case USB_DFU_REQ_DNLOAD:
265 if (!(dfu_desc.bmAttributes & USB_DFU_ATTR_CAN_DNLOAD)) {
266 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
267 return -ENOTSUP;
268 }
269
270 if (data->image == NULL || data->image->write_cb == NULL) {
271 dfu_error(c_data, DFU_ERROR, ERR_VENDOR);
272 return -ENOTSUP;
273 }
274
275 if (setup->wLength == 0) {
276 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
277 return -ENOTSUP;
278 }
279
280 data->next = DFU_DNLOAD_SYNC;
281 return 0;
282 case USB_DFU_REQ_UPLOAD:
283 if (!(dfu_desc.bmAttributes & USB_DFU_ATTR_CAN_UPLOAD)) {
284 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
285 return -ENOTSUP;
286 }
287
288 if (data->image == NULL || data->image->read_cb == NULL) {
289 dfu_error(c_data, DFU_ERROR, ERR_VENDOR);
290 return -ENOTSUP;
291 }
292
293 if (setup->wLength > sys_le16_to_cpu(dfu_desc.wTransferSize)) {
294 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
295 return -ENOTSUP;
296 }
297
298 data->next = DFU_UPLOAD_IDLE;
299 return 0;
300 case USB_DFU_REQ_ABORT:
301 __fallthrough;
302 case USB_DFU_REQ_GETSTATUS:
303 __fallthrough;
304 case USB_DFU_REQ_GETSTATE:
305 return 0;
306 default:
307 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
308 return -ENOTSUP;
309 }
310 }
311
dfu_dnload_sync_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)312 static int dfu_dnload_sync_next(struct usbd_class_data *const c_data,
313 const struct usb_setup_packet *const setup)
314 {
315 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
316
317 switch (setup->bRequest) {
318 case USB_DFU_REQ_GETSTATUS:
319 /* Chack if image backend can change DFU_DNLOAD_SYNC -> DFU_DNLOAD_IDLE */
320 if (usbd_dfu_image_next(c_data, DFU_DNLOAD_IDLE)) {
321 data->next = DFU_DNLOAD_IDLE;
322 } else {
323 data->next = DFU_DNBUSY;
324 }
325
326 return 0;
327 case USB_DFU_REQ_GETSTATE:
328 return 0;
329 default:
330 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
331 return -ENOTSUP;
332 }
333 }
334
dfu_dnbusy_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)335 static int dfu_dnbusy_next(struct usbd_class_data *const c_data,
336 const struct usb_setup_packet *const setup)
337 {
338 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
339
340 /* Do not enforce bmPollTimeout (allow GET_STATUS immediately) */
341 data->state = DFU_DNLOAD_SYNC;
342
343 return dfu_dnload_sync_next(c_data, setup);
344 }
345
dfu_dnload_idle_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)346 static int dfu_dnload_idle_next(struct usbd_class_data *const c_data,
347 const struct usb_setup_packet *const setup)
348 {
349 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
350
351 switch (setup->bRequest) {
352 case USB_DFU_REQ_DNLOAD:
353 if (setup->wLength == 0) {
354 data->next = DFU_MANIFEST_SYNC;
355 } else {
356 data->next = DFU_DNLOAD_SYNC;
357 }
358
359 return 0;
360 case USB_DFU_REQ_ABORT:
361 data->next = DFU_IDLE;
362 /* Notify image backend about DFU_DNLOAD_IDLE -> DFU_IDLE change */
363 usbd_dfu_image_next(c_data, data->next);
364 case USB_DFU_REQ_GETSTATUS:
365 __fallthrough;
366 case USB_DFU_REQ_GETSTATE:
367 return 0;
368 default:
369 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
370 return -ENOTSUP;
371 }
372 }
373
dfu_manifest_sync_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)374 static int dfu_manifest_sync_next(struct usbd_class_data *const c_data,
375 const struct usb_setup_packet *const setup)
376 {
377 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
378
379 switch (setup->bRequest) {
380 case USB_DFU_REQ_GETSTATUS:
381 if (usbd_dfu_image_next(c_data, DFU_IDLE)) {
382 data->next = DFU_IDLE;
383 usbd_msg_pub_simple(data->ctx, USBD_MSG_DFU_DOWNLOAD_COMPLETED, 0);
384 } else {
385 data->next = DFU_MANIFEST;
386 }
387 case USB_DFU_REQ_GETSTATE:
388 return 0;
389 default:
390 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
391 return -ENOTSUP;
392 }
393
394 return 0;
395 }
396
dfu_manifest_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)397 static int dfu_manifest_next(struct usbd_class_data *const c_data,
398 const struct usb_setup_packet *const setup)
399 {
400 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
401
402 /* Ignore poll timeout, proceed directly to next state. */
403
404 if (dfu_desc.bmAttributes & USB_DFU_ATTR_MANIFESTATION_TOLERANT) {
405 data->state = DFU_MANIFEST_SYNC;
406 return dfu_manifest_sync_next(c_data, setup);
407 }
408
409 data->next = DFU_MANIFEST_WAIT_RST;
410 usbd_dfu_image_next(c_data, DFU_MANIFEST_WAIT_RST);
411
412 return 0;
413 }
414
dfu_manifest_wait_rst_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)415 static int dfu_manifest_wait_rst_next(struct usbd_class_data *const c_data,
416 const struct usb_setup_packet *const setup)
417 {
418 /* Ignore all requests, wait for system or bus reset */
419
420 return 0;
421 }
422
dfu_upload_idle_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)423 static int dfu_upload_idle_next(struct usbd_class_data *const c_data,
424 const struct usb_setup_packet *const setup)
425 {
426 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
427
428 switch (setup->bRequest) {
429 case USB_DFU_REQ_UPLOAD:
430 if (setup->wLength > sys_le16_to_cpu(dfu_desc.wTransferSize)) {
431 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
432 return -ENOTSUP;
433 }
434
435 data->next = DFU_UPLOAD_IDLE;
436 return 0;
437 case USB_DFU_REQ_ABORT:
438 data->next = DFU_IDLE;
439 /* Notify image backend about DFU_UPLOAD_IDLE -> DFU_IDLE change */
440 usbd_dfu_image_next(c_data, data->next);
441 case USB_DFU_REQ_GETSTATUS:
442 __fallthrough;
443 case USB_DFU_REQ_GETSTATE:
444 return 0;
445 default:
446 dfu_error(c_data, DFU_ERROR, ERR_STALLEDPKT);
447 return -ENOTSUP;
448 }
449 }
450
dfu_error_next(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)451 static int dfu_error_next(struct usbd_class_data *const c_data,
452 const struct usb_setup_packet *const setup)
453 {
454 switch (setup->bRequest) {
455 case USB_DFU_REQ_GETSTATUS:
456 __fallthrough;
457 case USB_DFU_REQ_GETSTATE:
458 return 0;
459 case USB_DFU_REQ_CLRSTATUS:
460 dfu_error(c_data, DFU_IDLE, ERR_OK);
461 return 0;
462 default:
463 return -ENOTSUP;
464 }
465 }
466
467 static int (*next_entries[])(struct usbd_class_data *const c_data,
468 const struct usb_setup_packet *const setup) = {
469 app_idle_next,
470 app_detach_next,
471 dfu_idle_next,
472 dfu_dnload_sync_next,
473 dfu_dnbusy_next,
474 dfu_dnload_idle_next,
475 dfu_manifest_sync_next,
476 dfu_manifest_next,
477 dfu_manifest_wait_rst_next,
478 dfu_upload_idle_next,
479 dfu_error_next,
480 };
481
482 BUILD_ASSERT(ARRAY_SIZE(next_entries) == DFU_STATE_MAX,
483 "Number of entries in next_entries is not equal to DFU_STATE_MAX");
484
485 /*
486 * Here we only set the next state based on the current state, image state and
487 * the new request. We do not copy/move any data and we do not update DFU state.
488 *
489 * The state change and additional actions are performed in four places, in the
490 * host/device requests in runtime mode and in the host/device request in DFU
491 * mode.
492 */
dfu_set_next_state(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup)493 static int dfu_set_next_state(struct usbd_class_data *const c_data,
494 const struct usb_setup_packet *const setup)
495 {
496 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
497 int err;
498
499 if (setup->RequestType.type != USB_REQTYPE_TYPE_CLASS) {
500 return -ENOTSUP;
501 }
502
503 if (setup->bRequest >= ARRAY_SIZE(next_entries)) {
504 return -ENOTSUP;
505 }
506
507 data->next = data->state;
508 err = next_entries[data->state](c_data, setup);
509
510 LOG_DBG("bRequest %s, state %s, next %s, error %d",
511 dfu_req_string(setup->bRequest), dfu_state_string(data->state),
512 dfu_state_string(data->next), err);
513
514 return err;
515 }
516
517 /* Run-Time mode instance implementation, for instance "dfu_runtime" */
518
handle_get_status(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)519 static int handle_get_status(struct usbd_class_data *const c_data,
520 const struct usb_setup_packet *const setup,
521 struct net_buf *const buf)
522 {
523 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
524 size_t len = MIN(setup->wLength, net_buf_tailroom(buf));
525 const size_t getstatus_len = 6;
526
527 if (len != getstatus_len) {
528 errno = -ENOTSUP;
529 return 0;
530 }
531
532 /*
533 * Add GET_STATUS response consisting of
534 * bStatus, bwPollTimeout, bStatus, iString (no strings defined)
535 */
536 net_buf_add_u8(buf, data->status);
537 net_buf_add_le16(buf, CONFIG_USBD_DFU_POLLTIMEOUT);
538 net_buf_add_u8(buf, 0);
539 net_buf_add_u8(buf, data->state);
540 net_buf_add_u8(buf, 0);
541
542 return 0;
543 }
544
handle_get_state(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)545 static int handle_get_state(struct usbd_class_data *const c_data,
546 const struct usb_setup_packet *const setup,
547 struct net_buf *const buf)
548 {
549 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
550 size_t len = MIN(setup->wLength, net_buf_tailroom(buf));
551 const size_t getstate_len = 1;
552
553 if (len != getstate_len) {
554 errno = -ENOTSUP;
555 return 0;
556 }
557
558 net_buf_add_u8(buf, data->state);
559
560 return 0;
561 }
562
runtime_mode_control_to_host(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)563 static int runtime_mode_control_to_host(struct usbd_class_data *const c_data,
564 const struct usb_setup_packet *const setup,
565 struct net_buf *const buf)
566 {
567 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
568
569 errno = dfu_set_next_state(c_data, setup);
570
571 if (errno == 0) {
572 switch (setup->bRequest) {
573 case USB_DFU_REQ_GETSTATUS:
574 errno = handle_get_status(c_data, setup, buf);
575 break;
576 case USB_DFU_REQ_GETSTATE:
577 errno = handle_get_state(c_data, setup, buf);
578 break;
579 default:
580 break;
581 }
582 }
583
584 data->state = data->next;
585
586 return 0;
587 }
588
runtime_mode_control_to_dev(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup,const struct net_buf * const buf)589 static int runtime_mode_control_to_dev(struct usbd_class_data *const c_data,
590 const struct usb_setup_packet *const setup,
591 const struct net_buf *const buf)
592 {
593 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
594
595 errno = dfu_set_next_state(c_data, setup);
596
597 if (errno == 0) {
598 if (setup->bRequest == USB_DFU_REQ_DETACH) {
599 k_work_reschedule(&data->dwork, K_MSEC(100));
600 }
601 }
602
603 data->state = data->next;
604
605 return 0;
606 }
607
runtime_mode_get_desc(struct usbd_class_data * const c_data,const enum usbd_speed speed)608 static void *runtime_mode_get_desc(struct usbd_class_data *const c_data,
609 const enum usbd_speed speed)
610 {
611 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
612
613 return data->runtime_mode_descs;
614 }
615
runtime_mode_init(struct usbd_class_data * const c_data)616 static int runtime_mode_init(struct usbd_class_data *const c_data)
617 {
618 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
619
620 LOG_DBG("Init class instance %p", c_data);
621 data->dfu_mode = false;
622 data->alternate = 0;
623 data->state = APP_IDLE;
624 data->next = APP_IDLE;
625 data->image = NULL;
626 data->ctx = usbd_class_get_ctx(c_data);
627
628 return 0;
629 }
630
631 struct usbd_class_api runtime_mode_api = {
632 .control_to_host = runtime_mode_control_to_host,
633 .control_to_dev = runtime_mode_control_to_dev,
634 .get_desc = runtime_mode_get_desc,
635 .init = runtime_mode_init,
636 };
637
638 USBD_DEFINE_CLASS(dfu_runtime, &runtime_mode_api, &dfu_data, NULL);
639
640 /* DFU mode instance implementation, for instance "dfu_dfu" */
641
handle_upload(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)642 static int handle_upload(struct usbd_class_data *const c_data,
643 const struct usb_setup_packet *const setup,
644 struct net_buf *const buf)
645 {
646 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
647 uint16_t size = MIN(setup->wLength, net_buf_tailroom(buf));
648 struct usbd_dfu_image *const image = data->image;
649 int ret;
650
651 ret = image->read_cb(image->priv, setup->wValue, size, buf->data);
652 if (ret >= 0) {
653 net_buf_add(buf, ret);
654 if (ret < sys_le16_to_cpu(dfu_desc.wTransferSize)) {
655 data->state = DFU_IDLE;
656 }
657 } else {
658 errno = -ENOTSUP;
659 dfu_error(c_data, DFU_ERROR, ERR_UNKNOWN);
660 }
661
662 return 0;
663 }
664
handle_download(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup,const struct net_buf * const buf)665 static int handle_download(struct usbd_class_data *const c_data,
666 const struct usb_setup_packet *const setup,
667 const struct net_buf *const buf)
668 {
669 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
670 struct usbd_dfu_image *const image = data->image;
671 uint16_t size = MIN(setup->wLength, buf->len);
672 int ret;
673
674 ret = image->write_cb(image->priv, setup->wValue, size, buf->data);
675 if (ret < 0) {
676 errno = -ENOTSUP;
677 dfu_error(c_data, DFU_ERROR, ERR_UNKNOWN);
678 }
679
680 return 0;
681 }
682
dfu_mode_control_to_host(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)683 static int dfu_mode_control_to_host(struct usbd_class_data *const c_data,
684 const struct usb_setup_packet *const setup,
685 struct net_buf *const buf)
686 {
687 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
688
689 errno = dfu_set_next_state(c_data, setup);
690
691 if (errno == 0) {
692 switch (setup->bRequest) {
693 case USB_DFU_REQ_GETSTATUS:
694 errno = handle_get_status(c_data, setup, buf);
695 break;
696 case USB_DFU_REQ_GETSTATE:
697 errno = handle_get_state(c_data, setup, buf);
698 break;
699 case USB_DFU_REQ_UPLOAD:
700 errno = handle_upload(c_data, setup, buf);
701 break;
702 default:
703 break;
704 }
705 }
706
707 data->state = data->next;
708
709 return 0;
710 }
711
dfu_mode_control_to_dev(struct usbd_class_data * const c_data,const struct usb_setup_packet * const setup,const struct net_buf * const buf)712 static int dfu_mode_control_to_dev(struct usbd_class_data *const c_data,
713 const struct usb_setup_packet *const setup,
714 const struct net_buf *const buf)
715 {
716 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
717
718 errno = dfu_set_next_state(c_data, setup);
719
720 if (errno == 0) {
721 if (setup->bRequest == USB_DFU_REQ_DNLOAD) {
722 handle_download(c_data, setup, buf);
723 }
724 }
725
726 data->state = data->next;
727
728 return 0;
729 }
730
dfu_mode_update(struct usbd_class_data * const c_data,const uint8_t iface,const uint8_t alternate)731 static void dfu_mode_update(struct usbd_class_data *const c_data,
732 const uint8_t iface, const uint8_t alternate)
733 {
734 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
735
736 LOG_DBG("Instance %p, interface %u alternate %u changed",
737 c_data, iface, alternate);
738
739 data->alternate = alternate;
740 data->image = NULL;
741
742 STRUCT_SECTION_FOREACH(usbd_dfu_image, image) {
743 if (image->if_desc->bAlternateSetting == alternate) {
744 data->image = image;
745 break;
746 }
747 }
748 }
749
dfu_mode_get_desc(struct usbd_class_data * const c_data,const enum usbd_speed speed)750 static void *dfu_mode_get_desc(struct usbd_class_data *const c_data,
751 const enum usbd_speed speed)
752 {
753 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
754
755 return data->dfu_mode_descs;
756 }
757
dfu_mode_init(struct usbd_class_data * const c_data)758 static int dfu_mode_init(struct usbd_class_data *const c_data)
759 {
760 struct usbd_dfu_data *data = usbd_class_get_private(c_data);
761 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
762
763 LOG_DBG("Init class instance %p", c_data);
764 data->dfu_mode = true;
765 data->alternate = 0;
766 data->image = NULL;
767 data->state = DFU_IDLE;
768 data->next = DFU_IDLE;
769 data->ctx = usbd_class_get_ctx(c_data);
770
771 STRUCT_SECTION_FOREACH(usbd_dfu_image, image) {
772 if (image->if_desc->bAlternateSetting == data->alternate) {
773 data->image = image;
774 }
775
776 if (usbd_add_descriptor(uds_ctx, image->sd_nd)) {
777 LOG_ERR("Failed to add string descriptor");
778 } else {
779 image->if_desc->iInterface = usbd_str_desc_get_idx(image->sd_nd);
780 }
781 }
782
783 return data->image == NULL ? -EINVAL : 0;
784 }
785
786 struct usbd_class_api dfu_api = {
787 .control_to_host = dfu_mode_control_to_host,
788 .control_to_dev = dfu_mode_control_to_dev,
789 .update = dfu_mode_update,
790 .get_desc = dfu_mode_get_desc,
791 .init = dfu_mode_init,
792 };
793
794 USBD_DEFINE_CLASS(dfu_dfu, &dfu_api, &dfu_data, NULL);
795