Hi, I little bit changed "hot-plugging" support added into "usb" branch by Vladimir - see included patch. For the first look it maybe works on UHCI, I did not test it on OHCI yet...
Basic info to this patch: 1. This patch is made against current usb branch code (rev. 2427). This patch is not committed into usb branch yet - it was tested only once and very simply and it does not work on hubs (i.e. on non-root hubs; some timeout happens here). I will continue on development and testing soon (if it will be possible...) and I will commit into usb some better version later (I hope...). 2. I am not using original comparing of "hub->speed[i]" and "speed" to detect connection status change (but I left the "hub" structure in code, it can be maybe useful in future). I am using built-in functions of OHCI/UHCI controllers (available via root hub port register) which should be more reliable and are more systematic and closed to specifications. 3. There is missing handling of disconnected devices. For this we will need to know on which port which device is attached - it is currently not known - and we need to know it "recursively" because devices can be connected indirectly via non-root hubs. On this part I probably will not work in near future (currently I have no simple and suitable idea how to do it). Regards Ales
diff -urB ./my-repo/usb/bus/usb/ohci.c ./usb/bus/usb/ohci.c --- ./my-repo/usb/bus/usb/ohci.c 2010-07-06 19:48:28.000000000 +0200 +++ ./usb/bus/usb/ohci.c 2010-07-06 22:33:35.000000000 +0200 @@ -148,6 +148,7 @@ #define GRUB_OHCI_REG_CONTROL_BULK_ENABLE (1 << 5) #define GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE (1 << 4) +#define GRUB_OHCI_RESET_CONNECT_CHANGE (1 << 16) #define GRUB_OHCI_CTRL_EDS 16 #define GRUB_OHCI_BULK_EDS 16 #define GRUB_OHCI_TDS 256 @@ -1219,6 +1220,11 @@ GRUB_OHCI_CLEAR_PORT_ENABLE); grub_millisleep (10); + if (enable) + /* Reset bit Connect Status Change */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_RESET_CONNECT_CHANGE); + grub_dprintf ("ohci", "end of portstatus=0x%02x\n", grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)); @@ -1226,7 +1232,7 @@ } static grub_usb_speed_t -grub_ohci_detect_dev (grub_usb_controller_t dev, int port) +grub_ohci_detect_dev (grub_usb_controller_t dev, int port, int *changed) { struct grub_ohci *o = (struct grub_ohci *) dev->data; grub_uint32_t status; @@ -1235,6 +1241,9 @@ grub_dprintf ("ohci", "detect_dev status=0x%02x\n", status); + /* Connect Status Change bit - it detects change of connection */ + *changed = ((status & GRUB_OHCI_RESET_CONNECT_CHANGE) != 0); + if (! (status & 1)) return GRUB_USB_SPEED_NONE; else if (status & (1 << 9)) diff -urB ./my-repo/usb/bus/usb/uhci.c ./usb/bus/usb/uhci.c --- ./my-repo/usb/bus/usb/uhci.c 2010-07-06 19:48:28.000000000 +0200 +++ ./usb/bus/usb/uhci.c 2010-07-06 22:24:31.000000000 +0200 @@ -644,10 +644,14 @@ grub_dprintf ("uhci", "waiting for the port to be enabled\n"); endtime = grub_get_time_ms () + 1000; - while (! (grub_uhci_readreg16 (u, reg) & (1 << 2))) + while (! ((status = grub_uhci_readreg16 (u, reg)) & (1 << 2))) if (grub_get_time_ms () > endtime) return grub_error (GRUB_ERR_IO, "UHCI Timed out"); + /* Reset bit Connect Status Change */ + grub_uhci_writereg16 (u, reg, status | (1 << 1)); + + /* Read final port status */ status = grub_uhci_readreg16 (u, reg); grub_dprintf ("uhci", ">3detect=0x%02x\n", status); @@ -656,7 +660,7 @@ } static grub_usb_speed_t -grub_uhci_detect_dev (grub_usb_controller_t dev, int port) +grub_uhci_detect_dev (grub_usb_controller_t dev, int port, int *changed) { struct grub_uhci *u = (struct grub_uhci *) dev->data; int reg; @@ -676,6 +680,9 @@ grub_dprintf ("uhci", "detect=0x%02x port=%d\n", status, port); + /* Connect Status Change bit - it detects change of connection */ + *changed = ((status & (1 << 1)) != 0); + if (! (status & 1)) return GRUB_USB_SPEED_NONE; else if (status & (1 << 8)) diff -urB ./my-repo/usb/bus/usb/usb.c ./usb/bus/usb/usb.c --- ./my-repo/usb/bus/usb/usb.c 2010-07-06 19:48:28.000000000 +0200 +++ ./usb/bus/usb/usb.c 2010-07-07 00:23:38.000000000 +0200 @@ -225,6 +225,20 @@ } } + return GRUB_USB_ERR_NONE; + + fail: + + for (i = 0; i < 8; i++) + grub_free (dev->config[i].descconf); + + return err; +} + +void grub_usb_device_attach (grub_usb_device_t dev) +{ + int i; + /* XXX: Just check configuration 0 for now. */ for (i = 0; i < dev->config[0].descconf->numif; i++) { @@ -243,15 +257,6 @@ if (interf->class == desc->class && desc->hook (dev, 0, i)) dev->config[0].interf[i].attached = 1; } - - return GRUB_USB_ERR_NONE; - - fail: - - for (i = 0; i < 8; i++) - grub_free (dev->config[i].descconf); - - return err; } void diff -urB ./my-repo/usb/bus/usb/usbhub.c ./usb/bus/usb/usbhub.c --- ./my-repo/usb/bus/usb/usbhub.c 2010-07-06 19:48:28.000000000 +0200 +++ ./usb/bus/usb/usbhub.c 2010-07-07 00:19:13.000000000 +0200 @@ -23,8 +23,10 @@ #include <grub/misc.h> #include <grub/time.h> +#define GRUB_USBHUB_MAX_DEVICES 128 + /* USB Supports 127 devices, with device 0 as special case. */ -static struct grub_usb_device *grub_usb_devs[128]; +static struct grub_usb_device *grub_usb_devs[GRUB_USBHUB_MAX_DEVICES]; struct grub_usb_hub { @@ -44,6 +46,7 @@ { grub_usb_device_t dev; int i; + grub_usb_err_t err; dev = grub_zalloc (sizeof (struct grub_usb_device)); if (! dev) @@ -52,31 +55,48 @@ dev->controller = *controller; dev->speed = speed; - grub_usb_device_initialize (dev); + err = grub_usb_device_initialize (dev); + if (err) + { + grub_free (dev); + return NULL; + } /* Assign a new address to the device. */ - for (i = 1; i < 128; i++) + for (i = 1; i < GRUB_USBHUB_MAX_DEVICES; i++) { if (! grub_usb_devs[i]) break; } - if (i == 128) + if (i == GRUB_USBHUB_MAX_DEVICES) { grub_error (GRUB_ERR_IO, "can't assign address to USB device"); + for (i = 0; i < 8; i++) + grub_free (dev->config[i].descconf); + grub_free (dev); return NULL; } - grub_usb_control_msg (dev, - (GRUB_USB_REQTYPE_OUT - | GRUB_USB_REQTYPE_STANDARD - | GRUB_USB_REQTYPE_TARGET_DEV), - GRUB_USB_REQ_SET_ADDRESS, - i, 0, 0, NULL); + err = grub_usb_control_msg (dev, + (GRUB_USB_REQTYPE_OUT + | GRUB_USB_REQTYPE_STANDARD + | GRUB_USB_REQTYPE_TARGET_DEV), + GRUB_USB_REQ_SET_ADDRESS, + i, 0, 0, NULL); + if (err) + { + for (i = 0; i < 8; i++) + grub_free (dev->config[i].descconf); + grub_free (dev); + return NULL; + } dev->addr = i; dev->initialized = 1; grub_usb_devs[i] = dev; + grub_usb_device_attach (dev); + return dev; } @@ -144,7 +164,7 @@ if (err) continue; grub_dprintf ("usb", "Hub port %d status: 0x%02x\n", i, status); - + /* If connected, reset and enable the port. */ if (status & GRUB_USB_HUB_STATUS_CONNECTED) { @@ -192,6 +212,18 @@ if (err || !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) ) continue; + /* Do reset of connection change bit */ + err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT + | GRUB_USB_REQTYPE_CLASS + | GRUB_USB_REQTYPE_TARGET_OTHER), + GRUB_USB_REQ_CLEAR_FEATURE, + GRUB_USB_HUB_FEATURE_C_CONNECTED, + i, 0, 0); + /* Just ignore the device if the Hub reports some error */ + if (err) + continue; + grub_dprintf ("usb", "Hub port - cleared connection change\n"); + /* Add the device and assign a device address to it. */ grub_dprintf ("usb", "Call hub_add_dev - port %d\n", i); next_dev = grub_usb_hub_add_dev (&dev->controller, speed); @@ -239,6 +271,7 @@ { int i; struct grub_usb_hub *hub; + int changed=0; hub = grub_malloc (sizeof (*hub)); if (!hub) @@ -248,7 +281,10 @@ hubs = hub; hub->controller = grub_malloc (sizeof (*controller)); if (!hub->controller) - return GRUB_USB_ERR_INTERNAL; + { + grub_free (hub); + return GRUB_USB_ERR_INTERNAL; + } grub_memcpy (hub->controller, controller, sizeof (*controller)); hub->dev = 0; @@ -258,13 +294,15 @@ hub->speed = grub_malloc (sizeof (hub->speed[0]) * hub->nports); if (!hub->speed) { + grub_free (hub->controller); grub_free (hub); return GRUB_USB_ERR_INTERNAL; } for (i = 0; i < hub->nports; i++) { - hub->speed[i] = controller->dev->detect_dev (hub->controller, i); + hub->speed[i] = controller->dev->detect_dev (hub->controller, i, + &changed); if (hub->speed[i] != GRUB_USB_SPEED_NONE) attach_root_port (hub->controller, i, hub->speed[i]); @@ -273,30 +311,154 @@ return GRUB_USB_ERR_NONE; } +static void +poll_nonroot_hub (grub_usb_device_t dev) +{ + struct grub_usb_usb_hubdesc hubdesc; + grub_err_t err; + int i; + grub_uint64_t timeout; + grub_usb_device_t next_dev; + + err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN + | GRUB_USB_REQTYPE_CLASS + | GRUB_USB_REQTYPE_TARGET_DEV), + GRUB_USB_REQ_GET_DESCRIPTOR, + (GRUB_USB_DESCRIPTOR_HUB << 8) | 0, + 0, sizeof (hubdesc), (char *) &hubdesc); + if (err) + return; + + /* Iterate over the Hub ports. */ + for (i = 1; i <= hubdesc.portcnt; i++) + { + grub_uint32_t status; + + /* Get the port status. */ + err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN + | GRUB_USB_REQTYPE_CLASS + | GRUB_USB_REQTYPE_TARGET_OTHER), + GRUB_USB_REQ_GET_STATUS, + 0, i, sizeof (status), (char *) &status); + /* Just ignore the device if the Hub does not report the + status. */ + if (err) + continue; + + /* Connected and status of connection changed ? */ + if ((status & GRUB_USB_HUB_STATUS_CONNECTED) + && (status & GRUB_USB_HUB_STATUS_C_CONNECTED)) + { + grub_usb_speed_t speed; + + /* Determine the device speed. */ + if (status & GRUB_USB_HUB_STATUS_LOWSPEED) + speed = GRUB_USB_SPEED_LOW; + else + { + if (status & GRUB_USB_HUB_STATUS_HIGHSPEED) + speed = GRUB_USB_SPEED_HIGH; + else + speed = GRUB_USB_SPEED_FULL; + } + + /* A device is actually connected to this port. + * Now do reset of port. */ + err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT + | GRUB_USB_REQTYPE_CLASS + | GRUB_USB_REQTYPE_TARGET_OTHER), + GRUB_USB_REQ_SET_FEATURE, + GRUB_USB_HUB_FEATURE_PORT_RESET, + i, 0, 0); + /* If the Hub does not cooperate for this port, just skip + the port. */ + if (err) + continue; + + /* Wait for reset procedure done */ + timeout = grub_get_time_ms () + 1000; + do + { + /* Get the port status. */ + err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN + | GRUB_USB_REQTYPE_CLASS + | GRUB_USB_REQTYPE_TARGET_OTHER), + GRUB_USB_REQ_GET_STATUS, + 0, i, sizeof (status), (char *) &status); + } + while (!err && + !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) && + (grub_get_time_ms() < timeout) ); + if (err || !(status & GRUB_USB_HUB_STATUS_C_PORT_RESET) ) + continue; + + /* Do reset of connection change bit */ + err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT + | GRUB_USB_REQTYPE_CLASS + | GRUB_USB_REQTYPE_TARGET_OTHER), + GRUB_USB_REQ_CLEAR_FEATURE, + GRUB_USB_HUB_FEATURE_C_CONNECTED, + i, 0, 0); + /* Just ignore the device if the Hub reports some error */ + if (err) + continue; + + /* Add the device and assign a device address to it. */ + next_dev = grub_usb_hub_add_dev (&dev->controller, speed); + if (! next_dev) + continue; + + /* If the device is a Hub, scan it for more devices. */ + if (next_dev->descdev.class == 0x09) + grub_usb_add_hub (next_dev); + } + } + + return; +} + void grub_usb_poll_devices (void) { struct grub_usb_hub *hub; + int i; for (hub = hubs; hub; hub = hub->next) { - int i; + int changed=0; /* Do we have to recheck number of ports? */ + /* No, it should be never changed, it should be constant. */ for (i = 0; i < hub->nports; i++) { grub_usb_speed_t speed; - speed = hub->controller->dev->detect_dev (hub->controller, i); + speed = hub->controller->dev->detect_dev (hub->controller, i, + &changed); - if (speed == hub->speed[i]) - continue; + if (speed != GRUB_USB_SPEED_NONE) + { + if (changed) + attach_root_port (hub->controller, i, speed); + } - if (hub->speed[i] == GRUB_USB_SPEED_NONE - && speed != GRUB_USB_SPEED_NONE) - attach_root_port (hub->controller, i, speed); + /* XXX: There should be also handling + * of disconnected devices. */ + hub->speed[i] = speed; } } + + /* We should check changes of non-root hubs too. */ + for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++) + { + grub_usb_device_t dev = grub_usb_devs[i]; + + if (dev && dev->descdev.class == 0x09) + { + poll_nonroot_hub (dev); + } + } + } int @@ -304,7 +466,7 @@ { int i; - for (i = 0; i < 128; i++) + for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++) { if (grub_usb_devs[i]) { diff -urB ./my-repo/usb/include/grub/usb.h ./usb/include/grub/usb.h --- ./my-repo/usb/include/grub/usb.h 2010-07-06 19:48:28.000000000 +0200 +++ ./usb/include/grub/usb.h 2010-07-07 00:01:43.000000000 +0200 @@ -104,7 +104,7 @@ grub_err_t (*portstatus) (grub_usb_controller_t dev, unsigned int port, unsigned int enable); - grub_usb_speed_t (*detect_dev) (grub_usb_controller_t dev, int port); + grub_usb_speed_t (*detect_dev) (grub_usb_controller_t dev, int port, int *changed); /* The next host controller. */ struct grub_usb_controller_dev *next; @@ -229,4 +229,6 @@ void grub_usb_poll_devices (void); +void grub_usb_device_attach (grub_usb_device_t dev); + #endif /* GRUB_USB_H */ diff -urB ./my-repo/usb/include/grub/usbtrans.h ./usb/include/grub/usbtrans.h --- ./my-repo/usb/include/grub/usbtrans.h 2010-07-06 19:48:28.000000000 +0200 +++ ./usb/include/grub/usbtrans.h 2010-07-06 22:04:26.000000000 +0200 @@ -93,10 +93,12 @@ #define GRUB_USB_HUB_FEATURE_PORT_RESET 0x04 #define GRUB_USB_HUB_FEATURE_PORT_POWER 0x08 +#define GRUB_USB_HUB_FEATURE_C_CONNECTED 0x10 #define GRUB_USB_HUB_STATUS_CONNECTED (1 << 0) #define GRUB_USB_HUB_STATUS_LOWSPEED (1 << 9) #define GRUB_USB_HUB_STATUS_HIGHSPEED (1 << 10) +#define GRUB_USB_HUB_STATUS_C_CONNECTED (1 << 16) #define GRUB_USB_HUB_STATUS_C_PORT_RESET (1 << 20) struct grub_usb_packet_setup
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel