2 * Copyright 2003-2008, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Michael Lotz <mmlr@mlotz.ch>
12 #include <util/kernel_cpp.h>
13 #include "usb_private.h"
14 #include "PhysicalMemoryAllocator.h"
16 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
22 fFirstExploreDone(false),
26 fObjectMaxCount(1024),
30 TRACE("stack init\n");
32 mutex_init(&fStackLock
, "usb stack lock");
33 mutex_init(&fExploreLock
, "usb explore lock");
35 size_t objectArraySize
= fObjectMaxCount
* sizeof(Object
*);
36 fObjectArray
= (Object
**)malloc(objectArraySize
);
37 if (fObjectArray
== NULL
) {
38 TRACE_ERROR("failed to allocate object array\n");
42 memset(fObjectArray
, 0, objectArraySize
);
44 fAllocator
= new(std::nothrow
) PhysicalMemoryAllocator("USB Stack Allocator",
45 8, B_PAGE_SIZE
* 32, 64);
46 if (!fAllocator
|| fAllocator
->InitCheck() < B_OK
) {
47 TRACE_ERROR("failed to allocate the allocator\n");
53 // Check for host controller modules
54 // While using a fixed list of names is inflexible it allows us to control
55 // the order in which we try modules. There are controllers/BIOSes that
56 // require UHCI/OHCI to be initialized before EHCI or otherwise they
57 // refuse to publish any high-speed devices.
58 // On other systems the ordering is probably ensured because the EHCI
59 // controller is required to have a higher PCI function number than the
60 // companion host controllers (per the EHCI specs) and it would therefore
61 // be enumerated as the last item. As this does not apply to us we have to
62 // ensure ordering using another method.
63 // Intel Lynx Point and Panther Point chipsets have ports shared between
64 // EHCI and XHCI, defaulting to EHCI. The XHCI module will switch USB 2.0
65 // ports before the EHCI module discovers them.
66 const char *moduleNames
[] = {
74 TRACE("looking for host controller modules\n");
75 for (uint32 i
= 0; moduleNames
[i
]; i
++) {
76 TRACE("looking for module %s\n", moduleNames
[i
]);
78 usb_host_controller_info
*module
= NULL
;
79 if (get_module(moduleNames
[i
], (module_info
**)&module
) != B_OK
)
82 TRACE("adding module %s\n", moduleNames
[i
]);
83 if (module
->add_to(this) < B_OK
) {
84 put_module(moduleNames
[i
]);
88 TRACE("module %s successfully loaded\n", moduleNames
[i
]);
91 if (fBusManagers
.Count() == 0) {
92 TRACE_ERROR("no bus managers available\n");
96 fExploreThread
= spawn_kernel_thread(ExploreThread
, "usb explore",
97 B_LOW_PRIORITY
, this);
98 resume_thread(fExploreThread
);
100 // wait for the first explore to complete. this ensures that a driver that
101 // is opening the module does not get rescanned while or before installing
103 while (!fFirstExploreDone
)
112 wait_for_thread(fExploreThread
, &result
);
114 mutex_lock(&fStackLock
);
115 mutex_destroy(&fStackLock
);
116 mutex_lock(&fExploreLock
);
117 mutex_destroy(&fExploreLock
);
119 // Release the bus modules
120 for (Vector
<BusManager
*>::Iterator i
= fBusManagers
.Begin();
121 i
!= fBusManagers
.End(); i
++) {
133 if (fBusManagers
.Count() == 0)
142 return (mutex_lock(&fStackLock
) == B_OK
);
149 mutex_unlock(&fStackLock
);
154 Stack::GetUSBID(Object
*object
)
159 uint32 id
= fObjectIndex
;
160 uint32 tries
= fObjectMaxCount
;
161 while (tries
-- > 0) {
162 if (fObjectArray
[id
] == NULL
) {
163 fObjectIndex
= (id
+ 1) % fObjectMaxCount
;
164 fObjectArray
[id
] = object
;
169 id
= (id
+ 1) % fObjectMaxCount
;
172 TRACE_ERROR("the stack did run out of usb_ids\n");
179 Stack::PutUSBID(usb_id id
)
184 if (id
>= fObjectMaxCount
) {
185 TRACE_ERROR("tried to put an invalid usb_id\n");
190 fObjectArray
[id
] = NULL
;
196 Stack::GetObject(usb_id id
)
201 if (id
>= fObjectMaxCount
) {
202 TRACE_ERROR("tried to get object with invalid usb_id\n");
207 Object
*result
= fObjectArray
[id
];
215 Stack::GetObjectNoLock(usb_id id
) const
217 if (id
>= fObjectMaxCount
)
219 return fObjectArray
[id
];
224 Stack::ExploreThread(void *data
)
226 Stack
*stack
= (Stack
*)data
;
228 while (!stack
->fStopThreads
) {
229 if (mutex_lock(&stack
->fExploreLock
) != B_OK
)
232 rescan_item
*rescanList
= NULL
;
233 change_item
*changeItem
= NULL
;
234 for (int32 i
= 0; i
< stack
->fBusManagers
.Count(); i
++) {
235 Hub
*rootHub
= stack
->fBusManagers
.ElementAt(i
)->GetRootHub();
237 rootHub
->Explore(&changeItem
);
241 stack
->NotifyDeviceChange(changeItem
->device
, &rescanList
, changeItem
->added
);
242 if (!changeItem
->added
) {
243 // everyone possibly holding a reference is now notified so we
244 // can delete the device
245 changeItem
->device
->GetBusManager()->FreeDevice(changeItem
->device
);
248 change_item
*next
= changeItem
->link
;
253 stack
->fFirstExploreDone
= true;
254 mutex_unlock(&stack
->fExploreLock
);
255 stack
->RescanDrivers(rescanList
);
256 snooze(USB_DELAY_HUB_EXPLORE
);
264 Stack::AddBusManager(BusManager
*busManager
)
266 fBusManagers
.PushBack(busManager
);
271 Stack::IndexOfBusManager(BusManager
*busManager
)
273 return fBusManagers
.IndexOf(busManager
);
278 Stack::BusManagerAt(int32 index
) const
280 return fBusManagers
.ElementAt(index
);
285 Stack::AllocateChunk(void **logicalAddress
, phys_addr_t
*physicalAddress
,
288 return fAllocator
->Allocate(size
, logicalAddress
, physicalAddress
);
293 Stack::FreeChunk(void *logicalAddress
, phys_addr_t physicalAddress
,
296 return fAllocator
->Deallocate(size
, logicalAddress
, physicalAddress
);
301 Stack::AllocateArea(void **logicalAddress
, phys_addr_t
*physicalAddress
, size_t size
,
304 TRACE("allocating %ld bytes for %s\n", size
, name
);
307 size
= (size
+ B_PAGE_SIZE
- 1) & ~(B_PAGE_SIZE
- 1);
308 area_id area
= create_area(name
, &logAddress
, B_ANY_KERNEL_ADDRESS
, size
,
309 B_32_BIT_CONTIGUOUS
, 0);
310 // TODO: Use B_CONTIGUOUS when the TODOs regarding 64 bit physical
311 // addresses are fixed (if possible).
314 TRACE_ERROR("couldn't allocate area %s\n", name
);
318 physical_entry physicalEntry
;
319 status_t result
= get_memory_map(logAddress
, size
, &physicalEntry
, 1);
322 TRACE_ERROR("couldn't map area %s\n", name
);
326 memset(logAddress
, 0, size
);
328 *logicalAddress
= logAddress
;
331 *physicalAddress
= (phys_addr_t
)physicalEntry
.address
;
333 TRACE("area = %" B_PRId32
", size = %" B_PRIuSIZE
", log = %p, phy = %#"
334 B_PRIxPHYSADDR
"\n", area
, size
, logAddress
, physicalEntry
.address
);
340 Stack::NotifyDeviceChange(Device
*device
, rescan_item
**rescanList
, bool added
)
342 TRACE("device %s\n", added
? "added" : "removed");
344 usb_driver_info
*element
= fDriverList
;
346 status_t result
= device
->ReportDevice(element
->support_descriptors
,
347 element
->support_descriptor_count
, &element
->notify_hooks
,
348 &element
->cookies
, added
, false);
350 if (result
>= B_OK
) {
351 const char *driverName
= element
->driver_name
;
352 if (element
->republish_driver_name
)
353 driverName
= element
->republish_driver_name
;
355 bool already
= false;
356 rescan_item
*rescanItem
= *rescanList
;
358 if (strcmp(rescanItem
->name
, driverName
) == 0) {
359 // this driver is going to be rescanned already
363 rescanItem
= rescanItem
->link
;
367 rescanItem
= new(std::nothrow
) rescan_item
;
371 rescanItem
->name
= driverName
;
372 rescanItem
->link
= *rescanList
;
373 *rescanList
= rescanItem
;
377 element
= element
->link
;
383 Stack::RescanDrivers(rescan_item
*rescanItem
)
386 // the device is supported by this driver. it either got notified
387 // already by the hooks or it is not loaded at this time. in any
388 // case we will rescan the driver so it either is loaded and can
389 // scan for supported devices or its publish_devices hook will be
390 // called to expose changed devices.
392 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
393 // the R5 way to republish a device in devfs
394 int devFS
= open("/dev", O_WRONLY
);
395 write(devFS
, rescanItem
->name
, strlen(rescanItem
->name
));
398 // use the private devfs API under Haiku
399 devfs_rescan_driver(rescanItem
->name
);
402 rescan_item
*next
= rescanItem
->link
;
410 Stack::RegisterDriver(const char *driverName
,
411 const usb_support_descriptor
*descriptors
,
412 size_t descriptorCount
, const char *republishDriverName
)
414 TRACE("register driver \"%s\"\n", driverName
);
421 usb_driver_info
*element
= fDriverList
;
423 if (strcmp(element
->driver_name
, driverName
) == 0) {
424 // we already have an entry for this driver, just update it
425 free((char *)element
->republish_driver_name
);
426 element
->republish_driver_name
= strdup(republishDriverName
);
428 free(element
->support_descriptors
);
429 size_t descriptorsSize
= descriptorCount
* sizeof(usb_support_descriptor
);
430 element
->support_descriptors
= (usb_support_descriptor
*)malloc(descriptorsSize
);
431 memcpy(element
->support_descriptors
, descriptors
, descriptorsSize
);
432 element
->support_descriptor_count
= descriptorCount
;
438 element
= element
->link
;
441 // this is a new driver, add it to the driver list
442 usb_driver_info
*info
= new(std::nothrow
) usb_driver_info
;
448 info
->driver_name
= strdup(driverName
);
449 info
->republish_driver_name
= strdup(republishDriverName
);
451 size_t descriptorsSize
= descriptorCount
* sizeof(usb_support_descriptor
);
452 info
->support_descriptors
= (usb_support_descriptor
*)malloc(descriptorsSize
);
453 memcpy(info
->support_descriptors
, descriptors
, descriptorsSize
);
454 info
->support_descriptor_count
= descriptorCount
;
456 info
->notify_hooks
.device_added
= NULL
;
457 info
->notify_hooks
.device_removed
= NULL
;
458 info
->cookies
= NULL
;
462 usb_driver_info
*element
= fDriverList
;
463 while (element
->link
)
464 element
= element
->link
;
466 element
->link
= info
;
476 Stack::InstallNotify(const char *driverName
, const usb_notify_hooks
*hooks
)
478 TRACE("installing notify hooks for driver \"%s\"\n", driverName
);
480 usb_driver_info
*element
= fDriverList
;
482 if (strcmp(element
->driver_name
, driverName
) == 0) {
483 if (mutex_lock(&fExploreLock
) != B_OK
)
486 // inform driver about any already present devices
487 for (int32 i
= 0; i
< fBusManagers
.Count(); i
++) {
488 Hub
*rootHub
= fBusManagers
.ElementAt(i
)->GetRootHub();
490 // Report device will recurse down the whole tree
491 rootHub
->ReportDevice(element
->support_descriptors
,
492 element
->support_descriptor_count
, hooks
,
493 &element
->cookies
, true, true);
497 element
->notify_hooks
.device_added
= hooks
->device_added
;
498 element
->notify_hooks
.device_removed
= hooks
->device_removed
;
499 mutex_unlock(&fExploreLock
);
503 element
= element
->link
;
506 return B_NAME_NOT_FOUND
;
511 Stack::UninstallNotify(const char *driverName
)
513 TRACE("uninstalling notify hooks for driver \"%s\"\n", driverName
);
515 usb_driver_info
*element
= fDriverList
;
517 if (strcmp(element
->driver_name
, driverName
) == 0) {
518 if (mutex_lock(&fExploreLock
) != B_OK
)
521 // trigger the device removed hook
522 for (int32 i
= 0; i
< fBusManagers
.Count(); i
++) {
523 Hub
*rootHub
= fBusManagers
.ElementAt(i
)->GetRootHub();
525 rootHub
->ReportDevice(element
->support_descriptors
,
526 element
->support_descriptor_count
,
527 &element
->notify_hooks
, &element
->cookies
, false, true);
530 element
->notify_hooks
.device_added
= NULL
;
531 element
->notify_hooks
.device_removed
= NULL
;
532 mutex_unlock(&fExploreLock
);
536 element
= element
->link
;
539 return B_NAME_NOT_FOUND
;