small corrections and version bump
[AROS.git] / arch / arm-raspi / usb / usb2otg / usb2otg_hub.c
blob73addb753f86fa9752130c1299303b0e998a68f2
1 /*
2 Copyright © 2013, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #define DEBUG 1
7 #include <aros/debug.h>
9 #include <proto/exec.h>
10 #include <proto/kernel.h>
11 #include <proto/utility.h>
13 #include <asm/bcm2835.h>
14 #include <hardware/usb2otg.h>
15 #include <devices/usb_hub.h>
17 #include "usb2otg_intern.h"
18 #include "usb2otg_hub.h"
20 WORD FNAME_HUB(cmdControlXFer)(struct IOUsbHWReq *ioreq,
21 struct USB2OTGUnit *otg_Unit,
22 LIBBASETYPEPTR USB2OTGBase)
24 UWORD rt = ioreq->iouh_SetupData.bmRequestType;
25 UWORD req = ioreq->iouh_SetupData.bRequest;
26 UWORD idx = AROS_WORD2LE(ioreq->iouh_SetupData.wIndex);
27 UWORD val = AROS_WORD2LE(ioreq->iouh_SetupData.wValue);
28 UWORD len = AROS_WORD2LE(ioreq->iouh_SetupData.wLength);
29 BOOL cmdgood;
30 ULONG cnt;
32 #if defined(OTG_FORCEHOSTMODE)
33 if (ioreq->iouh_Endpoint)
35 return (UHIOERR_STALL);
37 #endif
39 if (len != ioreq->iouh_Length)
41 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: IOReq Len mismatch! %ld != %ld\n", len, ioreq->iouh_Length));
42 return (UHIOERR_STALL);
44 switch (rt)
46 case (URTF_STANDARD|URTF_DEVICE):
47 switch (req)
49 case USR_SET_ADDRESS:
50 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Set Device Address to #%ld\n", val));
51 otg_Unit->hu_HubAddr = val;
52 ioreq->iouh_Actual = len;
53 return (0);
55 case USR_SET_CONFIGURATION:
56 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Set Device Configuration to #%ld\n", val));
57 ioreq->iouh_Actual = len;
58 return (0);
60 break;
62 case (URTF_IN|URTF_STANDARD|URTF_DEVICE):
63 switch (req)
65 case USR_GET_STATUS:
66 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetStatus (%ld)\n", len));
67 if (len == 2);
69 UWORD *mptr = ioreq->iouh_Data;
70 *mptr++ = AROS_WORD2LE(U_GSF_SELF_POWERED);
71 return (0);
73 break;
75 case USR_GET_DESCRIPTOR:
76 switch (val >> 8)
78 case UDT_DEVICE:
79 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetDeviceDescriptor (%ld)\n", len));
80 ioreq->iouh_Actual = (len > sizeof(struct UsbStdDevDesc)) ? sizeof(struct UsbStdDevDesc) : len;
81 CopyMem((APTR)&OTGRootHubDevDesc, ioreq->iouh_Data, ioreq->iouh_Actual);
83 return (0);
85 case UDT_CONFIGURATION:
86 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetConfigDescriptor (%ld)\n", len));
87 ioreq->iouh_Actual = (len > sizeof(struct OTGHubCfg)) ? sizeof(struct OTGHubCfg) : len;
88 CopyMem((APTR)&OTGRootHubCfg, ioreq->iouh_Data, ioreq->iouh_Actual);
90 return (0);
92 case UDT_STRING:
93 if (val & 0xFF) /* get lang array */
95 CONST_STRPTR source = NULL;
96 UWORD *mptr = ioreq->iouh_Data;
97 UWORD slen;
98 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetString %04lx (%ld)\n", val, len));
99 if ((val & 0xFF) > 4) /* index too high? */
101 return (UHIOERR_STALL);
104 source = OTGRootHubStrings[(val & 0xFF) - 1];
105 slen = strlen(source);
107 if (len > 1)
109 ioreq->iouh_Actual = 2;
110 *mptr++ = AROS_WORD2BE((slen << 9)|UDT_STRING);
111 /* "expand" string to utf16 */
112 while ((ioreq->iouh_Actual + 1) < len)
114 *mptr++ = AROS_WORD2LE(*source++);
115 ioreq->iouh_Actual += 2;
116 if (!(*source))
118 break;
122 } else {
123 UWORD *mptr = ioreq->iouh_Data;
124 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetLangArray %04lx (%ld)\n", val, len));
125 if (len > 1)
127 ioreq->iouh_Actual = 2;
128 mptr[0] = AROS_WORD2BE((4 << 8)|UDT_STRING);
129 if (len > 3)
131 ioreq->iouh_Actual += 2;
132 mptr[1] = AROS_WORD2LE(0x0409);
136 return (0);
138 default:
139 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported Descriptor %04lx\n", idx));
141 break;
143 case USR_GET_CONFIGURATION:
144 if (len == 1)
146 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetConfiguration\n"));
147 ((UBYTE *) ioreq->iouh_Data)[0] = 1;
148 ioreq->iouh_Actual = len;
149 return (0);
151 break;
153 break;
155 case (URTF_CLASS|URTF_OTHER):
156 switch (req)
158 case USR_SET_FEATURE:
159 if ((!idx) && (idx > 1))
161 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port %ld out of range\n", idx));
162 return (UHIOERR_STALL);
165 // hciport = idx - 1;
167 // D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Set Feature %ld maps from glob. Port %ld to local Port %ld\n", val, idx, hciport));
168 cmdgood = FALSE;
170 // UWORD portreg = OHCI_PORTSTATUS + (hciport<<2);
171 // ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg);
173 switch (val)
175 case UFS_PORT_ENABLE:
176 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Enabling Port\n"));
177 // WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTENABLE);
178 cmdgood = TRUE;
179 break;
181 case UFS_PORT_SUSPEND:
182 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Suspending Port\n"));
183 //hc->hc_PortChangeMap[hciport] |= UPSF_PORT_SUSPEND; // manually fake suspend change
184 // WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTSUSPEND);
185 cmdgood = TRUE;
186 break;
188 case UFS_PORT_RESET:
189 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Resetting Port\n"));
190 // make sure we have at least 50ms of reset time here, as required for a root hub port
191 #if (0)
192 WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
193 uhwDelayMS(10, otg_Unit);
194 WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
195 uhwDelayMS(10, otg_Unit);
196 WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
197 uhwDelayMS(10, otg_Unit);
198 WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
199 uhwDelayMS(10, otg_Unit);
200 WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
201 uhwDelayMS(15, otg_Unit);
202 oldval = READREG32_LE(hc->hc_RegBase, portreg);
203 #endif
204 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Reset release\n"));
205 #if (0)
206 if(oldval & OHPF_PORTRESET)
208 uhwDelayMS(40, otg_Unit);
209 oldval = READREG32_LE(hc->hc_RegBase, portreg);
210 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Reset 2nd release (%s %s)\n", oldval & OHPF_PORTRESET ? "didn't turn off" : "okay",
211 oldval & OHPF_PORTENABLE ? "enabled" : "still not enabled"));
213 // make enumeration possible
214 otg_Unit->hu_DevControllers[0] = hc;
215 #endif
216 cmdgood = TRUE;
217 break;
219 case UFS_PORT_POWER:
220 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Powering Port\n"));
221 // WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTPOWER);
222 cmdgood = TRUE;
223 break;
226 case UFS_PORT_LOW_SPEED:
227 case UFS_C_PORT_CONNECTION:
228 case UFS_C_PORT_OVER_CURRENT:
231 if (cmdgood)
233 return (0);
236 break;
238 case USR_CLEAR_FEATURE:
239 if ((!idx) && (idx > 1))
241 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port %ld out of range\n", idx));
242 return (UHIOERR_STALL);
244 // hciport = idx - 1;
246 // D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Clear Feature %ld maps from glob. Port %ld to local Port %ld\n", val, idx, hciport));
247 cmdgood = FALSE;
249 #if (0)
250 UWORD portreg = hciport ? UHCI_PORT2STSCTRL : UHCI_PORT1STSCTRL;
251 ULONG oldval = READIO16_LE(hc->hc_RegBase, portreg) & ~(UHPF_ENABLECHANGE|UHPF_CONNECTCHANGE); // these are clear-on-write!
252 ULONG newval = oldval;
253 #endif
254 switch (val)
256 case UFS_PORT_ENABLE:
257 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Disabling Port\n"));
258 #if (0)
259 newval &= ~UHPF_PORTENABLE;
260 // disable enumeration
261 otg_Unit->hu_DevControllers[0] = NULL;
262 #endif
263 cmdgood = TRUE;
264 break;
266 case UFS_PORT_SUSPEND:
267 #if (0)
268 newval &= ~UHPF_PORTSUSPEND;
269 #endif
270 cmdgood = TRUE;
271 break;
273 case UFS_PORT_POWER:
274 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Disabling Power\n"));
275 #if (0)
276 newval &= ~UHPF_PORTENABLE;
277 #endif
278 cmdgood = TRUE;
279 break;
281 case UFS_C_PORT_CONNECTION:
282 #if (0)
283 newval |= UHPF_CONNECTCHANGE; // clear-on-write!
284 hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_CONNECTION;
285 #endif
286 cmdgood = TRUE;
287 break;
289 case UFS_C_PORT_ENABLE:
290 #if (0)
291 newval |= UHPF_ENABLECHANGE; // clear-on-write!
292 hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_ENABLE;
293 #endif
294 cmdgood = TRUE;
295 break;
297 case UFS_C_PORT_SUSPEND:
298 #if (0)
299 hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_SUSPEND; // manually fake suspend change clearing
300 #endif
301 cmdgood = TRUE;
302 break;
304 case UFS_C_PORT_OVER_CURRENT:
305 #if (0)
306 hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_OVER_CURRENT; // manually fake over current clearing
307 #endif
308 cmdgood = TRUE;
309 break;
311 case UFS_C_PORT_RESET:
312 #if (0)
313 hc->hc_PortChangeMap[hciport] &= ~UPSF_PORT_RESET; // manually fake reset change clearing
314 #endif
315 cmdgood = TRUE;
316 break;
318 if (cmdgood)
320 // D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port %ld CLEAR_FEATURE %04lx->%04lx\n", idx, oldval, newval));
321 #if (0)
322 WRITEIO16_LE(hc->hc_RegBase, portreg, newval);
323 if(hc->hc_PortChangeMap[hciport])
325 otg_Unit->hu_RootPortChanges |= 1UL<<idx;
326 } else {
327 otg_Unit->hu_RootPortChanges &= ~(1UL<<idx);
329 #endif
330 return (0);
332 break;
335 break;
337 case (URTF_IN|URTF_CLASS|URTF_OTHER):
338 switch (req)
340 case USR_GET_STATUS:
342 UWORD *mptr = ioreq->iouh_Data;
343 if (len != sizeof(struct UsbPortStatus))
345 return (UHIOERR_STALL);
347 if ((!idx) && (idx > 1))
349 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port %ld out of range\n", idx));
350 return (UHIOERR_STALL);
352 // hciport = idx - 1;
354 #if (0)
355 UWORD portreg = hciport ? UHCI_PORT2STSCTRL : UHCI_PORT1STSCTRL;
356 UWORD oldval = READIO16_LE(hc->hc_RegBase, portreg);
357 *mptr = AROS_WORD2LE(UPSF_PORT_POWER);
358 if(oldval & UHPF_PORTCONNECTED) *mptr |= AROS_WORD2LE(UPSF_PORT_CONNECTION);
359 if(oldval & UHPF_PORTENABLE) *mptr |= AROS_WORD2LE(UPSF_PORT_ENABLE);
360 if(oldval & UHPF_LOWSPEED) *mptr |= AROS_WORD2LE(UPSF_PORT_LOW_SPEED);
361 if(oldval & UHPF_PORTRESET) *mptr |= AROS_WORD2LE(UPSF_PORT_RESET);
362 if(oldval & UHPF_PORTSUSPEND) *mptr |= AROS_WORD2LE(UPSF_PORT_SUSPEND);
364 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port %ld is %s\n", idx, oldval & UHPF_LOWSPEED ? "LOWSPEED" : "FULLSPEED"));
365 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port %ld Status %08lx\n", idx, *mptr));
367 mptr++;
368 if(oldval & UHPF_ENABLECHANGE)
370 hc->hc_PortChangeMap[hciport] |= UPSF_PORT_ENABLE;
372 if(oldval & UHPF_CONNECTCHANGE)
374 hc->hc_PortChangeMap[hciport] |= UPSF_PORT_CONNECTION;
376 if(oldval & UHPF_RESUMEDTX)
378 hc->hc_PortChangeMap[hciport] |= UPSF_PORT_SUSPEND|UPSF_PORT_ENABLE;
380 *mptr = AROS_WORD2LE(hc->hc_PortChangeMap[hciport]);
381 WRITEIO16_LE(hc->hc_RegBase, portreg, oldval);
382 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port %ld Change %08lx\n", idx, *mptr));
383 #endif
384 return (0);
388 break;
390 case (URTF_IN|URTF_CLASS|URTF_DEVICE):
391 switch (req)
393 case USR_GET_STATUS:
395 UWORD *mptr = ioreq->iouh_Data;
396 if (len < sizeof(struct UsbHubStatus))
398 return(UHIOERR_STALL);
400 *mptr++ = 0;
401 *mptr++ = 0;
402 ioreq->iouh_Actual = 4;
403 return (0);
406 case USR_GET_DESCRIPTOR:
407 switch (val >> 8)
409 case UDT_HUB:
411 ULONG hubdesclen = 9;
412 ULONG powergood = 1;
414 struct UsbHubDesc *uhd = (struct UsbHubDesc *) ioreq->iouh_Data;
415 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetHubDescriptor (%ld)\n", len));
417 ioreq->iouh_Actual = (len > hubdesclen) ? hubdesclen : len;
418 CopyMem((APTR)&OTGRootHubDesc, ioreq->iouh_Data, ioreq->iouh_Actual);
420 if (ioreq->iouh_Length)
422 uhd->bLength = hubdesclen;
425 if (ioreq->iouh_Length >= hubdesclen)
427 if (hubdesclen == 9)
429 uhd->DeviceRemovable = 0;
430 uhd->PortPwrCtrlMask = (1 << 3) - 2;
431 } else {
432 // each field is 16 bits wide
433 uhd->DeviceRemovable = 0;
434 uhd->PortPwrCtrlMask = 0;
435 ((UBYTE *)ioreq->iouh_Data)[9] = (1 << 3) - 2;
436 ((UBYTE *)ioreq->iouh_Data)[10] = ((1 << 3) - 2) >> 8;
439 return (0);
442 default:
443 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported Descriptor %04lx\n", idx));
445 break;
450 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported command %02lx %02lx %04lx %04lx %04lx!\n", rt, req, idx, val, len));
452 return (UHIOERR_STALL);