summaryrefslogtreecommitdiffstats
path: root/drivers/usb/hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/hub.c')
-rw-r--r--drivers/usb/hub.c66
1 files changed, 40 insertions, 26 deletions
diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c
index c066c3c43..3eca7a857 100644
--- a/drivers/usb/hub.c
+++ b/drivers/usb/hub.c
@@ -5,7 +5,7 @@
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Gregory P. Smith
*
- * $Id: hub.c,v 1.15 1999/12/27 15:17:45 acher Exp $
+ * $Id: hub.c,v 1.21 2000/01/16 21:19:44 acher Exp $
*/
#include <linux/kernel.h>
@@ -22,15 +22,6 @@
#include "usb.h"
#include "hub.h"
-#ifdef __alpha
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-extern long __kernel_thread(unsigned long, int (*)(void *), void *);
-static inline long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
-{
- return __kernel_thread(flags | CLONE_VM, fn, arg);
-}
-#endif
-#endif
/* Wakes up khubd */
static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
@@ -112,6 +103,16 @@ static int hub_irq(int status, void *__buffer, int len, void *dev_id)
return 1;
}
+static void usb_hub_power_on(struct usb_hub *hub)
+{
+ int i;
+
+ /* Enable power to the ports */
+ dbg("enabling power on all ports");
+ for (i = 0; i < hub->nports; i++)
+ usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER);
+}
+
static int usb_hub_configure(struct usb_hub *hub)
{
struct usb_device *dev = hub->dev;
@@ -191,10 +192,8 @@ static int usb_hub_configure(struct usb_hub *hub)
dbg("%sover-current condition exists",
(le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no ");
- /* Enable power to the ports */
- dbg("enabling power on all ports");
- for (i = 0; i < hub->nports; i++)
- usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
+ usb_hub_power_on(hub);
+
return 0;
}
@@ -322,13 +321,17 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port)
portchange = le16_to_cpu(portsts.wPortChange);
dbg("portstatus %x, change %x, %s", portstatus, portchange,
portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
- /* If it's not in CONNECT and ENABLE state, we're done */
- if ((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
- (!(portstatus & USB_PORT_STAT_ENABLE))) {
- /* Disconnect anything that may have been there */
+
+ /* Clear the connection change status */
+ usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);
+
+ /* Disconnect any existing devices under this port */
+ if (((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
+ (!(portstatus & USB_PORT_STAT_ENABLE)))|| (hub->children[port])) {
usb_disconnect(&hub->children[port]);
- /* We're done now, we already disconnected the device */
- return;
+ /* Return now if nothing is connected */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION))
+ return;
}
wait_ms(400);
@@ -350,7 +353,11 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port)
dbg("portstatus %x, change %x, %s", portstatus ,portchange,
portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
- if ((portstatus&(1<<USB_PORT_FEAT_ENABLE)))
+ if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
+ !(portstatus & USB_PORT_STAT_CONNECTION))
+ return;
+
+ if (portstatus & USB_PORT_STAT_ENABLE)
break;
wait_ms(200);
@@ -361,6 +368,9 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port)
err("Maybe the USB cable is bad?");
return;
}
+
+ usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET);
+
/* Allocate a new device struct for it */
usb = usb_alloc_dev(hub, hub->bus);
@@ -432,8 +442,6 @@ static void usb_hub_events(void)
if (portchange & USB_PORT_STAT_C_CONNECTION) {
dbg("port %d connection change", i + 1);
- usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_CONNECTION);
-
usb_hub_port_connect_change(dev, i);
}
@@ -442,12 +450,15 @@ static void usb_hub_events(void)
usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE);
}
- if (portchange & USB_PORT_STAT_C_SUSPEND)
+ if (portstatus & USB_PORT_STAT_SUSPEND) {
dbg("port %d suspend change", i + 1);
-
+ usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND);
+ }
+
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
- dbg("port %d over-current change", i + 1);
+ err("port %d over-current change", i + 1);
usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT);
+ usb_hub_power_on(hub);
}
if (portchange & USB_PORT_STAT_C_RESET) {
@@ -468,7 +479,9 @@ static void usb_hub_events(void)
}
if (hubchange & HUB_CHANGE_OVERCURRENT) {
dbg("hub overcurrent change");
+ wait_ms(500); //Cool down
usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT);
+ usb_hub_power_on(hub);
}
}
} /* end while (1) */
@@ -491,6 +504,7 @@ static int usb_hub_thread(void *__hub)
* This thread doesn't need any user-level access,
* so get rid of all our resources
*/
+ exit_files(current); /* daemonize doesn't do exit_files */
daemonize();
/* Setup a nice name */