xref: /haiku/src/add-ons/kernel/bus_managers/usb/Stack.cpp (revision fae7ea18b62865f5e1159e1678c851d3aa1ddce0)
1 /*
2  * Copyright 2003-2008, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Lotz <mmlr@mlotz.ch>
7  *		Niels S. Reedijk
8  */
9 
10 #include <module.h>
11 #include <unistd.h>
12 #include <util/kernel_cpp.h>
13 #include "usb_private.h"
14 #include "PhysicalMemoryAllocator.h"
15 
16 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
17 #include <fs/devfs.h>
18 #endif
19 
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)
29 {
30 	TRACE("stack init\n");
31 
32 	mutex_init(&fStackLock, "usb stack lock");
33 	mutex_init(&fExploreLock, "usb explore lock");
34 
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;
40 	}
41 
42 	memset(fObjectArray, 0, objectArraySize);
43 
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;
51 	}
52 
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
72 	};
73 
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]);
77 
78 		usb_host_controller_info *module = NULL;
79 		if (get_module(moduleNames[i], (module_info **)&module) != B_OK)
80 			continue;
81 
82 		TRACE("adding module %s\n", moduleNames[i]);
83 		if (module->add_to(this) < B_OK) {
84 			put_module(moduleNames[i]);
85 			continue;
86 		}
87 
88 		TRACE("module %s successfully loaded\n", moduleNames[i]);
89 	}
90 
91 	if (fBusManagers.Count() == 0) {
92 		TRACE_ERROR("no bus managers available\n");
93 		return;
94 	}
95 
96 	fExploreThread = spawn_kernel_thread(ExploreThread, "usb explore",
97 		B_LOW_PRIORITY, this);
98 	resume_thread(fExploreThread);
99 
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);
105 }
106 
107 
108 Stack::~Stack()
109 {
110 	int32 result;
111 	fStopThreads = true;
112 	wait_for_thread(fExploreThread, &result);
113 
114 	mutex_lock(&fStackLock);
115 	mutex_destroy(&fStackLock);
116 	mutex_lock(&fExploreLock);
117 	mutex_destroy(&fExploreLock);
118 
119 	// Release the bus modules
120 	for (Vector<BusManager *>::Iterator i = fBusManagers.Begin();
121 		i != fBusManagers.End(); i++) {
122 		delete (*i);
123 	}
124 
125 	delete fAllocator;
126 	free(fObjectArray);
127 }
128 
129 
130 status_t
131 Stack::InitCheck()
132 {
133 	if (fBusManagers.Count() == 0)
134 		return ENODEV;
135 	return B_OK;
136 }
137 
138 
139 bool
140 Stack::Lock()
141 {
142 	return (mutex_lock(&fStackLock) == B_OK);
143 }
144 
145 
146 void
147 Stack::Unlock()
148 {
149 	mutex_unlock(&fStackLock);
150 }
151 
152 
153 usb_id
154 Stack::GetUSBID(Object *object)
155 {
156 	if (!Lock())
157 		return 0;
158 
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;
167 		}
168 
169 		id = (id + 1) % fObjectMaxCount;
170 	}
171 
172 	TRACE_ERROR("the stack did run out of usb_ids\n");
173 	Unlock();
174 	return 0;
175 }
176 
177 
178 void
179 Stack::PutUSBID(usb_id id)
180 {
181 	if (!Lock())
182 		return;
183 
184 	if (id >= fObjectMaxCount) {
185 		TRACE_ERROR("tried to put an invalid usb_id\n");
186 		Unlock();
187 		return;
188 	}
189 
190 	fObjectArray[id] = NULL;
191 	Unlock();
192 }
193 
194 
195 Object *
196 Stack::GetObject(usb_id id)
197 {
198 	if (!Lock())
199 		return NULL;
200 
201 	if (id >= fObjectMaxCount) {
202 		TRACE_ERROR("tried to get object with invalid usb_id\n");
203 		Unlock();
204 		return NULL;
205 	}
206 
207 	Object *result = fObjectArray[id];
208 
209 	Unlock();
210 	return result;
211 }
212 
213 
214 Object *
215 Stack::GetObjectNoLock(usb_id id) const
216 {
217 	if (id >= fObjectMaxCount)
218 		return NULL;
219 	return fObjectArray[id];
220 }
221 
222 
223 int32
224 Stack::ExploreThread(void *data)
225 {
226 	Stack *stack = (Stack *)data;
227 
228 	while (!stack->fStopThreads) {
229 		if (mutex_lock(&stack->fExploreLock) != B_OK)
230 			break;
231 
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);
238 		}
239 
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);
246 			}
247 
248 			change_item *next = changeItem->link;
249 			delete changeItem;
250 			changeItem = next;
251 		}
252 
253 		stack->fFirstExploreDone = true;
254 		mutex_unlock(&stack->fExploreLock);
255 		stack->RescanDrivers(rescanList);
256 		snooze(USB_DELAY_HUB_EXPLORE);
257 	}
258 
259 	return B_OK;
260 }
261 
262 
263 void
264 Stack::AddBusManager(BusManager *busManager)
265 {
266 	fBusManagers.PushBack(busManager);
267 }
268 
269 
270 int32
271 Stack::IndexOfBusManager(BusManager *busManager)
272 {
273 	return fBusManagers.IndexOf(busManager);
274 }
275 
276 
277 BusManager *
278 Stack::BusManagerAt(int32 index) const
279 {
280 	return fBusManagers.ElementAt(index);
281 }
282 
283 
284 status_t
285 Stack::AllocateChunk(void **logicalAddress, phys_addr_t *physicalAddress,
286 	size_t size)
287 {
288 	return fAllocator->Allocate(size, logicalAddress, physicalAddress);
289 }
290 
291 
292 status_t
293 Stack::FreeChunk(void *logicalAddress, phys_addr_t physicalAddress,
294 	size_t size)
295 {
296 	return fAllocator->Deallocate(size, logicalAddress, physicalAddress);
297 }
298 
299 
300 area_id
301 Stack::AllocateArea(void **logicalAddress, phys_addr_t *physicalAddress, size_t size,
302 	const char *name)
303 {
304 	TRACE("allocating %ld bytes for %s\n", size, name);
305 
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).
312 
313 	if (area < B_OK) {
314 		TRACE_ERROR("couldn't allocate area %s\n", name);
315 		return B_ERROR;
316 	}
317 
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;
324 	}
325 
326 	memset(logAddress, 0, size);
327 	if (logicalAddress)
328 		*logicalAddress = logAddress;
329 
330 	if (physicalAddress)
331 		*physicalAddress = (phys_addr_t)physicalEntry.address;
332 
333 	TRACE("area = %" B_PRId32 ", size = %" B_PRIuSIZE ", log = %p, phy = %#"
334 		B_PRIxPHYSADDR "\n", area, size, logAddress, physicalEntry.address);
335 	return area;
336 }
337 
338 
339 void
340 Stack::NotifyDeviceChange(Device *device, rescan_item **rescanList, bool added)
341 {
342 	TRACE("device %s\n", added ? "added" : "removed");
343 
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);
349 
350 		if (result >= B_OK) {
351 			const char *driverName = element->driver_name;
352 			if (element->republish_driver_name)
353 				driverName = element->republish_driver_name;
354 
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;
362 				}
363 				rescanItem = rescanItem->link;
364 			}
365 
366 			if (!already) {
367 				rescanItem = new(std::nothrow) rescan_item;
368 				if (!rescanItem)
369 					return;
370 
371 				rescanItem->name = driverName;
372 				rescanItem->link = *rescanList;
373 				*rescanList = rescanItem;
374 			}
375 		}
376 
377 		element = element->link;
378 	}
379 }
380 
381 
382 void
383 Stack::RescanDrivers(rescan_item *rescanItem)
384 {
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.
391 
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
401 
402 		rescan_item *next = rescanItem->link;
403 		delete rescanItem;
404 		rescanItem = next;
405 	}
406 }
407 
408 
409 status_t
410 Stack::RegisterDriver(const char *driverName,
411 	const usb_support_descriptor *descriptors,
412 	size_t descriptorCount, const char *republishDriverName)
413 {
414 	TRACE("register driver \"%s\"\n", driverName);
415 	if (!driverName)
416 		return B_BAD_VALUE;
417 
418 	if (!Lock())
419 		return B_ERROR;
420 
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);
427 
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;
433 
434 			Unlock();
435 			return B_OK;
436 		}
437 
438 		element = element->link;
439 	}
440 
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;
446 	}
447 
448 	info->driver_name = strdup(driverName);
449 	info->republish_driver_name = strdup(republishDriverName);
450 
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;
455 
456 	info->notify_hooks.device_added = NULL;
457 	info->notify_hooks.device_removed = NULL;
458 	info->cookies = NULL;
459 	info->link = NULL;
460 
461 	if (fDriverList) {
462 		usb_driver_info *element = fDriverList;
463 		while (element->link)
464 			element = element->link;
465 
466 		element->link = info;
467 	} else
468 		fDriverList = info;
469 
470 	Unlock();
471 	return B_OK;
472 }
473 
474 
475 status_t
476 Stack::InstallNotify(const char *driverName, const usb_notify_hooks *hooks)
477 {
478 	TRACE("installing notify hooks for driver \"%s\"\n", driverName);
479 
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;
485 
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);
494 				}
495 			}
496 
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;
501 		}
502 
503 		element = element->link;
504 	}
505 
506 	return B_NAME_NOT_FOUND;
507 }
508 
509 
510 status_t
511 Stack::UninstallNotify(const char *driverName)
512 {
513 	TRACE("uninstalling notify hooks for driver \"%s\"\n", driverName);
514 
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;
520 
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);
528 			}
529 
530 			element->notify_hooks.device_added = NULL;
531 			element->notify_hooks.device_removed = NULL;
532 			mutex_unlock(&fExploreLock);
533 			return B_OK;
534 		}
535 
536 		element = element->link;
537 	}
538 
539 	return B_NAME_NOT_FOUND;
540 }
541