2 Copyright © 2013, The AROS Development Team. All rights reserved.
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
);
32 #if defined(OTG_FORCEHOSTMODE)
33 if (ioreq
->iouh_Endpoint
)
35 return (UHIOERR_STALL
);
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
);
46 case (URTF_STANDARD
|URTF_DEVICE
):
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
;
55 case USR_SET_CONFIGURATION
:
56 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Set Device Configuration to #%ld\n", val
));
57 ioreq
->iouh_Actual
= len
;
62 case (URTF_IN
|URTF_STANDARD
|URTF_DEVICE
):
66 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetStatus (%ld)\n", len
));
69 UWORD
*mptr
= ioreq
->iouh_Data
;
70 *mptr
++ = AROS_WORD2LE(U_GSF_SELF_POWERED
);
75 case USR_GET_DESCRIPTOR
:
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
);
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
);
93 if (val
& 0xFF) /* get lang array */
95 CONST_STRPTR source
= NULL
;
96 UWORD
*mptr
= ioreq
->iouh_Data
;
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
);
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;
123 UWORD
*mptr
= ioreq
->iouh_Data
;
124 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetLangArray %04lx (%ld)\n", val
, len
));
127 ioreq
->iouh_Actual
= 2;
128 mptr
[0] = AROS_WORD2BE((4 << 8)|UDT_STRING
);
131 ioreq
->iouh_Actual
+= 2;
132 mptr
[1] = AROS_WORD2LE(0x0409);
139 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported Descriptor %04lx\n", idx
));
143 case USR_GET_CONFIGURATION
:
146 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetConfiguration\n"));
147 ((UBYTE
*) ioreq
->iouh_Data
)[0] = 1;
148 ioreq
->iouh_Actual
= len
;
155 case (URTF_CLASS
|URTF_OTHER
):
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));
170 // UWORD portreg = OHCI_PORTSTATUS + (hciport<<2);
171 // ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg);
175 case UFS_PORT_ENABLE
:
176 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Enabling Port\n"));
177 // WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTENABLE);
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);
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
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
);
204 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Reset release\n"));
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
;
220 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Powering Port\n"));
221 // WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTPOWER);
226 case UFS_PORT_LOW_SPEED:
227 case UFS_C_PORT_CONNECTION:
228 case UFS_C_PORT_OVER_CURRENT:
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));
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
;
256 case UFS_PORT_ENABLE
:
257 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Disabling Port\n"));
259 newval
&= ~UHPF_PORTENABLE
;
260 // disable enumeration
261 otg_Unit
->hu_DevControllers
[0] = NULL
;
266 case UFS_PORT_SUSPEND
:
268 newval
&= ~UHPF_PORTSUSPEND
;
274 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Disabling Power\n"));
276 newval
&= ~UHPF_PORTENABLE
;
281 case UFS_C_PORT_CONNECTION
:
283 newval
|= UHPF_CONNECTCHANGE
; // clear-on-write!
284 hc
->hc_PortChangeMap
[hciport
] &= ~UPSF_PORT_CONNECTION
;
289 case UFS_C_PORT_ENABLE
:
291 newval
|= UHPF_ENABLECHANGE
; // clear-on-write!
292 hc
->hc_PortChangeMap
[hciport
] &= ~UPSF_PORT_ENABLE
;
297 case UFS_C_PORT_SUSPEND
:
299 hc
->hc_PortChangeMap
[hciport
] &= ~UPSF_PORT_SUSPEND
; // manually fake suspend change clearing
304 case UFS_C_PORT_OVER_CURRENT
:
306 hc
->hc_PortChangeMap
[hciport
] &= ~UPSF_PORT_OVER_CURRENT
; // manually fake over current clearing
311 case UFS_C_PORT_RESET
:
313 hc
->hc_PortChangeMap
[hciport
] &= ~UPSF_PORT_RESET
; // manually fake reset change clearing
320 // D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port %ld CLEAR_FEATURE %04lx->%04lx\n", idx, oldval, newval));
322 WRITEIO16_LE(hc
->hc_RegBase
, portreg
, newval
);
323 if(hc
->hc_PortChangeMap
[hciport
])
325 otg_Unit
->hu_RootPortChanges
|= 1UL<<idx
;
327 otg_Unit
->hu_RootPortChanges
&= ~(1UL<<idx
);
337 case (URTF_IN
|URTF_CLASS
|URTF_OTHER
):
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;
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
));
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
));
390 case (URTF_IN
|URTF_CLASS
|URTF_DEVICE
):
395 UWORD
*mptr
= ioreq
->iouh_Data
;
396 if (len
< sizeof(struct UsbHubStatus
))
398 return(UHIOERR_STALL
);
402 ioreq
->iouh_Actual
= 4;
406 case USR_GET_DESCRIPTOR
:
411 ULONG hubdesclen
= 9;
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
)
429 uhd
->DeviceRemovable
= 0;
430 uhd
->PortPwrCtrlMask
= (1 << 3) - 2;
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;
443 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported Descriptor %04lx\n", idx
));
450 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported command %02lx %02lx %04lx %04lx %04lx!\n", rt
, req
, idx
, val
, len
));
452 return (UHIOERR_STALL
);