2 Copyright © 2014, The AROS Development Team. All rights reserved.
5 Desc: Virtual XHCI USB host controller
14 #include <aros/macros.h>
16 #include <proto/exec.h>
17 #include <proto/stdc.h>
18 #include <proto/arossupport.h>
20 #include <devices/usb.h>
21 #include <devices/usb_hub.h>
22 #include <devices/newstyle.h>
23 #include <devices/usbhardware.h>
25 #include "vxhci_device.h"
27 #include LC_LIBDEFS_FILE
29 struct VXHCIUnit
*VXHCI_AddNewUnit(ULONG unitnum
, UWORD bcdusb
);
30 struct VXHCIPort
*VXHCI_AddNewPort(struct VXHCIUnit
*unit
, ULONG portnum
);
32 static int GM_UNIQUENAME(Init
)(LIBBASETYPEPTR VXHCIBase
) {
33 mybug(0,("[VXHCI] Init: Entering function\n"));
35 struct VXHCIUnit
*unit
;
38 NEWLIST(&VXHCIBase
->unit_list
);
39 VXHCIBase
->unit_count
= 0;
41 for (i
=0; i
<VXHCI_NUMCONTROLLERS
; i
++) {
43 #ifdef VXHCI_NUMPORTS20
44 unit
= VXHCI_AddNewUnit(VXHCIBase
->unit_count
, 0x210);
46 mybug(-1, ("[VXHCI] Init: Failed to create new unit!\n"));
49 Free previous units if any exists
52 ForeachNode(&VXHCIBase
->unit_list
, unit
) {
53 mybug(-1,("[VXHCI] Init: Removing unit structure %s at %p\n", unit
->node
.ln_Name
, unit
));
59 AddTail(&VXHCIBase
->unit_list
,(struct Node
*)unit
);
60 VXHCIBase
->unit_count
++;
64 unit
= VXHCI_AddNewUnit(VXHCIBase
->unit_count
, 0x310);
66 mybug(-1, ("[VXHCI] Init: Failed to create new unit!\n"));
69 Free previous units if any exists
72 ForeachNode(&VXHCIBase
->unit_list
, unit
) {
73 mybug(-1,("[VXHCI] Init: Removing unit structure %s at %p\n", unit
->node
.ln_Name
, unit
));
79 AddTail(&VXHCIBase
->unit_list
,(struct Node
*)unit
);
80 VXHCIBase
->unit_count
++;
85 D(ForeachNode(&VXHCIBase
->unit_list
, unit
) {
86 mybug(-1, ("[VXHCI] Init: Created unit %d at %p %s\n", unit
->number
, unit
, unit
->name
));
87 struct VXHCIPort
*port
;
88 ForeachNode(&unit
->roothub
.port_list
, port
) {
89 mybug(-1, (" port %d at %p %s\n", port
->number
, port
, port
->name
));
97 static int GM_UNIQUENAME(Open
)(LIBBASETYPEPTR VXHCIBase
, struct IOUsbHWReq
*ioreq
, ULONG unitnum
, ULONG flags
) {
98 mybug(0, ("[VXHCI] Open: Entering function\n"));
99 mybug(0, ("[VXHCI] Open: Unit %d\n", unitnum
));
101 struct VXHCIUnit
*unit
;
103 /* Default to open failure. */
104 ioreq
->iouh_Req
.io_Error
= IOERR_OPENFAIL
;
105 ioreq
->iouh_Req
.io_Unit
= NULL
;
108 Number of units eg. virtual xhci controllers.
109 Host controller is divided into individual units if it has both usb2.0 and usb3.0 ports
111 if(unitnum
<VXHCIBase
->unit_count
) {
113 if(ioreq
->iouh_Req
.io_Message
.mn_Length
< sizeof(struct IOUsbHWReq
)) {
114 mybug(-1, ("[VXHCI] Open: Invalid MN_LENGTH!\n"));
115 ioreq
->iouh_Req
.io_Error
= IOERR_BADLENGTH
;
118 ioreq
->iouh_Req
.io_Unit
= NULL
;
120 ForeachNode(&VXHCIBase
->unit_list
, unit
) {
121 mybug(0, ("[VXHCI] Open: Opening unit number %d\n", unitnum
));
122 if(unit
->number
== unitnum
) {
123 mybug(0, (" Found unit from node list %s %p\n\n", unit
->name
, unit
));
124 ioreq
->iouh_Req
.io_Unit
= (struct Unit
*) unit
;
129 if(ioreq
->iouh_Req
.io_Unit
!= NULL
) {
132 ioreq
->iouh_Req
.io_Message
.mn_Node
.ln_Type
= NT_REPLYMSG
;
133 ioreq
->iouh_Req
.io_Error
= 0;
144 static int GM_UNIQUENAME(Close
)(LIBBASETYPEPTR VXHCIBase
, struct IOUsbHWReq
*ioreq
) {
145 mybug(0, ("[VXHCI] Close: Entering function\n"));
147 ioreq
->iouh_Req
.io_Unit
= (APTR
) -1;
148 ioreq
->iouh_Req
.io_Device
= (APTR
) -1;
153 ADD2INITLIB(GM_UNIQUENAME(Init
), 0)
154 ADD2OPENDEV(GM_UNIQUENAME(Open
), 0)
155 ADD2CLOSEDEV(GM_UNIQUENAME(Close
), 0)
157 AROS_LH1(void, BeginIO
, AROS_LHA(struct IOUsbHWReq
*, ioreq
, A1
), struct VXHCIBase
*, VXHCIBase
, 5, VXHCI
) {
159 mybug(0, ("[VXHCI] BeginIO: Entering function\n"));
163 ioreq
->iouh_Req
.io_Message
.mn_Node
.ln_Type
= NT_MESSAGE
;
164 ioreq
->iouh_Req
.io_Error
= UHIOERR_NO_ERROR
;
166 struct VXHCIUnit
*unit
= (struct VXHCIUnit
*) ioreq
->iouh_Req
.io_Unit
;
170 switch (ioreq
->iouh_Req
.io_Command
) {
172 mybug_unit(0, ("CMD_RESET\n"));
175 mybug_unit(0, ("CMD_FLUSH\n"));
177 case UHCMD_QUERYDEVICE
:
178 mybug_unit(0, ("UHCMD_QUERYDEVICE\n"));
179 ret
= cmdQueryDevice(ioreq
);
182 mybug_unit(0, ("UHCMD_USBRESET\n"));
183 ret
= cmdUsbReset(ioreq
);
185 case UHCMD_USBRESUME
:
186 mybug_unit(0, ("UHCMD_USBRESUME\n"));
188 case UHCMD_USBSUSPEND
:
189 mybug_unit(0, ("UHCMD_USBSUSPEND\n"));
192 mybug_unit(0, ("UHCMD_USBOPER\n"));
193 //ret = cmdUsbOper(ioreq);
195 case UHCMD_CONTROLXFER
:
196 mybug_unit(0, ("UHCMD_CONTROLXFER unit %p %s\n", unit
, unit
->name
));
197 ret
= cmdControlXFer(ioreq
);
200 mybug_unit(0, ("UHCMD_BULKXFER\n"));
203 mybug_unit(0, ("UHCMD_INTXFER unit %p %s\n", unit
, unit
->name
));
204 ret
= cmdIntXFer(ioreq
);
207 mybug_unit(0, ("UHCMD_ISOXFER\n"));
210 /* Poseidon doesn't actually check this, ever... */
211 case NSCMD_DEVICEQUERY
:
212 mybug_unit(0, ("NSCMD_DEVICEQUERY\n"));
214 static const UWORD NSDSupported
[] = {
215 CMD_FLUSH
, CMD_RESET
,
229 struct NSDeviceQueryResult
*nsdq
= (struct NSDeviceQueryResult
*)((struct IOStdReq
*)(ioreq
))->io_Data
;
230 nsdq
->DevQueryFormat
= 0;
231 nsdq
->SizeAvailable
= sizeof(struct NSDeviceQueryResult
);
232 nsdq
->DeviceType
= NSDEVTYPE_USBHARDWARE
;
233 nsdq
->DeviceSubType
= 0;
234 nsdq
->SupportedCommands
= (UWORD
*)NSDSupported
;
238 mybug_unit(-1, ("IOERR_NOCMD\n"));
243 /* We have aborted the request as unit is invalid */
247 if(ret
!= RC_DONTREPLY
) {
248 /* Set error codes */
250 ioreq
->iouh_Req
.io_Error
= ret
& 0xff;
252 /* Terminate the iorequest */
253 ioreq
->iouh_Req
.io_Message
.mn_Node
.ln_Type
= NT_FREEMSG
;
254 /* If not quick I/O, reply the message */
255 if(!(ioreq
->iouh_Req
.io_Flags
& IOF_QUICK
)) {
256 ReplyMsg(&ioreq
->iouh_Req
.io_Message
);
263 AROS_LH1(LONG
, AbortIO
, AROS_LHA(struct IOUsbHWReq
*, ioreq
, A1
), struct VXHCIBase
*, VXHCIBase
, 6, VXHCI
) {
265 mybug(-1, ("[VXHCI] AbortIO: Entering function\n"));
267 struct VXHCIUnit
*unit
= (struct VXHCIUnit
*) ioreq
->iouh_Req
.io_Unit
;
269 mybug_unit(-1, ("Nothing done!\n\n"));
275 struct VXHCIUnit
*VXHCI_AddNewUnit(ULONG unitnum
, UWORD bcdusb
) {
277 struct VXHCIUnit
*unit
;
278 struct VXHCIPort
*port
;
282 unit
= AllocVec(sizeof(struct VXHCIUnit
), MEMF_ANY
|MEMF_CLEAR
);
284 mybug(-1, ("[VXHCI] VXHCI_AddNewUnit: Failed to create new unit structure\n"));
287 unit
->node
.ln_Type
= NT_USER
;
288 unit
->number
= unitnum
;
289 unit
->node
.ln_Name
= (STRPTR
)&unit
->name
;
290 unit
->state
= UHSF_SUSPENDED
;
292 NEWLIST(&unit
->roothub
.port_list
);
294 /* Set the correct bcdUSB and bcdDevice for the hub device descriptor */
296 unit
->roothub
.devdesc
.bcdUSB
= AROS_WORD2LE(0x0300);
298 unit
->roothub
.devdesc
.bcdUSB
= AROS_WORD2LE(0x0200);
301 unit
->roothub
.devdesc
.bcdDevice
= AROS_WORD2LE(bcdusb
);
303 sprintf(unit
->name
, "VXHCI_USB%x%x[%d]", (AROS_LE2WORD(unit
->roothub
.devdesc
.bcdUSB
)>>8)&0xf, (AROS_LE2WORD(unit
->roothub
.devdesc
.bcdUSB
)>>4)&0xf, unit
->number
);
305 #ifdef VXHCI_NUMPORTS20
306 if( (bcdusb
>= 0x0200) && (bcdusb
< 0x0300) ) {
307 unit
->roothub
.devdesc
.bMaxPacketSize0
= 8;
308 unit
->roothub
.devdesc
.bDeviceProtocol
= 1;
309 unit
->roothub
.config
.epdesc
.wMaxPacketSize
= AROS_WORD2LE(8);
310 imax
= VXHCI_NUMPORTS20
;
312 unit
->roothub
.devdesc
.bMaxPacketSize0
= 9;
313 unit
->roothub
.devdesc
.bDeviceProtocol
= 3;
314 unit
->roothub
.config
.epdesc
.wMaxPacketSize
= AROS_WORD2LE(1024);
315 imax
= VXHCI_NUMPORTS30
;
318 unit
->roothub
.devdesc
.bMaxPacketSize0
= 9;
319 unit
->roothub
.devdesc
.bDeviceProtocol
= 3;
320 unit
->roothub
.config
.epdesc
.wMaxPacketSize
= AROS_WORD2LE(1024);
321 imax
= VXHCI_NUMPORTS30
;
324 for (i
=0; i
<imax
; i
++) {
326 port
= VXHCI_AddNewPort(unit
, i
);
328 mybug(-1, ("[VXHCI] VXHCI_AddNewUnit: Failed to create new port structure\n"));
331 Free previous ports if any exists and delete this unit
334 ForeachNode(&unit
->roothub
.port_list
, port
) {
335 mybug(-1, ("[VXHCI] VXHCI_AddNewUnit: Removing port structure %s at %p\n", port
->node
.ln_Name
, port
));
342 AddTail(&unit
->roothub
.port_list
,(struct Node
*)port
);
343 unit
->roothub
.port_count
++;
347 /* This is our root hub device descriptor */
348 unit
->roothub
.devdesc
.bLength
= sizeof(struct UsbStdDevDesc
);
349 unit
->roothub
.devdesc
.bDescriptorType
= UDT_DEVICE
;
350 //unit->roothub.devdesc.bcdUSB = AROS_WORD2LE(0xJJMN);
351 unit
->roothub
.devdesc
.bDeviceClass
= HUB_CLASSCODE
;
352 //unit->roothub.devdesc.bDeviceSubClass = 0;
353 //unit->roothub.devdesc.bDeviceProtocol = 0;
354 //unit->roothub.devdesc.bMaxPacketSize0 = 9; // Valid values are 8, 9(SuperSpeed), 16, 32, 64
355 //unit->roothub.devdesc.idVendor = AROS_WORD2LE(0x0000);
356 //unit->roothub.devdesc.idProduct = AROS_WORD2LE(0x0000);
357 //unit->roothub.devdesc.bcdDevice = AROS_WORD2LE(0xJJMN);
358 unit
->roothub
.devdesc
.iManufacturer
= 1;
359 unit
->roothub
.devdesc
.iProduct
= 2;
360 //unit->roothub.devdesc.iSerialNumber = 0;
361 unit
->roothub
.devdesc
.bNumConfigurations
= 1;
363 /* This is our root hub config descriptor */
364 unit
->roothub
.config
.cfgdesc
.bLength
= sizeof(struct UsbStdCfgDesc
);
365 unit
->roothub
.config
.cfgdesc
.bLength
= sizeof(struct UsbStdCfgDesc
);
366 unit
->roothub
.config
.cfgdesc
.bDescriptorType
= UDT_CONFIGURATION
;
367 unit
->roothub
.config
.cfgdesc
.wTotalLength
= AROS_WORD2LE(sizeof(struct RHConfig
));
368 unit
->roothub
.config
.cfgdesc
.bNumInterfaces
= 1;
369 unit
->roothub
.config
.cfgdesc
.bConfigurationValue
= 1;
370 unit
->roothub
.config
.cfgdesc
.iConfiguration
= 3;
371 unit
->roothub
.config
.cfgdesc
.bmAttributes
= (USCAF_ONE
|USCAF_SELF_POWERED
);
372 //unit->roothub.config.cfgdesc.bMaxPower = 0;
374 unit
->roothub
.config
.ifdesc
.bLength
= sizeof(struct UsbStdIfDesc
);
375 unit
->roothub
.config
.ifdesc
.bDescriptorType
= UDT_INTERFACE
;
376 //unit->roothub.config.ifdesc.bInterfaceNumber = 0;
377 //unit->roothub.config.ifdesc.bAlternateSetting = 0;
378 unit
->roothub
.config
.ifdesc
.bNumEndpoints
= 1;
379 unit
->roothub
.config
.ifdesc
.bInterfaceClass
= HUB_CLASSCODE
;
380 //unit->roothub.config.ifdesc.bInterfaceSubClass = 0;
381 //unit->roothub.config.ifdesc.bInterfaceProtocol = 0;
382 unit
->roothub
.config
.ifdesc
.iInterface
= 4;
384 unit
->roothub
.config
.epdesc
.bLength
= sizeof(struct UsbStdEPDesc
);
385 unit
->roothub
.config
.epdesc
.bDescriptorType
= UDT_ENDPOINT
;
386 unit
->roothub
.config
.epdesc
.bEndpointAddress
= (URTF_IN
|1);
387 unit
->roothub
.config
.epdesc
.bmAttributes
= USEAF_INTERRUPT
;
388 //unit->roothub.config.epdesc.wMaxPacketSize = AROS_WORD2LE(8);
389 unit
->roothub
.config
.epdesc
.bInterval
= 12;
391 /* This is our root hub hub descriptor */
392 if( (bcdusb
>= 0x0200) && (bcdusb
< 0x0300) ) {
393 unit
->roothub
.hubdesc
.usb20
.bLength
= sizeof(struct UsbHubDesc
);
394 unit
->roothub
.hubdesc
.usb20
.bDescriptorType
= UDT_HUB
;
395 unit
->roothub
.hubdesc
.usb20
.bNbrPorts
= (UBYTE
) unit
->roothub
.port_count
;
396 unit
->roothub
.hubdesc
.usb20
.wHubCharacteristics
= AROS_WORD2LE(UHCF_INDIVID_POWER
|UHCF_INDIVID_OVP
);
397 //unit->roothub.hubdesc.usb20.bPwrOn2PwrGood = 0;
398 unit
->roothub
.hubdesc
.usb20
.bHubContrCurrent
= 1;
399 unit
->roothub
.hubdesc
.usb20
.DeviceRemovable
= 1;
400 //unit->roothub.hubdesc.usb20.PortPwrCtrlMask = 0;
402 unit
->roothub
.hubdesc
.usb30
.bLength
= sizeof(struct UsbSSHubDesc
);
403 unit
->roothub
.hubdesc
.usb30
.bDescriptorType
= UDT_SSHUB
;
404 unit
->roothub
.hubdesc
.usb30
.bNbrPorts
= (UBYTE
) unit
->roothub
.port_count
;;
405 unit
->roothub
.hubdesc
.usb30
.wHubCharacteristics
= AROS_WORD2LE(UHCF_INDIVID_POWER
|UHCF_INDIVID_OVP
);
406 //unit->roothub.hubdesc.usb30.bPwrOn2PwrGood = 0;
407 unit
->roothub
.hubdesc
.usb30
.bHubContrCurrent
= 10;
408 //unit->roothub.hubdesc.usb30.bHubHdrDecLat = 0;
409 //unit->roothub.hubdesc.usb30.wHubDelay = 0;
410 //unit->roothub.hubdesc.usb30.DeviceRemovable = 0;
413 D( mybug(0, ("[VXHCI] VXHCI_AddNewUnit:\n"));
414 mybug(0, (" Created new unit numbered %d at %p\n",unit
->number
, unit
));
415 mybug(0, (" Unit node name %s\n", unit
->node
.ln_Name
));
417 switch(unit
->state
) {
419 mybug(0, (" Unit state: UHSF_SUSPENDED\n"));
421 case UHSF_OPERATIONAL
:
422 mybug(0, (" Unit state: UHSF_OPERATIONAL\n"));
425 mybug(0, (" Unit state: %lx (Error?)\n", unit
->state
));
433 struct VXHCIPort
*VXHCI_AddNewPort(struct VXHCIUnit
*unit
, ULONG portnum
) {
434 struct VXHCIPort
*port
;
436 port
= AllocVec(sizeof(struct VXHCIPort
), MEMF_ANY
|MEMF_CLEAR
);
438 mybug(-1, ("[VXHCI] VXHCI_AddNewPort: Failed to create new port structure\n"));
441 port
->node
.ln_Type
= NT_USER
;
442 /* Poseidon treats port number 0 as roothub */
443 port
->number
= portnum
+1;
445 sprintf(port
->name
, "VXHCI_USB%x%x[%d:%d]", (AROS_LE2WORD(unit
->roothub
.devdesc
.bcdUSB
)>>8)&0xf, (AROS_LE2WORD(unit
->roothub
.devdesc
.bcdUSB
)>>4)&0xf, unit
->number
, port
->number
);
446 port
->node
.ln_Name
= (STRPTR
)&port
->name
;
449 mybug(0, ("[VXHCI] VXHCI_AddNewPort:\n"));
450 mybug(0, (" Created new port numbered %d at %p\n",port
->number
, port
));
451 mybug(0, (" Port node name %s\n", port
->node
.ln_Name
));