Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/drivers/usb/hub.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 /* USB Hub driver */
0019 #include <smp/apic.h>
0020 #include <drivers/usb/usb.h>
0021 #include <drivers/usb/uhci.h>
0022 #include <util/printf.h>
0023 #include <kernel.h>
0024 #include <sched/sched.h>
0025 
0026 #define USB_HUB_CLASS 0x9
0027 
0028 #define DEBUG_USB_HUB
0029 
0030 #ifdef DEBUG_USB_HUB
0031 #define DLOG(fmt,...) DLOG_PREFIX("usb-hub",fmt,##__VA_ARGS__)
0032 #else
0033 #define DLOG(fmt,...) ;
0034 #endif
0035 
0036 bool uhci_enumerate (void);
0037 
0038 #define HUB_PORT_STAT_POWER 0x0100
0039 #define HUB_PORT_RESET 4
0040 #define HUB_PORT_POWER 8
0041 #define HUB_PORT_C_RESET 20
0042 
0043 struct usb_hub_desc
0044 {
0045   uint8 bDescLength;
0046   uint8 bDescriptorType;
0047   uint8 bNbrPorts;
0048   union {
0049     uint16 wHubCharacteristics;
0050     struct {
0051       uint16 lpsMode:2;         /* logical power switching */
0052       uint16 compound:1;        /* identifies compound device */
0053       uint16 opMode:2;          /* over-current protection */
0054       uint16 _reserved:11;
0055     };
0056   };
0057   uint8 bPwrOn2PwrGood;         /* (in 2ms intervals) */
0058   uint8 bHubContrCurrent;       /* max power requirement in mA */
0059   /* followed by DeviceRemovable / PortPwrCtrlMask variable-length fields */
0060 } PACKED;
0061 typedef struct usb_hub_desc USB_HUB_DESC;
0062 
0063 static uint16
0064 hub_port_status (uint address, uint port)
0065 {
0066   sint status;
0067   USB_DEV_REQ req;
0068   uint8 data[4];
0069 
0070   req.bmRequestType = 0xA3;
0071   req.bRequest = USB_GET_STATUS;
0072   req.wValue = 0;
0073   req.wIndex = port;
0074   req.wLength = 4;
0075   /* We assume this is a full speed device, use the maximum, 64 bytes */
0076   status = uhci_control_transfer (address, &req, sizeof (req), data, 4, 64);
0077   DLOG ("GET_PORT_STATUS: status=%d port status: %.04X",
0078         status, *((uint16 *)data));
0079 
0080   return *((uint16 *)data);
0081 }
0082 
0083 static bool
0084 hub_set_port_feature (uint address, uint port, uint feature)
0085 {
0086   sint status;
0087   USB_DEV_REQ req;
0088 
0089   req.bmRequestType = 0x23;
0090   req.bRequest = USB_SET_FEATURE;
0091   req.wValue = feature;
0092   req.wIndex = port;
0093   req.wLength = 0;
0094   /* We assume this is a full speed device, use the maximum, 64 bytes */
0095   status = uhci_control_transfer (address, &req, sizeof (req), NULL, 0, 64);
0096   DLOG ("SET_PORT_FEATURE: status=%d", status);
0097 
0098   return status == 0;
0099 }
0100 
0101 static bool
0102 hub_clr_port_feature (uint address, uint port, uint feature)
0103 {
0104   sint status;
0105   USB_DEV_REQ req;
0106 
0107   req.bmRequestType = 0x23;
0108   req.bRequest = USB_CLEAR_FEATURE;
0109   req.wValue = feature;
0110   req.wIndex = port;
0111   req.wLength = 0;
0112   /* We assume this is a full speed device, use the maximum, 64 bytes */
0113   status = uhci_control_transfer (address, &req, sizeof (req), NULL, 0, 64);
0114   DLOG ("CLEAR_PORT_FEATURE: status=%d", status);
0115 
0116   return status == 0;
0117 }
0118 
0119 static bool
0120 probe_hub (USB_DEVICE_INFO *info, USB_CFG_DESC *cfgd, USB_IF_DESC *ifd)
0121 {
0122   sint status, i, address = info->address;
0123   USB_DEV_REQ req;
0124   USB_HUB_DESC hubd;
0125 
0126   if (ifd->bInterfaceClass != USB_HUB_CLASS)
0127     return FALSE;
0128 
0129   /* it's a hub, set the configuration */
0130   usb_set_configuration (info, cfgd->bConfigurationValue);
0131 
0132   memset (&hubd, 0, sizeof (hubd));
0133 
0134   DLOG ("Probing hub @ %d", address);
0135   delay (100);
0136   req.bmRequestType = 0xA0;
0137   req.bRequest = USB_GET_DESCRIPTOR;
0138   req.wValue = 0x29 << 8;
0139   req.wIndex = 0;
0140   req.wLength = sizeof (USB_HUB_DESC);
0141   /* We assume this is a full speed device, use the maximum, 64 bytes */
0142   status = uhci_control_transfer (address, &req, sizeof (req), &hubd, sizeof (hubd), 64);
0143   DLOG ("GET_HUB_DESCRIPTOR: status=%d len=%d nbrports=%d delay=%d",
0144         status, hubd.bDescLength, hubd.bNbrPorts, hubd.bPwrOn2PwrGood);
0145   if (status != 0) return FALSE;
0146   for (i=1; i<=hubd.bNbrPorts; i++) {
0147     /* power-on port if necessary */
0148     while (!((status=hub_port_status (address, i)) & HUB_PORT_STAT_POWER)) {
0149       hub_set_port_feature (address, i, HUB_PORT_POWER);
0150       delay (2*hubd.bPwrOn2PwrGood);
0151     }
0152     if (status & 1) {
0153       /* potential device on port i */
0154       hub_set_port_feature (address, i, HUB_PORT_RESET);
0155       delay (10);
0156       hub_port_status (address, i);
0157       hub_clr_port_feature (address, i, HUB_PORT_C_RESET);
0158       hub_port_status (address, i);
0159       delay (2*hubd.bPwrOn2PwrGood);
0160       uhci_enumerate ();
0161     }
0162   }
0163   return TRUE;
0164 }
0165 
0166 static USB_DRIVER hub_driver = {
0167   .probe = probe_hub
0168 };
0169 
0170 extern bool
0171 usb_hub_driver_init (void)
0172 {
0173   return usb_register_driver (&hub_driver);
0174 }
0175 
0176 #include "module/header.h"
0177 
0178 static const struct module_ops mod_ops = {
0179   .init = usb_hub_driver_init
0180 };
0181 
0182 DEF_MODULE (usb___hub, "USB hub driver", &mod_ops, {"usb"});
0183 
0184 /*
0185  * Local Variables:
0186  * indent-tabs-mode: nil
0187  * mode: C
0188  * c-file-style: "gnu"
0189  * c-basic-offset: 2
0190  * End:
0191  */
0192 
0193 /* vi: set et sw=2 sts=2: */