2 Copyright (C) 2006 by Michal Schulz
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this program; if not, write to the
17 Free Software Foundation, Inc.,
18 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 * ---------- ------------------ -------------------------------------------------------------------
24 * 2009-03-07 T. Wiszkowski Taught hub to re-attempt device discovery at later time
25 * if no suitable driver is present initially
26 * 2008-06-02 T. Wiszkowski Updated device detection mechanism so the stack is aware of already
34 #include <aros/debug.h>
36 #include <exec/types.h>
39 #include <hidd/hidd.h>
41 #include <usb/usb_core.h>
44 #include <dos/dosextens.h>
46 #include <devices/timer.h>
48 #include <proto/oop.h>
49 #include <proto/utility.h>
50 #include <proto/dos.h>
57 #define STACK_SIZE 10240
59 static void hub_process();
62 * The HubInterrupt() is a tiny software interrupt routine that signals the
63 * hub process about the need of hub exploration
65 AROS_INTH1(HubInterrupt
, HubData
*, hub
)
69 /* Signal the HUB process about incoming interrupt */
71 Signal(hub
->hub_task
, 1 << hub
->sigInterrupt
);
79 * Initialize HUB class properly. Find out all relevant data and start the
82 OOP_Object
*METHOD(USBHub
, Root
, New
)
87 D(bug("[USB] USBHub::New()\n"));
89 o
= (OOP_Object
*)OOP_DoSuperMethod(cl
, o
, (OOP_Msg
) msg
);
92 HubData
*hub
= OOP_INST_DATA(cl
, o
);
95 /* HUB devices do need a bit longer timeout than anything else */
96 HIDD_USBDevice_SetTimeout(o
, NULL
, 5000);
98 hub
->root
= GetTagData(aHidd_USBHub_IsRoot
, FALSE
, msg
->attrList
);
100 hub
->got_descriptor
= FALSE
;
102 OOP_GetAttr(o
, aHidd_USBDevice_ProductName
, (intptr_t *)&name
);
104 hub
->got_descriptor
= HIDD_USBHub_GetHubDescriptor(o
, &hub
->descriptor
);
106 if (hub
->got_descriptor
)
107 DumpDescriptor((usb_descriptor_t
*)&hub
->descriptor
);
110 D(bug("[USBHub] HUB descriptor not present. I will try later...\n"));
111 hub
->descriptor
.bNbrPorts
= GetTagData(aHidd_USBHub_NumPorts
, 1, msg
->attrList
);
114 D(bug("[USBHub] %s hub with %d ports\n",
115 hub
->root
? "Root" : "A", hub
->descriptor
.bNbrPorts
));
117 hub
->children
= AllocVecPooled(SD(cl
)->MemPool
, hub
->descriptor
.bNbrPorts
* sizeof(OOP_Object
*));
119 D(bug("[USB] USBHub has name \"%s\"\n", name
));
122 hub
->hub_name
= name
? name
: "unknown";
123 hub
->proc_name
= AllocVecPooled(SD(cl
)->MemPool
, 10 + strlen(name
? name
: "unknown"));
125 sprintf(hub
->proc_name
, "USBHub (%s)", name
? name
: "unknown");
127 HIDD_USBDevice_Configure(o
, 0);
129 usb_endpoint_descriptor_t
*ep
= HIDD_USBDevice_GetEndpoint(o
, 0, 0);
131 D(bug("[USBHub] Endpoint descriptor %p\n", ep
));
135 DumpDescriptor((usb_descriptor_t
*)ep
);
137 if ((ep
->bmAttributes
& UE_XFERTYPE
) != UE_INTERRUPT
)
139 bug("[USBHub] Wrong endpoint type\n");
140 HIDD_USBDevice_Configure(o
, USB_UNCONFIG_INDEX
);
141 // TODO: unconfigure, error, coercemethod
144 OOP_Object
*drv
= NULL
;
145 OOP_GetAttr(o
, aHidd_USBDevice_Bus
, (IPTR
*)&drv
);
149 hub
->interrupt
.is_Data
= hub
;
150 hub
->interrupt
.is_Code
= (VOID_FUNC
)HubInterrupt
;
151 hub
->intr_pipe
= HIDD_USBDevice_CreatePipe(o
, PIPE_Interrupt
, ep
->bEndpointAddress
, ep
->bInterval
, AROS_LE2WORD(ep
->wMaxPacketSize
), 0);
152 // HIDD_USBDrv_AddInterrupt(drv, hub->intr_pipe, &hub->status[0], AROS_LE2WORD(ep->wMaxPacketSize), &hub->interrupt);
153 HIDD_USBDrv_AddInterrupt(drv
, hub
->intr_pipe
, &hub
->status
[0], 1, &hub
->interrupt
);
158 struct TagItem tags
[] = {
159 { TASKTAG_ARG1
, (IPTR
)hub
},
160 { TASKTAG_ARG2
, (IPTR
)o
},
161 { TASKTAG_ARG3
, (IPTR
)FindTask(NULL
) },
165 t
= AllocMem(sizeof(struct Task
), MEMF_PUBLIC
|MEMF_CLEAR
);
166 ml
= AllocMem(sizeof(struct MemList
) + sizeof(struct MemEntry
), MEMF_PUBLIC
|MEMF_CLEAR
);
170 char *sp
= AllocMem(STACK_SIZE
, MEMF_PUBLIC
|MEMF_CLEAR
);
172 t
->tc_SPUpper
= sp
+ STACK_SIZE
;
173 #if AROS_STACK_GROWS_DOWNWARDS
174 t
->tc_SPReg
= (char *)t
->tc_SPUpper
- SP_OFFSET
;
176 t
->tc_SPReg
= (char *)t
->tc_SPLower
+ SP_OFFSET
;
179 ml
->ml_NumEntries
= 2;
180 ml
->ml_ME
[0].me_Addr
= t
;
181 ml
->ml_ME
[0].me_Length
= sizeof(struct Task
);
182 ml
->ml_ME
[1].me_Addr
= sp
;
183 ml
->ml_ME
[1].me_Length
= STACK_SIZE
;
185 NEWLIST(&t
->tc_MemEntry
);
186 ADDHEAD(&t
->tc_MemEntry
, &ml
->ml_Node
);
188 t
->tc_Node
.ln_Name
= hub
->proc_name
;
189 t
->tc_Node
.ln_Type
= NT_TASK
;
190 t
->tc_Node
.ln_Pri
= 0;
192 NewAddTask(t
, hub_process
, NULL
, &tags
[0]);
199 D(bug("[USB] USBHub::New() = %p\n",o
));
204 void METHOD(USBHub
, Root
, Dispose
)
206 D(bug("[USB] USBHub::Dispose\n"));
207 HubData
*hub
= OOP_INST_DATA(cl
, o
);
208 struct usbEvent message
;
210 message
.ev_Message
.mn_ReplyPort
= CreateMsgPort();
211 message
.ev_Type
= evt_Cleanup
;
213 PutMsg(hub
->hub_port
, (struct Message
*)&message
);
214 WaitPort(message
.ev_Message
.mn_ReplyPort
);
215 DeleteMsgPort(message
.ev_Message
.mn_ReplyPort
);
217 FreeVecPooled(SD(cl
)->MemPool
, hub
->children
);
218 FreeVecPooled(SD(cl
)->MemPool
, hub
->proc_name
);
220 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
223 void METHOD(USBHub
, Root
, Get
)
226 HubData
*hub
= OOP_INST_DATA(cl
, o
);
228 if (IS_USBHUB_ATTR(msg
->attrID
, idx
))
232 case aoHidd_USBHub_IsRoot
:
233 *msg
->storage
= hub
->root
;
235 case aoHidd_USBHub_IsCompound
:
238 case aoHidd_USBHub_HubCurrent
:
241 case aoHidd_USBHub_NumPorts
:
242 *msg
->storage
= hub
->descriptor
.bNbrPorts
;
245 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
249 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
252 void METHOD(USBHub
, Root
, Set
)
256 struct TagItem
*tags
= msg
->attrList
;
258 while ((tag
= NextTagItem(&tags
)))
260 if (IS_USBHUB_ATTR(tag
->ti_Tag
, idx
))
266 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
269 OOP_Object
* METHOD(USBHub
, Hidd_USBHub
, GetChild
)
271 HubData
*hub
= OOP_INST_DATA(cl
, o
);
272 if ((msg
->port
> 0) && (msg
->port
<= hub
->descriptor
.bNbrPorts
))
273 return (hub
->children
[msg
->port
-1]);
278 BOOL
METHOD(USBHub
, Hidd_USBHub
, OnOff
)
280 HubData
*hub
= OOP_INST_DATA(cl
, o
);
281 struct usbEvent message
;
283 D(bug("[USBHub] USBHub::OnOff(%d)\n", msg
->on
));
285 hub
->enabled
= msg
->on
;
287 message
.ev_Message
.mn_ReplyPort
= CreateMsgPort();
288 message
.ev_Type
= evt_OnOff
;
290 PutMsg(hub
->hub_port
, (struct Message
*)&message
);
291 WaitPort(message
.ev_Message
.mn_ReplyPort
);
292 DeleteMsgPort(message
.ev_Message
.mn_ReplyPort
);
297 BOOL
METHOD(USBHub
, Hidd_USBHub
, PortEnable
)
299 D(bug("[USBHub] USBHub::PortEnable(%d, %d)\n", msg
->portNummer
, msg
->enable
));
303 BOOL
METHOD(USBHub
, Hidd_USBHub
, PortReset
)
305 HubData
*hub
= OOP_INST_DATA(cl
, o
);
307 USBDevice_Request req
;
309 usb_port_status_t ps
;
311 req
.bmRequestType
= UT_WRITE_CLASS_OTHER
;
312 req
.bRequest
= UR_SET_FEATURE
;
313 req
.wValue
= AROS_WORD2LE(UHF_PORT_RESET
);
314 req
.wIndex
= AROS_WORD2LE(msg
->portNummer
);
315 req
.wLength
= AROS_WORD2LE(0);
317 retval
= HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, NULL
, 0);
321 USBDelay(hub
->tr
, USB_PORT_RESET_DELAY
);
323 retval
= HIDD_USBHub_GetPortStatus(o
, msg
->portNummer
, &ps
);
328 if (!(AROS_LE2WORD(ps
.wPortStatus
) & UPS_CURRENT_CONNECT_STATUS
))
333 } while ((AROS_LE2WORD(ps
.wPortChange
) & UPS_C_PORT_RESET
) == 0 && --n
> 0);
339 HIDD_USBHub_ClearPortFeature(o
, msg
->portNummer
, UHF_C_PORT_RESET
);
340 USBDelay(hub
->tr
, USB_PORT_RESET_RECOVERY
);
343 D(bug("[USBHub] USBHub::PortReset(%d) %s\n", msg
->portNummer
, retval
? "OK":"Error"));
348 BOOL
METHOD(USBHub
, Hidd_USBHub
, GetPortStatus
)
350 D(bug("[USBHub] USBHub::GetPortStatus()\n"));
352 USBDevice_Request req
;
354 req
.bmRequestType
= UT_READ_CLASS_OTHER
;
355 req
.bRequest
= UR_GET_STATUS
;
356 req
.wValue
= AROS_WORD2LE(0);
357 req
.wIndex
= AROS_WORD2LE(msg
->port
);
358 req
.wLength
= AROS_WORD2LE(sizeof(usb_port_status_t
));
360 return HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, msg
->status
, sizeof(usb_port_status_t
));
363 BOOL
METHOD(USBHub
, Hidd_USBHub
, GetHubStatus
)
365 D(bug("[USBHub] USBHub::GetHubStatus()\n"));
367 USBDevice_Request req
;
369 req
.bmRequestType
= UT_READ_CLASS_DEVICE
;
370 req
.bRequest
= UR_GET_STATUS
;
371 req
.wValue
= AROS_WORD2LE(0);
372 req
.wIndex
= AROS_WORD2LE(0);
373 req
.wLength
= AROS_WORD2LE(sizeof(usb_hub_status_t
));
375 return HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, msg
->status
, sizeof(usb_hub_status_t
));
378 BOOL
METHOD(USBHub
, Hidd_USBHub
, ClearHubFeature
)
380 D(bug("[USBHub] USBHub::ClearHubFeature()\n"));
382 USBDevice_Request req
;
384 req
.bmRequestType
= UT_WRITE_CLASS_DEVICE
;
385 req
.bRequest
= UR_CLEAR_FEATURE
;
386 req
.wValue
= AROS_WORD2LE(msg
->feature
);
387 req
.wIndex
= AROS_WORD2LE(0);
388 req
.wLength
= AROS_WORD2LE(0);
390 return HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, NULL
, 0);
393 BOOL
METHOD(USBHub
, Hidd_USBHub
, SetHubFeature
)
395 D(bug("[USBHub] USBHub::SetHubFeature()\n"));
397 USBDevice_Request req
;
399 req
.bmRequestType
= UT_WRITE_CLASS_DEVICE
;
400 req
.bRequest
= UR_SET_FEATURE
;
401 req
.wValue
= AROS_WORD2LE(msg
->feature
);
402 req
.wIndex
= AROS_WORD2LE(0);
403 req
.wLength
= AROS_WORD2LE(0);
405 return HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, NULL
, 0);
408 BOOL
METHOD(USBHub
, Hidd_USBHub
, ClearPortFeature
)
410 D(bug("[USBHub] USBHub::ClearPortFeature(%p, %d, %d)\n", o
, msg
->port
, msg
->feature
));
412 USBDevice_Request req
;
414 req
.bmRequestType
= UT_WRITE_CLASS_OTHER
;
415 req
.bRequest
= UR_CLEAR_FEATURE
;
416 req
.wValue
= AROS_WORD2LE(msg
->feature
);
417 req
.wIndex
= AROS_WORD2LE(msg
->port
);
418 req
.wLength
= AROS_WORD2LE(0);
420 return HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, NULL
, 0);
423 BOOL
METHOD(USBHub
, Hidd_USBHub
, SetPortFeature
)
425 D(bug("[USBHub] USBHub::SetPortFeature(%p, %d, %d)\n", o
, msg
->port
, msg
->feature
));
427 USBDevice_Request req
;
429 req
.bmRequestType
= UT_WRITE_CLASS_OTHER
;
430 req
.bRequest
= UR_SET_FEATURE
;
431 req
.wValue
= AROS_WORD2LE(msg
->feature
);
432 req
.wIndex
= AROS_WORD2LE(msg
->port
);
433 req
.wLength
= AROS_WORD2LE(0);
435 return HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, NULL
, 0);
438 BOOL
METHOD(USBHub
, Hidd_USBHub
, GetHubDescriptor
)
440 USBDevice_Request request
= {
441 bmRequestType
: UT_READ_CLASS_DEVICE
,
442 bRequest
: UR_GET_DESCRIPTOR
,
443 wValue
: AROS_WORD2LE(((uint8_t)UDESC_HUB
) << 8),
444 wIndex
: AROS_WORD2LE(0),
445 wLength
: AROS_WORD2LE(USB_HUB_DESCRIPTOR_SIZE
)
448 return HIDD_USBDevice_ControlMessage(o
, NULL
, &request
, msg
->descriptor
, USB_HUB_DESCRIPTOR_SIZE
);
453 static void hub_enable(OOP_Class
*cl
, OOP_Object
*o
)
455 HubData
*hub
= OOP_INST_DATA(cl
, o
);
458 if (!hub
->got_descriptor
)
459 hub
->got_descriptor
= HIDD_USBHub_GetHubDescriptor(o
, &hub
->descriptor
);
461 pwrdly
= hub
->descriptor
.bPwrOn2PwrGood
* UHD_PWRON_FACTOR
+ USB_EXTRA_POWER_UP_TIME
;
463 for (port
= 1; port
<= hub
->descriptor
.bNbrPorts
; port
++)
465 if (!HIDD_USBHub_SetPortFeature(o
, port
, UHF_PORT_POWER
))
466 bug("[USBHub] PowerOn on port %d failed\n", port
);
468 USBDelay(hub
->tr
, pwrdly
);
472 static void hub_disable(OOP_Class
*cl
, OOP_Object
*o
)
474 HubData
*hub
= OOP_INST_DATA(cl
, o
);
477 if (!hub
->got_descriptor
)
478 hub
->got_descriptor
= HIDD_USBHub_GetHubDescriptor(o
, &hub
->descriptor
);
480 pwrdly
= hub
->descriptor
.bPwrOn2PwrGood
* UHD_PWRON_FACTOR
+ USB_EXTRA_POWER_UP_TIME
;
482 for (port
= 1; port
<= hub
->descriptor
.bNbrPorts
; port
++)
484 if (!HIDD_USBHub_ClearPortFeature(o
, port
, UHF_PORT_POWER
))
485 bug("[USBHub] PowerOff on port %d failed\n", port
);
487 USBDelay(hub
->tr
, pwrdly
);
491 static BOOL
hub_explore(OOP_Class
*cl
, OOP_Object
*o
)
493 HubData
*hub
= OOP_INST_DATA(cl
, o
);
497 D(bug("[USBHub Process] hub_explore()\n"));
499 if (!hub
->got_descriptor
)
500 hub
->got_descriptor
= HIDD_USBHub_GetHubDescriptor(o
, &hub
->descriptor
);
502 for (port
=1; port
<= hub
->descriptor
.bNbrPorts
; port
++)
504 usb_port_status_t port_status
;
505 uint16_t status
, change
;
507 if (!HIDD_USBHub_GetPortStatus(o
, port
, &port_status
))
509 D(bug("[USBHub Process] HIDD_USBHub_GetPortStatus(%p, %d, %p) failed\n",
514 status
= AROS_LE2WORD(port_status
.wPortStatus
);
515 change
= AROS_LE2WORD(port_status
.wPortChange
);
517 D(bug("[USBHub Process] Port %d, status %04x, change %04x\n", port
, status
, change
));
519 if (change
& UPS_C_PORT_ENABLED
)
521 D(bug("[USBHub Process] C_PORT_ENABLED\n"));
522 HIDD_USBHub_ClearPortFeature(o
, port
, UHF_C_PORT_ENABLE
);
526 if (change
& UPS_C_CONNECT_STATUS
)
528 D(bug("[USBHub Process] C_CONNECT_STATUS\n"));
529 HIDD_USBHub_ClearPortFeature(o
, port
, UHF_C_PORT_CONNECTION
);
533 * if connection status has not changed and device is still disconnected skip port.
534 * the original method did not analyse ports after reset.
536 if ((0 == (status
& UPS_CURRENT_CONNECT_STATUS
)) == (0 == hub
->children
[port
-1]))
538 // D(bug("[USBHub Process] !C_CONNECT_STATUS\n"));
539 D(bug("[USBHub Process] UPS_CURRENT_CONNECT_STATUS reflects actual mapping\n"));
543 if (hub
->children
[port
-1])
545 OOP_DisposeObject(hub
->children
[port
-1]);
546 hub
->children
[port
-1] = NULL
;
549 if (!(status
& UPS_CURRENT_CONNECT_STATUS
))
551 D(bug("[USBHub Process] !CURRENT_CONNECT_STATUS\n"));
555 if (!(status
& UPS_PORT_POWER
))
556 D(bug("[USBHub Process] Port %d without power???\n", port
));
559 * i am not sure we want to keep restarting USB ports
560 * in case where we have no driver to utilize the device
561 * however i will leave it here for now
563 USBDelay(hub
->tr
, USB_PORT_POWERUP_DELAY
);
565 D(bug("[USBHub Process]\tRestarting device in port %d\n", port
));
566 if (!HIDD_USBHub_PortReset(o
, port
))
568 D(bug("[USBHub Process] Port %d reset failed\n", port
));
572 if (!HIDD_USBHub_GetPortStatus(o
, port
, &port_status
))
574 D(bug("[USBHub Process] HIDD_USBHub_GetPortStatus(%p, %d, %p) failed\n",
578 status
= AROS_LE2WORD(port_status
.wPortStatus
);
579 change
= AROS_LE2WORD(port_status
.wPortChange
);
581 D(bug("[USBHub Process] Port %d, status %04x, change %04x\n", port
, status
, change
));
583 if (!(status
& UPS_CURRENT_CONNECT_STATUS
))
585 D(bug("[USBHub Process] Device on port %d disappeared after reset???\n", port
));
589 hub
->children
[port
-1] = HIDD_USB_NewDevice(SD(cl
)->usb
, o
, !(status
& UPS_LOW_SPEED
));
590 if (NULL
== hub
->children
[port
-1])
592 D(bug("[USBHub Process]\tNo known handler for selected drive. Restoring connection flag.\n"));
594 //HIDD_USBHub_SetPortFeature(o, port, UHF_C_PORT_CONNECTION);
601 static void hub_process(HubData
*hub
, OOP_Object
*o
, struct Task
*parent
)
603 struct Task
*hub_task
= FindTask(NULL
);
604 struct usb_staticdata
*sd
= hub
->sd
;
605 OOP_Object
*drv
= NULL
;
606 OOP_Class
*cl
= sd
->hubClass
;
607 struct usbEvent
*ev
= NULL
;
608 struct timerequest
*rescan
;
610 uint32_t sigmask
= 0;
612 hub
->tr
= USBCreateTimer();
613 hub
->hub_port
= CreateMsgPort();
615 rescan
= USBCreateTimer();
617 OOP_GetAttr(o
, aHidd_USBDevice_Bus
, (IPTR
*)&drv
);
618 SetTaskPri(hub_task
, 10);
620 D(bug("[USBHub Process] HUB process (%p)\n", FindTask(NULL
)));
622 Signal(parent
, SIGF_SINGLE
);
626 D(bug("[USBHub Process] YAWN...\n"));
628 sigset
= Wait( (1 << hub
->hub_port
->mp_SigBit
) |
630 (1 << hub
->sigInterrupt
)
632 D(bug("[USBHub Process] signals rcvd: %p\n", sigset
));
634 /* handle messages */
635 while ((ev
= (struct usbEvent
*)GetMsg(hub
->hub_port
)) != NULL
)
642 D(bug("[USBHub Process] Startup MSG\n"));
646 D(bug("[USBHub Process] Sync method call\n"));
647 ev
->ev_RetVal
= OOP_DoMethod(ev
->ev_Target
, (OOP_Msg
)&ev
->ev_Event
);
650 case evt_AsyncMethod
:
651 D(bug("[USBHub Process] Async method call\n"));
652 OOP_DoMethod(ev
->ev_Target
, (OOP_Msg
)&ev
->ev_Event
);
653 FreeVecPooled(sd
->MemPool
, ev
);
658 D(bug("[USBHub Process] Cleanup MSG\n"));
659 USBDeleteTimer(hub
->tr
);
660 ReplyMsg(&ev
->ev_Message
);
664 D(bug("[USBHub Process] Hub %s\n", hub
->enabled
? "on" : "off"));
665 D(bug("----->MARKER 1<-----\n"));
670 D(bug("----->MARKER 2<-----\n"));
676 D(bug("----->MARKER 3<-----\n"));
678 ReplyMsg(&ev
->ev_Message
);
681 D(bug("----->MARKER 4<-----\n"));
684 if ((sigset
& (1 << hub
->sigInterrupt
)) ||
687 struct usb_driver
*d
= NULL
, *d2
= NULL
;
689 if (sigset
& sigmask
)
690 USBTimerDone(rescan
);
692 D(bug("[USBHub Process] Interrupt signalled\n"));
694 ObtainSemaphore(&sd
->driverListLock
);
695 ForeachNode(&sd
->driverList
, d
)
697 if (d
->d_Driver
== drv
)
703 ReleaseSemaphore(&sd
->driverListLock
);
707 ObtainSemaphore(&d2
->d_Lock
);
710 * check if some devices failed to be created
711 * if so, attempt to re-detect in 10 seconds,
712 * maybe we will have adequate drivers available
714 if (hub_explore(cl
, o
))
716 /* wake up in 10 secs and check again */
717 sigmask
= USBTimer(rescan
, 10000);
719 ReleaseSemaphore(&d2
->d_Lock
);