diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2005-05-10 15:34:16 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-06-27 17:43:59 -0400 |
commit | 391eca9d8892a940ff8dbfee2ca78942e05c2d37 (patch) | |
tree | 09459bed19e34676108da3ebf8f41c6d5d575143 /drivers/usb/gadget | |
parent | cc095b0b5b653dca3e106710a72ba28b5bb7456b (diff) |
[PATCH] USB: dummy_hcd: add suspend/resume support
This patch adds support to dummy_hcd for suspending and resuming the root
hub and the emulated platform devices.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget')
-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 | /*-------------------------------------------------------------------------*/ |