Use status change pipe for hub hotplug detection
This commit is contained in:
parent
f609c84a7f
commit
ff62c48f5a
5 changed files with 167 additions and 32 deletions
110
bus/usb/usbhub.c
110
bus/usb/usbhub.c
|
@ -177,16 +177,16 @@ grub_usb_add_hub (grub_usb_device_t dev)
|
||||||
grub_dprintf ("usb", "Hub port %d status: 0x%02x\n", i, status);
|
grub_dprintf ("usb", "Hub port %d status: 0x%02x\n", i, status);
|
||||||
|
|
||||||
/* If connected, reset and enable the port. */
|
/* If connected, reset and enable the port. */
|
||||||
if (status & GRUB_USB_HUB_STATUS_CONNECTED)
|
if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)
|
||||||
{
|
{
|
||||||
grub_usb_speed_t speed;
|
grub_usb_speed_t speed;
|
||||||
|
|
||||||
/* Determine the device speed. */
|
/* Determine the device speed. */
|
||||||
if (status & GRUB_USB_HUB_STATUS_LOWSPEED)
|
if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
|
||||||
speed = GRUB_USB_SPEED_LOW;
|
speed = GRUB_USB_SPEED_LOW;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (status & GRUB_USB_HUB_STATUS_HIGHSPEED)
|
if (status & GRUB_USB_HUB_STATUS_PORT_HIGHSPEED)
|
||||||
speed = GRUB_USB_SPEED_HIGH;
|
speed = GRUB_USB_SPEED_HIGH;
|
||||||
else
|
else
|
||||||
speed = GRUB_USB_SPEED_FULL;
|
speed = GRUB_USB_SPEED_FULL;
|
||||||
|
@ -231,7 +231,7 @@ grub_usb_add_hub (grub_usb_device_t dev)
|
||||||
| GRUB_USB_REQTYPE_CLASS
|
| GRUB_USB_REQTYPE_CLASS
|
||||||
| GRUB_USB_REQTYPE_TARGET_OTHER),
|
| GRUB_USB_REQTYPE_TARGET_OTHER),
|
||||||
GRUB_USB_REQ_CLEAR_FEATURE,
|
GRUB_USB_REQ_CLEAR_FEATURE,
|
||||||
GRUB_USB_HUB_FEATURE_C_CONNECTED,
|
GRUB_USB_HUB_FEATURE_C_PORT_CONNECTED,
|
||||||
i, 0, 0);
|
i, 0, 0);
|
||||||
/* Just ignore the device if the Hub reports some error */
|
/* Just ignore the device if the Hub reports some error */
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -252,6 +252,25 @@ grub_usb_add_hub (grub_usb_device_t dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < dev->config[0].interf[0].descif->endpointcnt;
|
||||||
|
i++)
|
||||||
|
{
|
||||||
|
struct grub_usb_desc_endp *endp = NULL;
|
||||||
|
endp = &dev->config[0].interf[0].descendp[i];
|
||||||
|
|
||||||
|
if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
|
||||||
|
== GRUB_USB_EP_INTERRUPT)
|
||||||
|
{
|
||||||
|
dev->hub_endpoint = endp;
|
||||||
|
dev->hub_transfer
|
||||||
|
= grub_usb_bulk_read_background (dev, endp->endp_addr,
|
||||||
|
grub_min (endp->maxpacket,
|
||||||
|
sizeof (dev->statuschange)),
|
||||||
|
(char *) &dev->statuschange);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,6 +360,9 @@ detach_device (grub_usb_device_t dev)
|
||||||
return;
|
return;
|
||||||
if (dev->descdev.class == GRUB_USB_CLASS_HUB)
|
if (dev->descdev.class == GRUB_USB_CLASS_HUB)
|
||||||
{
|
{
|
||||||
|
if (dev->hub_transfer)
|
||||||
|
grub_usb_cancel_transfer (dev->hub_transfer);
|
||||||
|
|
||||||
for (i = 0; i < dev->nports; i++)
|
for (i = 0; i < dev->nports; i++)
|
||||||
detach_device (dev->children[i]);
|
detach_device (dev->children[i]);
|
||||||
grub_free (dev->children);
|
grub_free (dev->children);
|
||||||
|
@ -364,41 +386,105 @@ poll_nonroot_hub (grub_usb_device_t dev)
|
||||||
grub_uint64_t timeout;
|
grub_uint64_t timeout;
|
||||||
grub_usb_device_t next_dev;
|
grub_usb_device_t next_dev;
|
||||||
grub_usb_device_t *attached_devices = dev->children;
|
grub_usb_device_t *attached_devices = dev->children;
|
||||||
|
grub_uint8_t changed;
|
||||||
|
grub_size_t actual;
|
||||||
|
|
||||||
|
if (!dev->hub_transfer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
err = grub_usb_check_transfer (dev->hub_transfer, &actual);
|
||||||
|
|
||||||
|
if (err == GRUB_USB_ERR_WAIT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
changed = dev->statuschange;
|
||||||
|
|
||||||
|
dev->hub_transfer
|
||||||
|
= grub_usb_bulk_read_background (dev, dev->hub_endpoint->endp_addr,
|
||||||
|
grub_min (dev->hub_endpoint->maxpacket,
|
||||||
|
sizeof (dev->statuschange)),
|
||||||
|
(char *) &dev->statuschange);
|
||||||
|
|
||||||
|
if (err || actual == 0 || changed == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
grub_dprintf ("usb", "statuschanged = %02x, err = %d, actual = %d\n",
|
||||||
|
changed, err, actual);
|
||||||
|
|
||||||
/* Iterate over the Hub ports. */
|
/* Iterate over the Hub ports. */
|
||||||
for (i = 1; i <= dev->nports; i++)
|
for (i = 1; i <= dev->nports; i++)
|
||||||
{
|
{
|
||||||
grub_uint32_t status;
|
grub_uint32_t status;
|
||||||
|
|
||||||
|
if (!(changed & (1 << i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Get the port status. */
|
/* Get the port status. */
|
||||||
err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
|
err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
|
||||||
| GRUB_USB_REQTYPE_CLASS
|
| GRUB_USB_REQTYPE_CLASS
|
||||||
| GRUB_USB_REQTYPE_TARGET_OTHER),
|
| GRUB_USB_REQTYPE_TARGET_OTHER),
|
||||||
GRUB_USB_REQ_GET_STATUS,
|
GRUB_USB_REQ_GET_STATUS,
|
||||||
0, i, sizeof (status), (char *) &status);
|
0, i, sizeof (status), (char *) &status);
|
||||||
/* Just ignore the device if the Hub does not report the
|
|
||||||
status. */
|
|
||||||
if (err)
|
if (err)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (status & GRUB_USB_HUB_STATUS_C_CONNECTED)
|
/* FIXME: properly handle these conditions. */
|
||||||
|
if (status & GRUB_USB_HUB_STATUS_C_PORT_RESET)
|
||||||
|
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_PORT_RESET, i, 0, 0);
|
||||||
|
|
||||||
|
if (status & GRUB_USB_HUB_STATUS_C_PORT_ENABLED)
|
||||||
|
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_PORT_ENABLED, i, 0, 0);
|
||||||
|
|
||||||
|
if (status & GRUB_USB_HUB_STATUS_C_PORT_SUSPEND)
|
||||||
|
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_PORT_SUSPEND, i, 0, 0);
|
||||||
|
|
||||||
|
if (status & GRUB_USB_HUB_STATUS_C_PORT_OVERCURRENT)
|
||||||
|
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_PORT_OVERCURRENT, i, 0, 0);
|
||||||
|
|
||||||
|
if (!(status & GRUB_USB_HUB_STATUS_C_PORT_CONNECTED))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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_PORT_CONNECTED, i, 0, 0);
|
||||||
|
|
||||||
|
if (status & GRUB_USB_HUB_STATUS_C_PORT_CONNECTED)
|
||||||
{
|
{
|
||||||
detach_device (attached_devices[i-1]);
|
detach_device (attached_devices[i-1]);
|
||||||
attached_devices[i - 1] = NULL;
|
attached_devices[i - 1] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Connected and status of connection changed ? */
|
/* Connected and status of connection changed ? */
|
||||||
if ((status & GRUB_USB_HUB_STATUS_CONNECTED)
|
if ((status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)
|
||||||
&& (status & GRUB_USB_HUB_STATUS_C_CONNECTED))
|
&& (status & GRUB_USB_HUB_STATUS_C_PORT_CONNECTED))
|
||||||
{
|
{
|
||||||
grub_usb_speed_t speed;
|
grub_usb_speed_t speed;
|
||||||
|
|
||||||
/* Determine the device speed. */
|
/* Determine the device speed. */
|
||||||
if (status & GRUB_USB_HUB_STATUS_LOWSPEED)
|
if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
|
||||||
speed = GRUB_USB_SPEED_LOW;
|
speed = GRUB_USB_SPEED_LOW;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (status & GRUB_USB_HUB_STATUS_HIGHSPEED)
|
if (status & GRUB_USB_HUB_STATUS_PORT_HIGHSPEED)
|
||||||
speed = GRUB_USB_SPEED_HIGH;
|
speed = GRUB_USB_SPEED_HIGH;
|
||||||
else
|
else
|
||||||
speed = GRUB_USB_SPEED_FULL;
|
speed = GRUB_USB_SPEED_FULL;
|
||||||
|
@ -442,7 +528,7 @@ poll_nonroot_hub (grub_usb_device_t dev)
|
||||||
| GRUB_USB_REQTYPE_CLASS
|
| GRUB_USB_REQTYPE_CLASS
|
||||||
| GRUB_USB_REQTYPE_TARGET_OTHER),
|
| GRUB_USB_REQTYPE_TARGET_OTHER),
|
||||||
GRUB_USB_REQ_CLEAR_FEATURE,
|
GRUB_USB_REQ_CLEAR_FEATURE,
|
||||||
GRUB_USB_HUB_FEATURE_C_CONNECTED,
|
GRUB_USB_HUB_FEATURE_C_PORT_CONNECTED,
|
||||||
i, 0, 0);
|
i, 0, 0);
|
||||||
/* Just ignore the device if the Hub reports some error */
|
/* Just ignore the device if the Hub reports some error */
|
||||||
if (err)
|
if (err)
|
||||||
|
|
|
@ -392,6 +392,14 @@ grub_usb_bulk_read_background (grub_usb_device_t dev,
|
||||||
return transfer;
|
return transfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grub_usb_cancel_transfer (grub_usb_transfer_t transfer)
|
||||||
|
{
|
||||||
|
grub_usb_device_t dev = transfer->dev;
|
||||||
|
dev->controller.dev->cancel_transfer (&dev->controller, transfer);
|
||||||
|
grub_errno = GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
grub_usb_err_t
|
grub_usb_err_t
|
||||||
grub_usb_bulk_read_extended (grub_usb_device_t dev,
|
grub_usb_bulk_read_extended (grub_usb_device_t dev,
|
||||||
int endpoint, grub_size_t size, char *data,
|
int endpoint, grub_size_t size, char *data,
|
||||||
|
|
|
@ -181,11 +181,19 @@ struct grub_usb_device
|
||||||
/* Used by libusb wrapper. Schedulded for removal. */
|
/* Used by libusb wrapper. Schedulded for removal. */
|
||||||
void *data;
|
void *data;
|
||||||
|
|
||||||
|
/* Hub information. */
|
||||||
|
|
||||||
/* Array of children for a hub. */
|
/* Array of children for a hub. */
|
||||||
grub_usb_device_t *children;
|
grub_usb_device_t *children;
|
||||||
|
|
||||||
/* Number of hub ports. */
|
/* Number of hub ports. */
|
||||||
unsigned nports;
|
unsigned nports;
|
||||||
|
|
||||||
|
grub_usb_transfer_t hub_transfer;
|
||||||
|
|
||||||
|
grub_uint32_t statuschange;
|
||||||
|
|
||||||
|
struct grub_usb_desc_endp *hub_endpoint;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -271,5 +279,7 @@ grub_usb_bulk_read_background (grub_usb_device_t dev,
|
||||||
int endpoint, grub_size_t size, void *data);
|
int endpoint, grub_size_t size, void *data);
|
||||||
grub_usb_err_t
|
grub_usb_err_t
|
||||||
grub_usb_check_transfer (grub_usb_transfer_t trans, grub_size_t *actual);
|
grub_usb_check_transfer (grub_usb_transfer_t trans, grub_size_t *actual);
|
||||||
|
void
|
||||||
|
grub_usb_cancel_transfer (grub_usb_transfer_t trans);
|
||||||
|
|
||||||
#endif /* GRUB_USB_H */
|
#endif /* GRUB_USB_H */
|
||||||
|
|
|
@ -94,31 +94,50 @@ enum
|
||||||
GRUB_USB_REQTYPE_VENDOR_IN = GRUB_USB_REQTYPE_VENDOR | GRUB_USB_REQTYPE_IN
|
GRUB_USB_REQTYPE_VENDOR_IN = GRUB_USB_REQTYPE_VENDOR | GRUB_USB_REQTYPE_IN
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GRUB_USB_REQ_GET_STATUS 0x00
|
enum
|
||||||
#define GRUB_USB_REQ_CLEAR_FEATURE 0x01
|
{
|
||||||
#define GRUB_USB_REQ_SET_FEATURE 0x03
|
GRUB_USB_REQ_GET_STATUS = 0x00,
|
||||||
#define GRUB_USB_REQ_SET_ADDRESS 0x05
|
GRUB_USB_REQ_CLEAR_FEATURE = 0x01,
|
||||||
#define GRUB_USB_REQ_GET_DESCRIPTOR 0x06
|
GRUB_USB_REQ_SET_FEATURE = 0x03,
|
||||||
#define GRUB_USB_REQ_SET_DESCRIPTOR 0x07
|
GRUB_USB_REQ_SET_ADDRESS = 0x05,
|
||||||
#define GRUB_USB_REQ_GET_CONFIGURATION 0x08
|
GRUB_USB_REQ_GET_DESCRIPTOR = 0x06,
|
||||||
#define GRUB_USB_REQ_SET_CONFIGURATION 0x09
|
GRUB_USB_REQ_SET_DESCRIPTOR = 0x07,
|
||||||
#define GRUB_USB_REQ_GET_INTERFACE 0x0A
|
GRUB_USB_REQ_GET_CONFIGURATION = 0x08,
|
||||||
#define GRUB_USB_REQ_SET_INTERFACE 0x0B
|
GRUB_USB_REQ_SET_CONFIGURATION = 0x09,
|
||||||
#define GRUB_USB_REQ_SYNC_FRAME 0x0C
|
GRUB_USB_REQ_GET_INTERFACE = 0x0A,
|
||||||
|
GRUB_USB_REQ_SET_INTERFACE = 0x0B,
|
||||||
|
GRUB_USB_REQ_SYNC_FRAME = 0x0C
|
||||||
|
};
|
||||||
|
|
||||||
#define GRUB_USB_FEATURE_ENDP_HALT 0x00
|
#define GRUB_USB_FEATURE_ENDP_HALT 0x00
|
||||||
#define GRUB_USB_FEATURE_DEV_REMOTE_WU 0x01
|
#define GRUB_USB_FEATURE_DEV_REMOTE_WU 0x01
|
||||||
#define GRUB_USB_FEATURE_TEST_MODE 0x02
|
#define GRUB_USB_FEATURE_TEST_MODE 0x02
|
||||||
|
|
||||||
#define GRUB_USB_HUB_FEATURE_PORT_RESET 0x04
|
enum
|
||||||
#define GRUB_USB_HUB_FEATURE_PORT_POWER 0x08
|
{
|
||||||
#define GRUB_USB_HUB_FEATURE_C_CONNECTED 0x10
|
GRUB_USB_HUB_FEATURE_PORT_RESET = 0x04,
|
||||||
|
GRUB_USB_HUB_FEATURE_PORT_POWER = 0x08,
|
||||||
|
GRUB_USB_HUB_FEATURE_C_PORT_CONNECTED = 0x10,
|
||||||
|
GRUB_USB_HUB_FEATURE_C_PORT_ENABLED = 0x11,
|
||||||
|
GRUB_USB_HUB_FEATURE_C_PORT_SUSPEND = 0x12,
|
||||||
|
GRUB_USB_HUB_FEATURE_C_PORT_OVERCURRENT = 0x13,
|
||||||
|
GRUB_USB_HUB_FEATURE_C_PORT_RESET = 0x14
|
||||||
|
};
|
||||||
|
|
||||||
#define GRUB_USB_HUB_STATUS_CONNECTED (1 << 0)
|
enum
|
||||||
#define GRUB_USB_HUB_STATUS_LOWSPEED (1 << 9)
|
{
|
||||||
#define GRUB_USB_HUB_STATUS_HIGHSPEED (1 << 10)
|
GRUB_USB_HUB_STATUS_PORT_CONNECTED = (1 << 0),
|
||||||
#define GRUB_USB_HUB_STATUS_C_CONNECTED (1 << 16)
|
GRUB_USB_HUB_STATUS_PORT_ENABLED = (1 << 1),
|
||||||
#define GRUB_USB_HUB_STATUS_C_PORT_RESET (1 << 20)
|
GRUB_USB_HUB_STATUS_PORT_SUSPEND = (1 << 2),
|
||||||
|
GRUB_USB_HUB_STATUS_PORT_OVERCURRENT = (1 << 3),
|
||||||
|
GRUB_USB_HUB_STATUS_PORT_LOWSPEED = (1 << 9),
|
||||||
|
GRUB_USB_HUB_STATUS_PORT_HIGHSPEED = (1 << 10),
|
||||||
|
GRUB_USB_HUB_STATUS_C_PORT_CONNECTED = (1 << 16),
|
||||||
|
GRUB_USB_HUB_STATUS_C_PORT_ENABLED = (1 << 17),
|
||||||
|
GRUB_USB_HUB_STATUS_C_PORT_SUSPEND = (1 << 18),
|
||||||
|
GRUB_USB_HUB_STATUS_C_PORT_OVERCURRENT = (1 << 19),
|
||||||
|
GRUB_USB_HUB_STATUS_C_PORT_RESET = (1 << 20)
|
||||||
|
};
|
||||||
|
|
||||||
struct grub_usb_packet_setup
|
struct grub_usb_packet_setup
|
||||||
{
|
{
|
||||||
|
|
|
@ -107,6 +107,9 @@ grub_usb_keyboard_detach (grub_usb_device_t usbdev,
|
||||||
if (data->usbdev != usbdev)
|
if (data->usbdev != usbdev)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (data->transfer)
|
||||||
|
grub_usb_cancel_transfer (data->transfer);
|
||||||
|
|
||||||
grub_term_unregister_input (&grub_usb_keyboards[i]);
|
grub_term_unregister_input (&grub_usb_keyboards[i]);
|
||||||
grub_free ((char *) grub_usb_keyboards[i].name);
|
grub_free ((char *) grub_usb_keyboards[i].name);
|
||||||
grub_usb_keyboards[i].name = NULL;
|
grub_usb_keyboards[i].name = NULL;
|
||||||
|
@ -351,9 +354,18 @@ GRUB_MOD_FINI(usb_keyboard)
|
||||||
for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
|
for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
|
||||||
if (grub_usb_keyboards[i].data)
|
if (grub_usb_keyboards[i].data)
|
||||||
{
|
{
|
||||||
|
struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (data->transfer)
|
||||||
|
grub_usb_cancel_transfer (data->transfer);
|
||||||
|
|
||||||
grub_term_unregister_input (&grub_usb_keyboards[i]);
|
grub_term_unregister_input (&grub_usb_keyboards[i]);
|
||||||
grub_free ((char *) grub_usb_keyboards[i].name);
|
grub_free ((char *) grub_usb_keyboards[i].name);
|
||||||
grub_usb_keyboards[i].name = NULL;
|
grub_usb_keyboards[i].name = NULL;
|
||||||
|
grub_free (grub_usb_keyboards[i].data);
|
||||||
grub_usb_keyboards[i].data = 0;
|
grub_usb_keyboards[i].data = 0;
|
||||||
}
|
}
|
||||||
grub_usb_unregister_attach_hook_class (&attach_hook);
|
grub_usb_unregister_attach_hook_class (&attach_hook);
|
||||||
|
|
Loading…
Reference in a new issue