diff options
| -rw-r--r-- | drivers/usb/gadget/dummy_hcd.c | 179 |
1 files changed, 162 insertions, 17 deletions
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index c78c64ae87af..4d692670f288 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c | |||
| @@ -65,7 +65,7 @@ | |||
| 65 | 65 | ||
| 66 | 66 | ||
| 67 | #define DRIVER_DESC "USB Host+Gadget Emulator" | 67 | #define DRIVER_DESC "USB Host+Gadget Emulator" |
| 68 | #define DRIVER_VERSION "17 Dec 2004" | 68 | #define DRIVER_VERSION "02 May 2005" |
| 69 | 69 | ||
| 70 | static const char driver_name [] = "dummy_hcd"; | 70 | static const char driver_name [] = "dummy_hcd"; |
| 71 | static const char driver_desc [] = "USB Host+Gadget Emulator"; | 71 | static const char driver_desc [] = "USB Host+Gadget Emulator"; |
| @@ -150,6 +150,13 @@ struct urbp { | |||
| 150 | struct list_head urbp_list; | 150 | struct list_head urbp_list; |
| 151 | }; | 151 | }; |
| 152 | 152 | ||
| 153 | |||
| 154 | enum dummy_rh_state { | ||
| 155 | DUMMY_RH_RESET, | ||
| 156 | DUMMY_RH_SUSPENDED, | ||
| 157 | DUMMY_RH_RUNNING | ||
| 158 | }; | ||
| 159 | |||
| 153 | struct dummy { | 160 | struct dummy { |
| 154 | spinlock_t lock; | 161 | spinlock_t lock; |
| 155 | 162 | ||
| @@ -163,6 +170,7 @@ struct dummy { | |||
| 163 | struct dummy_request fifo_req; | 170 | struct dummy_request fifo_req; |
| 164 | u8 fifo_buf [FIFO_SIZE]; | 171 | u8 fifo_buf [FIFO_SIZE]; |
| 165 | u16 devstatus; | 172 | u16 devstatus; |
| 173 | unsigned udc_suspended:1; | ||
| 166 | unsigned pullup:1; | 174 | unsigned pullup:1; |
| 167 | unsigned active:1; | 175 | unsigned active:1; |
| 168 | unsigned old_active:1; | 176 | unsigned old_active:1; |
| @@ -170,6 +178,7 @@ struct dummy { | |||
| 170 | /* | 178 | /* |
| 171 | * MASTER/HOST side support | 179 | * MASTER/HOST side support |
| 172 | */ | 180 | */ |
| 181 | enum dummy_rh_state rh_state; | ||
| 173 | struct timer_list timer; | 182 | struct timer_list timer; |
| 174 | u32 port_status; | 183 | u32 port_status; |
| 175 | u32 old_status; | 184 | u32 old_status; |
| @@ -262,7 +271,9 @@ set_link_state (struct dummy *dum) | |||
| 262 | dum->active = 0; | 271 | dum->active = 0; |
| 263 | if ((dum->port_status & USB_PORT_STAT_POWER) == 0) | 272 | if ((dum->port_status & USB_PORT_STAT_POWER) == 0) |
| 264 | dum->port_status = 0; | 273 | dum->port_status = 0; |
| 265 | else if (!dum->pullup) { | 274 | |
| 275 | /* UDC suspend must cause a disconnect */ | ||
| 276 | else if (!dum->pullup || dum->udc_suspended) { | ||
| 266 | dum->port_status &= ~(USB_PORT_STAT_CONNECTION | | 277 | dum->port_status &= ~(USB_PORT_STAT_CONNECTION | |
| 267 | USB_PORT_STAT_ENABLE | | 278 | USB_PORT_STAT_ENABLE | |
| 268 | USB_PORT_STAT_LOW_SPEED | | 279 | USB_PORT_STAT_LOW_SPEED | |
| @@ -276,7 +287,8 @@ set_link_state (struct dummy *dum) | |||
| 276 | dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); | 287 | dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16); |
| 277 | if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0) | 288 | if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0) |
| 278 | dum->port_status &= ~USB_PORT_STAT_SUSPEND; | 289 | dum->port_status &= ~USB_PORT_STAT_SUSPEND; |
| 279 | else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0) | 290 | else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 && |
| 291 | dum->rh_state != DUMMY_RH_SUSPENDED) | ||
| 280 | dum->active = 1; | 292 | dum->active = 1; |
| 281 | } | 293 | } |
| 282 | 294 | ||
| @@ -675,11 +687,16 @@ static int dummy_wakeup (struct usb_gadget *_gadget) | |||
| 675 | struct dummy *dum; | 687 | struct dummy *dum; |
| 676 | 688 | ||
| 677 | dum = gadget_to_dummy (_gadget); | 689 | dum = gadget_to_dummy (_gadget); |
| 678 | if (!(dum->port_status & USB_PORT_STAT_SUSPEND) | 690 | if (!(dum->devstatus & ( (1 << USB_DEVICE_B_HNP_ENABLE) |
| 679 | || !(dum->devstatus & | ||
| 680 | ( (1 << USB_DEVICE_B_HNP_ENABLE) | ||
| 681 | | (1 << USB_DEVICE_REMOTE_WAKEUP)))) | 691 | | (1 << USB_DEVICE_REMOTE_WAKEUP)))) |
| 682 | return -EINVAL; | 692 | return -EINVAL; |
| 693 | if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0) | ||
| 694 | return -ENOLINK; | ||
| 695 | if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 && | ||
| 696 | dum->rh_state != DUMMY_RH_SUSPENDED) | ||
| 697 | return -EIO; | ||
| 698 | |||
| 699 | /* FIXME: What if the root hub is suspended but the port isn't? */ | ||
| 683 | 700 | ||
| 684 | /* hub notices our request, issues downstream resume, etc */ | 701 | /* hub notices our request, issues downstream resume, etc */ |
| 685 | dum->resuming = 1; | 702 | dum->resuming = 1; |
| @@ -917,11 +934,50 @@ static int dummy_udc_remove (struct device *dev) | |||
| 917 | return 0; | 934 | return 0; |
| 918 | } | 935 | } |
| 919 | 936 | ||
| 937 | static int dummy_udc_suspend (struct device *dev, pm_message_t state, | ||
| 938 | u32 level) | ||
| 939 | { | ||
| 940 | struct dummy *dum = dev_get_drvdata(dev); | ||
| 941 | |||
| 942 | if (level != SUSPEND_DISABLE) | ||
| 943 | return 0; | ||
| 944 | |||
| 945 | dev_dbg (dev, "%s\n", __FUNCTION__); | ||
| 946 | spin_lock_irq (&dum->lock); | ||
| 947 | dum->udc_suspended = 1; | ||
| 948 | set_link_state (dum); | ||
| 949 | spin_unlock_irq (&dum->lock); | ||
| 950 | |||
| 951 | dev->power.power_state = state; | ||
| 952 | usb_hcd_poll_rh_status (dummy_to_hcd (dum)); | ||
| 953 | return 0; | ||
| 954 | } | ||
| 955 | |||
| 956 | static int dummy_udc_resume (struct device *dev, u32 level) | ||
| 957 | { | ||
| 958 | struct dummy *dum = dev_get_drvdata(dev); | ||
| 959 | |||
| 960 | if (level != RESUME_ENABLE) | ||
| 961 | return 0; | ||
| 962 | |||
| 963 | dev_dbg (dev, "%s\n", __FUNCTION__); | ||
| 964 | spin_lock_irq (&dum->lock); | ||
| 965 | dum->udc_suspended = 0; | ||
| 966 | set_link_state (dum); | ||
| 967 | spin_unlock_irq (&dum->lock); | ||
| 968 | |||
| 969 | dev->power.power_state = PMSG_ON; | ||
| 970 | usb_hcd_poll_rh_status (dummy_to_hcd (dum)); | ||
| 971 | return 0; | ||
| 972 | } | ||
| 973 | |||
| 920 | static struct device_driver dummy_udc_driver = { | 974 | static struct device_driver dummy_udc_driver = { |
| 921 | .name = (char *) gadget_name, | 975 | .name = (char *) gadget_name, |
| 922 | .bus = &platform_bus_type, | 976 | .bus = &platform_bus_type, |
| 923 | .probe = dummy_udc_probe, | 977 | .probe = dummy_udc_probe, |
| 924 | .remove = dummy_udc_remove, | 978 | .remove = dummy_udc_remove, |
| 979 | .suspend = dummy_udc_suspend, | ||
| 980 | .resume = dummy_udc_resume, | ||
| 925 | }; | 981 | }; |
| 926 | 982 | ||
| 927 | /*-------------------------------------------------------------------------*/ | 983 | /*-------------------------------------------------------------------------*/ |
| @@ -980,7 +1036,16 @@ static int dummy_urb_enqueue ( | |||
| 980 | 1036 | ||
| 981 | static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) | 1037 | static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) |
| 982 | { | 1038 | { |
| 983 | /* giveback happens automatically in timer callback */ | 1039 | struct dummy *dum; |
| 1040 | unsigned long flags; | ||
| 1041 | |||
| 1042 | /* giveback happens automatically in timer callback, | ||
| 1043 | * so make sure the callback happens */ | ||
| 1044 | dum = hcd_to_dummy (hcd); | ||
| 1045 | spin_lock_irqsave (&dum->lock, flags); | ||
| 1046 | if (dum->rh_state != DUMMY_RH_RUNNING && !list_empty(&dum->urbp_list)) | ||
| 1047 | mod_timer (&dum->timer, jiffies); | ||
| 1048 | spin_unlock_irqrestore (&dum->lock, flags); | ||
| 984 | return 0; | 1049 | return 0; |
| 985 | } | 1050 | } |
| 986 | 1051 | ||
| @@ -1222,7 +1287,8 @@ restart: | |||
| 1222 | if (urb->status != -EINPROGRESS) { | 1287 | if (urb->status != -EINPROGRESS) { |
| 1223 | /* likely it was just unlinked */ | 1288 | /* likely it was just unlinked */ |
| 1224 | goto return_urb; | 1289 | goto return_urb; |
| 1225 | } | 1290 | } else if (dum->rh_state != DUMMY_RH_RUNNING) |
| 1291 | continue; | ||
| 1226 | type = usb_pipetype (urb->pipe); | 1292 | type = usb_pipetype (urb->pipe); |
| 1227 | 1293 | ||
| 1228 | /* used up this frame's non-periodic bandwidth? | 1294 | /* used up this frame's non-periodic bandwidth? |
| @@ -1486,12 +1552,12 @@ return_urb: | |||
| 1486 | goto restart; | 1552 | goto restart; |
| 1487 | } | 1553 | } |
| 1488 | 1554 | ||
| 1489 | /* want a 1 msec delay here */ | 1555 | if (list_empty (&dum->urbp_list)) { |
| 1490 | if (!list_empty (&dum->urbp_list)) | ||
| 1491 | mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1)); | ||
| 1492 | else { | ||
| 1493 | usb_put_dev (dum->udev); | 1556 | usb_put_dev (dum->udev); |
| 1494 | dum->udev = NULL; | 1557 | dum->udev = NULL; |
| 1558 | } else if (dum->rh_state == DUMMY_RH_RUNNING) { | ||
| 1559 | /* want a 1 msec delay here */ | ||
| 1560 | mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1)); | ||
| 1495 | } | 1561 | } |
| 1496 | 1562 | ||
| 1497 | spin_unlock_irqrestore (&dum->lock, flags); | 1563 | spin_unlock_irqrestore (&dum->lock, flags); |
| @@ -1510,11 +1576,13 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf) | |||
| 1510 | { | 1576 | { |
| 1511 | struct dummy *dum; | 1577 | struct dummy *dum; |
| 1512 | unsigned long flags; | 1578 | unsigned long flags; |
| 1513 | int retval; | 1579 | int retval = 0; |
| 1514 | 1580 | ||
| 1515 | dum = hcd_to_dummy (hcd); | 1581 | dum = hcd_to_dummy (hcd); |
| 1516 | 1582 | ||
| 1517 | spin_lock_irqsave (&dum->lock, flags); | 1583 | spin_lock_irqsave (&dum->lock, flags); |
| 1584 | if (hcd->state != HC_STATE_RUNNING) | ||
| 1585 | goto done; | ||
| 1518 | 1586 | ||
| 1519 | if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { | 1587 | if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { |
| 1520 | dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); | 1588 | dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16); |
| @@ -1522,14 +1590,15 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf) | |||
| 1522 | set_link_state (dum); | 1590 | set_link_state (dum); |
| 1523 | } | 1591 | } |
| 1524 | 1592 | ||
| 1525 | if (!(dum->port_status & PORT_C_MASK)) | 1593 | if ((dum->port_status & PORT_C_MASK) != 0) { |
| 1526 | retval = 0; | ||
| 1527 | else { | ||
| 1528 | *buf = (1 << 1); | 1594 | *buf = (1 << 1); |
| 1529 | dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n", | 1595 | dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n", |
| 1530 | dum->port_status); | 1596 | dum->port_status); |
| 1531 | retval = 1; | 1597 | retval = 1; |
| 1598 | if (dum->rh_state == DUMMY_RH_SUSPENDED) | ||
| 1599 | usb_hcd_resume_root_hub (hcd); | ||
| 1532 | } | 1600 | } |
| 1601 | done: | ||
| 1533 | spin_unlock_irqrestore (&dum->lock, flags); | 1602 | spin_unlock_irqrestore (&dum->lock, flags); |
| 1534 | return retval; | 1603 | return retval; |
| 1535 | } | 1604 | } |
| @@ -1559,6 +1628,9 @@ static int dummy_hub_control ( | |||
| 1559 | int retval = 0; | 1628 | int retval = 0; |
| 1560 | unsigned long flags; | 1629 | unsigned long flags; |
| 1561 | 1630 | ||
| 1631 | if (hcd->state != HC_STATE_RUNNING) | ||
| 1632 | return -ETIMEDOUT; | ||
| 1633 | |||
| 1562 | dum = hcd_to_dummy (hcd); | 1634 | dum = hcd_to_dummy (hcd); |
| 1563 | spin_lock_irqsave (&dum->lock, flags); | 1635 | spin_lock_irqsave (&dum->lock, flags); |
| 1564 | switch (typeReq) { | 1636 | switch (typeReq) { |
| @@ -1658,6 +1730,7 @@ static int dummy_hub_control ( | |||
| 1658 | dum->port_status &= ~(USB_PORT_STAT_ENABLE | 1730 | dum->port_status &= ~(USB_PORT_STAT_ENABLE |
| 1659 | | USB_PORT_STAT_LOW_SPEED | 1731 | | USB_PORT_STAT_LOW_SPEED |
| 1660 | | USB_PORT_STAT_HIGH_SPEED); | 1732 | | USB_PORT_STAT_HIGH_SPEED); |
| 1733 | dum->devstatus = 0; | ||
| 1661 | /* 50msec reset signaling */ | 1734 | /* 50msec reset signaling */ |
| 1662 | dum->re_timeout = jiffies + msecs_to_jiffies(50); | 1735 | dum->re_timeout = jiffies + msecs_to_jiffies(50); |
| 1663 | /* FALLS THROUGH */ | 1736 | /* FALLS THROUGH */ |
| @@ -1684,6 +1757,29 @@ static int dummy_hub_control ( | |||
| 1684 | return retval; | 1757 | return retval; |
| 1685 | } | 1758 | } |
| 1686 | 1759 | ||
| 1760 | static int dummy_hub_suspend (struct usb_hcd *hcd) | ||
| 1761 | { | ||
| 1762 | struct dummy *dum = hcd_to_dummy (hcd); | ||
| 1763 | |||
| 1764 | spin_lock_irq (&dum->lock); | ||
| 1765 | dum->rh_state = DUMMY_RH_SUSPENDED; | ||
| 1766 | set_link_state (dum); | ||
| 1767 | spin_unlock_irq (&dum->lock); | ||
| 1768 | return 0; | ||
| 1769 | } | ||
| 1770 | |||
| 1771 | static int dummy_hub_resume (struct usb_hcd *hcd) | ||
| 1772 | { | ||
| 1773 | struct dummy *dum = hcd_to_dummy (hcd); | ||
| 1774 | |||
| 1775 | spin_lock_irq (&dum->lock); | ||
| 1776 | dum->rh_state = DUMMY_RH_RUNNING; | ||
| 1777 | set_link_state (dum); | ||
| 1778 | if (!list_empty(&dum->urbp_list)) | ||
| 1779 | mod_timer (&dum->timer, jiffies); | ||
| 1780 | spin_unlock_irq (&dum->lock); | ||
| 1781 | return 0; | ||
| 1782 | } | ||
| 1687 | 1783 | ||
| 1688 | /*-------------------------------------------------------------------------*/ | 1784 | /*-------------------------------------------------------------------------*/ |
| 1689 | 1785 | ||
| @@ -1751,6 +1847,7 @@ static int dummy_start (struct usb_hcd *hcd) | |||
| 1751 | init_timer (&dum->timer); | 1847 | init_timer (&dum->timer); |
| 1752 | dum->timer.function = dummy_timer; | 1848 | dum->timer.function = dummy_timer; |
| 1753 | dum->timer.data = (unsigned long) dum; | 1849 | dum->timer.data = (unsigned long) dum; |
| 1850 | dum->rh_state = DUMMY_RH_RUNNING; | ||
| 1754 | 1851 | ||
| 1755 | INIT_LIST_HEAD (&dum->urbp_list); | 1852 | INIT_LIST_HEAD (&dum->urbp_list); |
| 1756 | 1853 | ||
| @@ -1803,6 +1900,8 @@ static const struct hc_driver dummy_hcd = { | |||
| 1803 | 1900 | ||
| 1804 | .hub_status_data = dummy_hub_status, | 1901 | .hub_status_data = dummy_hub_status, |
| 1805 | .hub_control = dummy_hub_control, | 1902 | .hub_control = dummy_hub_control, |
| 1903 | .hub_suspend = dummy_hub_suspend, | ||
| 1904 | .hub_resume = dummy_hub_resume, | ||
| 1806 | }; | 1905 | }; |
| 1807 | 1906 | ||
| 1808 | static int dummy_hcd_probe (struct device *dev) | 1907 | static int dummy_hcd_probe (struct device *dev) |
| @@ -1836,11 +1935,57 @@ static int dummy_hcd_remove (struct device *dev) | |||
| 1836 | return 0; | 1935 | return 0; |
| 1837 | } | 1936 | } |
| 1838 | 1937 | ||
| 1938 | static int dummy_hcd_suspend (struct device *dev, pm_message_t state, | ||
| 1939 | u32 level) | ||
| 1940 | { | ||
| 1941 | struct usb_hcd *hcd; | ||
| 1942 | |||
| 1943 | if (level != SUSPEND_DISABLE) | ||
| 1944 | return 0; | ||
| 1945 | |||
| 1946 | dev_dbg (dev, "%s\n", __FUNCTION__); | ||
| 1947 | hcd = dev_get_drvdata (dev); | ||
| 1948 | |||
| 1949 | #ifndef CONFIG_USB_SUSPEND | ||
| 1950 | /* Otherwise this would never happen */ | ||
| 1951 | usb_lock_device (hcd->self.root_hub); | ||
| 1952 | dummy_hub_suspend (hcd); | ||
| 1953 | usb_unlock_device (hcd->self.root_hub); | ||
| 1954 | #endif | ||
| 1955 | |||
| 1956 | hcd->state = HC_STATE_SUSPENDED; | ||
| 1957 | return 0; | ||
| 1958 | } | ||
| 1959 | |||
| 1960 | static int dummy_hcd_resume (struct device *dev, u32 level) | ||
| 1961 | { | ||
| 1962 | struct usb_hcd *hcd; | ||
| 1963 | |||
| 1964 | if (level != RESUME_ENABLE) | ||
| 1965 | return 0; | ||
| 1966 | |||
| 1967 | dev_dbg (dev, "%s\n", __FUNCTION__); | ||
| 1968 | hcd = dev_get_drvdata (dev); | ||
| 1969 | hcd->state = HC_STATE_RUNNING; | ||
| 1970 | |||
| 1971 | #ifndef CONFIG_USB_SUSPEND | ||
| 1972 | /* Otherwise this would never happen */ | ||
| 1973 | usb_lock_device (hcd->self.root_hub); | ||
| 1974 | dummy_hub_resume (hcd); | ||
| 1975 | usb_unlock_device (hcd->self.root_hub); | ||
| 1976 | #endif | ||
| 1977 | |||
| 1978 | usb_hcd_poll_rh_status (hcd); | ||
| 1979 | return 0; | ||
| 1980 | } | ||
| 1981 | |||
| 1839 | static struct device_driver dummy_hcd_driver = { | 1982 | static struct device_driver dummy_hcd_driver = { |
| 1840 | .name = (char *) driver_name, | 1983 | .name = (char *) driver_name, |
| 1841 | .bus = &platform_bus_type, | 1984 | .bus = &platform_bus_type, |
| 1842 | .probe = dummy_hcd_probe, | 1985 | .probe = dummy_hcd_probe, |
| 1843 | .remove = dummy_hcd_remove, | 1986 | .remove = dummy_hcd_remove, |
| 1987 | .suspend = dummy_hcd_suspend, | ||
| 1988 | .resume = dummy_hcd_resume, | ||
| 1844 | }; | 1989 | }; |
| 1845 | 1990 | ||
| 1846 | /*-------------------------------------------------------------------------*/ | 1991 | /*-------------------------------------------------------------------------*/ |
