diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/dummy_hcd.c | 254 |
1 files changed, 162 insertions, 92 deletions
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index dc0e3233b0e9..2d6d22951326 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c | |||
@@ -163,12 +163,16 @@ struct dummy { | |||
163 | struct dummy_request fifo_req; | 163 | struct dummy_request fifo_req; |
164 | u8 fifo_buf [FIFO_SIZE]; | 164 | u8 fifo_buf [FIFO_SIZE]; |
165 | u16 devstatus; | 165 | u16 devstatus; |
166 | unsigned pullup:1; | ||
167 | unsigned active:1; | ||
168 | unsigned old_active:1; | ||
166 | 169 | ||
167 | /* | 170 | /* |
168 | * MASTER/HOST side support | 171 | * MASTER/HOST side support |
169 | */ | 172 | */ |
170 | struct timer_list timer; | 173 | struct timer_list timer; |
171 | u32 port_status; | 174 | u32 port_status; |
175 | u32 old_status; | ||
172 | unsigned resuming:1; | 176 | unsigned resuming:1; |
173 | unsigned long re_timeout; | 177 | unsigned long re_timeout; |
174 | 178 | ||
@@ -215,6 +219,98 @@ static struct dummy *the_controller; | |||
215 | 219 | ||
216 | /*-------------------------------------------------------------------------*/ | 220 | /*-------------------------------------------------------------------------*/ |
217 | 221 | ||
222 | /* SLAVE/GADGET SIDE UTILITY ROUTINES */ | ||
223 | |||
224 | /* called with spinlock held */ | ||
225 | static void nuke (struct dummy *dum, struct dummy_ep *ep) | ||
226 | { | ||
227 | while (!list_empty (&ep->queue)) { | ||
228 | struct dummy_request *req; | ||
229 | |||
230 | req = list_entry (ep->queue.next, struct dummy_request, queue); | ||
231 | list_del_init (&req->queue); | ||
232 | req->req.status = -ESHUTDOWN; | ||
233 | |||
234 | spin_unlock (&dum->lock); | ||
235 | req->req.complete (&ep->ep, &req->req); | ||
236 | spin_lock (&dum->lock); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | /* caller must hold lock */ | ||
241 | static void | ||
242 | stop_activity (struct dummy *dum) | ||
243 | { | ||
244 | struct dummy_ep *ep; | ||
245 | |||
246 | /* prevent any more requests */ | ||
247 | dum->address = 0; | ||
248 | |||
249 | /* The timer is left running so that outstanding URBs can fail */ | ||
250 | |||
251 | /* nuke any pending requests first, so driver i/o is quiesced */ | ||
252 | list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list) | ||
253 | nuke (dum, ep); | ||
254 | |||
255 | /* driver now does any non-usb quiescing necessary */ | ||
256 | } | ||
257 | |||
258 | /* caller must hold lock */ | ||
259 | static void | ||
260 | set_link_state (struct dummy *dum) | ||
261 | { | ||
262 | dum->active = 0; | ||
263 | if ((dum->port_status & USB_PORT_STAT_POWER) == 0) | ||
264 | dum->port_status = 0; | ||
265 | else if (!dum->pullup) { | ||
266 | dum->port_status &= ~(USB_PORT_STAT_CONNECTION | | ||
267 | USB_PORT_STAT_ENABLE | | ||
268 | USB_PORT_STAT_LOW_SPEED | | ||
269 | USB_PORT_STAT_HIGH_SPEED | | ||
270 | USB_PORT_STAT_SUSPEND); | ||
271 | if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0) | ||
272 | dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); | ||
273 | } else { | ||
274 | dum->port_status |= USB_PORT_STAT_CONNECTION; | ||
275 | if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0) | ||
276 | dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); | ||
277 | if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0) | ||
278 | dum->port_status &= ~USB_PORT_STAT_SUSPEND; | ||
279 | else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0) | ||
280 | dum->active = 1; | ||
281 | } | ||
282 | |||
283 | if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active) | ||
284 | dum->resuming = 0; | ||
285 | |||
286 | if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 || | ||
287 | (dum->port_status & USB_PORT_STAT_RESET) != 0) { | ||
288 | if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 && | ||
289 | (dum->old_status & USB_PORT_STAT_RESET) == 0 && | ||
290 | dum->driver) { | ||
291 | stop_activity (dum); | ||
292 | spin_unlock (&dum->lock); | ||
293 | dum->driver->disconnect (&dum->gadget); | ||
294 | spin_lock (&dum->lock); | ||
295 | } | ||
296 | } else if (dum->active != dum->old_active) { | ||
297 | if (dum->old_active && dum->driver->suspend) { | ||
298 | spin_unlock (&dum->lock); | ||
299 | dum->driver->suspend (&dum->gadget); | ||
300 | spin_lock (&dum->lock); | ||
301 | } else if (!dum->old_active && dum->driver->resume) { | ||
302 | spin_unlock (&dum->lock); | ||
303 | dum->driver->resume (&dum->gadget); | ||
304 | spin_lock (&dum->lock); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | dum->old_status = dum->port_status; | ||
309 | dum->old_active = dum->active; | ||
310 | } | ||
311 | |||
312 | /*-------------------------------------------------------------------------*/ | ||
313 | |||
218 | /* SLAVE/GADGET SIDE DRIVER | 314 | /* SLAVE/GADGET SIDE DRIVER |
219 | * | 315 | * |
220 | * This only tracks gadget state. All the work is done when the host | 316 | * This only tracks gadget state. All the work is done when the host |
@@ -339,22 +435,6 @@ done: | |||
339 | return retval; | 435 | return retval; |
340 | } | 436 | } |
341 | 437 | ||
342 | /* called with spinlock held */ | ||
343 | static void nuke (struct dummy *dum, struct dummy_ep *ep) | ||
344 | { | ||
345 | while (!list_empty (&ep->queue)) { | ||
346 | struct dummy_request *req; | ||
347 | |||
348 | req = list_entry (ep->queue.next, struct dummy_request, queue); | ||
349 | list_del_init (&req->queue); | ||
350 | req->req.status = -ESHUTDOWN; | ||
351 | |||
352 | spin_unlock (&dum->lock); | ||
353 | req->req.complete (&ep->ep, &req->req); | ||
354 | spin_lock (&dum->lock); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | static int dummy_disable (struct usb_ep *_ep) | 438 | static int dummy_disable (struct usb_ep *_ep) |
359 | { | 439 | { |
360 | struct dummy_ep *ep; | 440 | struct dummy_ep *ep; |
@@ -603,7 +683,7 @@ static int dummy_wakeup (struct usb_gadget *_gadget) | |||
603 | 683 | ||
604 | /* hub notices our request, issues downstream resume, etc */ | 684 | /* hub notices our request, issues downstream resume, etc */ |
605 | dum->resuming = 1; | 685 | dum->resuming = 1; |
606 | dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); | 686 | dum->re_timeout = jiffies + msecs_to_jiffies(20); |
607 | return 0; | 687 | return 0; |
608 | } | 688 | } |
609 | 689 | ||
@@ -619,10 +699,24 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) | |||
619 | return 0; | 699 | return 0; |
620 | } | 700 | } |
621 | 701 | ||
702 | static int dummy_pullup (struct usb_gadget *_gadget, int value) | ||
703 | { | ||
704 | struct dummy *dum; | ||
705 | unsigned long flags; | ||
706 | |||
707 | dum = gadget_to_dummy (_gadget); | ||
708 | spin_lock_irqsave (&dum->lock, flags); | ||
709 | dum->pullup = (value != 0); | ||
710 | set_link_state (dum); | ||
711 | spin_unlock_irqrestore (&dum->lock, flags); | ||
712 | return 0; | ||
713 | } | ||
714 | |||
622 | static const struct usb_gadget_ops dummy_ops = { | 715 | static const struct usb_gadget_ops dummy_ops = { |
623 | .get_frame = dummy_g_get_frame, | 716 | .get_frame = dummy_g_get_frame, |
624 | .wakeup = dummy_wakeup, | 717 | .wakeup = dummy_wakeup, |
625 | .set_selfpowered = dummy_set_selfpowered, | 718 | .set_selfpowered = dummy_set_selfpowered, |
719 | .pullup = dummy_pullup, | ||
626 | }; | 720 | }; |
627 | 721 | ||
628 | /*-------------------------------------------------------------------------*/ | 722 | /*-------------------------------------------------------------------------*/ |
@@ -675,7 +769,6 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) | |||
675 | */ | 769 | */ |
676 | 770 | ||
677 | dum->devstatus = 0; | 771 | dum->devstatus = 0; |
678 | dum->resuming = 0; | ||
679 | 772 | ||
680 | INIT_LIST_HEAD (&dum->gadget.ep_list); | 773 | INIT_LIST_HEAD (&dum->gadget.ep_list); |
681 | for (i = 0; i < DUMMY_ENDPOINTS; i++) { | 774 | for (i = 0; i < DUMMY_ENDPOINTS; i++) { |
@@ -714,35 +807,14 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) | |||
714 | device_bind_driver (&dum->gadget.dev); | 807 | device_bind_driver (&dum->gadget.dev); |
715 | 808 | ||
716 | /* khubd will enumerate this in a while */ | 809 | /* khubd will enumerate this in a while */ |
717 | dum->port_status |= USB_PORT_STAT_CONNECTION | 810 | spin_lock_irq (&dum->lock); |
718 | | (USB_PORT_STAT_C_CONNECTION << 16); | 811 | dum->pullup = 1; |
812 | set_link_state (dum); | ||
813 | spin_unlock_irq (&dum->lock); | ||
719 | return 0; | 814 | return 0; |
720 | } | 815 | } |
721 | EXPORT_SYMBOL (usb_gadget_register_driver); | 816 | EXPORT_SYMBOL (usb_gadget_register_driver); |
722 | 817 | ||
723 | /* caller must hold lock */ | ||
724 | static void | ||
725 | stop_activity (struct dummy *dum, struct usb_gadget_driver *driver) | ||
726 | { | ||
727 | struct dummy_ep *ep; | ||
728 | |||
729 | /* prevent any more requests */ | ||
730 | dum->address = 0; | ||
731 | |||
732 | /* The timer is left running so that outstanding URBs can fail */ | ||
733 | |||
734 | /* nuke any pending requests first, so driver i/o is quiesced */ | ||
735 | list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list) | ||
736 | nuke (dum, ep); | ||
737 | |||
738 | /* driver now does any non-usb quiescing necessary */ | ||
739 | if (driver) { | ||
740 | spin_unlock (&dum->lock); | ||
741 | driver->disconnect (&dum->gadget); | ||
742 | spin_lock (&dum->lock); | ||
743 | } | ||
744 | } | ||
745 | |||
746 | int | 818 | int |
747 | usb_gadget_unregister_driver (struct usb_gadget_driver *driver) | 819 | usb_gadget_unregister_driver (struct usb_gadget_driver *driver) |
748 | { | 820 | { |
@@ -758,10 +830,8 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver) | |||
758 | driver->driver.name); | 830 | driver->driver.name); |
759 | 831 | ||
760 | spin_lock_irqsave (&dum->lock, flags); | 832 | spin_lock_irqsave (&dum->lock, flags); |
761 | stop_activity (dum, driver); | 833 | dum->pullup = 0; |
762 | dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | | 834 | set_link_state (dum); |
763 | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); | ||
764 | dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); | ||
765 | spin_unlock_irqrestore (&dum->lock, flags); | 835 | spin_unlock_irqrestore (&dum->lock, flags); |
766 | 836 | ||
767 | driver->unbind (&dum->gadget); | 837 | driver->unbind (&dum->gadget); |
@@ -770,6 +840,11 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver) | |||
770 | device_release_driver (&dum->gadget.dev); | 840 | device_release_driver (&dum->gadget.dev); |
771 | driver_unregister (&driver->driver); | 841 | driver_unregister (&driver->driver); |
772 | 842 | ||
843 | spin_lock_irqsave (&dum->lock, flags); | ||
844 | dum->pullup = 0; | ||
845 | set_link_state (dum); | ||
846 | spin_unlock_irqrestore (&dum->lock, flags); | ||
847 | |||
773 | return 0; | 848 | return 0; |
774 | } | 849 | } |
775 | EXPORT_SYMBOL (usb_gadget_unregister_driver); | 850 | EXPORT_SYMBOL (usb_gadget_unregister_driver); |
@@ -1432,6 +1507,13 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf) | |||
1432 | dum = hcd_to_dummy (hcd); | 1507 | dum = hcd_to_dummy (hcd); |
1433 | 1508 | ||
1434 | spin_lock_irqsave (&dum->lock, flags); | 1509 | spin_lock_irqsave (&dum->lock, flags); |
1510 | |||
1511 | if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { | ||
1512 | dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); | ||
1513 | dum->port_status &= ~USB_PORT_STAT_SUSPEND; | ||
1514 | set_link_state (dum); | ||
1515 | } | ||
1516 | |||
1435 | if (!(dum->port_status & PORT_C_MASK)) | 1517 | if (!(dum->port_status & PORT_C_MASK)) |
1436 | retval = 0; | 1518 | retval = 0; |
1437 | else { | 1519 | else { |
@@ -1480,16 +1562,16 @@ static int dummy_hub_control ( | |||
1480 | /* 20msec resume signaling */ | 1562 | /* 20msec resume signaling */ |
1481 | dum->resuming = 1; | 1563 | dum->resuming = 1; |
1482 | dum->re_timeout = jiffies + | 1564 | dum->re_timeout = jiffies + |
1483 | msecs_to_jiffies(20); | 1565 | msecs_to_jiffies(20); |
1484 | } | 1566 | } |
1485 | break; | 1567 | break; |
1486 | case USB_PORT_FEAT_POWER: | 1568 | case USB_PORT_FEAT_POWER: |
1487 | dum->port_status = 0; | 1569 | if (dum->port_status & USB_PORT_STAT_POWER) |
1488 | dum->resuming = 0; | 1570 | dev_dbg (dummy_dev(dum), "power-off\n"); |
1489 | stop_activity(dum, dum->driver); | 1571 | /* FALLS THROUGH */ |
1490 | break; | ||
1491 | default: | 1572 | default: |
1492 | dum->port_status &= ~(1 << wValue); | 1573 | dum->port_status &= ~(1 << wValue); |
1574 | set_link_state (dum); | ||
1493 | } | 1575 | } |
1494 | break; | 1576 | break; |
1495 | case GetHubDescriptor: | 1577 | case GetHubDescriptor: |
@@ -1505,23 +1587,16 @@ static int dummy_hub_control ( | |||
1505 | /* whoever resets or resumes must GetPortStatus to | 1587 | /* whoever resets or resumes must GetPortStatus to |
1506 | * complete it!! | 1588 | * complete it!! |
1507 | */ | 1589 | */ |
1508 | if (dum->resuming && time_after (jiffies, dum->re_timeout)) { | 1590 | if (dum->resuming && |
1591 | time_after_eq (jiffies, dum->re_timeout)) { | ||
1509 | dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); | 1592 | dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); |
1510 | dum->port_status &= ~USB_PORT_STAT_SUSPEND; | 1593 | dum->port_status &= ~USB_PORT_STAT_SUSPEND; |
1511 | dum->resuming = 0; | ||
1512 | dum->re_timeout = 0; | ||
1513 | if (dum->driver && dum->driver->resume) { | ||
1514 | spin_unlock (&dum->lock); | ||
1515 | dum->driver->resume (&dum->gadget); | ||
1516 | spin_lock (&dum->lock); | ||
1517 | } | ||
1518 | } | 1594 | } |
1519 | if ((dum->port_status & USB_PORT_STAT_RESET) != 0 | 1595 | if ((dum->port_status & USB_PORT_STAT_RESET) != 0 && |
1520 | && time_after (jiffies, dum->re_timeout)) { | 1596 | time_after_eq (jiffies, dum->re_timeout)) { |
1521 | dum->port_status |= (USB_PORT_STAT_C_RESET << 16); | 1597 | dum->port_status |= (USB_PORT_STAT_C_RESET << 16); |
1522 | dum->port_status &= ~USB_PORT_STAT_RESET; | 1598 | dum->port_status &= ~USB_PORT_STAT_RESET; |
1523 | dum->re_timeout = 0; | 1599 | if (dum->pullup) { |
1524 | if (dum->driver) { | ||
1525 | dum->port_status |= USB_PORT_STAT_ENABLE; | 1600 | dum->port_status |= USB_PORT_STAT_ENABLE; |
1526 | /* give it the best speed we agree on */ | 1601 | /* give it the best speed we agree on */ |
1527 | dum->gadget.speed = dum->driver->speed; | 1602 | dum->gadget.speed = dum->driver->speed; |
@@ -1542,6 +1617,7 @@ static int dummy_hub_control ( | |||
1542 | } | 1617 | } |
1543 | } | 1618 | } |
1544 | } | 1619 | } |
1620 | set_link_state (dum); | ||
1545 | ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status); | 1621 | ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status); |
1546 | ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); | 1622 | ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16); |
1547 | break; | 1623 | break; |
@@ -1551,42 +1627,36 @@ static int dummy_hub_control ( | |||
1551 | case SetPortFeature: | 1627 | case SetPortFeature: |
1552 | switch (wValue) { | 1628 | switch (wValue) { |
1553 | case USB_PORT_FEAT_SUSPEND: | 1629 | case USB_PORT_FEAT_SUSPEND: |
1554 | if ((dum->port_status & USB_PORT_STAT_SUSPEND) | 1630 | if (dum->active) { |
1555 | == 0) { | ||
1556 | dum->port_status |= USB_PORT_STAT_SUSPEND; | 1631 | dum->port_status |= USB_PORT_STAT_SUSPEND; |
1557 | if (dum->driver && dum->driver->suspend) { | 1632 | |
1558 | spin_unlock (&dum->lock); | 1633 | /* HNP would happen here; for now we |
1559 | dum->driver->suspend (&dum->gadget); | 1634 | * assume b_bus_req is always true. |
1560 | spin_lock (&dum->lock); | 1635 | */ |
1561 | /* HNP would happen here; for now we | 1636 | set_link_state (dum); |
1562 | * assume b_bus_req is always true. | 1637 | if (((1 << USB_DEVICE_B_HNP_ENABLE) |
1563 | */ | 1638 | & dum->devstatus) != 0) |
1564 | if (((1 << USB_DEVICE_B_HNP_ENABLE) | 1639 | dev_dbg (dummy_dev(dum), |
1565 | & dum->devstatus) != 0) | ||
1566 | dev_dbg (dummy_dev(dum), | ||
1567 | "no HNP yet!\n"); | 1640 | "no HNP yet!\n"); |
1568 | } | ||
1569 | } | 1641 | } |
1570 | break; | 1642 | break; |
1643 | case USB_PORT_FEAT_POWER: | ||
1644 | dum->port_status |= USB_PORT_STAT_POWER; | ||
1645 | set_link_state (dum); | ||
1646 | break; | ||
1571 | case USB_PORT_FEAT_RESET: | 1647 | case USB_PORT_FEAT_RESET: |
1572 | /* if it's already running, disconnect first */ | 1648 | /* if it's already enabled, disable */ |
1573 | if (dum->port_status & USB_PORT_STAT_ENABLE) { | 1649 | dum->port_status &= ~(USB_PORT_STAT_ENABLE |
1574 | dum->port_status &= ~(USB_PORT_STAT_ENABLE | 1650 | | USB_PORT_STAT_LOW_SPEED |
1575 | | USB_PORT_STAT_LOW_SPEED | 1651 | | USB_PORT_STAT_HIGH_SPEED); |
1576 | | USB_PORT_STAT_HIGH_SPEED); | ||
1577 | if (dum->driver) { | ||
1578 | dev_dbg (udc_dev(dum), | ||
1579 | "disconnect\n"); | ||
1580 | stop_activity (dum, dum->driver); | ||
1581 | } | ||
1582 | |||
1583 | /* FIXME test that code path! */ | ||
1584 | } | ||
1585 | /* 50msec reset signaling */ | 1652 | /* 50msec reset signaling */ |
1586 | dum->re_timeout = jiffies + msecs_to_jiffies(50); | 1653 | dum->re_timeout = jiffies + msecs_to_jiffies(50); |
1587 | /* FALLTHROUGH */ | 1654 | /* FALLS THROUGH */ |
1588 | default: | 1655 | default: |
1589 | dum->port_status |= (1 << wValue); | 1656 | if ((dum->port_status & USB_PORT_STAT_POWER) != 0) { |
1657 | dum->port_status |= (1 << wValue); | ||
1658 | set_link_state (dum); | ||
1659 | } | ||
1590 | } | 1660 | } |
1591 | break; | 1661 | break; |
1592 | 1662 | ||