Fix #8729.
[haiku.git] / src / add-ons / kernel / bus_managers / usb / Stack.cpp
blob049d6f0c3e2da9ddecb10587d312268117967d9a
1 /*
2 * Copyright 2003-2008, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Michael Lotz <mmlr@mlotz.ch>
7 * Niels S. Reedijk
8 */
10 #include <module.h>
11 #include <unistd.h>
12 #include <util/kernel_cpp.h>
13 #include "usb_private.h"
14 #include "PhysicalMemoryAllocator.h"
16 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
17 #include <fs/devfs.h>
18 #endif
20 Stack::Stack()
21 : fExploreThread(-1),
22 fFirstExploreDone(false),
23 fStopThreads(false),
24 fAllocator(NULL),
25 fObjectIndex(1),
26 fObjectMaxCount(1024),
27 fObjectArray(NULL),
28 fDriverList(NULL)
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");
39 return;
42 memset(fObjectArray, 0, objectArraySize);
44 fAllocator = new(std::nothrow) PhysicalMemoryAllocator("USB Stack Allocator",
45 8, B_PAGE_SIZE * 4, 64);
46 if (!fAllocator || fAllocator->InitCheck() < B_OK) {
47 TRACE_ERROR("failed to allocate the allocator\n");
48 delete fAllocator;
49 fAllocator = NULL;
50 return;
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 const char *moduleNames[] = {
64 "busses/usb/uhci",
65 "busses/usb/ohci",
66 "busses/usb/ehci",
67 NULL
70 TRACE("looking for host controller modules\n");
71 for (uint32 i = 0; moduleNames[i]; i++) {
72 TRACE("looking for module %s\n", moduleNames[i]);
74 usb_host_controller_info *module = NULL;
75 if (get_module(moduleNames[i], (module_info **)&module) != B_OK)
76 continue;
78 TRACE("adding module %s\n", moduleNames[i]);
79 if (module->add_to(this) < B_OK) {
80 put_module(moduleNames[i]);
81 continue;
84 TRACE("module %s successfully loaded\n", moduleNames[i]);
87 if (fBusManagers.Count() == 0) {
88 TRACE_ERROR("no bus managers available\n");
89 return;
92 fExploreThread = spawn_kernel_thread(ExploreThread, "usb explore",
93 B_LOW_PRIORITY, this);
94 resume_thread(fExploreThread);
96 // wait for the first explore to complete. this ensures that a driver that
97 // is opening the module does not get rescanned while or before installing
98 // its hooks.
99 while (!fFirstExploreDone)
100 snooze(100000);
104 Stack::~Stack()
106 int32 result;
107 fStopThreads = true;
108 wait_for_thread(fExploreThread, &result);
110 mutex_lock(&fStackLock);
111 mutex_destroy(&fStackLock);
112 mutex_lock(&fExploreLock);
113 mutex_destroy(&fExploreLock);
115 // Release the bus modules
116 for (Vector<BusManager *>::Iterator i = fBusManagers.Begin();
117 i != fBusManagers.End(); i++) {
118 delete (*i);
121 delete fAllocator;
122 free(fObjectArray);
126 status_t
127 Stack::InitCheck()
129 if (fBusManagers.Count() == 0)
130 return ENODEV;
131 return B_OK;
135 bool
136 Stack::Lock()
138 return (mutex_lock(&fStackLock) == B_OK);
142 void
143 Stack::Unlock()
145 mutex_unlock(&fStackLock);
149 usb_id
150 Stack::GetUSBID(Object *object)
152 if (!Lock())
153 return 0;
155 uint32 id = fObjectIndex;
156 uint32 tries = fObjectMaxCount;
157 while (tries-- > 0) {
158 if (fObjectArray[id] == NULL) {
159 fObjectIndex = (id + 1) % fObjectMaxCount;
160 fObjectArray[id] = object;
161 Unlock();
162 return (usb_id)id;
165 id = (id + 1) % fObjectMaxCount;
168 TRACE_ERROR("the stack did run out of usb_ids\n");
169 Unlock();
170 return 0;
174 void
175 Stack::PutUSBID(usb_id id)
177 if (!Lock())
178 return;
180 if (id >= fObjectMaxCount) {
181 TRACE_ERROR("tried to put an invalid usb_id\n");
182 Unlock();
183 return;
186 fObjectArray[id] = NULL;
187 Unlock();
191 Object *
192 Stack::GetObject(usb_id id)
194 if (!Lock())
195 return NULL;
197 if (id >= fObjectMaxCount) {
198 TRACE_ERROR("tried to get object with invalid usb_id\n");
199 Unlock();
200 return NULL;
203 Object *result = fObjectArray[id];
205 Unlock();
206 return result;
210 Object *
211 Stack::GetObjectNoLock(usb_id id) const
213 if (id >= fObjectMaxCount)
214 return NULL;
215 return fObjectArray[id];
219 int32
220 Stack::ExploreThread(void *data)
222 Stack *stack = (Stack *)data;
224 while (!stack->fStopThreads) {
225 if (mutex_lock(&stack->fExploreLock) != B_OK)
226 break;
228 rescan_item *rescanList = NULL;
229 change_item *changeItem = NULL;
230 for (int32 i = 0; i < stack->fBusManagers.Count(); i++) {
231 Hub *rootHub = stack->fBusManagers.ElementAt(i)->GetRootHub();
232 if (rootHub)
233 rootHub->Explore(&changeItem);
236 while (changeItem) {
237 stack->NotifyDeviceChange(changeItem->device, &rescanList, changeItem->added);
238 if (!changeItem->added) {
239 // everyone possibly holding a reference is now notified so we
240 // can delete the device
241 changeItem->device->GetBusManager()->FreeDevice(changeItem->device);
244 change_item *next = changeItem->link;
245 delete changeItem;
246 changeItem = next;
249 stack->fFirstExploreDone = true;
250 mutex_unlock(&stack->fExploreLock);
251 stack->RescanDrivers(rescanList);
252 snooze(USB_DELAY_HUB_EXPLORE);
255 return B_OK;
259 void
260 Stack::AddBusManager(BusManager *busManager)
262 fBusManagers.PushBack(busManager);
266 int32
267 Stack::IndexOfBusManager(BusManager *busManager)
269 return fBusManagers.IndexOf(busManager);
273 BusManager *
274 Stack::BusManagerAt(int32 index) const
276 return fBusManagers.ElementAt(index);
280 status_t
281 Stack::AllocateChunk(void **logicalAddress, void **physicalAddress, size_t size)
283 return fAllocator->Allocate(size, logicalAddress, physicalAddress);
287 status_t
288 Stack::FreeChunk(void *logicalAddress, void *physicalAddress, size_t size)
290 return fAllocator->Deallocate(size, logicalAddress, physicalAddress);
294 area_id
295 Stack::AllocateArea(void **logicalAddress, void **physicalAddress, size_t size,
296 const char *name)
298 // TODO: physicalAddress should be a phys_addr_t*!
299 TRACE("allocating %ld bytes for %s\n", size, name);
301 void *logAddress;
302 size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
303 area_id area = create_area(name, &logAddress, B_ANY_KERNEL_ADDRESS, size,
304 B_32_BIT_CONTIGUOUS, 0);
305 // TODO: Use B_CONTIGUOUS when the TODOs regarding 64 bit physical
306 // addresses are fixed (if possible).
308 if (area < B_OK) {
309 TRACE_ERROR("couldn't allocate area %s\n", name);
310 return B_ERROR;
313 physical_entry physicalEntry;
314 status_t result = get_memory_map(logAddress, size, &physicalEntry, 1);
315 if (result < B_OK) {
316 delete_area(area);
317 TRACE_ERROR("couldn't map area %s\n", name);
318 return B_ERROR;
321 memset(logAddress, 0, size);
322 if (logicalAddress)
323 *logicalAddress = logAddress;
325 if (physicalAddress)
326 *physicalAddress = (void*)(addr_t)physicalEntry.address;
328 TRACE("area = %ld, size = %ld, log = %p, phy = %#" B_PRIxPHYSADDR "\n",
329 area, size, logAddress, physicalEntry.address);
330 return area;
334 void
335 Stack::NotifyDeviceChange(Device *device, rescan_item **rescanList, bool added)
337 TRACE("device %s\n", added ? "added" : "removed");
339 usb_driver_info *element = fDriverList;
340 while (element) {
341 status_t result = device->ReportDevice(element->support_descriptors,
342 element->support_descriptor_count, &element->notify_hooks,
343 &element->cookies, added, false);
345 if (result >= B_OK) {
346 const char *driverName = element->driver_name;
347 if (element->republish_driver_name)
348 driverName = element->republish_driver_name;
350 bool already = false;
351 rescan_item *rescanItem = *rescanList;
352 while (rescanItem) {
353 if (strcmp(rescanItem->name, driverName) == 0) {
354 // this driver is going to be rescanned already
355 already = true;
356 break;
358 rescanItem = rescanItem->link;
361 if (!already) {
362 rescanItem = new(std::nothrow) rescan_item;
363 if (!rescanItem)
364 return;
366 rescanItem->name = driverName;
367 rescanItem->link = *rescanList;
368 *rescanList = rescanItem;
372 element = element->link;
377 void
378 Stack::RescanDrivers(rescan_item *rescanItem)
380 while (rescanItem) {
381 // the device is supported by this driver. it either got notified
382 // already by the hooks or it is not loaded at this time. in any
383 // case we will rescan the driver so it either is loaded and can
384 // scan for supported devices or its publish_devices hook will be
385 // called to expose changed devices.
387 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
388 // the R5 way to republish a device in devfs
389 int devFS = open("/dev", O_WRONLY);
390 write(devFS, rescanItem->name, strlen(rescanItem->name));
391 close(devFS);
392 #else
393 // use the private devfs API under Haiku
394 devfs_rescan_driver(rescanItem->name);
395 #endif
397 rescan_item *next = rescanItem->link;
398 delete rescanItem;
399 rescanItem = next;
404 status_t
405 Stack::RegisterDriver(const char *driverName,
406 const usb_support_descriptor *descriptors,
407 size_t descriptorCount, const char *republishDriverName)
409 TRACE("register driver \"%s\"\n", driverName);
410 if (!driverName)
411 return B_BAD_VALUE;
413 if (!Lock())
414 return B_ERROR;
416 usb_driver_info *element = fDriverList;
417 while (element) {
418 if (strcmp(element->driver_name, driverName) == 0) {
419 // we already have an entry for this driver, just update it
420 free((char *)element->republish_driver_name);
421 element->republish_driver_name = strdup(republishDriverName);
423 free(element->support_descriptors);
424 size_t descriptorsSize = descriptorCount * sizeof(usb_support_descriptor);
425 element->support_descriptors = (usb_support_descriptor *)malloc(descriptorsSize);
426 memcpy(element->support_descriptors, descriptors, descriptorsSize);
427 element->support_descriptor_count = descriptorCount;
429 Unlock();
430 return B_OK;
433 element = element->link;
436 // this is a new driver, add it to the driver list
437 usb_driver_info *info = new(std::nothrow) usb_driver_info;
438 if (!info) {
439 Unlock();
440 return B_NO_MEMORY;
443 info->driver_name = strdup(driverName);
444 info->republish_driver_name = strdup(republishDriverName);
446 size_t descriptorsSize = descriptorCount * sizeof(usb_support_descriptor);
447 info->support_descriptors = (usb_support_descriptor *)malloc(descriptorsSize);
448 memcpy(info->support_descriptors, descriptors, descriptorsSize);
449 info->support_descriptor_count = descriptorCount;
451 info->notify_hooks.device_added = NULL;
452 info->notify_hooks.device_removed = NULL;
453 info->cookies = NULL;
454 info->link = NULL;
456 if (fDriverList) {
457 usb_driver_info *element = fDriverList;
458 while (element->link)
459 element = element->link;
461 element->link = info;
462 } else
463 fDriverList = info;
465 Unlock();
466 return B_OK;
470 status_t
471 Stack::InstallNotify(const char *driverName, const usb_notify_hooks *hooks)
473 TRACE("installing notify hooks for driver \"%s\"\n", driverName);
475 usb_driver_info *element = fDriverList;
476 while (element) {
477 if (strcmp(element->driver_name, driverName) == 0) {
478 if (mutex_lock(&fExploreLock) != B_OK)
479 return B_ERROR;
481 // inform driver about any already present devices
482 for (int32 i = 0; i < fBusManagers.Count(); i++) {
483 Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
484 if (rootHub) {
485 // Report device will recurse down the whole tree
486 rootHub->ReportDevice(element->support_descriptors,
487 element->support_descriptor_count, hooks,
488 &element->cookies, true, true);
492 element->notify_hooks.device_added = hooks->device_added;
493 element->notify_hooks.device_removed = hooks->device_removed;
494 mutex_unlock(&fExploreLock);
495 return B_OK;
498 element = element->link;
501 return B_NAME_NOT_FOUND;
505 status_t
506 Stack::UninstallNotify(const char *driverName)
508 TRACE("uninstalling notify hooks for driver \"%s\"\n", driverName);
510 usb_driver_info *element = fDriverList;
511 while (element) {
512 if (strcmp(element->driver_name, driverName) == 0) {
513 if (mutex_lock(&fExploreLock) != B_OK)
514 return B_ERROR;
516 // trigger the device removed hook
517 for (int32 i = 0; i < fBusManagers.Count(); i++) {
518 Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
519 if (rootHub)
520 rootHub->ReportDevice(element->support_descriptors,
521 element->support_descriptor_count,
522 &element->notify_hooks, &element->cookies, false, true);
525 element->notify_hooks.device_added = NULL;
526 element->notify_hooks.device_removed = NULL;
527 mutex_unlock(&fExploreLock);
528 return B_OK;
531 element = element->link;
534 return B_NAME_NOT_FOUND;