| .. | .. |
|---|
| 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 | } |
|---|