diff options
Diffstat (limited to 'payloads/libpayload/drivers')
-rw-r--r-- | payloads/libpayload/drivers/usb/usbhub.c | 204 |
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); } |