summaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb/usbhub.c
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/drivers/usb/usbhub.c')
-rw-r--r--payloads/libpayload/drivers/usb/usbhub.c204
1 files changed, 72 insertions, 132 deletions
diff --git a/payloads/libpayload/drivers/usb/usbhub.c b/payloads/libpayload/drivers/usb/usbhub.c
index 90e08dbde4..20e064e872 100644
--- a/payloads/libpayload/drivers/usb/usbhub.c
+++ b/payloads/libpayload/drivers/usb/usbhub.c
@@ -1,7 +1,7 @@
/*
* This file is part of the libpayload project.
*
- * Copyright (C) 2008-2010 coresystems GmbH
+ * Copyright (C) 2013 secunet Security Networks AG
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -28,170 +28,110 @@
*/
#include <usb/usb.h>
+#include "generic_hub.h"
-// assume that host_to_device is overwritten if necessary
+/* assume that host_to_device is overwritten if necessary */
#define DR_PORT gen_bmRequestType(host_to_device, class_type, other_recp)
-/* status bits */
+/* status (and status change) bits */
#define PORT_CONNECTION 0x1
#define PORT_ENABLE 0x2
#define PORT_RESET 0x10
-/* status change bits */
-#define C_PORT_CONNECTION 0x1
/* feature selectors (for setting / clearing features) */
#define SEL_PORT_RESET 0x4
#define SEL_PORT_POWER 0x8
#define SEL_C_PORT_CONNECTION 0x10
-typedef struct {
- int num_ports;
- int *ports;
- hub_descriptor_t *descriptor;
-} usbhub_inst_t;
-
-#define HUB_INST(dev) ((usbhub_inst_t*)(dev)->data)
-
-static void
-usb_hub_destroy (usbdev_t *dev)
+static int
+usb_hub_port_status_changed(usbdev_t *const dev, const int port)
{
- int i;
-
- /* First, detach all devices behind this hub. */
- int *const ports = HUB_INST (dev)->ports;
- for (i = 1; i <= HUB_INST (dev)->num_ports; i++) {
- if (ports[i] != -1) {
- usb_detach_device(dev->controller, ports[i]);
- ports[i] = -1;
- }
- }
-
- free (HUB_INST (dev)->ports);
- free (HUB_INST (dev)->descriptor);
- free (HUB_INST (dev));
+ unsigned short buf[2] = { 0, 0 };
+ get_status (dev, port, DR_PORT, 4, buf);
+ clear_feature (dev, port, SEL_C_PORT_CONNECTION, DR_PORT);
+ return buf[1] & PORT_CONNECTION;
}
-static void
-usb_hub_scanport (usbdev_t *dev, int port)
+static int
+usb_hub_port_connected(usbdev_t *const dev, const int port)
{
- int timeout;
-
- unsigned short buf[2];
+ unsigned short buf[2] = { 0, 0 };
get_status (dev, port, DR_PORT, 4, buf);
- if (!(buf[1] & C_PORT_CONNECTION))
- /* no change */
- return;
-
- /* clear Port Connection status change */
- clear_feature (dev, port, SEL_C_PORT_CONNECTION, DR_PORT);
-
- int devno = HUB_INST (dev)->ports[port];
- if (devno != -1) {
- /* detach device, either because of re-/ or disconnect */
- usb_detach_device(dev->controller, devno);
- HUB_INST (dev)->ports[port] = -1;
- }
-
- if (!(buf[0] & PORT_CONNECTION))
- /* no device connected, nothing to do */
- return;
-
- /* wait 100ms for port to become stable */
- mdelay (100); // usb20 spec 9.1.2
-
- /* reset port */
- set_feature (dev, port, SEL_PORT_RESET, DR_PORT);
- /* wait at least 10ms (usb2.0 spec 11.5.1.5: 10ms to 20ms) */
- mdelay (10);
- /* wait for hub to finish reset */
- timeout = 30; /* time out after 10ms (spec) + 20ms (kindly) */
- do {
- get_status (dev, port, DR_PORT, 4, buf);
- mdelay(1); timeout--;
- } while ((buf[0] & PORT_RESET) && timeout);
- if (!timeout)
- usb_debug("Warning: usbhub: port reset timed out.\n");
-
- /* wait for port to be enabled. the hub is responsible for this */
- timeout = 500; /* time out after 500ms */
- do {
- get_status (dev, port, DR_PORT, 4, buf);
- mdelay(1); timeout--;
- } while (!(buf[0] & PORT_ENABLE) && timeout);
- if (!timeout)
- usb_debug("Warning: usbhub: port enabling timed out.\n");
+ return buf[0] & PORT_CONNECTION;
+}
+static int
+usb_hub_port_in_reset(usbdev_t *const dev, const int port)
+{
+ unsigned short buf[2] = { 0, 0 };
get_status (dev, port, DR_PORT, 4, buf);
- /* bit 10 9
- * 0 0 full speed
- * 0 1 low speed
- * 1 0 high speed
- */
- int speed = ((buf[0] >> 9) & 3) ;
-
- HUB_INST (dev)->ports[port] = usb_attach_device(dev->controller, dev->address, port, speed);
-
- /* clear Port Connection status change */
- clear_feature (dev, port, SEL_C_PORT_CONNECTION, DR_PORT);
+ return buf[0] & PORT_RESET;
}
static int
-usb_hub_report_port_changes (usbdev_t *dev)
+usb_hub_port_enabled(usbdev_t *const dev, const int port)
{
- int port;
- unsigned short buf[2];
- for (port = 1; port <= HUB_INST (dev)->num_ports; port++) {
- get_status (dev, port, DR_PORT, 4, buf);
- if (buf[1] & C_PORT_CONNECTION)
- return port;
- }
-
- // no change
- return -1;
+ unsigned short buf[2] = { 0, 0 };
+ get_status (dev, port, DR_PORT, 4, buf);
+ return (buf[0] & PORT_ENABLE) != 0;
}
-static void
-usb_hub_enable_port (usbdev_t *dev, int port)
+static int
+usb_hub_port_speed(usbdev_t *const dev, const int port)
{
- set_feature (dev, port, SEL_PORT_POWER, DR_PORT);
- mdelay (20);
+ unsigned short buf[2] = { 0, 0 };
+ get_status (dev, port, DR_PORT, 4, buf);
+ if (buf[0] & PORT_ENABLE) {
+ /* bit 10 9
+ * 0 0 full speed
+ * 0 1 low speed
+ * 1 0 high speed
+ */
+ return (buf[0] >> 9) & 0x3;
+ } else {
+ return -1;
+ }
}
-#if 0
-static void
-usb_hub_disable_port (usbdev_t *dev, int port)
+static int
+usb_hub_enable_port(usbdev_t *const dev, const int port)
{
+ set_feature(dev, port, SEL_PORT_POWER, DR_PORT);
+ return 0;
}
-#endif
-static void
-usb_hub_poll (usbdev_t *dev)
+static int
+usb_hub_start_port_reset(usbdev_t *const dev, const int port)
{
- int port;
- while ((port = usb_hub_report_port_changes (dev)) != -1)
- usb_hub_scanport (dev, port);
+ set_feature (dev, port, SEL_PORT_RESET, DR_PORT);
+ return 0;
}
+static const generic_hub_ops_t usb_hub_ops = {
+ .hub_status_changed = NULL,
+ .port_status_changed = usb_hub_port_status_changed,
+ .port_connected = usb_hub_port_connected,
+ .port_in_reset = usb_hub_port_in_reset,
+ .port_enabled = usb_hub_port_enabled,
+ .port_speed = usb_hub_port_speed,
+ .enable_port = usb_hub_enable_port,
+ .disable_port = NULL,
+ .start_port_reset = usb_hub_start_port_reset,
+ .reset_port = generic_hub_resetport,
+};
+
void
-usb_hub_init (usbdev_t *dev)
+usb_hub_init(usbdev_t *const dev)
{
- int i;
- dev->destroy = usb_hub_destroy;
- dev->poll = usb_hub_poll;
-
- dev->data = malloc (sizeof (usbhub_inst_t));
-
- if (!dev->data)
- fatal("Not enough memory for USB hub.\n");
-
- HUB_INST (dev)->descriptor = (hub_descriptor_t *) get_descriptor(dev,
- gen_bmRequestType(device_to_host, class_type, dev_recp), 0x29, 0, 0);
- HUB_INST (dev)->num_ports = HUB_INST (dev)->descriptor->bNbrPorts;
- HUB_INST (dev)->ports =
- malloc (sizeof (int) * (HUB_INST (dev)->num_ports + 1));
- if (! HUB_INST (dev)->ports)
- fatal("Not enough memory for USB hub ports.\n");
+ hub_descriptor_t *const descriptor = (hub_descriptor_t *)
+ get_descriptor(
+ dev,
+ gen_bmRequestType(device_to_host, class_type, dev_recp),
+ 0x29, 0, 0);
+ if (!descriptor) {
+ usb_debug("usbhub: ERROR: Failed to fetch hub descriptor\n");
+ return;
+ }
+ const int num_ports = descriptor->bNbrPorts;
+ free(descriptor);
- for (i = 1; i <= HUB_INST (dev)->num_ports; i++)
- HUB_INST (dev)->ports[i] = -1;
- for (i = 1; i <= HUB_INST (dev)->num_ports; i++)
- usb_hub_enable_port (dev, i);
+ generic_hub_init(dev, num_ports, &usb_hub_ops);
}