.. | .. |
---|
50 | 50 | |
---|
51 | 51 | static void ast_vhub_dev_enable(struct ast_vhub_dev *d) |
---|
52 | 52 | { |
---|
53 | | - u32 reg, hmsk; |
---|
| 53 | + u32 reg, hmsk, i; |
---|
54 | 54 | |
---|
55 | 55 | if (d->enabled) |
---|
56 | 56 | return; |
---|
| 57 | + |
---|
| 58 | + /* Cleanup EP0 state */ |
---|
| 59 | + ast_vhub_reset_ep0(d); |
---|
57 | 60 | |
---|
58 | 61 | /* Enable device and its EP0 interrupts */ |
---|
59 | 62 | reg = VHUB_DEV_EN_ENABLE_PORT | |
---|
.. | .. |
---|
73 | 76 | /* Set EP0 DMA buffer address */ |
---|
74 | 77 | writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA); |
---|
75 | 78 | |
---|
| 79 | + /* Clear stall on all EPs */ |
---|
| 80 | + for (i = 0; i < d->max_epns; i++) { |
---|
| 81 | + struct ast_vhub_ep *ep = d->epns[i]; |
---|
| 82 | + |
---|
| 83 | + if (ep && (ep->epn.stalled || ep->epn.wedged)) { |
---|
| 84 | + ep->epn.stalled = false; |
---|
| 85 | + ep->epn.wedged = false; |
---|
| 86 | + ast_vhub_update_epn_stall(ep); |
---|
| 87 | + } |
---|
| 88 | + } |
---|
| 89 | + |
---|
| 90 | + /* Additional cleanups */ |
---|
| 91 | + d->wakeup_en = false; |
---|
76 | 92 | d->enabled = true; |
---|
77 | 93 | } |
---|
78 | 94 | |
---|
.. | .. |
---|
93 | 109 | writel(0, d->regs + AST_VHUB_DEV_EN_CTRL); |
---|
94 | 110 | d->gadget.speed = USB_SPEED_UNKNOWN; |
---|
95 | 111 | d->enabled = false; |
---|
96 | | - d->suspended = false; |
---|
97 | 112 | } |
---|
98 | 113 | |
---|
99 | 114 | static int ast_vhub_dev_feature(struct ast_vhub_dev *d, |
---|
.. | .. |
---|
122 | 137 | is_set ? "SET" : "CLEAR", ep_num, wValue); |
---|
123 | 138 | if (ep_num == 0) |
---|
124 | 139 | return std_req_complete; |
---|
125 | | - if (ep_num >= AST_VHUB_NUM_GEN_EPs || !d->epns[ep_num - 1]) |
---|
| 140 | + if (ep_num >= d->max_epns || !d->epns[ep_num - 1]) |
---|
126 | 141 | return std_req_stall; |
---|
127 | 142 | if (wValue != USB_ENDPOINT_HALT) |
---|
128 | 143 | return std_req_driver; |
---|
.. | .. |
---|
166 | 181 | |
---|
167 | 182 | DDBG(d, "GET_STATUS(ep%d)\n", ep_num); |
---|
168 | 183 | |
---|
169 | | - if (ep_num >= AST_VHUB_NUM_GEN_EPs) |
---|
| 184 | + if (ep_num >= d->max_epns) |
---|
170 | 185 | return std_req_stall; |
---|
171 | 186 | if (ep_num != 0) { |
---|
172 | 187 | ep = d->epns[ep_num - 1]; |
---|
.. | .. |
---|
201 | 216 | u16 wValue, wIndex; |
---|
202 | 217 | |
---|
203 | 218 | /* No driver, we shouldn't be enabled ... */ |
---|
204 | | - if (!d->driver || !d->enabled || d->suspended) { |
---|
| 219 | + if (!d->driver || !d->enabled) { |
---|
205 | 220 | EPDBG(ep, |
---|
206 | | - "Device is wrong state driver=%p enabled=%d" |
---|
207 | | - " suspended=%d\n", |
---|
208 | | - d->driver, d->enabled, d->suspended); |
---|
| 221 | + "Device is wrong state driver=%p enabled=%d\n", |
---|
| 222 | + d->driver, d->enabled); |
---|
209 | 223 | return std_req_stall; |
---|
210 | 224 | } |
---|
| 225 | + |
---|
| 226 | + /* |
---|
| 227 | + * Note: we used to reject/stall requests while suspended, |
---|
| 228 | + * we don't do that anymore as we seem to have cases of |
---|
| 229 | + * mass storage getting very upset. |
---|
| 230 | + */ |
---|
211 | 231 | |
---|
212 | 232 | /* First packet, grab speed */ |
---|
213 | 233 | if (d->gadget.speed == USB_SPEED_UNKNOWN) { |
---|
.. | .. |
---|
279 | 299 | { |
---|
280 | 300 | unsigned int i; |
---|
281 | 301 | |
---|
282 | | - for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) { |
---|
| 302 | + for (i = 0; i < d->max_epns; i++) { |
---|
283 | 303 | if (!d->epns[i]) |
---|
284 | 304 | continue; |
---|
285 | 305 | ast_vhub_nuke(d->epns[i], -ESHUTDOWN); |
---|
.. | .. |
---|
396 | 416 | * that will allow the generic code to use our |
---|
397 | 417 | * assigned address. |
---|
398 | 418 | */ |
---|
399 | | - for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) |
---|
| 419 | + for (i = 0; i < d->max_epns; i++) |
---|
400 | 420 | if (d->epns[i] == NULL) |
---|
401 | 421 | break; |
---|
402 | | - if (i >= AST_VHUB_NUM_GEN_EPs) |
---|
| 422 | + if (i >= d->max_epns) |
---|
403 | 423 | return NULL; |
---|
404 | 424 | addr = i + 1; |
---|
405 | 425 | |
---|
.. | .. |
---|
438 | 458 | return 0; |
---|
439 | 459 | } |
---|
440 | 460 | |
---|
441 | | -static struct usb_gadget_ops ast_vhub_udc_ops = { |
---|
| 461 | +static const struct usb_gadget_ops ast_vhub_udc_ops = { |
---|
442 | 462 | .get_frame = ast_vhub_udc_get_frame, |
---|
443 | 463 | .wakeup = ast_vhub_udc_wakeup, |
---|
444 | 464 | .pullup = ast_vhub_udc_pullup, |
---|
.. | .. |
---|
449 | 469 | |
---|
450 | 470 | void ast_vhub_dev_suspend(struct ast_vhub_dev *d) |
---|
451 | 471 | { |
---|
452 | | - d->suspended = true; |
---|
453 | | - if (d->driver) { |
---|
| 472 | + if (d->driver && d->driver->suspend) { |
---|
454 | 473 | spin_unlock(&d->vhub->lock); |
---|
455 | 474 | d->driver->suspend(&d->gadget); |
---|
456 | 475 | spin_lock(&d->vhub->lock); |
---|
.. | .. |
---|
459 | 478 | |
---|
460 | 479 | void ast_vhub_dev_resume(struct ast_vhub_dev *d) |
---|
461 | 480 | { |
---|
462 | | - d->suspended = false; |
---|
463 | | - if (d->driver) { |
---|
| 481 | + if (d->driver && d->driver->resume) { |
---|
464 | 482 | spin_unlock(&d->vhub->lock); |
---|
465 | 483 | d->driver->resume(&d->gadget); |
---|
466 | 484 | spin_lock(&d->vhub->lock); |
---|
.. | .. |
---|
469 | 487 | |
---|
470 | 488 | void ast_vhub_dev_reset(struct ast_vhub_dev *d) |
---|
471 | 489 | { |
---|
472 | | - /* |
---|
473 | | - * If speed is not set, we enable the port. If it is, |
---|
474 | | - * send reset to the gadget and reset "speed". |
---|
475 | | - * |
---|
476 | | - * Speed is an indication that we have got the first |
---|
477 | | - * setup packet to the device. |
---|
478 | | - */ |
---|
479 | | - if (d->gadget.speed == USB_SPEED_UNKNOWN && !d->enabled) { |
---|
480 | | - DDBG(d, "Reset at unknown speed of disabled device, enabling...\n"); |
---|
481 | | - ast_vhub_dev_enable(d); |
---|
482 | | - d->suspended = false; |
---|
| 490 | + /* No driver, just disable the device and return */ |
---|
| 491 | + if (!d->driver) { |
---|
| 492 | + ast_vhub_dev_disable(d); |
---|
| 493 | + return; |
---|
483 | 494 | } |
---|
484 | | - if (d->gadget.speed != USB_SPEED_UNKNOWN && d->driver) { |
---|
485 | | - unsigned int i; |
---|
486 | 495 | |
---|
487 | | - DDBG(d, "Reset at known speed of bound device, resetting...\n"); |
---|
| 496 | + /* If the port isn't enabled, just enable it */ |
---|
| 497 | + if (!d->enabled) { |
---|
| 498 | + DDBG(d, "Reset of disabled device, enabling...\n"); |
---|
| 499 | + ast_vhub_dev_enable(d); |
---|
| 500 | + } else { |
---|
| 501 | + DDBG(d, "Reset of enabled device, resetting...\n"); |
---|
488 | 502 | spin_unlock(&d->vhub->lock); |
---|
489 | | - d->driver->reset(&d->gadget); |
---|
| 503 | + usb_gadget_udc_reset(&d->gadget, d->driver); |
---|
490 | 504 | spin_lock(&d->vhub->lock); |
---|
491 | 505 | |
---|
492 | 506 | /* |
---|
493 | | - * Disable/re-enable HW, this will clear the address |
---|
| 507 | + * Disable and maybe re-enable HW, this will clear the address |
---|
494 | 508 | * and speed setting. |
---|
495 | 509 | */ |
---|
496 | 510 | ast_vhub_dev_disable(d); |
---|
497 | 511 | ast_vhub_dev_enable(d); |
---|
498 | | - |
---|
499 | | - /* Clear stall on all EPs */ |
---|
500 | | - for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) { |
---|
501 | | - struct ast_vhub_ep *ep = d->epns[i]; |
---|
502 | | - |
---|
503 | | - if (ep && ep->epn.stalled) { |
---|
504 | | - ep->epn.stalled = false; |
---|
505 | | - ast_vhub_update_epn_stall(ep); |
---|
506 | | - } |
---|
507 | | - } |
---|
508 | | - |
---|
509 | | - /* Additional cleanups */ |
---|
510 | | - d->wakeup_en = false; |
---|
511 | | - d->suspended = false; |
---|
512 | 512 | } |
---|
513 | 513 | } |
---|
514 | 514 | |
---|
.. | .. |
---|
526 | 526 | |
---|
527 | 527 | usb_del_gadget_udc(&d->gadget); |
---|
528 | 528 | device_unregister(d->port_dev); |
---|
| 529 | + kfree(d->epns); |
---|
529 | 530 | } |
---|
530 | 531 | |
---|
531 | 532 | static void ast_vhub_dev_release(struct device *dev) |
---|
.. | .. |
---|
547 | 548 | ast_vhub_init_ep0(vhub, &d->ep0, d); |
---|
548 | 549 | |
---|
549 | 550 | /* |
---|
| 551 | + * A USB device can have up to 30 endpoints besides control |
---|
| 552 | + * endpoint 0. |
---|
| 553 | + */ |
---|
| 554 | + d->max_epns = min_t(u32, vhub->max_epns, 30); |
---|
| 555 | + d->epns = kcalloc(d->max_epns, sizeof(*d->epns), GFP_KERNEL); |
---|
| 556 | + if (!d->epns) |
---|
| 557 | + return -ENOMEM; |
---|
| 558 | + |
---|
| 559 | + /* |
---|
550 | 560 | * The UDC core really needs us to have separate and uniquely |
---|
551 | 561 | * named "parent" devices for each port so we create a sub device |
---|
552 | 562 | * here for that purpose |
---|
553 | 563 | */ |
---|
554 | 564 | d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL); |
---|
555 | | - if (!d->port_dev) |
---|
556 | | - return -ENOMEM; |
---|
| 565 | + if (!d->port_dev) { |
---|
| 566 | + rc = -ENOMEM; |
---|
| 567 | + goto fail_alloc; |
---|
| 568 | + } |
---|
557 | 569 | device_initialize(d->port_dev); |
---|
558 | 570 | d->port_dev->release = ast_vhub_dev_release; |
---|
559 | 571 | d->port_dev->parent = parent; |
---|
.. | .. |
---|
584 | 596 | device_del(d->port_dev); |
---|
585 | 597 | fail_add: |
---|
586 | 598 | put_device(d->port_dev); |
---|
| 599 | + fail_alloc: |
---|
| 600 | + kfree(d->epns); |
---|
587 | 601 | |
---|
588 | 602 | return rc; |
---|
589 | 603 | } |
---|