revert commit 56204.
[AROS.git] / rom / usb / classes / hubss / hubss_class.c
blobb516a21e4400eb8829b1cdc7c1a381e8b714874b
1 /*
2 Copyright © 2014, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: SuperSpeed USB3.0 hub for Poseidon (based upon hub.class.c by Chris Hodges <chrisly@platon42.de>)
6 Lang: english
7 */
9 #ifdef DEBUG
10 #undef DEBUG
11 #endif
12 #define DEBUG 1
14 #include <aros/debug.h>
16 #include <proto/poseidon.h>
17 #include <proto/arossupport.h>
19 #include <devices/usb.h>
20 #include <devices/usb_hub.h>
21 #include <devices/usbhardware.h>
22 #include <libraries/usbclass.h>
24 #include "hubss_class.h"
26 #include LC_LIBDEFS_FILE
28 struct NepClassHub * GM_UNIQUENAME(usbAttemptDeviceBinding)(struct NepHubBase *nh, struct PsdDevice *pd);
29 struct NepClassHub * GM_UNIQUENAME(usbForceDeviceBinding)(struct NepHubBase * nh, struct PsdDevice *pd);
30 void GM_UNIQUENAME(usbReleaseDeviceBinding)(struct NepHubBase *nh, struct NepClassHub *nch);
32 struct NepClassHub * GM_UNIQUENAME(nAllocHub)(void);
33 void GM_UNIQUENAME(nFreeHub)(struct NepClassHub *nch);
34 struct PsdDevice * GM_UNIQUENAME(nConfigurePort)(struct NepClassHub *nch, UWORD port);
35 LONG GM_UNIQUENAME(nClearPortStatus)(struct NepClassHub *nch, UWORD port);
36 BOOL GM_UNIQUENAME(nHubSuspendDevice)(struct NepClassHub *nch, struct PsdDevice *pd);
37 BOOL GM_UNIQUENAME(nHubResumeDevice)(struct NepClassHub *nch, struct PsdDevice *pd);
38 void GM_UNIQUENAME(nHandleHubMethod)(struct NepClassHub *nch, struct NepHubMsg *nhm);
39 AROS_UFP0(void, GM_UNIQUENAME(nHubssTask));
41 /* /// "Lib Stuff" */
42 static const STRPTR libname = MOD_NAME_STRING;
44 static int GM_UNIQUENAME(libInit)(LIBBASETYPEPTR nh) {
46 NEWLIST(&nh->nh_Bindings);
47 InitSemaphore(&nh->nh_Adr0Sema);
49 return TRUE;
52 ADD2INITLIB(GM_UNIQUENAME(libInit), 0)
54 /* \\\ */
57 * ***********************************************************************
58 * * Library functions *
59 * ***********************************************************************
62 /* /// "usbAttemptDeviceBinding()" */
63 struct NepClassHub * GM_UNIQUENAME(usbAttemptDeviceBinding)(struct NepHubBase *nh, struct PsdDevice *pd) {
64 struct Library *ps;
65 IPTR devclass;
66 IPTR issuperspeed = 0;
68 //KPRINTF(0, ("usbAttemptDeviceBinding(%p)\n", pd));
70 if((ps = OpenLibrary("poseidon.library", 4))) {
71 psdGetAttrs(PGA_DEVICE, pd, DA_Class, &devclass, DA_IsSuperspeed, &issuperspeed, TAG_DONE);
72 CloseLibrary(ps);
74 if((devclass == HUB_CLASSCODE) && (issuperspeed)) {
75 return(GM_UNIQUENAME(usbForceDeviceBinding)(nh, pd));
78 return(NULL);
81 /* /// "usbForceDeviceBinding()" */
82 struct NepClassHub * GM_UNIQUENAME(usbForceDeviceBinding)(struct NepHubBase * nh, struct PsdDevice *pd) {
83 struct Library *ps;
84 struct NepClassHub *nch;
85 STRPTR devname;
86 char buf[64];
87 struct Task *tmptask;
89 //KPRINTF(0, ("usbForceDeviceBinding(%p)\n", pd));
91 if((ps = OpenLibrary("poseidon.library", 4))) {
92 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_DONE);
94 if((nch = psdAllocVec(sizeof(struct NepClassHub)))) {
95 nch->nch_HubBase = nh;
96 nch->nch_Device = pd;
97 psdSafeRawDoFmt(buf, 64, "hubss.class<%p>", nch);
98 nch->nch_ReadySignal = SIGB_SINGLE;
99 nch->nch_ReadySigTask = FindTask(NULL);
100 SetSignal(0, SIGF_SINGLE);
102 if((tmptask = psdSpawnSubTask(buf, GM_UNIQUENAME(nHubssTask), nch))) {
103 psdBorrowLocksWait(tmptask, 1UL<<nch->nch_ReadySignal);
105 if(nch->nch_Task) {
106 nch->nch_ReadySigTask = NULL;
107 //FreeSignal(nch->nch_ReadySignal);
108 psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "I'm in love with superspeed hub '%s'.", devname);
110 Forbid();
111 AddTail(&nh->nh_Bindings, &nch->nch_Node);
112 Permit();
113 CloseLibrary(ps);
114 return(nch);
118 nch->nch_ReadySigTask = NULL;
119 //FreeSignal(nch->nch_ReadySignal);
120 psdFreeVec(nch);
123 CloseLibrary(ps);
126 return(NULL);
129 /* /// "usbReleaseDeviceBinding()" */
130 void GM_UNIQUENAME(usbReleaseDeviceBinding)(struct NepHubBase *nh, struct NepClassHub *nch) {
131 struct Library *ps;
132 STRPTR devname;
134 KPRINTF(1, ("nepHubReleaseDeviceBinding(%p)\n", nch));
136 if((ps = OpenLibrary("poseidon.library", 4))) {
138 Forbid();
139 nch->nch_ReadySignal = SIGB_SINGLE;
140 nch->nch_ReadySigTask = FindTask(NULL);
141 if(nch->nch_Task) {
142 KPRINTF(1, ("Sending Break\n"));
143 Signal(nch->nch_Task, SIGBREAKF_CTRL_C);
145 Permit();
147 while(nch->nch_Task) {
148 psdBorrowLocksWait(nch->nch_Task, 1UL<<nch->nch_ReadySignal);
150 KPRINTF(1, ("Task gone\n"));
152 //FreeSignal(nch->nch_ReadySignal);
153 psdGetAttrs(PGA_DEVICE, nch->nch_Device, DA_ProductName, &devname, TAG_END);
154 psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "Time to get rid of '%s'!", devname);
156 Forbid();
157 Remove(&nch->nch_Node);
158 Permit();
160 psdFreeVec(nch);
161 CloseLibrary(ps);
165 /* /// "usbGetAttrsA()" */
166 AROS_LH3(LONG, usbGetAttrsA, AROS_LHA(ULONG, type, D0), AROS_LHA(APTR, usbstruct, A0), AROS_LHA(struct TagItem *, taglist, A1), LIBBASETYPEPTR, nh, 5, hub) {
167 AROS_LIBFUNC_INIT
169 struct TagItem *ti;
170 LONG count = 0;
172 KPRINTF(1, ("nepHubGetAttrsA(%ld, %p, %p)\n", type, usbstruct, taglist));
174 switch(type) {
175 case UGA_CLASS:
176 while((ti = LibNextTagItem(&taglist)) != NULL) {
177 switch (ti->ti_Tag) {
178 case UCCA_Priority:
179 *((SIPTR *) ti->ti_Data) = 0;
180 count++;
181 break;
182 case UCCA_Description:
183 *((STRPTR *) ti->ti_Data) = "Root/external superspeed hub base class";
184 count++;
185 break;
186 case UCCA_HasClassCfgGUI:
187 *((IPTR *) ti->ti_Data) = FALSE;
188 count++;
189 break;
190 case UCCA_HasBindingCfgGUI:
191 *((IPTR *) ti->ti_Data) = FALSE;
192 count++;
193 break;
194 case UCCA_AfterDOSRestart:
195 *((IPTR *) ti->ti_Data) = FALSE;
196 count++;
197 break;
198 case UCCA_UsingDefaultCfg:
199 *((IPTR *) ti->ti_Data) = TRUE;
200 count++;
201 break;
202 case UCCA_SupportsSuspend:
203 *((IPTR *) ti->ti_Data) = TRUE;
204 count++;
205 break;
206 } /* switch (ti->ti_Tag) */
207 }; /* while((ti = LibNextTagItem(&taglist)) != NULL) */
208 break;
210 case UGA_BINDING:
211 if((ti = LibFindTagItem(UCBA_UsingDefaultCfg, taglist))) {
212 *((IPTR *) ti->ti_Data) = TRUE;
213 count++;
215 break;
218 return(count);
219 AROS_LIBFUNC_EXIT
222 /* /// "usbSetAttrsA()" */
223 AROS_LH3(LONG, usbSetAttrsA, AROS_LHA(ULONG, type, D0), AROS_LHA(APTR, usbstruct, A0), AROS_LHA(struct TagItem *, tags, A1), LIBBASETYPEPTR, nh, 6, hub) {
224 AROS_LIBFUNC_INIT
226 return(0);
227 AROS_LIBFUNC_EXIT
230 /* /// "usbDoMethodA()" */
231 AROS_LH2(IPTR, usbDoMethodA, AROS_LHA(ULONG, methodid, D0), AROS_LHA(IPTR *, methoddata, A1), LIBBASETYPEPTR, nh, 7, hub) {
232 AROS_LIBFUNC_INIT
234 struct NepClassHub *nch;
236 KPRINTF(1, ("Do Method %ld\n", methodid));
237 switch(methodid) {
238 case UCM_AttemptDeviceBinding:
239 return((IPTR) GM_UNIQUENAME(usbAttemptDeviceBinding)(nh, (struct PsdDevice *) methoddata[0]));
241 case UCM_ForceDeviceBinding:
242 return((IPTR) GM_UNIQUENAME(usbForceDeviceBinding)(nh, (struct PsdDevice *) methoddata[0]));
244 case UCM_ReleaseDeviceBinding:
245 GM_UNIQUENAME(usbReleaseDeviceBinding)(nh, (struct NepClassHub *) methoddata[0]);
246 return(TRUE);
248 case UCM_HubPowerCyclePort:
249 case UCM_HubDisablePort: {
250 struct PsdDevice *pd = (struct PsdDevice *) methoddata[0];
251 ULONG port = (ULONG) methoddata[1];
253 if(!(pd && port)) {
254 KPRINTF(20, ("HubPowerCycle/DisablePort Params Null!\n"));
255 return(FALSE);
258 Forbid();
259 nch = (struct NepClassHub *) nh->nh_Bindings.lh_Head;
260 while(nch->nch_Node.ln_Succ) {
261 if(nch->nch_Device == pd) {
262 KPRINTF(20, ("HubPowerCycle/DisablePort Dev found (port %ld)!\n", port));
263 if(port <= nch->nch_NumPorts) {
264 nch->nch_DisablePort |= 1UL<<port;
265 if(methodid == UCM_HubPowerCyclePort) {
266 nch->nch_PowerCycle |= 1UL<<port;
268 if(nch->nch_Task) {
269 Signal(nch->nch_Task, (1L<<nch->nch_TaskMsgPort->mp_SigBit));
271 Permit();
272 return(TRUE);
274 break;
276 nch = (struct NepClassHub *) nch->nch_Node.ln_Succ;
278 Permit();
280 return(FALSE);
281 } /* case UCM_HubDisablePort */
283 case UCM_HubClassScan: {
284 nch = (struct NepClassHub *) methoddata[0];
286 Forbid();
287 nch->nch_ClassScan = TRUE;
288 if(nch->nch_Task) {
289 Signal(nch->nch_Task, (1L<<nch->nch_TaskMsgPort->mp_SigBit));
291 Permit();
293 return(TRUE);
294 } /* case UCM_HubClassScan */
296 case UCM_AttemptSuspendDevice:
297 case UCM_AttemptResumeDevice:
298 case UCM_HubClaimAppBinding:
299 case UCM_HubReleaseIfBinding:
300 case UCM_HubReleaseDevBinding:
301 case UCM_HubSuspendDevice:
302 case UCM_HubResumeDevice: {
303 struct NepHubMsg nhm;
304 struct Library *ps;
305 nch = (struct NepClassHub *) methoddata[0];
306 nhm.nhm_Result = (IPTR) NULL;
307 nhm.nhm_MethodID = methodid;
308 nhm.nhm_Params = methoddata;
310 if((ps = OpenLibrary("poseidon.library", 4))) {
311 if(nch->nch_Task == FindTask(NULL)) {
312 // if we would send the message to ourself, we would deadlock, so handle this directly
313 GM_UNIQUENAME(nHandleHubMethod)(nch, &nhm);
314 } else {
315 nhm.nhm_Msg.mn_ReplyPort = CreateMsgPort();
316 nhm.nhm_Msg.mn_Length = sizeof(struct NepHubMsg);
318 Forbid();
319 if(nch->nch_Task && nhm.nhm_Msg.mn_ReplyPort) {
320 PutMsg(nch->nch_CtrlMsgPort, &nhm.nhm_Msg);
321 Permit();
323 while(!GetMsg(nhm.nhm_Msg.mn_ReplyPort)) {
324 psdBorrowLocksWait(nch->nch_Task, 1UL<<nhm.nhm_Msg.mn_ReplyPort->mp_SigBit);
326 } else {
327 Permit();
329 DeleteMsgPort(nhm.nhm_Msg.mn_ReplyPort);
331 CloseLibrary(ps);
334 return(nhm.nhm_Result);
335 }/* case UCM_HubResumeDevice */
337 default:
338 break;
341 return(0);
342 AROS_LIBFUNC_EXIT
345 #undef ps
346 #define ps nch->nch_Base
348 /* /// "nHubssTask()" */
349 AROS_UFH0(void, GM_UNIQUENAME(nHubssTask)) {
350 AROS_USERFUNC_INIT
352 struct NepClassHub *nch;
353 struct PsdPipe *pp;
354 ULONG sigmask;
355 ULONG sigs;
356 UWORD num;
357 LONG ioerr;
358 struct UsbPortStatus uhps;
359 struct UsbHubStatus uhhs;
360 ULONG count;
361 struct PsdDevice *pd;
362 STRPTR devname;
363 struct NepHubMsg *nhm;
365 if((nch = GM_UNIQUENAME(nAllocHub)())) {
366 Forbid();
367 if(nch->nch_ReadySigTask) {
368 Signal(nch->nch_ReadySigTask, 1L<<nch->nch_ReadySignal);
370 Permit();
371 count = 0;
372 for(num = 1; num <= nch->nch_NumPorts; num++) {
373 if(((nch->nch_Downstream)[num-1] = pd = GM_UNIQUENAME(nConfigurePort)(nch, num))) {
374 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
375 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
376 "Detected device '%s' at port %ld. I like it.",
377 devname, num);
378 count++;
381 if(count) {
382 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
383 "Hub has added %ld device(s). That'll be fun!",
384 count);
386 // do a class scan
387 for(num = 1; num <= nch->nch_NumPorts; num++) {
388 if((pd = (nch->nch_Downstream)[num-1])) {
389 psdHubClassScan(pd);
392 sigmask = (1L<<nch->nch_TaskMsgPort->mp_SigBit)|(1L<<nch->nch_CtrlMsgPort->mp_SigBit)|SIGBREAKF_CTRL_C;
393 nch->nch_Running = TRUE;
394 nch->nch_IOStarted = FALSE;
395 do {
396 if(nch->nch_Running && (!nch->nch_IOStarted)) {
397 psdSendPipe(nch->nch_EP1Pipe, nch->nch_PortChanges, (nch->nch_NumPorts+8)>>3);
398 nch->nch_IOStarted = TRUE;
400 sigs = Wait(sigmask);
402 while((nhm = (struct NepHubMsg *) GetMsg(nch->nch_CtrlMsgPort))) {
403 GM_UNIQUENAME(nHandleHubMethod)(nch, nhm);
404 ReplyMsg((struct Message *) nhm);
407 if(nch->nch_DisablePort) {
408 for(num = 1; num <= nch->nch_NumPorts; num++) {
409 if((nch->nch_DisablePort) & (1L<<num)) {
410 nch->nch_DisablePort &= ~(1L<<num);
411 /* Remove device */
412 if((pd = (nch->nch_Downstream)[num-1])) {
413 psdSetAttrs(PGA_DEVICE, pd, DA_IsConnected, FALSE, TAG_END);
414 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
415 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
416 "Zapping device '%s' at port %ld!",
417 devname, num);
418 psdFreeDevice(pd);
419 psdSendEvent(EHMB_REMDEVICE, pd, NULL);
420 (nch->nch_Downstream)[num-1] = NULL;
421 pd = NULL;
422 /* disable port */
423 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER,
424 USR_CLEAR_FEATURE, UFS_PORT_ENABLE, (ULONG) num);
425 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
426 if(ioerr) {
427 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
428 "CLEAR_PORT_ENABLE failed: %s (%ld)",
429 psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
430 KPRINTF(1, ("CLEAR_PORT_ENABLE failed %ld.\n", ioerr));
433 if(nch->nch_PowerCycle & (1<<num)) {
434 KPRINTF(2, ("Powercycle request for port %lu\n", num));
435 nch->nch_PowerCycle &= ~(1L<<num);
437 /* Wait for device to settle */
438 psdDelayMS(250);
439 if(((nch->nch_Downstream)[num-1] = pd = GM_UNIQUENAME(nConfigurePort)(nch, num))) {
440 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
441 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
442 "Device '%s' returned. Happy happy joy joy.",
443 devname);
444 psdHubClassScan(pd);
451 if(nch->nch_ClassScan) {
452 nch->nch_ClassScan = FALSE;
453 for(num = 1; num <= nch->nch_NumPorts; num++)
455 if((pd = (nch->nch_Downstream)[num-1]))
457 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
458 psdHubClassScan(pd);
462 while((pp = (struct PsdPipe *) GetMsg(nch->nch_TaskMsgPort))) {
463 if(pp == nch->nch_EP1Pipe) {
464 nch->nch_IOStarted = FALSE;
465 ioerr = psdGetPipeError(nch->nch_EP1Pipe);
466 if(ioerr == UHIOERR_TIMEOUT) {
467 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
468 "Hub involuntarily gone! Disconnecting...");
469 psdSetAttrs(PGA_DEVICE, nch->nch_Device,
470 DA_IsConnected, FALSE,
471 TAG_END);
472 nch->nch_PortChanges[0] = 0xff;
473 nch->nch_PortChanges[1] = 0xff;
474 nch->nch_PortChanges[2] = 0xff;
475 nch->nch_PortChanges[3] = 0xff;
476 sigs |= SIGBREAKF_CTRL_C;
478 if((!ioerr) || (ioerr == UHIOERR_TIMEOUT)) {
479 KPRINTF(2, ("Port changed at %p, Numports=%ld!\n", nch->nch_PortChanges[0], nch->nch_NumPorts));
481 if(nch->nch_PortChanges[0] & 1) {
482 psdPipeSetup(nch->nch_EP0Pipe, URTF_IN|URTF_CLASS|URTF_DEVICE,
483 USR_GET_STATUS, 0, 0);
484 ioerr = psdDoPipe(nch->nch_EP0Pipe, &uhhs, sizeof(struct UsbHubStatus));
485 uhhs.wHubStatus = AROS_WORD2LE(uhhs.wHubStatus);
486 uhhs.wHubChange = AROS_WORD2LE(uhhs.wHubChange);
487 if(!ioerr)
489 if(uhhs.wHubStatus & UHSF_OVER_CURRENT)
491 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
492 "Hub over-current situation detected! Unpowering ALL ports!");
493 for(num = 1; num <= nch->nch_NumPorts; num++)
495 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER,
496 USR_CLEAR_FEATURE, UFS_PORT_POWER, (ULONG) num);
497 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
498 if(ioerr)
500 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
501 "PORT_POWER for port %ld failed: %s (%ld)",
502 num, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
503 KPRINTF(1, ("PORT_POWER for port %ld failed %ld!\n", num, ioerr));
506 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER,
507 USR_CLEAR_FEATURE, UFS_C_PORT_OVER_CURRENT, (ULONG) num);
508 psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
510 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_DEVICE,
511 USR_CLEAR_FEATURE, UFS_C_HUB_OVER_CURRENT, 0);
512 psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
514 if(uhhs.wHubChange & UHSF_LOCAL_POWER_LOST)
516 struct PsdConfig *pc = NULL;
517 struct PsdHardware *phw = NULL;
518 psdGetAttrs(PGA_DEVICE, nch->nch_Device,
519 DA_Config, &pc,
520 DA_Hardware, &phw,
521 TAG_END);
522 if(uhhs.wHubStatus & UHSF_LOCAL_POWER_LOST)
524 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
525 "Hub is no longer self-powered! Low power conditions may occur.");
527 if(pc && phw)
529 psdSetAttrs(PGA_CONFIG, pc, CA_SelfPowered, FALSE, TAG_END);
530 psdCalculatePower(phw);
532 } else {
533 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
534 "Hub is now self-powered! Yay!");
535 if(pc && phw)
537 psdSetAttrs(PGA_CONFIG, pc, CA_SelfPowered, TRUE, TAG_END);
538 psdCalculatePower(phw);
541 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_DEVICE,
542 USR_CLEAR_FEATURE, UFS_C_HUB_LOCAL_POWER, 0);
543 psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
548 for(num = 1; num <= nch->nch_NumPorts; num++)
550 if(nch->nch_PortChanges[num>>3] & (1L<<(num & 7)))
552 psdPipeSetup(nch->nch_EP0Pipe, URTF_IN|URTF_CLASS|URTF_OTHER,
553 USR_GET_STATUS, 0, (ULONG) num);
554 ioerr = psdDoPipe(nch->nch_EP0Pipe, &uhps, sizeof(struct UsbPortStatus));
555 uhps.wPortStatus = AROS_WORD2LE(uhps.wPortStatus);
556 uhps.wPortChange = AROS_WORD2LE(uhps.wPortChange);
557 if(ioerr == UHIOERR_TIMEOUT)
559 uhps.wPortStatus = 0;
560 uhps.wPortChange = 0xffff;
561 ioerr = 0;
562 } else {
563 GM_UNIQUENAME(nClearPortStatus)(nch, num);
565 if(!ioerr)
567 pd = (nch->nch_Downstream)[num-1];
568 if(uhps.wPortStatus & UPSF_PORT_OVER_CURRENT)
570 if(pd)
572 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
573 } else {
574 devname = "a ghost";
576 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
577 "Over-current situation detected with %s at port %ld! Unpowering port!",
578 devname, num);
579 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER,
580 USR_CLEAR_FEATURE, UFS_PORT_POWER, (ULONG) num);
581 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
582 if(ioerr)
584 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
585 "PORT_POWER for port %ld failed: %s (%ld)",
586 num, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
587 KPRINTF(1, ("PORT_POWER for port %ld failed %ld!\n", num, ioerr));
590 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER,
591 USR_CLEAR_FEATURE, UFS_C_PORT_OVER_CURRENT, (ULONG) num);
592 psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
594 if(uhps.wPortChange & UPSF_PORT_SUSPEND)
596 if((!(uhps.wPortStatus & UPSF_PORT_SUSPEND)) && pd)
598 IPTR oldsusp = 0;
599 psdGetAttrs(PGA_DEVICE, pd, DA_IsSuspended, &oldsusp, TAG_END);
600 psdSetAttrs(PGA_DEVICE, pd, DA_IsSuspended, FALSE, TAG_END);
601 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
602 if(oldsusp)
604 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
605 "Device '%s' at port %ld resumed from remote!",
606 devname, num);
607 psdSendEvent(EHMB_DEVRESUMED, pd, NULL);
608 psdResumeBindings(pd);
611 else if((uhps.wPortStatus & UPSF_PORT_SUSPEND) && pd)
613 psdSetAttrs(PGA_DEVICE, pd, DA_IsSuspended, FALSE, TAG_END);
614 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
615 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
616 "Device '%s' at port %ld suspended!",
617 devname, num);
618 } else {
619 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
620 "Bogus suspend/resume change on port %ld.",
621 num);
624 if(uhps.wPortChange & UPSF_PORT_CONNECTION)
626 /* Remove device */
627 if((!(uhps.wPortStatus & UPSF_PORT_CONNECTION)) && pd)
629 psdSetAttrs(PGA_DEVICE, pd, DA_IsConnected, FALSE, TAG_END);
630 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
631 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
632 "Device '%s' at port %ld is gone!",
633 devname, num);
634 psdFreeDevice(pd);
635 psdSendEvent(EHMB_REMDEVICE, pd, NULL);
636 (nch->nch_Downstream)[num-1] = NULL;
637 pd = NULL;
639 /* add new device */
640 if((uhps.wPortStatus & UPSF_PORT_CONNECTION) && (!pd))
642 /* Wait for device to settle */
643 psdDelayMS(100);
644 if(((nch->nch_Downstream)[num-1] = pd = GM_UNIQUENAME(nConfigurePort)(nch, num)))
646 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
647 psdAddErrorMsg(RETURN_OK, (STRPTR) libname,
648 "New device '%s' at port %ld. Very nice.",
649 devname, num);
650 psdClassScan();
657 /* Bail out on time out. */
658 if(nch->nch_PortChanges[0] == 0xff)
660 break;
662 psdDelayMS(50);
663 } else {
664 if(ioerr != IOERR_ABORTED)
666 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
667 "Something weird happened to the status packet, it failed: %s (%ld)",
668 psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
669 psdDelayMS(200);
672 break;
673 } else {
674 KPRINTF(20, ("Bogus message received!\n"));
677 } while(!(sigs & SIGBREAKF_CTRL_C));
678 KPRINTF(20, ("Going down the river!\n"));
679 if(nch->nch_IOStarted)
681 psdAbortPipe(nch->nch_EP1Pipe);
682 psdWaitPipe(nch->nch_EP1Pipe);
684 psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "Oh no! I've been shot! Arrggghh...");
685 GM_UNIQUENAME(nFreeHub)(nch);
687 AROS_USERFUNC_EXIT
690 /* /// "nAllocHub()" */
691 struct NepClassHub * GM_UNIQUENAME(nAllocHub)(void) {
692 struct UsbSSHubDesc *usshd;
693 //struct UsbStdBOSDesc *usbosd;
694 struct Task *thistask;
695 struct NepClassHub *nch;
696 struct UsbHubStatus uhhs;
697 APTR parenthub;
698 LONG ioerr;
699 ULONG len;
700 UWORD num;
701 UBYTE buf[2];
702 IPTR issuperspeed = 0;
703 IPTR prodid;
704 IPTR vendid;
705 BOOL overcurrent = FALSE;
707 thistask = FindTask(NULL);
708 nch = thistask->tc_UserData;
710 do {
711 if(!(nch->nch_Base = OpenLibrary("poseidon.library", 4))) {
712 Alert(AG_OpenLib);
713 break;
716 psdGetAttrs(PGA_DEVICE, nch->nch_Device,
717 DA_Hardware, &nch->nch_Hardware,
718 DA_IsSuperspeed, &issuperspeed,
719 DA_ProductID, &prodid,
720 DA_VendorID, &vendid,
721 DA_HubDevice, &parenthub,
722 TAG_END);
724 nch->nch_IsRootHub = (parenthub ? FALSE : TRUE);
725 nch->nch_IsUSB30 = issuperspeed;
727 if(!nch->nch_Interface) {
728 nch->nch_Interface = psdFindInterface(nch->nch_Device, NULL, IFA_Class, HUB_CLASSCODE, TAG_END);
731 if(!nch->nch_Interface) {
732 KPRINTF(1, ("Ooops!?! No interfaces defined?\n"));
733 break;
736 nch->nch_EP1 = psdFindEndpoint(nch->nch_Interface, NULL, EA_IsIn, TRUE, EA_TransferType, USEAF_INTERRUPT, TAG_END);
738 if(!nch->nch_EP1) {
739 psdAddErrorMsg(RETURN_FAIL, (STRPTR) libname, "Ooops!?! No endpoints defined?");
740 KPRINTF(1, ("Ooops!?! No Endpoints defined?\n"));
741 break;
745 Device descriptor tree:
747 Device descriptor
748 Config descriptor
749 ...String descriptor
750 BOS descriptor
751 ...SS capability descriptor
752 ......USB2.0 LPM descriptor
753 Interface descriptor
754 Endpoint descriptor 0
755 ...Endpoint descriptor 1
756 ......Endpoint descriptor n
758 USB 3.0 enumeration:
760 Set address
761 Get device descriptor
762 Get BOS descriptor
763 Get config descriptor
768 see: http://youtu.be/5ChWxMLKzOs
772 if((nch->nch_CtrlMsgPort = CreateMsgPort())) {
773 if((nch->nch_TaskMsgPort = CreateMsgPort())) {
774 if((nch->nch_EP0Pipe = psdAllocPipe(nch->nch_Device, nch->nch_TaskMsgPort, NULL))) {
776 psdSetAttrs(PGA_PIPE, nch->nch_EP0Pipe, PPA_NakTimeout, TRUE, PPA_NakTimeoutTime, 1000, TAG_END);
777 psdSetAltInterface(nch->nch_EP0Pipe, nch->nch_Interface);
779 if((nch->nch_EP1Pipe = psdAllocPipe(nch->nch_Device, nch->nch_TaskMsgPort, nch->nch_EP1))) {
781 psdSetAttrs(PGA_PIPE, nch->nch_EP1Pipe, PPA_AllowRuntPackets, TRUE, TAG_END);
782 psdPipeSetup(nch->nch_EP0Pipe, URTF_IN|URTF_CLASS|URTF_DEVICE, USR_GET_DESCRIPTOR, UDT_SSHUB<<8, 0);
784 ioerr = psdDoPipe(nch->nch_EP0Pipe, &buf, 2);
786 if(buf[1] == UDT_SSHUB) {
788 if((!ioerr) || (ioerr == UHIOERR_OVERFLOW)) {
789 len = buf[0];
791 if((usshd = psdAllocVec(len))) {
792 ioerr = psdDoPipe(nch->nch_EP0Pipe, usshd, len);
794 if(!ioerr) {
795 nch->nch_NumPorts = (UWORD)usshd->bNbrPorts;
796 nch->nch_HubAttr = (UWORD)AROS_WORD2LE(usshd->wHubCharacteristics);
797 nch->nch_PwrGoodTime = (UWORD)usshd->bPwrOn2PwrGood<<1;
798 nch->nch_HubCurrent = (UWORD)usshd->bHubContrCurrent;
799 nch->nch_HubHdrDecLat = (UWORD)usshd->bHubHdrDecLat;
800 nch->nch_HubDelay = (UWORD)usshd->wHubDelay;
801 nch->nch_Removable = (UWORD)usshd->DeviceRemovable;
803 if(nch->nch_HubAttr & UHCM_THINK_TIME) {
804 psdSetAttrs(PGA_DEVICE, nch->nch_Device, DA_HubThinkTime, (nch->nch_HubAttr & UHCM_THINK_TIME)>>UHCS_THINK_TIME, TAG_END);
807 KPRINTF(2, ("Parsed SSHub descriptor\n"
808 " nch_NumPorts = %d\n"
809 " nch_HubAttr = 0x%04x\n"
810 " nch_PwrGoodTime = %d\n"
811 " nch_HubCurrent = %d\n"
812 " nch_HubHdrDecLat = %d\n"
813 " nch_HubDelay = %d\n"
814 " nch_Removable = 0x%04x\n\n",
815 (ULONG)nch->nch_NumPorts,
816 (ULONG)nch->nch_HubAttr,
817 (ULONG)nch->nch_PwrGoodTime,
818 (ULONG)nch->nch_HubCurrent,
819 (ULONG)nch->nch_HubHdrDecLat,
820 (ULONG)nch->nch_HubDelay,
821 (ULONG)nch->nch_Removable));
823 psdFreeVec(usshd);
825 psdPipeSetup(nch->nch_EP0Pipe, URTF_IN|URTF_CLASS|URTF_DEVICE, USR_GET_STATUS, 0, 0);
826 ioerr = psdDoPipe(nch->nch_EP0Pipe, &uhhs, sizeof(struct UsbHubStatus));
828 uhhs.wHubStatus = AROS_WORD2LE(uhhs.wHubStatus);
829 uhhs.wHubChange = AROS_WORD2LE(uhhs.wHubChange);
830 if(!ioerr)
832 struct PsdConfig *pc = NULL;
833 struct PsdHardware *phw = NULL;
834 if(uhhs.wHubStatus & UHSF_OVER_CURRENT)
836 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname,
837 "Hub over-current situation detected! Resolve this first!");
838 //overcurrent = TRUE;
841 psdGetAttrs(PGA_DEVICE, nch->nch_Device,
842 DA_Config, &pc,
843 DA_Hardware, &phw,
844 TAG_END);
845 if(uhhs.wHubStatus & UHSF_LOCAL_POWER_LOST)
847 if(pc && phw)
849 psdSetAttrs(PGA_CONFIG, pc, CA_SelfPowered, FALSE, TAG_END);
850 psdCalculatePower(phw);
852 } else {
853 if(pc && phw)
855 psdSetAttrs(PGA_CONFIG, pc, CA_SelfPowered, TRUE, TAG_END);
856 psdCalculatePower(phw);
860 if(!overcurrent) {
861 if((nch->nch_Downstream = psdAllocVec((ULONG) nch->nch_NumPorts*sizeof(APTR)))) {
862 /*for(num = 1; num <= nch->nch_NumPorts; num++)
864 GM_UNIQUENAME(nClearPortStatus)(nch, num);
866 psdDelayMS(20);*/
868 KPRINTF(2, ("Powering up ports...\n\n"));
870 for(num = 1; num <= nch->nch_NumPorts; num++) {
871 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_SET_FEATURE, UFS_PORT_POWER, (ULONG) num);
872 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
874 if(ioerr) {
875 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "PORT_POWER for port %ld failed: %s (%ld)", num, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
876 KPRINTF(1, ("PORT_POWER for port %ld failed %ld!\n", num, ioerr));
879 psdDelayMS((ULONG) nch->nch_PwrGoodTime + 15);
881 psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "Hub with %ld ports successfully configured.", nch->nch_NumPorts);
883 KPRINTF(10, ("%s ready!\n", thistask->tc_Node.ln_Name));
884 nch->nch_Task = thistask;
886 return(nch);
887 } else {
888 KPRINTF(1, ("No downstream port array memory!\n"));
891 } else {
892 psdFreeVec(usshd);
893 psdAddErrorMsg(RETURN_FAIL, (STRPTR) libname, "GET_HUB_DESCRIPTOR (%ld) failed: %s (%ld)", len, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
894 KPRINTF(1, ("GET_HUB_DESCRIPTOR (%ld) failed %ld!\n", len, ioerr));
897 } else {
898 KPRINTF(1, ("No Hub Descriptor memory!\n"));
900 } else {
901 psdAddErrorMsg(RETURN_FAIL, (STRPTR) libname, "GET_HUB_DESCRIPTOR (%ld) failed: %s (%ld)", 1, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
902 KPRINTF(1, ("GET_HUB_DESCRIPTOR (1) failed %ld!\n", ioerr));
907 psdFreePipe(nch->nch_EP1Pipe);
909 psdFreePipe(nch->nch_EP0Pipe);
911 DeleteMsgPort(nch->nch_TaskMsgPort);
913 DeleteMsgPort(nch->nch_CtrlMsgPort);
915 } while(FALSE);
917 CloseLibrary(nch->nch_Base);
919 Forbid();
920 nch->nch_Task = NULL;
922 if(nch->nch_ReadySigTask) {
923 Signal(nch->nch_ReadySigTask, 1L<<nch->nch_ReadySignal);
926 return(NULL);
929 /* /// "nFreeHub()" */
930 void GM_UNIQUENAME(nFreeHub)(struct NepClassHub *nch) {
931 UWORD num;
932 LONG ioerr;
933 struct PsdDevice *pd;
934 STRPTR devname;
935 IPTR isconnected;
936 struct Message *msg;
938 KPRINTF(1, ("FreeHub\n"));
939 psdGetAttrs(PGA_DEVICE, nch->nch_Device, DA_IsConnected, &isconnected, TAG_END);
940 for(num = 1; num <= nch->nch_NumPorts; num++) {
941 KPRINTF(1, ("Iterating Port %ld\n", num));
942 /* Remove downstream device */
943 pd = (nch->nch_Downstream)[num-1];
944 if(pd) {
945 if(!isconnected) {
946 psdSetAttrs(PGA_DEVICE, pd, DA_IsConnected, FALSE, TAG_END);
948 psdGetAttrs(PGA_DEVICE, pd, DA_ProductName, &devname, TAG_END);
949 psdAddErrorMsg(RETURN_OK, (STRPTR) libname, "My death killed device '%s' at port %ld!", devname, num);
950 KPRINTF(1, ("FreeDevice %p\n", pd));
951 psdFreeDevice(pd);
952 psdSendEvent(EHMB_REMDEVICE, pd, NULL);
953 (nch->nch_Downstream)[num-1] = NULL;
955 /* There's no sense trying to send out commands if the hub is already gone! */
956 if(isconnected) {
957 /* power down for port */
958 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_PORT_POWER, (ULONG) num);
959 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
960 if(ioerr) {
961 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "PORT_POWER for port %ld failed: %s (%ld)", num, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
962 KPRINTF(1, ("PORT_POWER for port %ld failed %ld!\n", num, ioerr));
967 KPRINTF(1, ("FreePipes\n"));
968 psdFreePipe(nch->nch_EP1Pipe);
969 psdFreePipe(nch->nch_EP0Pipe);
970 psdFreeVec(nch->nch_Downstream);
972 KPRINTF(1, ("Entering Forbid\n"));
973 Forbid();
974 // clear queue
975 while((msg = GetMsg(nch->nch_CtrlMsgPort))) {
976 ReplyMsg(msg);
979 DeleteMsgPort(nch->nch_TaskMsgPort);
980 DeleteMsgPort(nch->nch_CtrlMsgPort);
981 CloseLibrary(nch->nch_Base);
982 nch->nch_Task = NULL;
984 if(nch->nch_ReadySigTask) {
985 Signal(nch->nch_ReadySigTask, 1L<<nch->nch_ReadySignal);
988 KPRINTF(1, ("Really gone now!\n"));
992 /* *** HUBSS Class *** */
994 /* /// "nClearPortStatus()" */
995 LONG GM_UNIQUENAME(nClearPortStatus)(struct NepClassHub *nch, UWORD port) {
996 LONG ioerr;
998 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_C_PORT_CONNECTION, (ULONG) port);
999 if((ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0))) {
1000 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_FEATURE (C_PORT_CONNECTION) failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1001 KPRINTF(10, ("Some error occurred clearing hub status bits!\n"));
1002 return(ioerr);
1005 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_C_PORT_ENABLE, (ULONG) port);
1006 if((ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0))) {
1007 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_FEATURE (C_PORT_ENABLE) failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1008 KPRINTF(10, ("Some error occurred clearing hub status bits!\n"));
1009 return(ioerr);
1012 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_C_PORT_SUSPEND, (ULONG) port);
1013 if((ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0))) {
1014 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_FEATURE (C_PORT_SUSPEND) failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1015 KPRINTF(10, ("Some error occurred clearing hub status bits!\n"));
1016 return(ioerr);
1019 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_C_PORT_OVER_CURRENT, (ULONG) port);
1020 if((ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0))) {
1021 //psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_FEATURE (C_OVER_CURRENT) failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1022 KPRINTF(10, ("Some error occurred clearing hub status bits!\n"));
1025 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_C_PORT_RESET, (ULONG) port);
1026 if((ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0))) {
1027 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_FEATURE (C_PORT_RESET) failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1028 KPRINTF(10, ("Some error occurred clearing hub status bits!\n"));
1029 return(ioerr);
1031 return(0);
1034 /* /// "nConfigurePort()" */
1035 struct PsdDevice * GM_UNIQUENAME(nConfigurePort)(struct NepClassHub *nch, UWORD port) {
1036 LONG ioerr;
1037 LONG delayretries;
1038 LONG resetretries;
1039 ULONG delaytime = 10;
1040 struct UsbPortStatus uhps;
1041 struct PsdDevice *pd;
1042 struct PsdPipe *pp;
1043 BOOL washighspeed = FALSE;
1044 BOOL islowspeed = FALSE;
1046 KPRINTF(2, ("\nConfiguring port %ld of hub 0x%p\n", port, nch));
1048 uhps.wPortStatus = 0xDEAD;
1049 uhps.wPortChange = 0xDA1A;
1051 psdPipeSetup(nch->nch_EP0Pipe, URTF_IN|URTF_CLASS|URTF_OTHER, USR_GET_STATUS, UFS_PORT_CONNECTION, (ULONG) port);
1052 ioerr = psdDoPipe(nch->nch_EP0Pipe, &uhps, sizeof(struct UsbPortStatus));
1054 uhps.wPortStatus = AROS_WORD2LE(uhps.wPortStatus);
1055 uhps.wPortChange = AROS_WORD2LE(uhps.wPortChange);
1057 if(!ioerr) {
1058 KPRINTF(2, ("Status 0x%04x, change 0x%04x\n", uhps.wPortStatus, uhps.wPortChange));
1060 if(uhps.wPortStatus & UPSF_PORT_ENABLE) {
1061 KPRINTF(2, ("Disabling port %u\n", port));
1063 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_PORT_ENABLE, (ULONG) port);
1064 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
1065 if(ioerr) {
1066 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_ENABLE failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1067 KPRINTF(1, ("CLEAR_PORT_ENABLE failed %ld.\n", ioerr));
1068 } else {
1069 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "Disabling port %ld.", port);
1073 if(uhps.wPortStatus & UPSF_PORT_CONNECTION) {
1074 KPRINTF(2, ("There's something at port %ld!\n", port));
1076 Forbid();
1077 if((pd = psdAllocDevice(nch->nch_Hardware))) {
1078 psdLockWriteDevice(pd);
1079 Permit();
1080 /* Hub reference */
1081 psdSetAttrs(PGA_DEVICE, pd, DA_HubDevice, nch->nch_Device, DA_IsConnected, TRUE, DA_AtHubPortNumber, port, TAG_END);
1082 if(uhps.wPortStatus & UPSF_PORT_LOW_SPEED) {
1083 psdSetAttrs(PGA_DEVICE, pd, DA_IsLowspeed, TRUE, TAG_END);
1084 KPRINTF(2, (" It's a lowspeed device!\n"));
1085 islowspeed = TRUE;
1088 ObtainSemaphore(&nch->nch_HubBase->nh_Adr0Sema);
1090 for(resetretries = 0; resetretries < 3; resetretries++) {
1091 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_SET_FEATURE, UFS_PORT_RESET, (ULONG) port);
1092 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
1094 if(ioerr) {
1095 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "PORT_RESET for port %ld failed: %s (%ld)", port, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1096 KPRINTF(1, ("PORT_RESET failed %ld.\n", ioerr));
1097 break;
1100 if(nch->nch_IsRootHub) {
1101 // Root hubs need 50ms minimum delay
1102 psdDelayMS(50);
1105 for(delayretries = 0; delayretries < 500; delayretries += delaytime) {
1106 psdDelayMS(delaytime);
1107 psdPipeSetup(nch->nch_EP0Pipe, URTF_IN|URTF_CLASS|URTF_OTHER, USR_GET_STATUS, UFS_PORT_CONNECTION, (ULONG) port);
1108 ioerr = psdDoPipe(nch->nch_EP0Pipe, &uhps, sizeof(struct UsbPortStatus));
1110 uhps.wPortStatus = AROS_WORD2LE(uhps.wPortStatus);
1111 uhps.wPortChange = AROS_WORD2LE(uhps.wPortChange);
1113 if(ioerr) {
1114 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "GET_PORT_CONNECTION for port %ld failed: %s (%ld)", port, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1115 KPRINTF(1, ("GET_PORT_CONNECTION failed %ld.\n", ioerr));
1116 break;
1119 KPRINTF(2, ("After reset: status 0x%04x, change 0x%04x\n", uhps.wPortStatus, uhps.wPortChange));
1120 if(!(uhps.wPortStatus & UPSF_PORT_CONNECTION)) {
1121 break;
1124 if((uhps.wPortStatus & (UPSF_PORT_RESET|UPSF_PORT_CONNECTION|UPSF_PORT_ENABLE|UPSF_PORT_POWER|UPSF_PORT_OVER_CURRENT)) == (UPSF_PORT_CONNECTION|UPSF_PORT_ENABLE|UPSF_PORT_POWER)) {
1125 if((uhps.wPortStatus & UPSF_PORT_HIGH_SPEED) || washighspeed) {
1126 psdSetAttrs(PGA_DEVICE, pd, DA_IsHighspeed, TRUE, TAG_END);
1127 washighspeed = TRUE;
1128 KPRINTF(2, (" It's a highspeed device!\n"));
1129 } else {
1130 IPTR needssplit = 0;
1132 /* Some hubs (Apple Keyboard bultin hub) report speed correctly only after reset */
1133 if (uhps.wPortStatus & UPSF_PORT_LOW_SPEED) {
1134 psdSetAttrs(PGA_DEVICE, pd, DA_IsLowspeed, TRUE, TAG_END);
1135 KPRINTF(2, (" It's a lowspeed device!\n"));
1136 islowspeed = TRUE;
1139 // inherit needs split from hub
1140 psdGetAttrs(PGA_DEVICE, nch->nch_Device, DA_NeedsSplitTrans, &needssplit, TAG_END);
1141 KPRINTF(2, (" Needs split transfers: %ld\n", needssplit));
1143 psdSetAttrs(PGA_DEVICE, pd, DA_NeedsSplitTrans, needssplit, TAG_END);
1146 GM_UNIQUENAME(nClearPortStatus)(nch, port);
1147 psdDelayMS((ULONG) (islowspeed ? 1000 : 100));
1148 if((pp = psdAllocPipe(pd, nch->nch_TaskMsgPort, NULL))) {
1149 if(psdEnumerateDevice(pp)) {
1150 KPRINTF(2, (" Device successfully added!\n"));
1151 psdFreePipe(pp);
1152 psdUnlockDevice(pd);
1153 psdSendEvent(EHMB_ADDDEVICE, pd, NULL);
1154 ReleaseSemaphore(&nch->nch_HubBase->nh_Adr0Sema);
1155 return(pd);
1157 psdFreePipe(pp);
1159 break;
1160 } else {
1161 if(!(uhps.wPortStatus & UPSF_PORT_RESET)) {
1162 psdAddErrorMsg(RETURN_ERROR, (STRPTR) libname, "Wrong port status %04lx for port %ld!", uhps.wPortStatus, port);
1163 KPRINTF(2, ("Wrong port status %04lx for port %ld.\n", uhps.wPortStatus, port));
1167 if(delayretries > 20) {
1168 delaytime = 300;
1172 if((uhps.wPortStatus & \
1173 (UPSF_PORT_RESET|UPSF_PORT_CONNECTION|UPSF_PORT_ENABLE|UPSF_PORT_POWER|UPSF_PORT_OVER_CURRENT|UPSF_PORT_LOW_SPEED)) \
1174 == (UPSF_PORT_CONNECTION|UPSF_PORT_POWER|UPSF_PORT_LOW_SPEED)) {
1176 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "Strange port response, power-cycling port %ld", port);
1177 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_PORT_ENABLE, (ULONG) port);
1178 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
1180 if(ioerr) {
1181 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_ENABLE for port %ld failed: %s (%ld)", port, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1182 KPRINTF(1, ("CLEAR_PORT_ENABLE for port %ld failed %ld!\n", port, ioerr));
1184 psdDelayMS(50);
1186 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_PORT_POWER, (ULONG) port);
1187 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
1189 if(ioerr) {
1190 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_POWER for port %ld failed: %s (%ld)", port, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1191 KPRINTF(1, ("CLEAR_PORT_POWER for port %ld failed %ld!\n", port, ioerr));
1193 psdDelayMS(50);
1195 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_SET_FEATURE, UFS_PORT_POWER, (ULONG) port);
1196 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
1198 if(ioerr) {
1199 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "SET_PORT_POWER for port %ld failed: %s (%ld)", port, psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1200 KPRINTF(1, ("SET_PORT_POWER for port %ld failed %ld!\n", port, ioerr));
1202 psdDelayMS((ULONG) nch->nch_PwrGoodTime + 15);
1206 delaytime = 200;
1209 psdUnlockDevice(pd);
1210 psdFreeDevice(pd);
1211 /* Disable port! It's too dangerous having a connection with crazy devices on the bus open */
1212 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_PORT_ENABLE, (ULONG) port);
1213 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
1215 if(ioerr) {
1216 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_ENABLE failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1217 KPRINTF(1, ("CLEAR_PORT_ENABLE failed %ld.\n", ioerr));
1219 ReleaseSemaphore(&nch->nch_HubBase->nh_Adr0Sema);
1220 GM_UNIQUENAME(nClearPortStatus)(nch, port);
1221 } else {
1222 Permit();
1223 KPRINTF(1, ("AllocDevice() failed.\n"));
1226 } else {
1227 psdAddErrorMsg(RETURN_ERROR, (STRPTR) libname, "GET_PORT_CONNECTION failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1228 KPRINTF(1, ("GET_PORT_CONNECTION for port %d failed %ld.\n", port, ioerr));
1230 return(NULL);
1233 /* /// "nHandleHubMethod()" */
1234 void GM_UNIQUENAME(nHandleHubMethod)(struct NepClassHub *nch, struct NepHubMsg *nhm) {
1235 ULONG num;
1236 struct PsdDevice *pd;
1237 nhm->nhm_Result = 0;
1239 switch(nhm->nhm_MethodID) {
1240 case UCM_HubClaimAppBinding:
1241 nhm->nhm_Result = (IPTR) psdHubClaimAppBindingA((struct TagItem *) nhm->nhm_Params[1]);
1242 break;
1244 case UCM_HubReleaseIfBinding: {
1245 psdHubReleaseIfBinding((struct PsdInterface *) nhm->nhm_Params[1]);
1246 break;
1247 } /* case UCM_HubReleaseIfBinding */
1249 case UCM_HubReleaseDevBinding:
1250 psdHubReleaseDevBinding((struct PsdDevice *) nhm->nhm_Params[1]);
1251 break;
1253 case UCM_AttemptSuspendDevice:
1255 BOOL res = TRUE;
1257 for(num = 1; num <= nch->nch_NumPorts; num++) {
1258 if((pd = (nch->nch_Downstream)[num-1])) {
1259 res &= psdSuspendDevice(pd);
1263 if(res) {
1264 // suspending of all downstream devices successful, so stop all activity, too.
1265 psdAbortPipe(nch->nch_EP1Pipe);
1266 nch->nch_Running = FALSE;
1267 nhm->nhm_Result = TRUE;
1270 } /* case UCM_AttemptSuspendDevice */
1271 break;
1273 case UCM_AttemptResumeDevice:
1275 if(!nch->nch_Running) {
1276 psdWaitPipe(nch->nch_EP1Pipe);
1277 psdSendPipe(nch->nch_EP1Pipe, nch->nch_PortChanges, (nch->nch_NumPorts+8)>>3);
1278 nch->nch_Running = TRUE;
1281 nhm->nhm_Result = TRUE;
1283 for(num = 1; num <= nch->nch_NumPorts; num++) {
1284 if((pd = (nch->nch_Downstream)[num-1])) {
1285 psdResumeDevice(pd);
1289 break;
1291 case UCM_HubSuspendDevice:
1292 nhm->nhm_Result = GM_UNIQUENAME(nHubSuspendDevice)(nch, (struct PsdDevice *) nhm->nhm_Params[1]);
1293 break;
1295 case UCM_HubResumeDevice:
1296 nhm->nhm_Result = GM_UNIQUENAME(nHubResumeDevice)(nch, (struct PsdDevice *) nhm->nhm_Params[1]);
1297 break;
1302 /* /// "nHubSuspendDevice()" */
1303 BOOL GM_UNIQUENAME(nHubSuspendDevice)(struct NepClassHub *nch, struct PsdDevice *pd) {
1304 APTR binding = NULL;
1305 APTR puc = NULL;
1306 ULONG num;
1307 BOOL result = FALSE;
1308 LONG ioerr;
1310 psdGetAttrs(PGA_DEVICE, pd, DA_Binding, &binding, DA_BindingClass, &puc, TAG_END);
1312 for(num = 1; num <= nch->nch_NumPorts; num++) {
1313 if(pd == (nch->nch_Downstream)[num-1]) {
1314 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_SET_FEATURE, UFS_PORT_SUSPEND, num);
1315 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
1317 if(ioerr) {
1318 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "SET_PORT_SUSPEND failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1319 KPRINTF(1, ("SET_PORT_SUSPEND failed %ld.\n", ioerr));
1320 } else {
1321 result = TRUE;
1322 psdSetAttrs(PGA_DEVICE, pd, DA_IsSuspended, TRUE, TAG_END);
1323 psdSendEvent(EHMB_DEVSUSPENDED, pd, NULL);
1328 return result;
1331 /* /// "nHubResumeDevice()" */
1332 BOOL GM_UNIQUENAME(nHubResumeDevice)(struct NepClassHub *nch, struct PsdDevice *pd) {
1333 ULONG num;
1334 BOOL result = FALSE;
1335 LONG ioerr;
1337 for(num = 1; num <= nch->nch_NumPorts; num++) {
1338 if(pd == (nch->nch_Downstream)[num-1]) {
1339 psdPipeSetup(nch->nch_EP0Pipe, URTF_CLASS|URTF_OTHER, USR_CLEAR_FEATURE, UFS_PORT_SUSPEND, (ULONG) num);
1340 ioerr = psdDoPipe(nch->nch_EP0Pipe, NULL, 0);
1342 if(ioerr) {
1343 psdAddErrorMsg(RETURN_WARN, (STRPTR) libname, "CLEAR_PORT_SUSPEND failed: %s (%ld)", psdNumToStr(NTS_IOERR, ioerr, "unknown"), ioerr);
1344 KPRINTF(1, ("CLEAR_PORT_SUSPEND failed %ld.\n", ioerr));
1345 } else {
1346 psdSetAttrs(PGA_DEVICE, pd, DA_IsSuspended, FALSE, TAG_END);
1347 psdSendEvent(EHMB_DEVRESUMED, pd, NULL);
1348 result = TRUE;
1349 psdDelayMS(30);
1354 return result;