blob: 5321a689d4dc3905651fade571dd27fcb8b5dc2a [file] [log] [blame]
Lukasz Majewskib819ddb2012-08-06 14:41:06 +02001/*
2 * f_dfu.c -- Device Firmware Update USB function
3 *
4 * Copyright (C) 2012 Samsung Electronics
5 * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
6 * Lukasz Majewski <l.majewski@samsung.com>
7 *
Ɓukasz Majewski81c065d2013-07-05 11:40:47 +02008 * Based on OpenMoko u-boot: drivers/usb/usbdfu.c
9 * (C) 2007 by OpenMoko, Inc.
10 * Author: Harald Welte <laforge@openmoko.org>
11 *
12 * based on existing SAM7DFU code from OpenPCD:
13 * (C) Copyright 2006 by Harald Welte <hwelte at hmw-consulting.de>
14 *
Wolfgang Denk1a459662013-07-08 09:37:19 +020015 * SPDX-License-Identifier: GPL-2.0+
Lukasz Majewskib819ddb2012-08-06 14:41:06 +020016 */
17
18#include <errno.h>
19#include <common.h>
20#include <malloc.h>
21
22#include <linux/usb/ch9.h>
Lukasz Majewskib819ddb2012-08-06 14:41:06 +020023#include <linux/usb/gadget.h>
24#include <linux/usb/composite.h>
25
26#include <dfu.h>
27#include "f_dfu.h"
28
29struct f_dfu {
30 struct usb_function usb_function;
31
32 struct usb_descriptor_header **function;
33 struct usb_string *strings;
34
35 /* when configured, we have one config */
36 u8 config;
37 u8 altsetting;
38 enum dfu_state dfu_state;
39 unsigned int dfu_status;
40
41 /* Send/received block number is handy for data integrity check */
42 int blk_seq_num;
43};
44
45typedef int (*dfu_state_fn) (struct f_dfu *,
46 const struct usb_ctrlrequest *,
47 struct usb_gadget *,
48 struct usb_request *);
49
50static inline struct f_dfu *func_to_dfu(struct usb_function *f)
51{
52 return container_of(f, struct f_dfu, usb_function);
53}
54
55static const struct dfu_function_descriptor dfu_func = {
56 .bLength = sizeof dfu_func,
57 .bDescriptorType = DFU_DT_FUNC,
58 .bmAttributes = DFU_BIT_WILL_DETACH |
59 DFU_BIT_MANIFESTATION_TOLERANT |
60 DFU_BIT_CAN_UPLOAD |
61 DFU_BIT_CAN_DNLOAD,
62 .wDetachTimeOut = 0,
63 .wTransferSize = DFU_USB_BUFSIZ,
64 .bcdDFUVersion = __constant_cpu_to_le16(0x0110),
65};
66
67static struct usb_interface_descriptor dfu_intf_runtime = {
68 .bLength = sizeof dfu_intf_runtime,
69 .bDescriptorType = USB_DT_INTERFACE,
70 .bNumEndpoints = 0,
71 .bInterfaceClass = USB_CLASS_APP_SPEC,
72 .bInterfaceSubClass = 1,
73 .bInterfaceProtocol = 1,
74 /* .iInterface = DYNAMIC */
75};
76
77static struct usb_descriptor_header *dfu_runtime_descs[] = {
78 (struct usb_descriptor_header *) &dfu_intf_runtime,
79 NULL,
80};
81
82static const struct usb_qualifier_descriptor dev_qualifier = {
83 .bLength = sizeof dev_qualifier,
84 .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
85 .bcdUSB = __constant_cpu_to_le16(0x0200),
86 .bDeviceClass = USB_CLASS_VENDOR_SPEC,
87 .bNumConfigurations = 1,
88};
89
90static const char dfu_name[] = "Device Firmware Upgrade";
91
92/*
93 * static strings, in UTF-8
94 *
95 * dfu_generic configuration
96 */
97static struct usb_string strings_dfu_generic[] = {
98 [0].s = dfu_name,
99 { } /* end of list */
100};
101
102static struct usb_gadget_strings stringtab_dfu_generic = {
103 .language = 0x0409, /* en-us */
104 .strings = strings_dfu_generic,
105};
106
107static struct usb_gadget_strings *dfu_generic_strings[] = {
108 &stringtab_dfu_generic,
109 NULL,
110};
111
112/*
113 * usb_function specific
114 */
115static struct usb_gadget_strings stringtab_dfu = {
116 .language = 0x0409, /* en-us */
117 /*
118 * .strings
119 *
120 * assigned during initialization,
121 * depends on number of flash entities
122 *
123 */
124};
125
126static struct usb_gadget_strings *dfu_strings[] = {
127 &stringtab_dfu,
128 NULL,
129};
130
131/*-------------------------------------------------------------------------*/
132
133static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req)
134{
135 struct f_dfu *f_dfu = req->context;
136
137 dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
138 req->length, f_dfu->blk_seq_num);
139
140 if (req->length == 0)
141 puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n");
142}
143
144static void handle_getstatus(struct usb_request *req)
145{
146 struct dfu_status *dstat = (struct dfu_status *)req->buf;
147 struct f_dfu *f_dfu = req->context;
148
149 switch (f_dfu->dfu_state) {
150 case DFU_STATE_dfuDNLOAD_SYNC:
151 case DFU_STATE_dfuDNBUSY:
152 f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
153 break;
154 case DFU_STATE_dfuMANIFEST_SYNC:
155 break;
156 default:
157 break;
158 }
159
160 /* send status response */
161 dstat->bStatus = f_dfu->dfu_status;
Pantelis Antoniou80eb1bd2012-11-30 08:01:10 +0000162 dstat->bwPollTimeout[0] = 0;
163 dstat->bwPollTimeout[1] = 0;
164 dstat->bwPollTimeout[2] = 0;
Lukasz Majewskib819ddb2012-08-06 14:41:06 +0200165 dstat->bState = f_dfu->dfu_state;
166 dstat->iString = 0;
167}
168
169static void handle_getstate(struct usb_request *req)
170{
171 struct f_dfu *f_dfu = req->context;
172
173 ((u8 *)req->buf)[0] = f_dfu->dfu_state;
174 req->actual = sizeof(u8);
175}
176
177static inline void to_dfu_mode(struct f_dfu *f_dfu)
178{
179 f_dfu->usb_function.strings = dfu_strings;
180 f_dfu->usb_function.hs_descriptors = f_dfu->function;
Heiko Schocherad5f9772013-06-25 13:59:29 +0200181 f_dfu->dfu_state = DFU_STATE_dfuIDLE;
Lukasz Majewskib819ddb2012-08-06 14:41:06 +0200182}
183
184static inline void to_runtime_mode(struct f_dfu *f_dfu)
185{
186 f_dfu->usb_function.strings = NULL;
187 f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
188}
189
190static int handle_upload(struct usb_request *req, u16 len)
191{
192 struct f_dfu *f_dfu = req->context;
193
194 return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf,
195 req->length, f_dfu->blk_seq_num);
196}
197
198static int handle_dnload(struct usb_gadget *gadget, u16 len)
199{
200 struct usb_composite_dev *cdev = get_gadget_data(gadget);
201 struct usb_request *req = cdev->req;
202 struct f_dfu *f_dfu = req->context;
203
204 if (len == 0)
205 f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
206
207 req->complete = dnload_request_complete;
208
209 return len;
210}
211
212/*-------------------------------------------------------------------------*/
213/* DFU state machine */
214static int state_app_idle(struct f_dfu *f_dfu,
215 const struct usb_ctrlrequest *ctrl,
216 struct usb_gadget *gadget,
217 struct usb_request *req)
218{
219 int value = 0;
220
221 switch (ctrl->bRequest) {
222 case USB_REQ_DFU_GETSTATUS:
223 handle_getstatus(req);
224 value = RET_STAT_LEN;
225 break;
226 case USB_REQ_DFU_GETSTATE:
227 handle_getstate(req);
228 break;
229 case USB_REQ_DFU_DETACH:
230 f_dfu->dfu_state = DFU_STATE_appDETACH;
231 to_dfu_mode(f_dfu);
Lukasz Majewskib819ddb2012-08-06 14:41:06 +0200232 value = RET_ZLP;
233 break;
234 default:
235 value = RET_STALL;
236 break;
237 }
238
239 return value;
240}
241
242static int state_app_detach(struct f_dfu *f_dfu,
243 const struct usb_ctrlrequest *ctrl,
244 struct usb_gadget *gadget,
245 struct usb_request *req)
246{
247 int value = 0;
248
249 switch (ctrl->bRequest) {
250 case USB_REQ_DFU_GETSTATUS:
251 handle_getstatus(req);
252 value = RET_STAT_LEN;
253 break;
254 case USB_REQ_DFU_GETSTATE:
255 handle_getstate(req);
256 break;
257 default:
258 f_dfu->dfu_state = DFU_STATE_appIDLE;
259 value = RET_STALL;
260 break;
261 }
262
263 return value;
264}
265
266static int state_dfu_idle(struct f_dfu *f_dfu,
267 const struct usb_ctrlrequest *ctrl,
268 struct usb_gadget *gadget,
269 struct usb_request *req)
270{
271 u16 w_value = le16_to_cpu(ctrl->wValue);
272 u16 len = le16_to_cpu(ctrl->wLength);
273 int value = 0;
274
275 switch (ctrl->bRequest) {
276 case USB_REQ_DFU_DNLOAD:
277 if (len == 0) {
278 f_dfu->dfu_state = DFU_STATE_dfuERROR;
279 value = RET_STALL;
280 break;
281 }
282 f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
283 f_dfu->blk_seq_num = w_value;
284 value = handle_dnload(gadget, len);
285 break;
286 case USB_REQ_DFU_UPLOAD:
287 f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
288 f_dfu->blk_seq_num = 0;
289 value = handle_upload(req, len);
290 break;
291 case USB_REQ_DFU_ABORT:
292 /* no zlp? */
293 value = RET_ZLP;
294 break;
295 case USB_REQ_DFU_GETSTATUS:
296 handle_getstatus(req);
297 value = RET_STAT_LEN;
298 break;
299 case USB_REQ_DFU_GETSTATE:
300 handle_getstate(req);
301 break;
302 case USB_REQ_DFU_DETACH:
303 /*
304 * Proprietary extension: 'detach' from idle mode and
305 * get back to runtime mode in case of USB Reset. As
306 * much as I dislike this, we just can't use every USB
307 * bus reset to switch back to runtime mode, since at
308 * least the Linux USB stack likes to send a number of
309 * resets in a row :(
310 */
311 f_dfu->dfu_state =
312 DFU_STATE_dfuMANIFEST_WAIT_RST;
313 to_runtime_mode(f_dfu);
314 f_dfu->dfu_state = DFU_STATE_appIDLE;
315 break;
316 default:
317 f_dfu->dfu_state = DFU_STATE_dfuERROR;
318 value = RET_STALL;
319 break;
320 }
321
322 return value;
323}
324
325static int state_dfu_dnload_sync(struct f_dfu *f_dfu,
326 const struct usb_ctrlrequest *ctrl,
327 struct usb_gadget *gadget,
328 struct usb_request *req)
329{
330 int value = 0;
331
332 switch (ctrl->bRequest) {
333 case USB_REQ_DFU_GETSTATUS:
334 handle_getstatus(req);
335 value = RET_STAT_LEN;
336 break;
337 case USB_REQ_DFU_GETSTATE:
338 handle_getstate(req);
339 break;
340 default:
341 f_dfu->dfu_state = DFU_STATE_dfuERROR;
342 value = RET_STALL;
343 break;
344 }
345
346 return value;
347}
348
349static int state_dfu_dnbusy(struct f_dfu *f_dfu,
350 const struct usb_ctrlrequest *ctrl,
351 struct usb_gadget *gadget,
352 struct usb_request *req)
353{
354 int value = 0;
355
356 switch (ctrl->bRequest) {
357 case USB_REQ_DFU_GETSTATUS:
358 handle_getstatus(req);
359 value = RET_STAT_LEN;
360 break;
361 default:
362 f_dfu->dfu_state = DFU_STATE_dfuERROR;
363 value = RET_STALL;
364 break;
365 }
366
367 return value;
368}
369
370static int state_dfu_dnload_idle(struct f_dfu *f_dfu,
371 const struct usb_ctrlrequest *ctrl,
372 struct usb_gadget *gadget,
373 struct usb_request *req)
374{
375 u16 w_value = le16_to_cpu(ctrl->wValue);
376 u16 len = le16_to_cpu(ctrl->wLength);
377 int value = 0;
378
379 switch (ctrl->bRequest) {
380 case USB_REQ_DFU_DNLOAD:
381 f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
382 f_dfu->blk_seq_num = w_value;
383 value = handle_dnload(gadget, len);
384 break;
385 case USB_REQ_DFU_ABORT:
386 f_dfu->dfu_state = DFU_STATE_dfuIDLE;
387 value = RET_ZLP;
388 break;
389 case USB_REQ_DFU_GETSTATUS:
390 handle_getstatus(req);
391 value = RET_STAT_LEN;
392 break;
393 case USB_REQ_DFU_GETSTATE:
394 handle_getstate(req);
395 break;
396 default:
397 f_dfu->dfu_state = DFU_STATE_dfuERROR;
398 value = RET_STALL;
399 break;
400 }
401
402 return value;
403}
404
405static int state_dfu_manifest_sync(struct f_dfu *f_dfu,
406 const struct usb_ctrlrequest *ctrl,
407 struct usb_gadget *gadget,
408 struct usb_request *req)
409{
410 int value = 0;
411
412 switch (ctrl->bRequest) {
413 case USB_REQ_DFU_GETSTATUS:
414 /* We're MainfestationTolerant */
415 f_dfu->dfu_state = DFU_STATE_dfuIDLE;
416 handle_getstatus(req);
417 f_dfu->blk_seq_num = 0;
418 value = RET_STAT_LEN;
419 break;
420 case USB_REQ_DFU_GETSTATE:
421 handle_getstate(req);
422 break;
423 default:
424 f_dfu->dfu_state = DFU_STATE_dfuERROR;
425 value = RET_STALL;
426 break;
427 }
428
429 return value;
430}
431
432static int state_dfu_upload_idle(struct f_dfu *f_dfu,
433 const struct usb_ctrlrequest *ctrl,
434 struct usb_gadget *gadget,
435 struct usb_request *req)
436{
437 u16 w_value = le16_to_cpu(ctrl->wValue);
438 u16 len = le16_to_cpu(ctrl->wLength);
439 int value = 0;
440
441 switch (ctrl->bRequest) {
442 case USB_REQ_DFU_UPLOAD:
443 /* state transition if less data then requested */
444 f_dfu->blk_seq_num = w_value;
445 value = handle_upload(req, len);
446 if (value >= 0 && value < len)
447 f_dfu->dfu_state = DFU_STATE_dfuIDLE;
448 break;
449 case USB_REQ_DFU_ABORT:
450 f_dfu->dfu_state = DFU_STATE_dfuIDLE;
451 /* no zlp? */
452 value = RET_ZLP;
453 break;
454 case USB_REQ_DFU_GETSTATUS:
455 handle_getstatus(req);
456 value = RET_STAT_LEN;
457 break;
458 case USB_REQ_DFU_GETSTATE:
459 handle_getstate(req);
460 break;
461 default:
462 f_dfu->dfu_state = DFU_STATE_dfuERROR;
463 value = RET_STALL;
464 break;
465 }
466
467 return value;
468}
469
470static int state_dfu_error(struct f_dfu *f_dfu,
471 const struct usb_ctrlrequest *ctrl,
472 struct usb_gadget *gadget,
473 struct usb_request *req)
474{
475 int value = 0;
476
477 switch (ctrl->bRequest) {
478 case USB_REQ_DFU_GETSTATUS:
479 handle_getstatus(req);
480 value = RET_STAT_LEN;
481 break;
482 case USB_REQ_DFU_GETSTATE:
483 handle_getstate(req);
484 break;
485 case USB_REQ_DFU_CLRSTATUS:
486 f_dfu->dfu_state = DFU_STATE_dfuIDLE;
487 f_dfu->dfu_status = DFU_STATUS_OK;
488 /* no zlp? */
489 value = RET_ZLP;
490 break;
491 default:
492 f_dfu->dfu_state = DFU_STATE_dfuERROR;
493 value = RET_STALL;
494 break;
495 }
496
497 return value;
498}
499
500static dfu_state_fn dfu_state[] = {
501 state_app_idle, /* DFU_STATE_appIDLE */
502 state_app_detach, /* DFU_STATE_appDETACH */
503 state_dfu_idle, /* DFU_STATE_dfuIDLE */
504 state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */
505 state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */
506 state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */
507 state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */
508 NULL, /* DFU_STATE_dfuMANIFEST */
509 NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */
510 state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */
511 state_dfu_error /* DFU_STATE_dfuERROR */
512};
513
514static int
515dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
516{
517 struct usb_gadget *gadget = f->config->cdev->gadget;
518 struct usb_request *req = f->config->cdev->req;
519 struct f_dfu *f_dfu = f->config->cdev->req->context;
520 u16 len = le16_to_cpu(ctrl->wLength);
521 u16 w_value = le16_to_cpu(ctrl->wValue);
522 int value = 0;
523 u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
524
525 debug("w_value: 0x%x len: 0x%x\n", w_value, len);
526 debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n",
527 req_type, ctrl->bRequest, f_dfu->dfu_state);
528
529 if (req_type == USB_TYPE_STANDARD) {
530 if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR &&
531 (w_value >> 8) == DFU_DT_FUNC) {
532 value = min(len, (u16) sizeof(dfu_func));
533 memcpy(req->buf, &dfu_func, value);
534 }
535 } else /* DFU specific request */
536 value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req);
537
538 if (value >= 0) {
539 req->length = value;
540 req->zero = value < len;
541 value = usb_ep_queue(gadget->ep0, req, 0);
542 if (value < 0) {
543 debug("ep_queue --> %d\n", value);
544 req->status = 0;
545 }
546 }
547
548 return value;
549}
550
551/*-------------------------------------------------------------------------*/
552
553static int
554dfu_prepare_strings(struct f_dfu *f_dfu, int n)
555{
556 struct dfu_entity *de = NULL;
557 int i = 0;
558
559 f_dfu->strings = calloc(sizeof(struct usb_string), n + 1);
560 if (!f_dfu->strings)
561 goto enomem;
562
563 for (i = 0; i < n; ++i) {
564 de = dfu_get_entity(i);
565 f_dfu->strings[i].s = de->name;
566 }
567
568 f_dfu->strings[i].id = 0;
569 f_dfu->strings[i].s = NULL;
570
571 return 0;
572
573enomem:
574 while (i)
575 f_dfu->strings[--i].s = NULL;
576
577 free(f_dfu->strings);
578
579 return -ENOMEM;
580}
581
582static int dfu_prepare_function(struct f_dfu *f_dfu, int n)
583{
584 struct usb_interface_descriptor *d;
585 int i = 0;
586
Lukasz Majewskie059a402013-06-26 11:46:13 +0200587 f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n + 1);
Lukasz Majewskib819ddb2012-08-06 14:41:06 +0200588 if (!f_dfu->function)
589 goto enomem;
590
591 for (i = 0; i < n; ++i) {
592 d = calloc(sizeof(*d), 1);
593 if (!d)
594 goto enomem;
595
596 d->bLength = sizeof(*d);
597 d->bDescriptorType = USB_DT_INTERFACE;
598 d->bAlternateSetting = i;
599 d->bNumEndpoints = 0;
600 d->bInterfaceClass = USB_CLASS_APP_SPEC;
601 d->bInterfaceSubClass = 1;
602 d->bInterfaceProtocol = 2;
603
604 f_dfu->function[i] = (struct usb_descriptor_header *)d;
605 }
606 f_dfu->function[i] = NULL;
607
608 return 0;
609
610enomem:
611 while (i) {
612 free(f_dfu->function[--i]);
613 f_dfu->function[i] = NULL;
614 }
615 free(f_dfu->function);
616
617 return -ENOMEM;
618}
619
620static int dfu_bind(struct usb_configuration *c, struct usb_function *f)
621{
622 struct usb_composite_dev *cdev = c->cdev;
623 struct f_dfu *f_dfu = func_to_dfu(f);
624 int alt_num = dfu_get_alt_number();
625 int rv, id, i;
626
627 id = usb_interface_id(c, f);
628 if (id < 0)
629 return id;
630 dfu_intf_runtime.bInterfaceNumber = id;
631
632 f_dfu->dfu_state = DFU_STATE_appIDLE;
633 f_dfu->dfu_status = DFU_STATUS_OK;
634
635 rv = dfu_prepare_function(f_dfu, alt_num);
636 if (rv)
637 goto error;
638
639 rv = dfu_prepare_strings(f_dfu, alt_num);
640 if (rv)
641 goto error;
642 for (i = 0; i < alt_num; i++) {
643 id = usb_string_id(cdev);
644 if (id < 0)
645 return id;
646 f_dfu->strings[i].id = id;
647 ((struct usb_interface_descriptor *)f_dfu->function[i])
648 ->iInterface = id;
649 }
650
Heiko Schocherad5f9772013-06-25 13:59:29 +0200651 to_dfu_mode(f_dfu);
652
Lukasz Majewskib819ddb2012-08-06 14:41:06 +0200653 stringtab_dfu.strings = f_dfu->strings;
654
655 cdev->req->context = f_dfu;
656
657error:
658 return rv;
659}
660
661static void dfu_unbind(struct usb_configuration *c, struct usb_function *f)
662{
663 struct f_dfu *f_dfu = func_to_dfu(f);
664 int alt_num = dfu_get_alt_number();
665 int i;
666
667 if (f_dfu->strings) {
668 i = alt_num;
669 while (i)
670 f_dfu->strings[--i].s = NULL;
671
672 free(f_dfu->strings);
673 }
674
675 if (f_dfu->function) {
676 i = alt_num;
677 while (i) {
678 free(f_dfu->function[--i]);
679 f_dfu->function[i] = NULL;
680 }
681 free(f_dfu->function);
682 }
683
684 free(f_dfu);
685}
686
687static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
688{
689 struct f_dfu *f_dfu = func_to_dfu(f);
690
691 debug("%s: intf:%d alt:%d\n", __func__, intf, alt);
692
693 f_dfu->altsetting = alt;
694
695 return 0;
696}
697
698/* TODO: is this really what we need here? */
699static void dfu_disable(struct usb_function *f)
700{
701 struct f_dfu *f_dfu = func_to_dfu(f);
702 if (f_dfu->config == 0)
703 return;
704
705 debug("%s: reset config\n", __func__);
706
707 f_dfu->config = 0;
708}
709
710static int dfu_bind_config(struct usb_configuration *c)
711{
712 struct f_dfu *f_dfu;
713 int status;
714
715 f_dfu = calloc(sizeof(*f_dfu), 1);
716 if (!f_dfu)
717 return -ENOMEM;
718 f_dfu->usb_function.name = "dfu";
719 f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
720 f_dfu->usb_function.bind = dfu_bind;
721 f_dfu->usb_function.unbind = dfu_unbind;
722 f_dfu->usb_function.set_alt = dfu_set_alt;
723 f_dfu->usb_function.disable = dfu_disable;
724 f_dfu->usb_function.strings = dfu_generic_strings,
725 f_dfu->usb_function.setup = dfu_handle,
726
727 status = usb_add_function(c, &f_dfu->usb_function);
728 if (status)
729 free(f_dfu);
730
731 return status;
732}
733
734int dfu_add(struct usb_configuration *c)
735{
736 int id;
737
738 id = usb_string_id(c->cdev);
739 if (id < 0)
740 return id;
741 strings_dfu_generic[0].id = id;
742 dfu_intf_runtime.iInterface = id;
743
744 debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__,
745 c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
746
747 return dfu_bind_config(c);
748}