- Implement EnumServicesStatusW.
[reactos.git] / reactos / drivers / usb / miniport / usbohci / ohci-hub.c
1 /*
2 * OHCI HCD (Host Controller Driver) for USB.
3 *
4 * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
5 * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
6 *
7 * This file is licenced under GPL
8 */
9
10 /*-------------------------------------------------------------------------*/
11
12 /*
13 * OHCI Root Hub ... the nonsharable stuff
14 *
15 * Registers don't need cpu_to_le32, that happens transparently
16 */
17
18 /* AMD-756 (D2 rev) reports corrupt register contents in some cases.
19 * The erratum (#4) description is incorrect. AMD's workaround waits
20 * till some bits (mostly reserved) are clear; ok for all revs.
21 */
22 #define read_roothub(hc, register, mask) ({ \
23 u32 temp = readl (&hc->regs->roothub.register); \
24 if (temp == -1) \
25 disable (hc); \
26 else if (hc->flags & OHCI_QUIRK_AMD756) \
27 while (temp & mask) \
28 temp = readl (&hc->regs->roothub.register); \
29 temp; })
30
31 static u32 roothub_a (struct ohci_hcd *hc)
32 { return read_roothub (hc, a, 0xfc0fe000); }
33 static inline u32 roothub_b (struct ohci_hcd *hc)
34 { return readl (&hc->regs->roothub.b); }
35 static inline u32 roothub_status (struct ohci_hcd *hc)
36 { return readl (&hc->regs->roothub.status); }
37 static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
38 { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
39
40 /*-------------------------------------------------------------------------*/
41
42 #define dbg_port(hc,label,num,value) \
43 ohci_dbg (hc, \
44 "%s roothub.portstatus [%d] " \
45 "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
46 label, num, temp, \
47 (temp & RH_PS_PRSC) ? " PRSC" : "", \
48 (temp & RH_PS_OCIC) ? " OCIC" : "", \
49 (temp & RH_PS_PSSC) ? " PSSC" : "", \
50 (temp & RH_PS_PESC) ? " PESC" : "", \
51 (temp & RH_PS_CSC) ? " CSC" : "", \
52 \
53 (temp & RH_PS_LSDA) ? " LSDA" : "", \
54 (temp & RH_PS_PPS) ? " PPS" : "", \
55 (temp & RH_PS_PRS) ? " PRS" : "", \
56 (temp & RH_PS_POCI) ? " POCI" : "", \
57 (temp & RH_PS_PSS) ? " PSS" : "", \
58 \
59 (temp & RH_PS_PES) ? " PES" : "", \
60 (temp & RH_PS_CCS) ? " CCS" : "" \
61 );
62
63
64 /*-------------------------------------------------------------------------*/
65
66 /* build "status change" packet (one or two bytes) from HC registers */
67
68 static int
69 ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
70 {
71 struct ohci_hcd *ohci = hcd_to_ohci (hcd);
72 int ports, i, changed = 0, length = 1;
73
74 ports = roothub_a (ohci) & RH_A_NDP;
75 if (ports > MAX_ROOT_PORTS) {
76 if (ohci->disabled)
77 return -ESHUTDOWN;
78 ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n",
79 ports, readl (&ohci->regs->roothub.a) & RH_A_NDP);
80 /* retry later; "should not happen" */
81 return 0;
82 }
83
84 /* init status */
85 if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
86 buf [0] = changed = 1;
87 else
88 buf [0] = 0;
89 if (ports > 7) {
90 buf [1] = 0;
91 length++;
92 }
93
94 /* look at each port */
95 for (i = 0; i < ports; i++) {
96 u32 status = roothub_portstatus (ohci, i);
97
98 status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
99 | RH_PS_OCIC | RH_PS_PRSC;
100 if (status) {
101 changed = 1;
102 if (i < 7)
103 buf [0] |= 1 << (i + 1);
104 else
105 buf [1] |= 1 << (i - 7);
106 }
107 }
108 return changed ? length : 0;
109 }
110
111 /*-------------------------------------------------------------------------*/
112
113 static void
114 ohci_hub_descriptor (
115 struct ohci_hcd *ohci,
116 struct usb_hub_descriptor *desc
117 ) {
118 u32 rh = roothub_a (ohci);
119 int ports = rh & RH_A_NDP;
120 u16 temp;
121
122 desc->bDescriptorType = 0x29;
123 desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
124 desc->bHubContrCurrent = 0;
125
126 desc->bNbrPorts = ports;
127 temp = 1 + (ports / 8);
128 desc->bDescLength = 7 + 2 * temp;
129
130 temp = 0;
131 if (rh & RH_A_PSM) /* per-port power switching? */
132 temp |= 0x0001;
133 if (rh & RH_A_NOCP) /* no overcurrent reporting? */
134 temp |= 0x0010;
135 else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
136 temp |= 0x0008;
137 desc->wHubCharacteristics = cpu_to_le16 (temp);
138
139 /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
140 rh = roothub_b (ohci);
141 desc->bitmap [0] = rh & RH_B_DR;
142 if (ports > 7) {
143 desc->bitmap [1] = (rh & RH_B_DR) >> 8;
144 desc->bitmap [2] = desc->bitmap [3] = 0xff;
145 } else
146 desc->bitmap [1] = 0xff;
147 }
148
149 /*-------------------------------------------------------------------------*/
150
151 static int ohci_hub_control (
152 struct usb_hcd *hcd,
153 u16 typeReq,
154 u16 wValue,
155 u16 wIndex,
156 u8 *buf,
157 u16 wLength
158 ) {
159 struct ohci_hcd *ohci = hcd_to_ohci (hcd);
160 int ports = hcd_to_bus (hcd)->root_hub->maxchild;
161 u32 temp;
162 int retval = 0;
163
164 switch (typeReq) {
165 case ClearHubFeature:
166 switch (wValue) {
167 case C_HUB_OVER_CURRENT:
168 writel (RH_HS_OCIC, &ohci->regs->roothub.status);
169 case C_HUB_LOCAL_POWER:
170 break;
171 default:
172 goto error;
173 }
174 break;
175 case ClearPortFeature:
176 if (!wIndex || wIndex > ports)
177 goto error;
178 wIndex--;
179
180 switch (wValue) {
181 case USB_PORT_FEAT_ENABLE:
182 temp = RH_PS_CCS;
183 break;
184 case USB_PORT_FEAT_C_ENABLE:
185 temp = RH_PS_PESC;
186 break;
187 case USB_PORT_FEAT_SUSPEND:
188 temp = RH_PS_POCI;
189 break;
190 case USB_PORT_FEAT_C_SUSPEND:
191 temp = RH_PS_PSSC;
192 break;
193 case USB_PORT_FEAT_POWER:
194 temp = RH_PS_LSDA;
195 break;
196 case USB_PORT_FEAT_C_CONNECTION:
197 temp = RH_PS_CSC;
198 break;
199 case USB_PORT_FEAT_C_OVER_CURRENT:
200 temp = RH_PS_OCIC;
201 break;
202 case USB_PORT_FEAT_C_RESET:
203 temp = RH_PS_PRSC;
204 break;
205 default:
206 goto error;
207 }
208 writel (temp, &ohci->regs->roothub.portstatus [wIndex]);
209 // readl (&ohci->regs->roothub.portstatus [wIndex]);
210 break;
211 case GetHubDescriptor:
212 ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
213 break;
214 case GetHubStatus:
215 temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
216 *(u32 *) buf = cpu_to_le32 (temp);
217 break;
218 case GetPortStatus:
219 if (!wIndex || wIndex > ports)
220 goto error;
221 wIndex--;
222 temp = roothub_portstatus (ohci, wIndex);
223 *(u32 *) buf = cpu_to_le32 (temp);
224
225 #ifndef OHCI_VERBOSE_DEBUG
226 if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
227 #endif
228 dbg_port (ohci, "GetStatus", wIndex + 1, temp);
229 break;
230 case SetHubFeature:
231 switch (wValue) {
232 case C_HUB_OVER_CURRENT:
233 // FIXME: this can be cleared, yes?
234 case C_HUB_LOCAL_POWER:
235 break;
236 default:
237 goto error;
238 }
239 break;
240 case SetPortFeature:
241 if (!wIndex || wIndex > ports)
242 goto error;
243 wIndex--;
244 switch (wValue) {
245 case USB_PORT_FEAT_SUSPEND:
246 writel (RH_PS_PSS,
247 &ohci->regs->roothub.portstatus [wIndex]);
248 break;
249 case USB_PORT_FEAT_POWER:
250 writel (RH_PS_PPS,
251 &ohci->regs->roothub.portstatus [wIndex]);
252 break;
253 case USB_PORT_FEAT_RESET:
254 temp = readl (&ohci->regs->roothub.portstatus [wIndex]);
255 if (temp & RH_PS_CCS)
256 writel (RH_PS_PRS,
257 &ohci->regs->roothub.portstatus [wIndex]);
258 break;
259 default:
260 goto error;
261 }
262 break;
263
264 default:
265 error:
266 /* "protocol stall" on error */
267 retval = -EPIPE;
268 }
269 return retval;
270 }
271