}
static int
-set_port_feature(struct usb_pipe *pipe, int port, int feature)
+set_port_feature(struct usbhub_s *hub, int port, int feature)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
req.wValue = feature;
req.wIndex = port;
req.wLength = 0;
- return send_default_control(pipe, &req, NULL);
+ mutex_lock(&hub->lock);
+ int ret = send_default_control(hub->pipe, &req, NULL);
+ mutex_unlock(&hub->lock);
+ return ret;
}
static int
-clear_port_feature(struct usb_pipe *pipe, int port, int feature)
+clear_port_feature(struct usbhub_s *hub, int port, int feature)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
req.wValue = feature;
req.wIndex = port;
req.wLength = 0;
- return send_default_control(pipe, &req, NULL);
+ mutex_lock(&hub->lock);
+ int ret = send_default_control(hub->pipe, &req, NULL);
+ mutex_unlock(&hub->lock);
+ return ret;
}
static int
-get_port_status(struct usb_pipe *pipe, int port, struct usb_port_status *sts)
+get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
req.wValue = 0;
req.wIndex = port;
req.wLength = sizeof(*sts);
- return send_default_control(pipe, &req, sts);
+ mutex_lock(&hub->lock);
+ int ret = send_default_control(hub->pipe, &req, sts);
+ mutex_unlock(&hub->lock);
+ return ret;
+}
+
+static void
+init_hub_port(void *data)
+{
+ struct usbhub_s *hub = data;
+ u32 port = hub->port; // XXX - find better way to pass port
+
+ // Turn on power to port.
+ int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER);
+ if (ret)
+ goto fail;
+
+ // Wait for port power to stabilize.
+ msleep(hub->powerwait);
+
+ // Check periodically for a device connect.
+ struct usb_port_status sts;
+ u64 end = calc_future_tsc(USB_TIME_SIGATT);
+ for (;;) {
+ ret = get_port_status(hub, port, &sts);
+ if (ret)
+ goto fail;
+ if (sts.wPortStatus & USB_PORT_STAT_CONNECTION)
+ // Device connected.
+ break;
+ if (check_time(end))
+ // No device found.
+ goto done;
+ msleep(5);
+ }
+
+ // XXX - wait USB_TIME_ATTDB time?
+
+ // Reset port.
+ mutex_lock(&hub->cntl->resetlock);
+ ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
+ if (ret)
+ goto resetfail;
+
+ // Wait for reset to complete.
+ end = calc_future_tsc(USB_TIME_DRST * 2);
+ for (;;) {
+ ret = get_port_status(hub, port, &sts);
+ if (ret)
+ goto resetfail;
+ if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
+ break;
+ if (check_time(end)) {
+ warn_timeout();
+ goto resetfail;
+ }
+ msleep(5);
+ }
+
+ // Reset complete.
+ if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
+ // Device no longer present
+ goto resetfail;
+
+ // Set address of port
+ struct usb_pipe *pipe = usb_set_address(
+ hub->cntl, !!(sts.wPortStatus & USB_PORT_STAT_LOW_SPEED));
+ if (!pipe)
+ goto resetfail;
+ mutex_unlock(&hub->cntl->resetlock);
+
+ // Configure the device
+ int count = configure_usb_device(pipe);
+ free_pipe(pipe);
+ if (!count) {
+ ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
+ if (ret)
+ goto fail;
+ }
+ hub->devcount += count;
+done:
+ hub->threads--;
+ return;
+
+resetfail:
+ clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
+ mutex_unlock(&hub->cntl->resetlock);
+fail:
+ dprintf(1, "Failure on hub port %d setup\n", port);
+ goto done;
}
// Configure a usb hub and then find devices connected to it.
int
usb_hub_init(struct usb_pipe *pipe)
{
+ ASSERT32FLAT();
if (!CONFIG_USB_HUB)
return -1;
if (ret)
return ret;
- // Turn on power to all ports.
+ struct usbhub_s hub;
+ memset(&hub, 0, sizeof(hub));
+ hub.pipe = pipe;
+ hub.cntl = endp2cntl(pipe->endp);
+ hub.powerwait = desc.bPwrOn2PwrGood * 2;
+
+ // Launch a thread for every port.
int i;
for (i=1; i<=desc.bNbrPorts; i++) {
- ret = set_port_feature(pipe, i, USB_PORT_FEAT_POWER);
- if (ret)
- goto fail;
+ hub.port = i;
+ hub.threads++;
+ run_thread(init_hub_port, &hub);
}
- // Wait for port detection.
- msleep(desc.bPwrOn2PwrGood * 2 + USB_TIME_SIGATT);
- // XXX - should poll for ports becoming active sooner and then
- // possibly wait USB_TIME_ATTDB.
-
- // Detect down stream devices.
- struct usb_s *cntl = endp2cntl(pipe->endp);
- int totalcount = 0;
- for (i=1; i<=desc.bNbrPorts; i++) {
- struct usb_port_status sts;
- ret = get_port_status(pipe, i, &sts);
- if (ret)
- goto fail;
- if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
- // XXX - power down port?
- continue;
+ // Wait for threads to complete.
+ while (hub.threads)
+ yield();
- // Reset port.
- ret = set_port_feature(pipe, i, USB_PORT_FEAT_RESET);
- if (ret)
- goto fail;
-
- // Wait for reset to complete.
- u64 end = calc_future_tsc(USB_TIME_DRST * 2);
- for (;;) {
- ret = get_port_status(pipe, i, &sts);
- if (ret)
- goto fail;
- if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
- break;
- if (check_time(end)) {
- // Timeout.
- warn_timeout();
- goto fail;
- }
- yield();
- }
- if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
- // Device no longer present. XXX - power down port?
- continue;
-
- // XXX - should try to parallelize configuration.
- int count = configure_usb_device(
- cntl, !!(sts.wPortStatus & USB_PORT_STAT_LOW_SPEED));
- if (! count) {
- // Shutdown port
- ret = clear_port_feature(pipe, i, USB_PORT_FEAT_ENABLE);
- if (ret)
- goto fail;
- }
- totalcount += count;
- }
-
- dprintf(1, "Initialized USB HUB (%d ports used)\n", totalcount);
- if (totalcount)
+ dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
+ if (hub.devcount)
return 0;
return -1;
-
-fail:
- dprintf(1, "Failure on hub setup\n");
- return -1;
}
#ifndef __USB_HUB_H
#define __USB_HUB_H
+struct usbhub_s {
+ struct usb_pipe *pipe;
+ struct usb_s *cntl;
+ struct mutex_s lock;
+ u32 powerwait;
+ u32 port;
+ u32 threads;
+ u32 devcount;
+};
+
// usb-hub.c
struct usb_pipe;
int usb_hub_init(struct usb_pipe *pipe);
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
#include "usb.h" // struct usb_s
#include "farptr.h" // GET_FLATPTR
+#include "usb-hub.h" // struct usbhub_s
#define FIT (1 << 31)
+/****************************************************************
+ * Root hub
+ ****************************************************************/
+
+static void
+init_ohci_port(void *data)
+{
+ struct usbhub_s *hub = data;
+ u32 port = hub->port; // XXX - find better way to pass port
+
+ u32 sts = readl(&hub->cntl->ohci.regs->roothub_portstatus[port]);
+ if (!(sts & RH_PS_CCS))
+ // No device.
+ goto done;
+
+ // XXX - need to wait for USB_TIME_ATTDB if just powered up?
+
+ // Signal reset
+ mutex_lock(&hub->cntl->resetlock);
+ writel(&hub->cntl->ohci.regs->roothub_portstatus[port], RH_PS_PRS);
+ u64 end = calc_future_tsc(USB_TIME_DRSTR * 2);
+ for (;;) {
+ sts = readl(&hub->cntl->ohci.regs->roothub_portstatus[port]);
+ if (!(sts & RH_PS_PRS))
+ // XXX - need to ensure USB_TIME_DRSTR time in reset?
+ break;
+ if (check_time(end)) {
+ // Timeout.
+ warn_timeout();
+ goto resetfail;
+ }
+ yield();
+ }
+
+ if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES))
+ // Device no longer present
+ goto resetfail;
+
+ // Set address of port
+ struct usb_pipe *pipe = usb_set_address(hub->cntl, !!(sts & RH_PS_LSDA));
+ if (!pipe)
+ goto resetfail;
+ mutex_unlock(&hub->cntl->resetlock);
+
+ // Configure the device
+ int count = configure_usb_device(pipe);
+ free_pipe(pipe);
+ if (! count)
+ // Shutdown port
+ writel(&hub->cntl->ohci.regs->roothub_portstatus[port]
+ , RH_PS_CCS|RH_PS_LSDA);
+ hub->devcount += count;
+done:
+ hub->threads--;
+ return;
+
+resetfail:
+ // Shutdown port
+ writel(&hub->cntl->ohci.regs->roothub_portstatus[port]
+ , RH_PS_CCS|RH_PS_LSDA);
+ mutex_unlock(&hub->cntl->resetlock);
+ goto done;
+}
+
+// Find any devices connected to the root hub.
+static int
+check_ohci_ports(struct usb_s *cntl)
+{
+ ASSERT32FLAT();
+ // Turn on power for all devices on roothub.
+ u32 rha = readl(&cntl->ohci.regs->roothub_a);
+ rha &= ~(RH_A_PSM | RH_A_OCPM);
+ writel(&cntl->ohci.regs->roothub_status, RH_HS_LPSC);
+ writel(&cntl->ohci.regs->roothub_b, RH_B_PPCM);
+ msleep((rha >> 24) * 2);
+ // XXX - need to sleep for USB_TIME_SIGATT if just powered up?
+
+ // Lanuch a thread per port.
+ struct usbhub_s hub;
+ memset(&hub, 0, sizeof(hub));
+ hub.cntl = cntl;
+ int ports = rha & RH_A_NDP;
+ hub.threads = ports;
+ int i;
+ for (i=0; i<ports; i++) {
+ hub.port = i;
+ run_thread(init_ohci_port, &hub);
+ }
+
+ // Wait for threads to complete.
+ while (hub.threads)
+ yield();
+
+ return hub.devcount;
+}
+
+
/****************************************************************
* Setup
****************************************************************/
readl(&cntl->ohci.regs->control); // flush writes
}
-// Find any devices connected to the root hub.
-static int
-check_ohci_ports(struct usb_s *cntl)
-{
- // Turn on power for all devices on roothub.
- u32 rha = readl(&cntl->ohci.regs->roothub_a);
- rha &= ~(RH_A_PSM | RH_A_OCPM);
- writel(&cntl->ohci.regs->roothub_status, RH_HS_LPSC);
- writel(&cntl->ohci.regs->roothub_b, RH_B_PPCM);
- msleep((rha >> 24) * 2);
- // XXX - need to sleep for USB_TIME_SIGATT if just powered up?
-
- // Count and reset connected devices
- int ports = rha & RH_A_NDP;
- int totalcount = 0;
- int i;
- for (i=0; i<ports; i++) {
- u32 sts = readl(&cntl->ohci.regs->roothub_portstatus[i]);
- if (!(sts & RH_PS_CCS))
- continue;
- // XXX - need to wait for USB_TIME_ATTDB if just powered up?
- writel(&cntl->ohci.regs->roothub_portstatus[i], RH_PS_PRS);
- u64 end = calc_future_tsc(USB_TIME_DRSTR * 2);
- for (;;) {
- sts = readl(&cntl->ohci.regs->roothub_portstatus[i]);
- if (!(sts & RH_PS_PRS))
- // XXX - need to ensure USB_TIME_DRSTR time in reset?
- break;
- if (check_time(end)) {
- // Timeout.
- warn_timeout();
- goto shutdown;
- }
- yield();
- }
-
- if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES))
- // Device no longer present
- continue;
-
- msleep(USB_TIME_RSTRCY);
-
- // XXX - should try to parallelize configuration.
- int count = configure_usb_device(cntl, !!(sts & RH_PS_LSDA));
- if (! count)
- // Shutdown port
- writel(&cntl->ohci.regs->roothub_portstatus[i]
- , RH_PS_CCS|RH_PS_LSDA);
- totalcount += count;
- }
- if (!totalcount)
- // No devices connected
- goto shutdown;
- return totalcount;
-
-shutdown:
- // Turn off power to all ports
- writel(&cntl->ohci.regs->roothub_status, RH_HS_LPS);
- return 0;
-}
-
void
ohci_init(void *data)
{
#include "usb.h" // struct usb_s
#include "farptr.h" // GET_FLATPTR
#include "biosvar.h" // GET_GLOBAL
+#include "usb-hub.h" // struct usbhub_s
+
+
+/****************************************************************
+ * Root hub
+ ****************************************************************/
+
+static void
+init_uhci_port(void *data)
+{
+ struct usbhub_s *hub = data;
+ u32 port = hub->port; // XXX - find better way to pass port
+ u16 ioport = hub->cntl->uhci.iobase + USBPORTSC1 + port * 2;
+
+ u16 status = inw(ioport);
+ if (!(status & USBPORTSC_CCS))
+ // No device
+ goto done;
+
+ // XXX - if just powered up, need to wait for USB_TIME_ATTDB?
+
+ // Reset port
+ outw(USBPORTSC_PR, ioport);
+ msleep(USB_TIME_DRSTR);
+ mutex_lock(&hub->cntl->resetlock);
+ outw(0, ioport);
+ udelay(6); // 64 high-speed bit times
+ status = inw(ioport);
+ if (!(status & USBPORTSC_CCS))
+ // No longer connected
+ goto resetfail;
+ outw(USBPORTSC_PE, ioport);
+ struct usb_pipe *pipe = usb_set_address(
+ hub->cntl, !!(status & USBPORTSC_LSDA));
+ if (!pipe)
+ goto resetfail;
+ mutex_unlock(&hub->cntl->resetlock);
+
+ // Configure port
+ int count = configure_usb_device(pipe);
+ free_pipe(pipe);
+ if (! count)
+ // Disable port
+ outw(0, ioport);
+ hub->devcount += count;
+done:
+ hub->threads--;
+ return;
+
+resetfail:
+ outw(0, ioport);
+ mutex_unlock(&hub->cntl->resetlock);
+ goto done;
+}
+
+// Find any devices connected to the root hub.
+static int
+check_ports(struct usb_s *cntl)
+{
+ ASSERT32FLAT();
+ struct usbhub_s hub;
+ memset(&hub, 0, sizeof(hub));
+ hub.cntl = cntl;
+ hub.threads = 2;
+
+ // Launch a thread for every port.
+ run_thread(init_uhci_port, &hub);
+ hub.port = 1;
+ run_thread(init_uhci_port, &hub);
+
+ // Wait for threads to complete.
+ while (hub.threads)
+ yield();
+
+ return hub.devcount;
+}
/****************************************************************
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->uhci.iobase + USBCMD);
}
-// Find any devices connected to the root hub.
-static int
-check_ports(struct usb_s *cntl)
-{
- // XXX - if just powered up, need to wait for USB_TIME_SIGATT?
- u16 port1 = inw(cntl->uhci.iobase + USBPORTSC1);
- u16 port2 = inw(cntl->uhci.iobase + USBPORTSC2);
-
- if (!((port1 & USBPORTSC_CCS) || (port2 & USBPORTSC_CCS)))
- // No devices
- return 0;
-
- // XXX - if just powered up, need to wait for USB_TIME_ATTDB?
-
- // reset ports
- if (port1 & USBPORTSC_CCS)
- outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC1);
- if (port2 & USBPORTSC_CCS)
- outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC2);
- msleep(USB_TIME_DRSTR);
-
- // Configure ports
- int totalcount = 0;
- outw(0, cntl->uhci.iobase + USBPORTSC1);
- udelay(6); // 64 high-speed bit times
- port1 = inw(cntl->uhci.iobase + USBPORTSC1);
- if (port1 & USBPORTSC_CCS) {
- outw(USBPORTSC_PE, cntl->uhci.iobase + USBPORTSC1);
- msleep(USB_TIME_RSTRCY);
- int count = configure_usb_device(cntl, !!(port1 & USBPORTSC_LSDA));
- if (! count)
- outw(0, cntl->uhci.iobase + USBPORTSC1);
- totalcount += count;
- }
- outw(0, cntl->uhci.iobase + USBPORTSC2);
- udelay(6);
- port2 = inw(cntl->uhci.iobase + USBPORTSC2);
- if (port2 & USBPORTSC_CCS) {
- outw(USBPORTSC_PE, cntl->uhci.iobase + USBPORTSC2);
- msleep(USB_TIME_RSTRCY);
- int count = configure_usb_device(cntl, !!(port2 & USBPORTSC_LSDA));
- if (! count)
- outw(0, cntl->uhci.iobase + USBPORTSC2);
- totalcount += count;
- }
- return totalcount;
-}
-
void
uhci_init(void *data)
{
return config;
}
-static struct usb_pipe *
-set_address(struct usb_pipe *pipe)
-{
- ASSERT32FLAT();
- dprintf(3, "set_address %x\n", pipe->endp);
- struct usb_s *cntl = endp2cntl(pipe->endp);
- if (cntl->maxaddr >= USB_MAXADDR)
- return 0;
-
- struct usb_ctrlrequest req;
- req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
- req.bRequest = USB_REQ_SET_ADDRESS;
- req.wValue = cntl->maxaddr + 1;
- req.wIndex = 0;
- req.wLength = 0;
- int ret = send_default_control(pipe, &req, NULL);
- if (ret)
- return 0;
- msleep(USB_TIME_SETADDR_RECOVERY);
-
- cntl->maxaddr++;
- u32 endp = mkendp(cntl, cntl->maxaddr, 0
- , endp2speed(pipe->endp), endp2maxsize(pipe->endp));
- return alloc_control_pipe(endp);
-}
-
static int
set_configuration(struct usb_pipe *pipe, u16 val)
{
* Initialization and enumeration
****************************************************************/
-// Called for every found device - see if a driver is available for
-// this device and do setup if so.
-int
-configure_usb_device(struct usb_s *cntl, int lowspeed)
+// Assign an address to a device in the default state on the given
+// controller.
+struct usb_pipe *
+usb_set_address(struct usb_s *cntl, int lowspeed)
{
ASSERT32FLAT();
- dprintf(3, "config_usb: %p %d\n", cntl, lowspeed);
+ dprintf(3, "set_address %p\n", cntl);
+ if (cntl->maxaddr >= USB_MAXADDR)
+ return NULL;
- // Get device info
struct usb_pipe *defpipe = cntl->defaultpipe;
u32 endp = mkendp(cntl, 0, 0, lowspeed, 8);
if (!defpipe) {
cntl->defaultpipe = defpipe = alloc_control_pipe(endp);
if (!defpipe)
- return 0;
+ return NULL;
}
usb_alter_control(defpipe, endp);
+
+ msleep(USB_TIME_RSTRCY);
+
+ struct usb_ctrlrequest req;
+ req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+ req.bRequest = USB_REQ_SET_ADDRESS;
+ req.wValue = cntl->maxaddr + 1;
+ req.wIndex = 0;
+ req.wLength = 0;
+ int ret = send_default_control(defpipe, &req, NULL);
+ if (ret)
+ return NULL;
+
+ msleep(USB_TIME_SETADDR_RECOVERY);
+
+ cntl->maxaddr++;
+ endp = mkendp(cntl, cntl->maxaddr, 0, lowspeed, 8);
+ return alloc_control_pipe(endp);
+}
+
+// Called for every found device - see if a driver is available for
+// this device and do setup if so.
+int
+configure_usb_device(struct usb_pipe *pipe)
+{
+ ASSERT32FLAT();
+ struct usb_s *cntl = endp2cntl(pipe->endp);
+ dprintf(3, "config_usb: %p\n", cntl);
+
+ // Set the max packet size for endpoint 0 of this device.
struct usb_device_descriptor dinfo;
- int ret = get_device_info8(defpipe, &dinfo);
+ int ret = get_device_info8(pipe, &dinfo);
if (ret)
return 0;
dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%02x\n"
, dinfo.bDeviceProtocol, dinfo.bMaxPacketSize0);
if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64)
return 0;
- endp = mkendp(cntl, 0, 0, lowspeed, dinfo.bMaxPacketSize0);
- usb_alter_control(defpipe, endp);
+ u32 endp = mkendp(cntl, endp2devaddr(pipe->endp), 0
+ , endp2speed(pipe->endp), dinfo.bMaxPacketSize0);
+ usb_alter_control(pipe, endp);
// Get configuration
- struct usb_pipe *pipe = NULL;
- struct usb_config_descriptor *config = get_device_config(defpipe);
+ struct usb_config_descriptor *config = get_device_config(pipe);
if (!config)
return 0;
// Not a supported device.
goto fail;
- // Set the address and configure device.
- pipe = set_address(defpipe);
- if (!pipe)
- goto fail;
+ // Set the configuration.
ret = set_configuration(pipe, config->bConfigurationValue);
if (ret)
goto fail;
if (ret)
goto fail;
- free_pipe(pipe);
free(config);
return 1;
fail:
#ifndef __USB_H
#define __USB_H
+#include "util.h" // struct mutex_s
+
struct usb_pipe {
u32 endp;
};
u8 maxaddr;
u16 bdf;
struct usb_pipe *defaultpipe;
+ struct mutex_s resetlock;
union {
struct {
// usb.c
void usb_setup(void);
-int configure_usb_device(struct usb_s *cntl, int lowspeed);
+struct usb_pipe *usb_set_address(struct usb_s *cntl, int lowspeed);
+int configure_usb_device(struct usb_pipe *pipe);
struct usb_ctrlrequest;
int send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req
, void *data);