2 Copyright © 2002-2009, Chris Hodges. All rights reserved.
3 Copyright © 2009-2012, The AROS Development Team. All rights reserved.
7 #include <devices/usb_hub.h>
8 #include <proto/utility.h>
9 #include <proto/exec.h>
10 #include <proto/timer.h>
18 #include "cmd_protos.h"
19 #include "chip_protos.h"
21 /* we cannot use AROS_WORD2LE in struct initializer */
23 #define WORD2LE(w) (UWORD)(((w) >> 8) & 0x00FF) | (((w) << 8) & 0xFF00)
25 #define WORD2LE(w) (w)
29 static const struct UsbStdDevDesc RHDevDesc
=
31 sizeof(struct UsbStdDevDesc
), UDT_DEVICE
, WORD2LE(0x0110),
32 HUB_CLASSCODE
, 0, 0, 8, WORD2LE(0x0000), WORD2LE(0x0000),
33 WORD2LE(0x0100), 1, 2, 0, 1
36 static const struct UsbStdCfgDesc RHCfgDesc
=
38 9, UDT_CONFIGURATION
, WORD2LE(9 + 9 + 7), 1, 1, 3,
39 USCAF_ONE
| USCAF_SELF_POWERED
, 0
41 static const struct UsbStdIfDesc RHIfDesc
=
42 {9, UDT_INTERFACE
, 0, 0, 1, HUB_CLASSCODE
, 0, 0, 4};
43 static const struct UsbStdEPDesc RHEPDesc
=
44 {7, UDT_ENDPOINT
, URTF_IN
| 1, USEAF_INTERRUPT
, WORD2LE(8), 255};
45 static const struct UsbHubDesc RHHubDesc
=
50 WORD2LE(UHCF_INDIVID_POWER
| UHCF_INDIVID_OVP
),
57 static const CONST_STRPTR RHStrings
[] =
59 "Chris Hodges", "PCI Root Hub Unit x", "Standard Config",
63 /* /// "cmdControlXFerRootHub()" */
64 WORD
cmdControlXFerRootHub(struct IOUsbHWReq
*ioreq
,
65 struct PCIUnit
*unit
, struct PCIDevice
*base
)
67 struct PCIController
*hc
;
68 struct PCIController
*chc
;
69 UWORD rt
= ioreq
->iouh_SetupData
.bmRequestType
;
70 UWORD req
= ioreq
->iouh_SetupData
.bRequest
;
71 UWORD idx
= AROS_WORD2LE(ioreq
->iouh_SetupData
.wIndex
);
72 UWORD val
= AROS_WORD2LE(ioreq
->iouh_SetupData
.wValue
);
73 UWORD len
= AROS_WORD2LE(ioreq
->iouh_SetupData
.wLength
);
75 ULONG numports
= unit
->hu_RootHubPorts
, reg_val
, flag
;
78 if (ioreq
->iouh_Endpoint
)
83 if (len
!= ioreq
->iouh_Length
)
85 KPRINTF(20, ("RH: Len (%ld != %ld) mismatch!\n",
86 len
!= ioreq
->iouh_Length
));
91 case (URTF_STANDARD
| URTF_DEVICE
):
95 KPRINTF(1, ("RH: SetAddress = %ld\n", val
));
96 unit
->hu_RootHubAddr
= val
;
97 ioreq
->iouh_Actual
= len
;
100 case USR_SET_CONFIGURATION
:
101 KPRINTF(1, ("RH: SetConfiguration=%ld\n", val
));
102 ioreq
->iouh_Actual
= len
;
107 case (URTF_IN
| URTF_STANDARD
| URTF_DEVICE
):
112 UWORD
*mptr
= ioreq
->iouh_Data
;
113 if (len
!= sizeof(struct UsbPortStatus
))
115 return UHIOERR_STALL
;
117 if ((!idx
) && (idx
> numports
))
119 KPRINTF(20, ("Port %ld out of range\n", idx
));
120 return UHIOERR_STALL
;
122 hc
= unit
->hu_PortMap11
[idx
- 1];
123 hciport
= unit
->hu_PortNum11
[idx
- 1];
125 UWORD portreg
= OHCI_PORTSTATUS
+ (hciport
<< 2);
126 ULONG oldval
= READREG32_LE(hc
->hc_RegBase
, portreg
);
128 *mptr
= AROS_WORD2LE(TranslatePortFlags(oldval
,
129 OHPF_PORTPOWER
| OHPF_OVERCURRENT
130 | OHPF_PORTCONNECTED
| OHPF_PORTENABLE
131 | OHPF_LOWSPEED
| OHPF_PORTRESET
132 | OHPF_PORTSUSPEND
));
134 KPRINTF(5, ("OHCI Port %ld (glob. %ld) is %s\n",
136 oldval
& OHPF_LOWSPEED
? "LOWSPEED" :
138 KPRINTF(5, ("OHCI Port %ld Status %08lx (%08lx)\n", idx
,
142 hc
->hc_PortChangeMap
[hciport
] |=
143 TranslatePortFlags(oldval
,
144 OHPF_OVERCURRENTCHG
| OHPF_RESETCHANGE
|
145 OHPF_ENABLECHANGE
| OHPF_CONNECTCHANGE
|
147 *mptr
= AROS_WORD2LE(hc
->hc_PortChangeMap
[hciport
]);
148 KPRINTF(5, ("OHCI Port %ld Change %08lx\n", idx
,
156 case USR_GET_DESCRIPTOR
:
160 KPRINTF(1, ("RH: GetDeviceDescriptor (%ld)\n", len
));
163 sizeof(struct UsbStdDevDesc
)) ? sizeof(struct
164 UsbStdDevDesc
) : len
;
165 CopyMem((APTR
) & RHDevDesc
, ioreq
->iouh_Data
,
167 if (ioreq
->iouh_Length
>= sizeof(struct UsbStdDevDesc
))
169 if (unit
->hu_RootHub20Ports
)
171 struct UsbStdDevDesc
*usdd
=
172 (struct UsbStdDevDesc
*)ioreq
->iouh_Data
;
174 // signal a highspeed root hub
175 usdd
->bcdUSB
= AROS_WORD2LE(0x0200);
177 usdd
->bDeviceProtocol
= 1; // single TT
182 case UDT_CONFIGURATION
:
184 UBYTE tmpbuf
[9 + 9 + 7];
185 KPRINTF(1, ("RH: GetConfigDescriptor (%ld)\n", len
));
186 CopyMem((APTR
) & RHCfgDesc
, tmpbuf
, 9);
187 CopyMem((APTR
) & RHIfDesc
, &tmpbuf
[9], 9);
188 CopyMem((APTR
) & RHEPDesc
, &tmpbuf
[9 + 9], 7);
189 if (unit
->hu_RootHub20Ports
)
191 struct UsbStdEPDesc
*usepd
=
192 (struct UsbStdEPDesc
*)&tmpbuf
[9 + 9];
193 usepd
->bInterval
= 12; // 2048 µFrames
196 (len
> 9 + 9 + 7) ? 9 + 9 + 7 : len
;
197 CopyMem(tmpbuf
, ioreq
->iouh_Data
, ioreq
->iouh_Actual
);
202 if (val
& 0xff) /* get lang array */
204 CONST_STRPTR source
= NULL
;
205 UWORD
*mptr
= ioreq
->iouh_Data
;
207 KPRINTF(1, ("RH: GetString %04lx (%ld)\n", val
, len
));
208 if ((val
& 0xff) > 4) /* index too high? */
210 return UHIOERR_STALL
;
212 source
= RHStrings
[(val
& 0xff) - 1];
215 ioreq
->iouh_Actual
= 2;
220 source
= RHStrings
[(val
& 0xff) - 1];
221 *mptr
++ = AROS_WORD2BE((slen
<< 9) | UDT_STRING
);
222 while (ioreq
->iouh_Actual
+ 1 < len
)
224 // special hack for unit number in root hub string
225 if (((val
& 0xff) == 2) && (source
[1] == 0))
228 AROS_WORD2LE('0' + unit
->hu_UnitNo
);
232 *mptr
++ = AROS_WORD2LE(*source
);
235 ioreq
->iouh_Actual
+= 2;
245 UWORD
*mptr
= ioreq
->iouh_Data
;
246 KPRINTF(1, ("RH: GetLangArray %04lx (%ld)\n", val
,
250 ioreq
->iouh_Actual
= 2;
251 mptr
[0] = AROS_WORD2BE((4 << 8) | UDT_STRING
);
254 ioreq
->iouh_Actual
+= 2;
255 mptr
[1] = AROS_WORD2LE(0x0409);
262 KPRINTF(1, ("RH: Unsupported Descriptor %04lx\n", idx
));
266 case USR_GET_CONFIGURATION
:
269 KPRINTF(1, ("RH: GetConfiguration\n"));
270 ((UBYTE
*) ioreq
->iouh_Data
)[0] = 1;
271 ioreq
->iouh_Actual
= len
;
278 case (URTF_CLASS
| URTF_OTHER
):
281 case USR_SET_FEATURE
:
282 if ((!idx
) && (idx
> numports
))
284 KPRINTF(20, ("Port %ld out of range\n", idx
));
285 return UHIOERR_STALL
;
287 chc
= unit
->hu_PortMap11
[idx
- 1];
289 hciport
= unit
->hu_PortNum11
[idx
- 1];
291 ("Set Feature %ld maps from global Port %ld "
292 "to local Port %ld\n",
295 UWORD portreg
= OHCI_PORTSTATUS
+ (hciport
<< 2);
296 if (val
== UFS_PORT_RESET
)
298 KPRINTF(10, ("Resetting Port (%s)\n",
299 READREG32_LE(hc
->hc_RegBase
,
300 portreg
) & OHPF_PORTRESET
? "already" : "ok"));
301 // make sure we have at least 50ms of reset time here,
302 // as required for a root hub port
303 for (i
= 0; i
< 5; i
++)
305 WRITEREG32_LE(hc
->hc_RegBase
, portreg
, OHPF_PORTRESET
);
310 ULONG oldval
= READREG32_LE(hc
->hc_RegBase
, portreg
);
311 KPRINTF(10, ("OHCI Reset release (%s %s)\n",
312 oldval
& OHPF_PORTRESET
? "didn't turn off" :
314 oldval
& OHPF_PORTENABLE
? "enabled" :
316 if (oldval
& OHPF_PORTRESET
)
319 oldval
= READREG32_LE(hc
->hc_RegBase
, portreg
);
320 KPRINTF(10, ("OHCI Reset 2nd release (%s %s)\n",
321 oldval
& OHPF_PORTRESET
? "didn't turn off" :
323 oldval
& OHPF_PORTENABLE
? "enabled" :
324 "still not enabled"));
326 // make enumeration possible
327 unit
->hu_DevControllers
[0] = hc
;
334 /* case UFS_PORT_CONNECTION: not possible */
335 case UFS_PORT_ENABLE
:
336 KPRINTF(10, ("Enabling Port (%s)\n",
337 val
& OHPF_PORTENABLE
? "already" : "ok"));
338 reg_val
= OHPF_PORTENABLE
;
341 case UFS_PORT_SUSPEND
:
342 KPRINTF(10, ("Suspending Port (%s)\n",
343 val
& OHPF_PORTSUSPEND
? "already" : "ok"));
344 reg_val
= OHPF_PORTSUSPEND
;
347 /* case UFS_PORT_OVER_CURRENT: not possible */
349 KPRINTF(10, ("Powering Port (%s)\n",
350 val
& OHPF_PORTPOWER
? "already" : "ok"));
351 reg_val
= OHPF_PORTPOWER
;
354 /* case UFS_PORT_LOW_SPEED: not possible */
355 /* case UFS_C_PORT_CONNECTION:
356 case UFS_C_PORT_ENABLE:
357 case UFS_C_PORT_SUSPEND:
358 case UFS_C_PORT_OVER_CURRENT:
359 case UFS_C_PORT_RESET: */
365 WRITEREG32_LE(hc
->hc_RegBase
, portreg
, reg_val
);
373 case USR_CLEAR_FEATURE
:
374 if ((!idx
) && (idx
> numports
))
376 KPRINTF(20, ("Port %ld out of range\n", idx
));
377 return UHIOERR_STALL
;
379 if (unit
->hu_EhciOwned
[idx
- 1])
381 hc
= unit
->hu_PortMap20
[idx
- 1];
386 hc
= unit
->hu_PortMap11
[idx
- 1];
387 hciport
= unit
->hu_PortNum11
[idx
- 1];
390 ("Clear Feature %ld maps from global Port %ld "
391 "to local Port %ld\n",
396 UWORD portreg
= OHCI_PORTSTATUS
+ (hciport
<< 2);
397 ULONG __unused oldval
=
398 READREG32_LE(hc
->hc_RegBase
, portreg
);
402 case UFS_PORT_ENABLE
:
403 KPRINTF(10, ("Disabling Port (%s)\n",
404 oldval
& OHPF_PORTENABLE
? "ok" : "already"));
405 reg_val
= OHPF_PORTDISABLE
;
408 case UFS_PORT_SUSPEND
:
409 KPRINTF(10, ("Resuming Port (%s)\n",
410 oldval
& OHPF_PORTSUSPEND
? "ok" : "already"));
411 //flag = UPSF_PORT_SUSPEND; // manually fake suspend change
412 reg_val
= OHPF_RESUME
;
416 KPRINTF(10, ("Unpowering Port (%s)\n",
417 oldval
& OHPF_PORTPOWER
? "ok" : "already"));
418 reg_val
= OHPF_PORTUNPOWER
;
421 case UFS_C_PORT_CONNECTION
:
422 reg_val
= OHPF_CONNECTCHANGE
;
423 flag
= UPSF_PORT_CONNECTION
;
426 case UFS_C_PORT_ENABLE
:
427 reg_val
= OHPF_ENABLECHANGE
;
428 flag
= UPSF_PORT_ENABLE
;
431 case UFS_C_PORT_SUSPEND
:
432 reg_val
= OHPF_RESUMEDTX
;
433 flag
= UPSF_PORT_SUSPEND
;
436 case UFS_C_PORT_OVER_CURRENT
:
437 reg_val
= OHPF_OVERCURRENTCHG
;
438 flag
= UPSF_PORT_OVER_CURRENT
;
441 case UFS_C_PORT_RESET
:
442 reg_val
= OHPF_RESETCHANGE
;
443 flag
= UPSF_PORT_RESET
;
450 WRITEREG32_LE(hc
->hc_RegBase
, portreg
, reg_val
);
451 hc
->hc_PortChangeMap
[hciport
] &= ~flag
;
461 case (URTF_IN
| URTF_CLASS
| URTF_OTHER
):
466 UWORD
*mptr
= ioreq
->iouh_Data
;
467 if (len
!= sizeof(struct UsbPortStatus
))
469 return UHIOERR_STALL
;
471 if ((!idx
) && (idx
> numports
))
473 KPRINTF(20, ("Port %ld out of range\n", idx
));
474 return UHIOERR_STALL
;
476 if (unit
->hu_EhciOwned
[idx
- 1])
478 hc
= unit
->hu_PortMap20
[idx
- 1];
483 hc
= unit
->hu_PortMap11
[idx
- 1];
484 hciport
= unit
->hu_PortNum11
[idx
- 1];
487 UWORD portreg
= OHCI_PORTSTATUS
+ (hciport
<< 2);
488 ULONG oldval
= READREG32_LE(hc
->hc_RegBase
, portreg
);
491 *mptr
= AROS_WORD2LE(TranslatePortFlags(oldval
,
492 OHPF_PORTPOWER
| OHPF_OVERCURRENT
493 | OHPF_PORTCONNECTED
| OHPF_PORTENABLE
494 | OHPF_LOWSPEED
| OHPF_PORTRESET
495 | OHPF_PORTSUSPEND
));
497 KPRINTF(5, ("OHCI Port %ld (glob. %ld) is %s\n",
499 oldval
& OHPF_LOWSPEED
? "LOWSPEED" :
501 KPRINTF(5, ("OHCI Port %ld Status %08lx (%08lx)\n", idx
,
505 hc
->hc_PortChangeMap
[hciport
] |=
506 TranslatePortFlags(oldval
,
507 OHPF_OVERCURRENTCHG
| OHPF_RESETCHANGE
508 | OHPF_ENABLECHANGE
| OHPF_CONNECTCHANGE
510 *mptr
= AROS_WORD2LE(hc
->hc_PortChangeMap
[hciport
]);
511 KPRINTF(5, ("OHCI Port %ld Change %08lx\n", idx
,
522 case (URTF_IN
| URTF_CLASS
| URTF_DEVICE
):
527 UWORD
*mptr
= ioreq
->iouh_Data
;
528 if (len
< sizeof(struct UsbHubStatus
))
530 return UHIOERR_STALL
;
534 ioreq
->iouh_Actual
= 4;
538 case USR_GET_DESCRIPTOR
:
543 ULONG hubdesclen
= 9;
546 struct UsbHubDesc
*uhd
=
547 (struct UsbHubDesc
*)ioreq
->iouh_Data
;
548 KPRINTF(1, ("RH: GetHubDescriptor (%ld)\n", len
));
550 if (unit
->hu_RootHubPorts
> 7)
552 hubdesclen
+= 2; // needs two bytes for port masks
556 (len
> hubdesclen
) ? hubdesclen
: len
;
557 CopyMem((APTR
) & RHHubDesc
, ioreq
->iouh_Data
,
560 if (ioreq
->iouh_Length
)
562 uhd
->bLength
= hubdesclen
;
565 if (ioreq
->iouh_Length
>= 6)
567 hc
= (struct PCIController
*)unit
->
568 hu_Controllers
.lh_Head
;
569 while (hc
->hc_Node
.ln_Succ
)
573 (READREG32_LE(hc
->hc_RegBase
,
574 OHCI_HUBDESCA
) & OHAM_POWERGOOD
) >>
576 if (localpwgood
> powergood
)
578 powergood
= localpwgood
;
580 ("Increasing power good time to %ld\n",
584 hc
= (struct PCIController
*)hc
->
588 uhd
->bPwrOn2PwrGood
= powergood
;
590 if (ioreq
->iouh_Length
>= hubdesclen
)
592 uhd
->bNbrPorts
= unit
->hu_RootHubPorts
;
595 uhd
->DeviceRemovable
= 0;
596 uhd
->PortPwrCtrlMask
=
597 (1 << (unit
->hu_RootHubPorts
+ 2)) - 2;
601 // each field is now 16 bits wide
602 uhd
->DeviceRemovable
= 0;
603 uhd
->PortPwrCtrlMask
= 0;
604 ((UBYTE
*) ioreq
->iouh_Data
)[9] =
605 (1 << (unit
->hu_RootHubPorts
+ 2)) - 2;
606 ((UBYTE
*) ioreq
->iouh_Data
)[10] =
607 ((1 << (unit
->hu_RootHubPorts
+ 2)) -
615 KPRINTF(20, ("RH: Unsupported Descriptor %04lx\n", idx
));
621 KPRINTF(20, ("RH: Unsupported command %02x %02x %04x %04x %04x!\n",
622 (UWORD
) rt
, (UWORD
) req
, idx
, val
, len
));
623 return UHIOERR_STALL
;
627 /* /// "cmdIntXFerRootHub()" */
628 WORD
cmdIntXFerRootHub(struct IOUsbHWReq
* ioreq
,
629 struct PCIUnit
* unit
, struct PCIDevice
* base
)
631 if ((ioreq
->iouh_Endpoint
!= 1) || (!ioreq
->iouh_Length
))
633 return UHIOERR_STALL
;
636 if (unit
->hu_RootPortChanges
)
638 KPRINTF(1, ("Immediate Portchange map %04lx\n",
639 unit
->hu_RootPortChanges
));
640 if ((unit
->hu_RootHubPorts
< 8) || (ioreq
->iouh_Length
== 1))
642 *((UBYTE
*) ioreq
->iouh_Data
) = unit
->hu_RootPortChanges
;
643 ioreq
->iouh_Actual
= 1;
647 ((UBYTE
*) ioreq
->iouh_Data
)[0] = unit
->hu_RootPortChanges
;
648 ((UBYTE
*) ioreq
->iouh_Data
)[1] = unit
->hu_RootPortChanges
>> 8;
649 ioreq
->iouh_Actual
= 2;
651 unit
->hu_RootPortChanges
= 0;
654 ioreq
->iouh_Req
.io_Flags
&= ~IOF_QUICK
;
656 AddTail(&unit
->hu_RHIOQueue
, (struct Node
*)ioreq
);
662 /* /// "CheckRootHubChanges()" */
663 void CheckRootHubChanges(struct PCIUnit
*unit
)
665 struct IOUsbHWReq
*ioreq
;
667 if (unit
->hu_RootPortChanges
&& unit
->hu_RHIOQueue
.lh_Head
->ln_Succ
)
669 KPRINTF(1, ("Portchange map %04lx\n", unit
->hu_RootPortChanges
));
671 ioreq
= (struct IOUsbHWReq
*)unit
->hu_RHIOQueue
.lh_Head
;
672 while (((struct Node
*)ioreq
)->ln_Succ
)
674 Remove(&ioreq
->iouh_Req
.io_Message
.mn_Node
);
675 if ((unit
->hu_RootHubPorts
< 8) || (ioreq
->iouh_Length
== 1))
677 *((UBYTE
*) ioreq
->iouh_Data
) = unit
->hu_RootPortChanges
;
678 ioreq
->iouh_Actual
= 1;
682 ((UBYTE
*) ioreq
->iouh_Data
)[0] = unit
->hu_RootPortChanges
;
683 ((UBYTE
*) ioreq
->iouh_Data
)[1] =
684 unit
->hu_RootPortChanges
>> 8;
685 ioreq
->iouh_Actual
= 2;
687 ReplyMsg(&ioreq
->iouh_Req
.io_Message
);
688 ioreq
= (struct IOUsbHWReq
*)unit
->hu_RHIOQueue
.lh_Head
;
690 unit
->hu_RootPortChanges
= 0;