static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- int port;
+ unsigned long io_addr = uhci->io_addr;
+ int i;
*buf = 0;
- for (port = 0; port < uhci->rh_numports; ++port) {
- if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
- test_bit(port, &uhci->port_c_suspend))
- *buf |= (1 << (port + 1));
+ for (i = 0; i < uhci->rh_numports; i++) {
+ if (inw(io_addr + USBPORTSC1 + i * 2) & RWC_BITS)
+ *buf |= (1 << (i + 1));
}
- if (*buf && uhci->state == UHCI_SUSPENDED)
- uhci->resume_detect = 1;
return !!*buf;
}
status &= ~(RWC_BITS|WZ_BITS); \
outw(status, port_addr)
-/* UHCI controllers don't automatically stop resume signalling after 20 msec,
- * so we have to poll and check timeouts in order to take care of it.
- * FIXME: Synchronize access to these fields by a spinlock.
- */
-static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
- unsigned long port_addr)
-{
- int status;
-
- if (test_bit(port, &uhci->suspended_ports)) {
- CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
- clear_bit(port, &uhci->suspended_ports);
- clear_bit(port, &uhci->resuming_ports);
- set_bit(port, &uhci->port_c_suspend);
-
- /* The controller won't actually turn off the RD bit until
- * it has had a chance to send a low-speed EOP sequence,
- * which takes 3 bit times (= 2 microseconds). We'll delay
- * slightly longer for good luck. */
- udelay(4);
- }
-}
-
-static void uhci_check_resume(struct uhci_hcd *uhci)
-{
- unsigned int port;
- unsigned long port_addr;
-
- for (port = 0; port < uhci->rh_numports; ++port) {
- port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
- if (unlikely(inw(port_addr) & USBPORTSC_RD)) {
- if (!test_bit(port, &uhci->resuming_ports)) {
-
- /* Port received a wakeup request */
- set_bit(port, &uhci->resuming_ports);
- uhci->resume_timeout = jiffies +
- msecs_to_jiffies(20);
- } else if (time_after_eq(jiffies,
- uhci->resume_timeout)) {
- uhci_finish_suspend(uhci, port, port_addr);
- }
- }
- }
-}
/* size of returned buffer is part of USB spec */
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- int status, lstatus, retval = 0, len = 0;
- unsigned int port = wIndex - 1;
- unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
- u16 wPortChange, wPortStatus;
+ int status, retval = 0, len = 0;
+ unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1);
+ __u16 wPortChange, wPortStatus;
switch (typeReq) {
+ /* Request Destination:
+ without flags: Device,
+ RH_INTERFACE: interface,
+ RH_ENDPOINT: endpoint,
+ RH_CLASS means HUB here,
+ RH_OTHER | RH_CLASS almost ever means HUB_PORT here
+ */
case GetHubStatus:
*(__le32 *)buf = cpu_to_le32(0);
OK(4); /* hub power */
case GetPortStatus:
- if (port >= uhci->rh_numports)
+ if (!wIndex || wIndex > uhci->rh_numports)
goto err;
-
- if (uhci->resuming_ports)
- uhci_check_resume(uhci);
-
status = inw(port_addr);
/* Intel controllers report the OverCurrent bit active on.
PCI_VENDOR_ID_VIA)
status ^= USBPORTSC_OC;
- /* UHCI doesn't support C_RESET (always false) */
- wPortChange = lstatus = 0;
+ /* UHCI doesn't support C_SUSPEND and C_RESET (always false) */
+ wPortChange = 0;
if (status & USBPORTSC_CSC)
- wPortChange |= USB_PORT_STAT_C_CONNECTION;
+ wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16);
if (status & USBPORTSC_PEC)
- wPortChange |= USB_PORT_STAT_C_ENABLE;
+ wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16);
if (status & USBPORTSC_OCC)
- wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
-
- if (test_bit(port, &uhci->port_c_suspend)) {
- wPortChange |= USB_PORT_STAT_C_SUSPEND;
- lstatus |= 1;
- }
- if (test_bit(port, &uhci->suspended_ports))
- lstatus |= 2;
- if (test_bit(port, &uhci->resuming_ports))
- lstatus |= 4;
+ wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16);
/* UHCI has no power switching (always on) */
- wPortStatus = USB_PORT_STAT_POWER;
+ wPortStatus = 1 << USB_PORT_FEAT_POWER;
if (status & USBPORTSC_CCS)
- wPortStatus |= USB_PORT_STAT_CONNECTION;
+ wPortStatus |= 1 << USB_PORT_FEAT_CONNECTION;
if (status & USBPORTSC_PE) {
- wPortStatus |= USB_PORT_STAT_ENABLE;
+ wPortStatus |= 1 << USB_PORT_FEAT_ENABLE;
if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
- wPortStatus |= USB_PORT_STAT_SUSPEND;
+ wPortStatus |= 1 << USB_PORT_FEAT_SUSPEND;
}
if (status & USBPORTSC_OC)
- wPortStatus |= USB_PORT_STAT_OVERCURRENT;
+ wPortStatus |= 1 << USB_PORT_FEAT_OVER_CURRENT;
if (status & USBPORTSC_PR)
- wPortStatus |= USB_PORT_STAT_RESET;
+ wPortStatus |= 1 << USB_PORT_FEAT_RESET;
if (status & USBPORTSC_LSDA)
- wPortStatus |= USB_PORT_STAT_LOW_SPEED;
+ wPortStatus |= 1 << USB_PORT_FEAT_LOWSPEED;
if (wPortChange)
- dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n",
- wIndex, status, lstatus);
+ dev_dbg(uhci_dev(uhci), "port %d portsc %04x\n",
+ wIndex, status);
*(__le16 *)buf = cpu_to_le16(wPortStatus);
*(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
}
break;
case SetPortFeature:
- if (port >= uhci->rh_numports)
+ if (!wIndex || wIndex > uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- set_bit(port, &uhci->suspended_ports);
SET_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0);
case USB_PORT_FEAT_RESET:
mdelay(50); /* USB v1.1 7.1.7.3 */
CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10);
-
- /* Reset terminates Resume signalling */
- uhci_finish_suspend(uhci, port, port_addr);
SET_RH_PORTSTAT(USBPORTSC_PE);
mdelay(10);
CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC);
}
break;
case ClearPortFeature:
- if (port >= uhci->rh_numports)
+ if (!wIndex || wIndex > uhci->rh_numports)
goto err;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PE);
-
- /* Disable terminates Resume signalling */
- uhci_finish_suspend(uhci, port, port_addr);
OK(0);
case USB_PORT_FEAT_C_ENABLE:
CLR_RH_PORTSTAT(USBPORTSC_PEC);
OK(0);
case USB_PORT_FEAT_SUSPEND:
- if (test_bit(port, &uhci->suspended_ports) &&
- !test_and_set_bit(port,
- &uhci->resuming_ports)) {
- uhci->resume_timeout = jiffies +
- msecs_to_jiffies(20);
- SET_RH_PORTSTAT(USBPORTSC_RD);
-
- /* The controller won't allow RD to be set
- * if the port is disabled. When this happens
- * just skip the Resume signalling.
- */
- if (!(inw(port_addr) & USBPORTSC_RD))
- uhci_finish_suspend(uhci, port,
- port_addr);
- }
+ CLR_RH_PORTSTAT(USBPORTSC_SUSP);
OK(0);
case USB_PORT_FEAT_C_SUSPEND:
- clear_bit(port, &uhci->port_c_suspend);
+ /* this driver won't report these */
OK(0);
case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */