From d28b0fe8db0b5af2f4ddeeb48c01fa0c1ed7a920 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sun, 28 Mar 2010 15:11:19 -0400 Subject: [PATCH] Refactor USB hub code. All four implementations of hubs (and root hubs) were very similar. Replace them with a single implementation that uses callbacks for the three custom parts (detect, reset, disconnect) of each type of hub. --- src/usb-ehci.c | 100 +++++++++++++++++++++++----------------------- src/usb-hub.c | 106 +++++++++++++++++++++++-------------------------- src/usb-hub.h | 10 ----- src/usb-ohci.c | 80 ++++++++++++++++--------------------- src/usb-uhci.c | 77 +++++++++++++++++------------------ src/usb.c | 64 ++++++++++++++++++++++++++++- src/usb.h | 25 ++++++++++-- 7 files changed, 253 insertions(+), 209 deletions(-) diff --git a/src/usb-ehci.c b/src/usb-ehci.c index 8e3219f..1f683c8 100644 --- a/src/usb-ehci.c +++ b/src/usb-ehci.c @@ -13,7 +13,6 @@ #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 #include "usb-uhci.h" // init_uhci #include "usb-ohci.h" // init_ohci @@ -40,13 +39,17 @@ struct usb_ehci_s { #define EHCI_TIME_POSTPOWER 20 #define EHCI_TIME_POSTRESET 2 -// Start processing of companion controllers for full/low speed devices +// Check if need companion controllers for full/low speed devices static void -ehci_startcompanion(struct usb_ehci_s *cntl) +ehci_note_port(struct usb_ehci_s *cntl) { + if (--cntl->checkports) + // Ports still being detected. + return; if (! cntl->legacycount) // No full/low speed devices found. return; + // Start companion controllers. int i; for (i=0; icompanion); i++) { u16 type = cntl->companion[i].type; @@ -59,13 +62,11 @@ ehci_startcompanion(struct usb_ehci_s *cntl) } } -static void -init_ehci_port(void *data) +// Check if device attached to port +static int +ehci_hub_detect(struct usbhub_s *hub, u32 port) { - struct usbhub_s *hub = data; - u32 port = hub->port; // XXX - find better way to pass port struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); - u32 *portreg = &cntl->regs->portsc[port]; u32 portsc = readl(portreg); @@ -92,15 +93,31 @@ init_ehci_port(void *data) // XXX - if just powered up, need to wait for USB_TIME_ATTDB? - // Reset port + // Begin reset on port portsc = (portsc & ~PORT_PE) | PORT_RESET; writel(portreg, portsc); msleep(USB_TIME_DRSTR); - mutex_lock(&cntl->usb.resetlock); + return 0; + +doneearly: + ehci_note_port(cntl); + return -1; +} + +// Reset device on port +static int +ehci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + + // Finish reset on port portsc &= ~PORT_RESET; writel(portreg, portsc); msleep(EHCI_TIME_POSTRESET); + int rv = -1; portsc = readl(portreg); if (!(portsc & PORT_CONNECT)) // No longer connected @@ -112,58 +129,39 @@ init_ehci_port(void *data) goto resetfail; } - if (! --cntl->checkports) - ehci_startcompanion(cntl); - - struct usb_pipe *pipe = usb_set_address(hub, port, USB_HIGHSPEED); - if (!pipe) { - writel(portreg, portsc & ~PORT_PE); - mutex_unlock(&cntl->usb.resetlock); - goto done; - } - mutex_unlock(&cntl->usb.resetlock); - - // Configure port - int count = configure_usb_device(pipe); - free_pipe(pipe); - if (! count) - // Disable port - writel(portreg, portsc & ~PORT_PE); - hub->devcount += count; -done: - hub->threads--; - return; - + rv = USB_HIGHSPEED; resetfail: - mutex_unlock(&cntl->usb.resetlock); -doneearly: - if (! --cntl->checkports) - ehci_startcompanion(cntl); - goto done; + ehci_note_port(cntl); + return rv; +} + +// Disable port +static void +ehci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + writel(portreg, portsc & ~PORT_PE); } +static struct usbhub_op_s ehci_HubOp = { + .detect = ehci_hub_detect, + .reset = ehci_hub_reset, + .disconnect = ehci_hub_disconnect, +}; + // Find any devices connected to the root hub. static int check_ehci_ports(struct usb_ehci_s *cntl) { ASSERT32FLAT(); - - // Launch a thread for every port. struct usbhub_s hub; memset(&hub, 0, sizeof(hub)); hub.cntl = &cntl->usb; - int ports = cntl->checkports; - hub.threads = ports; - int i; - for (i=0; icheckports; + hub.op = &ehci_HubOp; + usb_enumerate(&hub); return hub.devcount; } diff --git a/src/usb-hub.c b/src/usb-hub.c index 9effbc3..1586bac 100644 --- a/src/usb-hub.c +++ b/src/usb-hub.c @@ -28,7 +28,7 @@ set_port_feature(struct usbhub_s *hub, int port, int feature) req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; req.bRequest = USB_REQ_SET_FEATURE; req.wValue = feature; - req.wIndex = port; + req.wIndex = port + 1; req.wLength = 0; mutex_lock(&hub->lock); int ret = send_default_control(hub->pipe, &req, NULL); @@ -43,7 +43,7 @@ clear_port_feature(struct usbhub_s *hub, int port, int feature) req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; req.bRequest = USB_REQ_CLEAR_FEATURE; req.wValue = feature; - req.wIndex = port; + req.wIndex = port + 1; req.wLength = 0; mutex_lock(&hub->lock); int ret = send_default_control(hub->pipe, &req, NULL); @@ -58,7 +58,7 @@ get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts) req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER; req.bRequest = USB_REQ_GET_STATUS; req.wValue = 0; - req.wIndex = port; + req.wIndex = port + 1; req.wLength = sizeof(*sts); mutex_lock(&hub->lock); int ret = send_default_control(hub->pipe, &req, sts); @@ -66,12 +66,10 @@ get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts) return ret; } -static void -init_hub_port(void *data) +// Check if device attached to port +static int +usb_hub_detect(struct usbhub_s *hub, u32 port) { - 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) @@ -92,29 +90,48 @@ init_hub_port(void *data) break; if (check_time(end)) // No device found. - goto done; + return -1; 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); + return 0; + +fail: + dprintf(1, "Failure on hub port %d detect\n", port); + return -1; +} + +// Disable port +static void +usb_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE); + if (ret) + dprintf(1, "Failure on hub port %d disconnect\n", port); +} + +// Reset device on port +static int +usb_hub_reset(struct usbhub_s *hub, u32 port) +{ + int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET); if (ret) - goto resetfail; + goto fail; // Wait for reset to complete. - end = calc_future_tsc(USB_TIME_DRST * 2); + struct usb_port_status sts; + u64 end = calc_future_tsc(USB_TIME_DRST * 2); for (;;) { ret = get_port_status(hub, port, &sts); if (ret) - goto resetfail; + goto fail; if (!(sts.wPortStatus & USB_PORT_STAT_RESET)) break; if (check_time(end)) { warn_timeout(); - goto resetfail; + goto fail; } msleep(5); } @@ -122,37 +139,23 @@ init_hub_port(void *data) // 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, port, ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) - >> USB_PORT_STAT_SPEED_SHIFT)); - 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); + return -1; + + return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) + >> USB_PORT_STAT_SPEED_SHIFT); + fail: - dprintf(1, "Failure on hub port %d setup\n", port); - goto done; + dprintf(1, "Failure on hub port %d reset\n", port); + usb_hub_disconnect(hub, port); + return -1; } +static struct usbhub_op_s HubOp = { + .detect = usb_hub_detect, + .reset = usb_hub_reset, + .disconnect = usb_hub_disconnect, +}; + // Configure a usb hub and then find devices connected to it. int usb_hub_init(struct usb_pipe *pipe) @@ -171,18 +174,9 @@ usb_hub_init(struct usb_pipe *pipe) hub.pipe = pipe; hub.cntl = pipe->cntl; hub.powerwait = desc.bPwrOn2PwrGood * 2; - - // Launch a thread for every port. - int i; - for (i=1; i<=desc.bNbrPorts; i++) { - hub.port = i; - hub.threads++; - run_thread(init_hub_port, &hub); - } - - // Wait for threads to complete. - while (hub.threads) - yield(); + hub.portcount = desc.bNbrPorts; + hub.op = &HubOp; + usb_enumerate(&hub); dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount); if (hub.devcount) diff --git a/src/usb-hub.h b/src/usb-hub.h index 0994320..7672028 100644 --- a/src/usb-hub.h +++ b/src/usb-hub.h @@ -1,16 +1,6 @@ #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); diff --git a/src/usb-ohci.c b/src/usb-ohci.c index 71ae561..43fe238 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -11,7 +11,6 @@ #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) @@ -25,23 +24,36 @@ struct usb_ohci_s { * Root hub ****************************************************************/ -static void -init_ohci_port(void *data) +// Check if device attached to port +static int +ohci_hub_detect(struct usbhub_s *hub, u32 port) { - struct usbhub_s *hub = data; - u32 port = hub->port; // XXX - find better way to pass port struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); - u32 sts = readl(&cntl->regs->roothub_portstatus[port]); if (!(sts & RH_PS_CCS)) // No device. - goto done; + return -1; // XXX - need to wait for USB_TIME_ATTDB if just powered up? - // Signal reset - mutex_lock(&cntl->usb.resetlock); + return 0; +} + +// Disable port +static void +ohci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA); +} + +// Reset device on port +static int +ohci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); writel(&cntl->regs->roothub_portstatus[port], RH_PS_PRS); + u32 sts; u64 end = calc_future_tsc(USB_TIME_DRSTR * 2); for (;;) { sts = readl(&cntl->regs->roothub_portstatus[port]); @@ -51,39 +63,25 @@ init_ohci_port(void *data) if (check_time(end)) { // Timeout. warn_timeout(); - goto resetfail; + ohci_hub_disconnect(hub, port); + return -1; } 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, port, !!(sts & RH_PS_LSDA)); - if (!pipe) - goto resetfail; - mutex_unlock(&cntl->usb.resetlock); - - // Configure the device - int count = configure_usb_device(pipe); - free_pipe(pipe); - if (! count) - // Shutdown port - writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA); - hub->devcount += count; -done: - hub->threads--; - return; + return -1; -resetfail: - // Shutdown port - writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA); - mutex_unlock(&cntl->usb.resetlock); - goto done; + return !!(sts & RH_PS_LSDA); } +static struct usbhub_op_s ohci_HubOp = { + .detect = ohci_hub_detect, + .reset = ohci_hub_reset, + .disconnect = ohci_hub_disconnect, +}; + // Find any devices connected to the root hub. static int check_ohci_ports(struct usb_ohci_s *cntl) @@ -97,22 +95,12 @@ check_ohci_ports(struct usb_ohci_s *cntl) 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->usb; - int ports = rha & RH_A_NDP; - hub.threads = ports; - int i; - for (i=0; iport; // XXX - find better way to pass port struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; u16 status = inw(ioport); if (!(status & USBPORTSC_CCS)) // No device - goto done; + return -1; // XXX - if just powered up, need to wait for USB_TIME_ATTDB? - // Reset port + // Begin reset on port outw(USBPORTSC_PR, ioport); msleep(USB_TIME_DRSTR); - mutex_lock(&cntl->usb.resetlock); + return 0; +} + +// Reset device on port +static int +uhci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + + // Finish reset on port outw(0, ioport); udelay(6); // 64 high-speed bit times - status = inw(ioport); + u16 status = inw(ioport); if (!(status & USBPORTSC_CCS)) // No longer connected - goto resetfail; + return -1; outw(USBPORTSC_PE, ioport); - struct usb_pipe *pipe = usb_set_address( - hub, port, !!(status & USBPORTSC_LSDA)); - if (!pipe) - goto resetfail; - mutex_unlock(&cntl->usb.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: + return !!(status & USBPORTSC_LSDA); +} + +// Disable port +static void +uhci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; outw(0, ioport); - mutex_unlock(&cntl->usb.resetlock); - goto done; } +static struct usbhub_op_s uhci_HubOp = { + .detect = uhci_hub_detect, + .reset = uhci_hub_reset, + .disconnect = uhci_hub_disconnect, +}; + // Find any devices connected to the root hub. static int check_uhci_ports(struct usb_uhci_s *cntl) @@ -83,17 +86,9 @@ check_uhci_ports(struct usb_uhci_s *cntl) struct usbhub_s hub; memset(&hub, 0, sizeof(hub)); hub.cntl = &cntl->usb; - 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(); - + hub.portcount = 2; + hub.op = &uhci_HubOp; + usb_enumerate(&hub); return hub.devcount; } diff --git a/src/usb.c b/src/usb.c index 8b3c36e..694ea28 100644 --- a/src/usb.c +++ b/src/usb.c @@ -242,7 +242,7 @@ set_configuration(struct usb_pipe *pipe, u16 val) // Assign an address to a device in the default state on the given // controller. -struct usb_pipe * +static struct usb_pipe * usb_set_address(struct usbhub_s *hub, int port, int speed) { ASSERT32FLAT(); @@ -299,7 +299,7 @@ usb_set_address(struct usbhub_s *hub, int port, int speed) // Called for every found device - see if a driver is available for // this device and do setup if so. -int +static int configure_usb_device(struct usb_pipe *pipe) { ASSERT32FLAT(); @@ -356,6 +356,66 @@ fail: return 0; } +static void +usb_init_hub_port(void *data) +{ + struct usbhub_s *hub = data; + u32 port = hub->port; // XXX - find better way to pass port + + // Detect if device present (and possibly start reset) + int ret = hub->op->detect(hub, port); + if (ret) + // No device present + goto done; + + // Reset port and determine device speed + mutex_lock(&hub->cntl->resetlock); + ret = hub->op->reset(hub, port); + if (ret < 0) + // Reset failed + goto resetfail; + + // Set address of port + struct usb_pipe *pipe = usb_set_address(hub, port, ret); + if (!pipe) { + hub->op->disconnect(hub, port); + goto resetfail; + } + mutex_unlock(&hub->cntl->resetlock); + + // Configure the device + int count = configure_usb_device(pipe); + free_pipe(pipe); + if (!count) + hub->op->disconnect(hub, port); + hub->devcount += count; +done: + hub->threads--; + return; + +resetfail: + mutex_unlock(&hub->cntl->resetlock); + goto done; +} + +void +usb_enumerate(struct usbhub_s *hub) +{ + u32 portcount = hub->portcount; + hub->threads = portcount; + + // Launch a thread for every port. + int i; + for (i=0; iport = i; + run_thread(usb_init_hub_port, hub); + } + + // Wait for threads to complete. + while (hub->threads) + yield(); +} + void usb_setup(void) { diff --git a/src/usb.h b/src/usb.h index b5cbf2e..f28a3a7 100644 --- a/src/usb.h +++ b/src/usb.h @@ -4,6 +4,7 @@ #include "util.h" // struct mutex_s +// Information on a USB end point. struct usb_pipe { struct usb_s *cntl; u8 type; @@ -24,6 +25,26 @@ struct usb_s { u8 maxaddr; }; +// Information for enumerating USB hubs +struct usbhub_s { + struct usbhub_op_s *op; + struct usb_pipe *pipe; + struct usb_s *cntl; + struct mutex_s lock; + u32 powerwait; + u32 port; + u32 threads; + u32 portcount; + u32 devcount; +}; + +// Hub callback (32bit) info +struct usbhub_op_s { + int (*detect)(struct usbhub_s *hub, u32 port); + int (*reset)(struct usbhub_s *hub, u32 port); + void (*disconnect)(struct usbhub_s *hub, u32 port); +}; + #define USB_TYPE_UHCI 1 #define USB_TYPE_OHCI 2 #define USB_TYPE_EHCI 3 @@ -175,9 +196,7 @@ struct usb_endpoint_descriptor { // usb.c void usb_setup(void); -struct usbhub_s; -struct usb_pipe *usb_set_address(struct usbhub_s *hub, int port, int speed); -int configure_usb_device(struct usb_pipe *pipe); +void usb_enumerate(struct usbhub_s *hub); int send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req , void *data); int usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize); -- 2.25.1