vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / bus_managers / usb / Stack.cpp
blob92cb858d4e94b8ef82ad50a3385f3331add2158b
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 * 32, 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 // 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[] = {
67 "busses/usb/xhci",
68 "busses/usb/uhci",
69 "busses/usb/ohci",
70 "busses/usb/ehci",
71 NULL
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)
80 continue;
82 TRACE("adding module %s\n", moduleNames[i]);
83 if (module->add_to(this) < B_OK) {
84 put_module(moduleNames[i]);
85 continue;
88 TRACE("module %s successfully loaded\n", moduleNames[i]);
91 if (fBusManagers.Count() == 0) {
92 TRACE_ERROR("no bus managers available\n");
93 return;
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
102 // its hooks.
103 while (!fFirstExploreDone)
104 snooze(10000);
108 Stack::~Stack()
110 int32 result;
111 fStopThreads = true;
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++) {
122 delete (*i);
125 delete fAllocator;
126 free(fObjectArray);
130 status_t
131 Stack::InitCheck()
133 if (fBusManagers.Count() == 0)
134 return ENODEV;
135 return B_OK;
139 bool
140 Stack::Lock()
142 return (mutex_lock(&fStackLock) == B_OK);
146 void
147 Stack::Unlock()
149 mutex_unlock(&fStackLock);
153 usb_id
154 Stack::GetUSBID(Object *object)
156 if (!Lock())
157 return 0;
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;
165 Unlock();
166 return (usb_id)id;
169 id = (id + 1) % fObjectMaxCount;
172 TRACE_ERROR("the stack did run out of usb_ids\n");
173 Unlock();
174 return 0;
178 void
179 Stack::PutUSBID(usb_id id)
181 if (!Lock())
182 return;
184 if (id >= fObjectMaxCount) {
185 TRACE_ERROR("tried to put an invalid usb_id\n");
186 Unlock();
187 return;
190 fObjectArray[id] = NULL;
191 Unlock();
195 Object *
196 Stack::GetObject(usb_id id)
198 if (!Lock())
199 return NULL;
201 if (id >= fObjectMaxCount) {
202 TRACE_ERROR("tried to get object with invalid usb_id\n");
203 Unlock();
204 return NULL;
207 Object *result = fObjectArray[id];
209 Unlock();
210 return result;
214 Object *
215 Stack::GetObjectNoLock(usb_id id) const
217 if (id >= fObjectMaxCount)
218 return NULL;
219 return fObjectArray[id];
223 int32
224 Stack::ExploreThread(void *data)
226 Stack *stack = (Stack *)data;
228 while (!stack->fStopThreads) {
229 if (mutex_lock(&stack->fExploreLock) != B_OK)
230 break;
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();
236 if (rootHub)
237 rootHub->Explore(&changeItem);
240 while (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;
249 delete changeItem;
250 changeItem = next;
253 stack->fFirstExploreDone = true;
254 mutex_unlock(&stack->fExploreLock);
255 stack->RescanDrivers(rescanList);
256 snooze(USB_DELAY_HUB_EXPLORE);
259 return B_OK;
263 void
264 Stack::AddBusManager(BusManager *busManager)
266 fBusManagers.PushBack(busManager);
270 int32
271 Stack::IndexOfBusManager(BusManager *busManager)
273 return fBusManagers.IndexOf(busManager);
277 BusManager *
278 Stack::BusManagerAt(int32 index) const
280 return fBusManagers.ElementAt(index);
284 status_t
285 Stack::AllocateChunk(void **logicalAddress, phys_addr_t *physicalAddress,
286 size_t size)
288 return fAllocator->Allocate(size, logicalAddress, physicalAddress);
292 status_t
293 Stack::FreeChunk(void *logicalAddress, phys_addr_t physicalAddress,
294 size_t size)
296 return fAllocator->Deallocate(size, logicalAddress, physicalAddress);
300 area_id
301 Stack::AllocateArea(void **logicalAddress, phys_addr_t *physicalAddress, size_t size,
302 const char *name)
304 TRACE("allocating %ld bytes for %s\n", size, name);
306 void *logAddress;
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).
313 if (area < B_OK) {
314 TRACE_ERROR("couldn't allocate area %s\n", name);
315 return B_ERROR;
318 physical_entry physicalEntry;
319 status_t result = get_memory_map(logAddress, size, &physicalEntry, 1);
320 if (result < B_OK) {
321 delete_area(area);
322 TRACE_ERROR("couldn't map area %s\n", name);
323 return B_ERROR;
326 memset(logAddress, 0, size);
327 if (logicalAddress)
328 *logicalAddress = logAddress;
330 if (physicalAddress)
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);
335 return area;
339 void
340 Stack::NotifyDeviceChange(Device *device, rescan_item **rescanList, bool added)
342 TRACE("device %s\n", added ? "added" : "removed");
344 usb_driver_info *element = fDriverList;
345 while (element) {
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;
357 while (rescanItem) {
358 if (strcmp(rescanItem->name, driverName) == 0) {
359 // this driver is going to be rescanned already
360 already = true;
361 break;
363 rescanItem = rescanItem->link;
366 if (!already) {
367 rescanItem = new(std::nothrow) rescan_item;
368 if (!rescanItem)
369 return;
371 rescanItem->name = driverName;
372 rescanItem->link = *rescanList;
373 *rescanList = rescanItem;
377 element = element->link;
382 void
383 Stack::RescanDrivers(rescan_item *rescanItem)
385 while (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));
396 close(devFS);
397 #else
398 // use the private devfs API under Haiku
399 devfs_rescan_driver(rescanItem->name);
400 #endif
402 rescan_item *next = rescanItem->link;
403 delete rescanItem;
404 rescanItem = next;
409 status_t
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);
415 if (!driverName)
416 return B_BAD_VALUE;
418 if (!Lock())
419 return B_ERROR;
421 usb_driver_info *element = fDriverList;
422 while (element) {
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;
434 Unlock();
435 return B_OK;
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;
443 if (!info) {
444 Unlock();
445 return B_NO_MEMORY;
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;
459 info->link = NULL;
461 if (fDriverList) {
462 usb_driver_info *element = fDriverList;
463 while (element->link)
464 element = element->link;
466 element->link = info;
467 } else
468 fDriverList = info;
470 Unlock();
471 return B_OK;
475 status_t
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;
481 while (element) {
482 if (strcmp(element->driver_name, driverName) == 0) {
483 if (mutex_lock(&fExploreLock) != B_OK)
484 return B_ERROR;
486 // inform driver about any already present devices
487 for (int32 i = 0; i < fBusManagers.Count(); i++) {
488 Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
489 if (rootHub) {
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);
500 return B_OK;
503 element = element->link;
506 return B_NAME_NOT_FOUND;
510 status_t
511 Stack::UninstallNotify(const char *driverName)
513 TRACE("uninstalling notify hooks for driver \"%s\"\n", driverName);
515 usb_driver_info *element = fDriverList;
516 while (element) {
517 if (strcmp(element->driver_name, driverName) == 0) {
518 if (mutex_lock(&fExploreLock) != B_OK)
519 return B_ERROR;
521 // trigger the device removed hook
522 for (int32 i = 0; i < fBusManagers.Count(); i++) {
523 Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
524 if (rootHub)
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);
533 return B_OK;
536 element = element->link;
539 return B_NAME_NOT_FOUND;