1 /* 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 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 #include <errno.h> 24 #include <common.h> 25 #include <malloc.h> 26 27 #include <linux/usb/ch9.h> 28 #include <linux/usb/gadget.h> 29 #include <linux/usb/composite.h> 30 31 #include <dfu.h> 32 #include "f_dfu.h" 33 34 struct f_dfu { 35 struct usb_function usb_function; 36 37 struct usb_descriptor_header **function; 38 struct usb_string *strings; 39 40 /* when configured, we have one config */ 41 u8 config; 42 u8 altsetting; 43 enum dfu_state dfu_state; 44 unsigned int dfu_status; 45 46 /* Send/received block number is handy for data integrity check */ 47 int blk_seq_num; 48 }; 49 50 typedef int (*dfu_state_fn) (struct f_dfu *, 51 const struct usb_ctrlrequest *, 52 struct usb_gadget *, 53 struct usb_request *); 54 55 static inline struct f_dfu *func_to_dfu(struct usb_function *f) 56 { 57 return container_of(f, struct f_dfu, usb_function); 58 } 59 60 static const struct dfu_function_descriptor dfu_func = { 61 .bLength = sizeof dfu_func, 62 .bDescriptorType = DFU_DT_FUNC, 63 .bmAttributes = DFU_BIT_WILL_DETACH | 64 DFU_BIT_MANIFESTATION_TOLERANT | 65 DFU_BIT_CAN_UPLOAD | 66 DFU_BIT_CAN_DNLOAD, 67 .wDetachTimeOut = 0, 68 .wTransferSize = DFU_USB_BUFSIZ, 69 .bcdDFUVersion = __constant_cpu_to_le16(0x0110), 70 }; 71 72 static struct usb_interface_descriptor dfu_intf_runtime = { 73 .bLength = sizeof dfu_intf_runtime, 74 .bDescriptorType = USB_DT_INTERFACE, 75 .bNumEndpoints = 0, 76 .bInterfaceClass = USB_CLASS_APP_SPEC, 77 .bInterfaceSubClass = 1, 78 .bInterfaceProtocol = 1, 79 /* .iInterface = DYNAMIC */ 80 }; 81 82 static struct usb_descriptor_header *dfu_runtime_descs[] = { 83 (struct usb_descriptor_header *) &dfu_intf_runtime, 84 NULL, 85 }; 86 87 static const struct usb_qualifier_descriptor dev_qualifier = { 88 .bLength = sizeof dev_qualifier, 89 .bDescriptorType = USB_DT_DEVICE_QUALIFIER, 90 .bcdUSB = __constant_cpu_to_le16(0x0200), 91 .bDeviceClass = USB_CLASS_VENDOR_SPEC, 92 .bNumConfigurations = 1, 93 }; 94 95 static const char dfu_name[] = "Device Firmware Upgrade"; 96 97 /* 98 * static strings, in UTF-8 99 * 100 * dfu_generic configuration 101 */ 102 static struct usb_string strings_dfu_generic[] = { 103 [0].s = dfu_name, 104 { } /* end of list */ 105 }; 106 107 static struct usb_gadget_strings stringtab_dfu_generic = { 108 .language = 0x0409, /* en-us */ 109 .strings = strings_dfu_generic, 110 }; 111 112 static struct usb_gadget_strings *dfu_generic_strings[] = { 113 &stringtab_dfu_generic, 114 NULL, 115 }; 116 117 /* 118 * usb_function specific 119 */ 120 static struct usb_gadget_strings stringtab_dfu = { 121 .language = 0x0409, /* en-us */ 122 /* 123 * .strings 124 * 125 * assigned during initialization, 126 * depends on number of flash entities 127 * 128 */ 129 }; 130 131 static struct usb_gadget_strings *dfu_strings[] = { 132 &stringtab_dfu, 133 NULL, 134 }; 135 136 /*-------------------------------------------------------------------------*/ 137 138 static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) 139 { 140 struct f_dfu *f_dfu = req->context; 141 142 dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, 143 req->length, f_dfu->blk_seq_num); 144 145 if (req->length == 0) 146 puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n"); 147 } 148 149 static void handle_getstatus(struct usb_request *req) 150 { 151 struct dfu_status *dstat = (struct dfu_status *)req->buf; 152 struct f_dfu *f_dfu = req->context; 153 154 switch (f_dfu->dfu_state) { 155 case DFU_STATE_dfuDNLOAD_SYNC: 156 case DFU_STATE_dfuDNBUSY: 157 f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; 158 break; 159 case DFU_STATE_dfuMANIFEST_SYNC: 160 break; 161 default: 162 break; 163 } 164 165 /* send status response */ 166 dstat->bStatus = f_dfu->dfu_status; 167 dstat->bState = f_dfu->dfu_state; 168 dstat->iString = 0; 169 } 170 171 static void handle_getstate(struct usb_request *req) 172 { 173 struct f_dfu *f_dfu = req->context; 174 175 ((u8 *)req->buf)[0] = f_dfu->dfu_state; 176 req->actual = sizeof(u8); 177 } 178 179 static inline void to_dfu_mode(struct f_dfu *f_dfu) 180 { 181 f_dfu->usb_function.strings = dfu_strings; 182 f_dfu->usb_function.hs_descriptors = f_dfu->function; 183 } 184 185 static inline void to_runtime_mode(struct f_dfu *f_dfu) 186 { 187 f_dfu->usb_function.strings = NULL; 188 f_dfu->usb_function.hs_descriptors = dfu_runtime_descs; 189 } 190 191 static int handle_upload(struct usb_request *req, u16 len) 192 { 193 struct f_dfu *f_dfu = req->context; 194 195 return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf, 196 req->length, f_dfu->blk_seq_num); 197 } 198 199 static int handle_dnload(struct usb_gadget *gadget, u16 len) 200 { 201 struct usb_composite_dev *cdev = get_gadget_data(gadget); 202 struct usb_request *req = cdev->req; 203 struct f_dfu *f_dfu = req->context; 204 205 if (len == 0) 206 f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC; 207 208 req->complete = dnload_request_complete; 209 210 return len; 211 } 212 213 /*-------------------------------------------------------------------------*/ 214 /* DFU state machine */ 215 static int state_app_idle(struct f_dfu *f_dfu, 216 const struct usb_ctrlrequest *ctrl, 217 struct usb_gadget *gadget, 218 struct usb_request *req) 219 { 220 int value = 0; 221 222 switch (ctrl->bRequest) { 223 case USB_REQ_DFU_GETSTATUS: 224 handle_getstatus(req); 225 value = RET_STAT_LEN; 226 break; 227 case USB_REQ_DFU_GETSTATE: 228 handle_getstate(req); 229 break; 230 case USB_REQ_DFU_DETACH: 231 f_dfu->dfu_state = DFU_STATE_appDETACH; 232 to_dfu_mode(f_dfu); 233 f_dfu->dfu_state = DFU_STATE_dfuIDLE; 234 value = RET_ZLP; 235 break; 236 default: 237 value = RET_STALL; 238 break; 239 } 240 241 return value; 242 } 243 244 static int state_app_detach(struct f_dfu *f_dfu, 245 const struct usb_ctrlrequest *ctrl, 246 struct usb_gadget *gadget, 247 struct usb_request *req) 248 { 249 int value = 0; 250 251 switch (ctrl->bRequest) { 252 case USB_REQ_DFU_GETSTATUS: 253 handle_getstatus(req); 254 value = RET_STAT_LEN; 255 break; 256 case USB_REQ_DFU_GETSTATE: 257 handle_getstate(req); 258 break; 259 default: 260 f_dfu->dfu_state = DFU_STATE_appIDLE; 261 value = RET_STALL; 262 break; 263 } 264 265 return value; 266 } 267 268 static int state_dfu_idle(struct f_dfu *f_dfu, 269 const struct usb_ctrlrequest *ctrl, 270 struct usb_gadget *gadget, 271 struct usb_request *req) 272 { 273 u16 w_value = le16_to_cpu(ctrl->wValue); 274 u16 len = le16_to_cpu(ctrl->wLength); 275 int value = 0; 276 277 switch (ctrl->bRequest) { 278 case USB_REQ_DFU_DNLOAD: 279 if (len == 0) { 280 f_dfu->dfu_state = DFU_STATE_dfuERROR; 281 value = RET_STALL; 282 break; 283 } 284 f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; 285 f_dfu->blk_seq_num = w_value; 286 value = handle_dnload(gadget, len); 287 break; 288 case USB_REQ_DFU_UPLOAD: 289 f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; 290 f_dfu->blk_seq_num = 0; 291 value = handle_upload(req, len); 292 break; 293 case USB_REQ_DFU_ABORT: 294 /* no zlp? */ 295 value = RET_ZLP; 296 break; 297 case USB_REQ_DFU_GETSTATUS: 298 handle_getstatus(req); 299 value = RET_STAT_LEN; 300 break; 301 case USB_REQ_DFU_GETSTATE: 302 handle_getstate(req); 303 break; 304 case USB_REQ_DFU_DETACH: 305 /* 306 * Proprietary extension: 'detach' from idle mode and 307 * get back to runtime mode in case of USB Reset. As 308 * much as I dislike this, we just can't use every USB 309 * bus reset to switch back to runtime mode, since at 310 * least the Linux USB stack likes to send a number of 311 * resets in a row :( 312 */ 313 f_dfu->dfu_state = 314 DFU_STATE_dfuMANIFEST_WAIT_RST; 315 to_runtime_mode(f_dfu); 316 f_dfu->dfu_state = DFU_STATE_appIDLE; 317 break; 318 default: 319 f_dfu->dfu_state = DFU_STATE_dfuERROR; 320 value = RET_STALL; 321 break; 322 } 323 324 return value; 325 } 326 327 static int state_dfu_dnload_sync(struct f_dfu *f_dfu, 328 const struct usb_ctrlrequest *ctrl, 329 struct usb_gadget *gadget, 330 struct usb_request *req) 331 { 332 int value = 0; 333 334 switch (ctrl->bRequest) { 335 case USB_REQ_DFU_GETSTATUS: 336 handle_getstatus(req); 337 value = RET_STAT_LEN; 338 break; 339 case USB_REQ_DFU_GETSTATE: 340 handle_getstate(req); 341 break; 342 default: 343 f_dfu->dfu_state = DFU_STATE_dfuERROR; 344 value = RET_STALL; 345 break; 346 } 347 348 return value; 349 } 350 351 static int state_dfu_dnbusy(struct f_dfu *f_dfu, 352 const struct usb_ctrlrequest *ctrl, 353 struct usb_gadget *gadget, 354 struct usb_request *req) 355 { 356 int value = 0; 357 358 switch (ctrl->bRequest) { 359 case USB_REQ_DFU_GETSTATUS: 360 handle_getstatus(req); 361 value = RET_STAT_LEN; 362 break; 363 default: 364 f_dfu->dfu_state = DFU_STATE_dfuERROR; 365 value = RET_STALL; 366 break; 367 } 368 369 return value; 370 } 371 372 static int state_dfu_dnload_idle(struct f_dfu *f_dfu, 373 const struct usb_ctrlrequest *ctrl, 374 struct usb_gadget *gadget, 375 struct usb_request *req) 376 { 377 u16 w_value = le16_to_cpu(ctrl->wValue); 378 u16 len = le16_to_cpu(ctrl->wLength); 379 int value = 0; 380 381 switch (ctrl->bRequest) { 382 case USB_REQ_DFU_DNLOAD: 383 f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; 384 f_dfu->blk_seq_num = w_value; 385 value = handle_dnload(gadget, len); 386 break; 387 case USB_REQ_DFU_ABORT: 388 f_dfu->dfu_state = DFU_STATE_dfuIDLE; 389 value = RET_ZLP; 390 break; 391 case USB_REQ_DFU_GETSTATUS: 392 handle_getstatus(req); 393 value = RET_STAT_LEN; 394 break; 395 case USB_REQ_DFU_GETSTATE: 396 handle_getstate(req); 397 break; 398 default: 399 f_dfu->dfu_state = DFU_STATE_dfuERROR; 400 value = RET_STALL; 401 break; 402 } 403 404 return value; 405 } 406 407 static int state_dfu_manifest_sync(struct f_dfu *f_dfu, 408 const struct usb_ctrlrequest *ctrl, 409 struct usb_gadget *gadget, 410 struct usb_request *req) 411 { 412 int value = 0; 413 414 switch (ctrl->bRequest) { 415 case USB_REQ_DFU_GETSTATUS: 416 /* We're MainfestationTolerant */ 417 f_dfu->dfu_state = DFU_STATE_dfuIDLE; 418 handle_getstatus(req); 419 f_dfu->blk_seq_num = 0; 420 value = RET_STAT_LEN; 421 break; 422 case USB_REQ_DFU_GETSTATE: 423 handle_getstate(req); 424 break; 425 default: 426 f_dfu->dfu_state = DFU_STATE_dfuERROR; 427 value = RET_STALL; 428 break; 429 } 430 431 return value; 432 } 433 434 static int state_dfu_upload_idle(struct f_dfu *f_dfu, 435 const struct usb_ctrlrequest *ctrl, 436 struct usb_gadget *gadget, 437 struct usb_request *req) 438 { 439 u16 w_value = le16_to_cpu(ctrl->wValue); 440 u16 len = le16_to_cpu(ctrl->wLength); 441 int value = 0; 442 443 switch (ctrl->bRequest) { 444 case USB_REQ_DFU_UPLOAD: 445 /* state transition if less data then requested */ 446 f_dfu->blk_seq_num = w_value; 447 value = handle_upload(req, len); 448 if (value >= 0 && value < len) 449 f_dfu->dfu_state = DFU_STATE_dfuIDLE; 450 break; 451 case USB_REQ_DFU_ABORT: 452 f_dfu->dfu_state = DFU_STATE_dfuIDLE; 453 /* no zlp? */ 454 value = RET_ZLP; 455 break; 456 case USB_REQ_DFU_GETSTATUS: 457 handle_getstatus(req); 458 value = RET_STAT_LEN; 459 break; 460 case USB_REQ_DFU_GETSTATE: 461 handle_getstate(req); 462 break; 463 default: 464 f_dfu->dfu_state = DFU_STATE_dfuERROR; 465 value = RET_STALL; 466 break; 467 } 468 469 return value; 470 } 471 472 static int state_dfu_error(struct f_dfu *f_dfu, 473 const struct usb_ctrlrequest *ctrl, 474 struct usb_gadget *gadget, 475 struct usb_request *req) 476 { 477 int value = 0; 478 479 switch (ctrl->bRequest) { 480 case USB_REQ_DFU_GETSTATUS: 481 handle_getstatus(req); 482 value = RET_STAT_LEN; 483 break; 484 case USB_REQ_DFU_GETSTATE: 485 handle_getstate(req); 486 break; 487 case USB_REQ_DFU_CLRSTATUS: 488 f_dfu->dfu_state = DFU_STATE_dfuIDLE; 489 f_dfu->dfu_status = DFU_STATUS_OK; 490 /* no zlp? */ 491 value = RET_ZLP; 492 break; 493 default: 494 f_dfu->dfu_state = DFU_STATE_dfuERROR; 495 value = RET_STALL; 496 break; 497 } 498 499 return value; 500 } 501 502 static dfu_state_fn dfu_state[] = { 503 state_app_idle, /* DFU_STATE_appIDLE */ 504 state_app_detach, /* DFU_STATE_appDETACH */ 505 state_dfu_idle, /* DFU_STATE_dfuIDLE */ 506 state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */ 507 state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */ 508 state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */ 509 state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */ 510 NULL, /* DFU_STATE_dfuMANIFEST */ 511 NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */ 512 state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */ 513 state_dfu_error /* DFU_STATE_dfuERROR */ 514 }; 515 516 static int 517 dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) 518 { 519 struct usb_gadget *gadget = f->config->cdev->gadget; 520 struct usb_request *req = f->config->cdev->req; 521 struct f_dfu *f_dfu = f->config->cdev->req->context; 522 u16 len = le16_to_cpu(ctrl->wLength); 523 u16 w_value = le16_to_cpu(ctrl->wValue); 524 int value = 0; 525 u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; 526 527 debug("w_value: 0x%x len: 0x%x\n", w_value, len); 528 debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", 529 req_type, ctrl->bRequest, f_dfu->dfu_state); 530 531 if (req_type == USB_TYPE_STANDARD) { 532 if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR && 533 (w_value >> 8) == DFU_DT_FUNC) { 534 value = min(len, (u16) sizeof(dfu_func)); 535 memcpy(req->buf, &dfu_func, value); 536 } 537 } else /* DFU specific request */ 538 value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); 539 540 if (value >= 0) { 541 req->length = value; 542 req->zero = value < len; 543 value = usb_ep_queue(gadget->ep0, req, 0); 544 if (value < 0) { 545 debug("ep_queue --> %d\n", value); 546 req->status = 0; 547 } 548 } 549 550 return value; 551 } 552 553 /*-------------------------------------------------------------------------*/ 554 555 static int 556 dfu_prepare_strings(struct f_dfu *f_dfu, int n) 557 { 558 struct dfu_entity *de = NULL; 559 int i = 0; 560 561 f_dfu->strings = calloc(sizeof(struct usb_string), n + 1); 562 if (!f_dfu->strings) 563 goto enomem; 564 565 for (i = 0; i < n; ++i) { 566 de = dfu_get_entity(i); 567 f_dfu->strings[i].s = de->name; 568 } 569 570 f_dfu->strings[i].id = 0; 571 f_dfu->strings[i].s = NULL; 572 573 return 0; 574 575 enomem: 576 while (i) 577 f_dfu->strings[--i].s = NULL; 578 579 free(f_dfu->strings); 580 581 return -ENOMEM; 582 } 583 584 static int dfu_prepare_function(struct f_dfu *f_dfu, int n) 585 { 586 struct usb_interface_descriptor *d; 587 int i = 0; 588 589 f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n); 590 if (!f_dfu->function) 591 goto enomem; 592 593 for (i = 0; i < n; ++i) { 594 d = calloc(sizeof(*d), 1); 595 if (!d) 596 goto enomem; 597 598 d->bLength = sizeof(*d); 599 d->bDescriptorType = USB_DT_INTERFACE; 600 d->bAlternateSetting = i; 601 d->bNumEndpoints = 0; 602 d->bInterfaceClass = USB_CLASS_APP_SPEC; 603 d->bInterfaceSubClass = 1; 604 d->bInterfaceProtocol = 2; 605 606 f_dfu->function[i] = (struct usb_descriptor_header *)d; 607 } 608 f_dfu->function[i] = NULL; 609 610 return 0; 611 612 enomem: 613 while (i) { 614 free(f_dfu->function[--i]); 615 f_dfu->function[i] = NULL; 616 } 617 free(f_dfu->function); 618 619 return -ENOMEM; 620 } 621 622 static int dfu_bind(struct usb_configuration *c, struct usb_function *f) 623 { 624 struct usb_composite_dev *cdev = c->cdev; 625 struct f_dfu *f_dfu = func_to_dfu(f); 626 int alt_num = dfu_get_alt_number(); 627 int rv, id, i; 628 629 id = usb_interface_id(c, f); 630 if (id < 0) 631 return id; 632 dfu_intf_runtime.bInterfaceNumber = id; 633 634 f_dfu->dfu_state = DFU_STATE_appIDLE; 635 f_dfu->dfu_status = DFU_STATUS_OK; 636 637 rv = dfu_prepare_function(f_dfu, alt_num); 638 if (rv) 639 goto error; 640 641 rv = dfu_prepare_strings(f_dfu, alt_num); 642 if (rv) 643 goto error; 644 for (i = 0; i < alt_num; i++) { 645 id = usb_string_id(cdev); 646 if (id < 0) 647 return id; 648 f_dfu->strings[i].id = id; 649 ((struct usb_interface_descriptor *)f_dfu->function[i]) 650 ->iInterface = id; 651 } 652 653 stringtab_dfu.strings = f_dfu->strings; 654 655 cdev->req->context = f_dfu; 656 657 error: 658 return rv; 659 } 660 661 static 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 687 static 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? */ 699 static 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 710 static 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 734 int 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 } 749