ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / usb / host / uhci-hub.c
1 /*
2  * Universal Host Controller Interface driver for USB.
3  *
4  * Maintainer: Alan Stern <stern@rowland.harvard.edu>
5  *
6  * (C) Copyright 1999 Linus Torvalds
7  * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
8  * (C) Copyright 1999 Randy Dunlap
9  * (C) Copyright 1999 Georg Acher, acher@in.tum.de
10  * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
11  * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
12  * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
13  */
14
15 static __u8 root_hub_hub_des[] =
16 {
17         0x09,                   /*  __u8  bLength; */
18         0x29,                   /*  __u8  bDescriptorType; Hub-descriptor */
19         0x02,                   /*  __u8  bNbrPorts; */
20         0x0a,                   /* __u16  wHubCharacteristics; */
21         0x00,                   /*   (per-port OC, no power switching) */
22         0x01,                   /*  __u8  bPwrOn2pwrGood; 2ms */
23         0x00,                   /*  __u8  bHubContrCurrent; 0 mA */
24         0x00,                   /*  __u8  DeviceRemovable; *** 7 Ports max *** */
25         0xff                    /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
26 };
27
28 #define UHCI_RH_MAXCHILD        7
29
30 /* must write as zeroes */
31 #define WZ_BITS         (USBPORTSC_RES2 | USBPORTSC_RES3 | USBPORTSC_RES4)
32
33 /* status change bits:  nonzero writes will clear */
34 #define RWC_BITS        (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC)
35
36 static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
37 {
38         struct uhci_hcd *uhci = hcd_to_uhci(hcd);
39         unsigned int io_addr = uhci->io_addr;
40         int i;
41
42         *buf = 0;
43         for (i = 0; i < uhci->rh_numports; i++) {
44                 if (inw(io_addr + USBPORTSC1 + i * 2) & RWC_BITS)
45                         *buf |= (1 << (i + 1));
46         }
47         return !!*buf;
48 }
49
50 #define OK(x)                   len = (x); break
51
52 #define CLR_RH_PORTSTAT(x) \
53         status = inw(port_addr); \
54         status &= ~(RWC_BITS|WZ_BITS); \
55         status &= ~(x); \
56         status |= RWC_BITS & (x); \
57         outw(status, port_addr)
58
59 #define SET_RH_PORTSTAT(x) \
60         status = inw(port_addr); \
61         status |= (x); \
62         status &= ~(RWC_BITS|WZ_BITS); \
63         outw(status, port_addr)
64
65
66 /* size of returned buffer is part of USB spec */
67 static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
68                         u16 wIndex, char *buf, u16 wLength)
69 {
70         struct uhci_hcd *uhci = hcd_to_uhci(hcd);
71         int status, retval = 0, len = 0;
72         unsigned int port_addr = uhci->io_addr + USBPORTSC1 + 2 * (wIndex-1);
73         __u16 wPortChange, wPortStatus;
74
75         switch (typeReq) {
76                 /* Request Destination:
77                    without flags: Device,
78                    RH_INTERFACE: interface,
79                    RH_ENDPOINT: endpoint,
80                    RH_CLASS means HUB here,
81                    RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
82                 */
83
84         case GetHubStatus:
85                 *(__u32 *)buf = cpu_to_le32(0);
86                 OK(4);          /* hub power */
87         case GetPortStatus:
88                 if (!wIndex || wIndex > uhci->rh_numports)
89                         goto err;
90                 status = inw(port_addr);
91
92                 /* Intel controllers report the OverCurrent bit active on.
93                  * VIA controllers report it active off, so we'll adjust the
94                  * bit value.  (It's not standardized in the UHCI spec.)
95                  */
96                 if (to_pci_dev(hcd->self.controller)->vendor ==
97                                 PCI_VENDOR_ID_VIA)
98                         status ^= USBPORTSC_OC;
99
100                 /* UHCI doesn't support C_SUSPEND and C_RESET (always false) */
101                 wPortChange = 0;
102                 if (status & USBPORTSC_CSC)
103                         wPortChange |= 1 << (USB_PORT_FEAT_C_CONNECTION - 16);
104                 if (status & USBPORTSC_PEC)
105                         wPortChange |= 1 << (USB_PORT_FEAT_C_ENABLE - 16);
106                 if (status & USBPORTSC_OCC)
107                         wPortChange |= 1 << (USB_PORT_FEAT_C_OVER_CURRENT - 16);
108
109                 /* UHCI has no power switching (always on) */
110                 wPortStatus = 1 << USB_PORT_FEAT_POWER;
111                 if (status & USBPORTSC_CCS)
112                         wPortStatus |= 1 << USB_PORT_FEAT_CONNECTION;
113                 if (status & USBPORTSC_PE) {
114                         wPortStatus |= 1 << USB_PORT_FEAT_ENABLE;
115                         if (status & (USBPORTSC_SUSP | USBPORTSC_RD))
116                                 wPortStatus |= 1 << USB_PORT_FEAT_SUSPEND;
117                 }
118                 if (status & USBPORTSC_OC)
119                         wPortStatus |= 1 << USB_PORT_FEAT_OVER_CURRENT;
120                 if (status & USBPORTSC_PR)
121                         wPortStatus |= 1 << USB_PORT_FEAT_RESET;
122                 if (status & USBPORTSC_LSDA)
123                         wPortStatus |= 1 << USB_PORT_FEAT_LOWSPEED;
124
125                 if (wPortChange)
126                         dev_dbg(uhci_dev(uhci), "port %d portsc %04x\n",
127                                         wIndex, status);
128
129                 *(__u16 *)buf = cpu_to_le16(wPortStatus);
130                 *(__u16 *)(buf + 2) = cpu_to_le16(wPortChange);
131                 OK(4);
132         case SetHubFeature:             /* We don't implement these */
133         case ClearHubFeature:
134                 switch (wValue) {
135                 case C_HUB_OVER_CURRENT:
136                 case C_HUB_LOCAL_POWER:
137                         OK(0);
138                 default:
139                         goto err;
140                 }
141                 break;
142         case SetPortFeature:
143                 if (!wIndex || wIndex > uhci->rh_numports)
144                         goto err;
145
146                 switch (wValue) {
147                 case USB_PORT_FEAT_SUSPEND:
148                         SET_RH_PORTSTAT(USBPORTSC_SUSP);
149                         OK(0);
150                 case USB_PORT_FEAT_RESET:
151                         SET_RH_PORTSTAT(USBPORTSC_PR);
152                         mdelay(50);     /* USB v1.1 7.1.7.3 */
153                         CLR_RH_PORTSTAT(USBPORTSC_PR);
154                         udelay(10);
155                         SET_RH_PORTSTAT(USBPORTSC_PE);
156                         mdelay(10);
157                         CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC);
158                         OK(0);
159                 case USB_PORT_FEAT_POWER:
160                         /* UHCI has no power switching */
161                         OK(0);
162                 default:
163                         goto err;
164                 }
165                 break;
166         case ClearPortFeature:
167                 if (!wIndex || wIndex > uhci->rh_numports)
168                         goto err;
169
170                 switch (wValue) {
171                 case USB_PORT_FEAT_ENABLE:
172                         CLR_RH_PORTSTAT(USBPORTSC_PE);
173                         OK(0);
174                 case USB_PORT_FEAT_C_ENABLE:
175                         CLR_RH_PORTSTAT(USBPORTSC_PEC);
176                         OK(0);
177                 case USB_PORT_FEAT_SUSPEND:
178                         CLR_RH_PORTSTAT(USBPORTSC_SUSP);
179                         OK(0);
180                 case USB_PORT_FEAT_C_SUSPEND:
181                         /* this driver won't report these */
182                         OK(0);
183                 case USB_PORT_FEAT_POWER:
184                         /* UHCI has no power switching */
185                         goto err;
186                 case USB_PORT_FEAT_C_CONNECTION:
187                         CLR_RH_PORTSTAT(USBPORTSC_CSC);
188                         OK(0);
189                 case USB_PORT_FEAT_C_OVER_CURRENT:
190                         CLR_RH_PORTSTAT(USBPORTSC_OCC);
191                         OK(0);
192                 case USB_PORT_FEAT_C_RESET:
193                         /* this driver won't report these */
194                         OK(0);
195                 default:
196                         goto err;
197                 }
198                 break;
199         case GetHubDescriptor:
200                 len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
201                 memcpy(buf, root_hub_hub_des, len);
202                 if (len > 2)
203                         buf[2] = uhci->rh_numports;
204                 OK(len);
205         default:
206 err:
207                 retval = -EPIPE;
208         }
209
210         return retval;
211 }