xref: /haiku/src/add-ons/kernel/bus_managers/usb/Stack.cpp (revision 4a3268e14fff4dd5a456d824b48ce6503368e4c1)
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 	const char *moduleNames[] = {
64 		"busses/usb/uhci",
65 		"busses/usb/ohci",
66 		"busses/usb/ehci",
67 		"busses/usb/xhci",
68 		NULL
69 	};
70 
71 	TRACE("looking for host controller modules\n");
72 	for (uint32 i = 0; moduleNames[i]; i++) {
73 		TRACE("looking for module %s\n", moduleNames[i]);
74 
75 		usb_host_controller_info *module = NULL;
76 		if (get_module(moduleNames[i], (module_info **)&module) != B_OK)
77 			continue;
78 
79 		TRACE("adding module %s\n", moduleNames[i]);
80 		if (module->add_to(this) < B_OK) {
81 			put_module(moduleNames[i]);
82 			continue;
83 		}
84 
85 		TRACE("module %s successfully loaded\n", moduleNames[i]);
86 	}
87 
88 	if (fBusManagers.Count() == 0) {
89 		TRACE_ERROR("no bus managers available\n");
90 		return;
91 	}
92 
93 	fExploreThread = spawn_kernel_thread(ExploreThread, "usb explore",
94 		B_LOW_PRIORITY, this);
95 	resume_thread(fExploreThread);
96 
97 	// wait for the first explore to complete. this ensures that a driver that
98 	// is opening the module does not get rescanned while or before installing
99 	// its hooks.
100 	while (!fFirstExploreDone)
101 		snooze(10000);
102 }
103 
104 
105 Stack::~Stack()
106 {
107 	int32 result;
108 	fStopThreads = true;
109 	wait_for_thread(fExploreThread, &result);
110 
111 	mutex_lock(&fStackLock);
112 	mutex_destroy(&fStackLock);
113 	mutex_lock(&fExploreLock);
114 	mutex_destroy(&fExploreLock);
115 
116 	// Release the bus modules
117 	for (Vector<BusManager *>::Iterator i = fBusManagers.Begin();
118 		i != fBusManagers.End(); i++) {
119 		delete (*i);
120 	}
121 
122 	delete fAllocator;
123 	free(fObjectArray);
124 }
125 
126 
127 status_t
128 Stack::InitCheck()
129 {
130 	if (fBusManagers.Count() == 0)
131 		return ENODEV;
132 	return B_OK;
133 }
134 
135 
136 bool
137 Stack::Lock()
138 {
139 	return (mutex_lock(&fStackLock) == B_OK);
140 }
141 
142 
143 void
144 Stack::Unlock()
145 {
146 	mutex_unlock(&fStackLock);
147 }
148 
149 
150 usb_id
151 Stack::GetUSBID(Object *object)
152 {
153 	if (!Lock())
154 		return 0;
155 
156 	uint32 id = fObjectIndex;
157 	uint32 tries = fObjectMaxCount;
158 	while (tries-- > 0) {
159 		if (fObjectArray[id] == NULL) {
160 			fObjectIndex = (id + 1) % fObjectMaxCount;
161 			fObjectArray[id] = object;
162 			Unlock();
163 			return (usb_id)id;
164 		}
165 
166 		id = (id + 1) % fObjectMaxCount;
167 	}
168 
169 	TRACE_ERROR("the stack did run out of usb_ids\n");
170 	Unlock();
171 	return 0;
172 }
173 
174 
175 void
176 Stack::PutUSBID(usb_id id)
177 {
178 	if (!Lock())
179 		return;
180 
181 	if (id >= fObjectMaxCount) {
182 		TRACE_ERROR("tried to put an invalid usb_id\n");
183 		Unlock();
184 		return;
185 	}
186 
187 	fObjectArray[id] = NULL;
188 	Unlock();
189 }
190 
191 
192 Object *
193 Stack::GetObject(usb_id id)
194 {
195 	if (!Lock())
196 		return NULL;
197 
198 	if (id >= fObjectMaxCount) {
199 		TRACE_ERROR("tried to get object with invalid usb_id\n");
200 		Unlock();
201 		return NULL;
202 	}
203 
204 	Object *result = fObjectArray[id];
205 
206 	Unlock();
207 	return result;
208 }
209 
210 
211 Object *
212 Stack::GetObjectNoLock(usb_id id) const
213 {
214 	if (id >= fObjectMaxCount)
215 		return NULL;
216 	return fObjectArray[id];
217 }
218 
219 
220 int32
221 Stack::ExploreThread(void *data)
222 {
223 	Stack *stack = (Stack *)data;
224 
225 	while (!stack->fStopThreads) {
226 		if (mutex_lock(&stack->fExploreLock) != B_OK)
227 			break;
228 
229 		rescan_item *rescanList = NULL;
230 		change_item *changeItem = NULL;
231 		for (int32 i = 0; i < stack->fBusManagers.Count(); i++) {
232 			Hub *rootHub = stack->fBusManagers.ElementAt(i)->GetRootHub();
233 			if (rootHub)
234 				rootHub->Explore(&changeItem);
235 		}
236 
237 		while (changeItem) {
238 			stack->NotifyDeviceChange(changeItem->device, &rescanList, changeItem->added);
239 			if (!changeItem->added) {
240 				// everyone possibly holding a reference is now notified so we
241 				// can delete the device
242 				changeItem->device->GetBusManager()->FreeDevice(changeItem->device);
243 			}
244 
245 			change_item *next = changeItem->link;
246 			delete changeItem;
247 			changeItem = next;
248 		}
249 
250 		stack->fFirstExploreDone = true;
251 		mutex_unlock(&stack->fExploreLock);
252 		stack->RescanDrivers(rescanList);
253 		snooze(USB_DELAY_HUB_EXPLORE);
254 	}
255 
256 	return B_OK;
257 }
258 
259 
260 void
261 Stack::AddBusManager(BusManager *busManager)
262 {
263 	fBusManagers.PushBack(busManager);
264 }
265 
266 
267 int32
268 Stack::IndexOfBusManager(BusManager *busManager)
269 {
270 	return fBusManagers.IndexOf(busManager);
271 }
272 
273 
274 BusManager *
275 Stack::BusManagerAt(int32 index) const
276 {
277 	return fBusManagers.ElementAt(index);
278 }
279 
280 
281 status_t
282 Stack::AllocateChunk(void **logicalAddress, phys_addr_t *physicalAddress,
283 	size_t size)
284 {
285 	return fAllocator->Allocate(size, logicalAddress, physicalAddress);
286 }
287 
288 
289 status_t
290 Stack::FreeChunk(void *logicalAddress, phys_addr_t physicalAddress,
291 	size_t size)
292 {
293 	return fAllocator->Deallocate(size, logicalAddress, physicalAddress);
294 }
295 
296 
297 area_id
298 Stack::AllocateArea(void **logicalAddress, phys_addr_t *physicalAddress, size_t size,
299 	const char *name)
300 {
301 	TRACE("allocating %ld bytes for %s\n", size, name);
302 
303 	void *logAddress;
304 	size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
305 	area_id area = create_area(name, &logAddress, B_ANY_KERNEL_ADDRESS, size,
306 		B_32_BIT_CONTIGUOUS, 0);
307 		// TODO: Use B_CONTIGUOUS when the TODOs regarding 64 bit physical
308 		// addresses are fixed (if possible).
309 
310 	if (area < B_OK) {
311 		TRACE_ERROR("couldn't allocate area %s\n", name);
312 		return B_ERROR;
313 	}
314 
315 	physical_entry physicalEntry;
316 	status_t result = get_memory_map(logAddress, size, &physicalEntry, 1);
317 	if (result < B_OK) {
318 		delete_area(area);
319 		TRACE_ERROR("couldn't map area %s\n", name);
320 		return B_ERROR;
321 	}
322 
323 	memset(logAddress, 0, size);
324 	if (logicalAddress)
325 		*logicalAddress = logAddress;
326 
327 	if (physicalAddress)
328 		*physicalAddress = (phys_addr_t)physicalEntry.address;
329 
330 	TRACE("area = %" B_PRId32 ", size = %" B_PRIuSIZE ", log = %p, phy = %#"
331 		B_PRIxPHYSADDR "\n", area, size, logAddress, physicalEntry.address);
332 	return area;
333 }
334 
335 
336 void
337 Stack::NotifyDeviceChange(Device *device, rescan_item **rescanList, bool added)
338 {
339 	TRACE("device %s\n", added ? "added" : "removed");
340 
341 	usb_driver_info *element = fDriverList;
342 	while (element) {
343 		status_t result = device->ReportDevice(element->support_descriptors,
344 			element->support_descriptor_count, &element->notify_hooks,
345 			&element->cookies, added, false);
346 
347 		if (result >= B_OK) {
348 			const char *driverName = element->driver_name;
349 			if (element->republish_driver_name)
350 				driverName = element->republish_driver_name;
351 
352 			bool already = false;
353 			rescan_item *rescanItem = *rescanList;
354 			while (rescanItem) {
355 				if (strcmp(rescanItem->name, driverName) == 0) {
356 					// this driver is going to be rescanned already
357 					already = true;
358 					break;
359 				}
360 				rescanItem = rescanItem->link;
361 			}
362 
363 			if (!already) {
364 				rescanItem = new(std::nothrow) rescan_item;
365 				if (!rescanItem)
366 					return;
367 
368 				rescanItem->name = driverName;
369 				rescanItem->link = *rescanList;
370 				*rescanList = rescanItem;
371 			}
372 		}
373 
374 		element = element->link;
375 	}
376 }
377 
378 
379 void
380 Stack::RescanDrivers(rescan_item *rescanItem)
381 {
382 	while (rescanItem) {
383 		// the device is supported by this driver. it either got notified
384 		// already by the hooks or it is not loaded at this time. in any
385 		// case we will rescan the driver so it either is loaded and can
386 		// scan for supported devices or its publish_devices hook will be
387 		// called to expose changed devices.
388 
389 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
390 		// the R5 way to republish a device in devfs
391 		int devFS = open("/dev", O_WRONLY);
392 		write(devFS, rescanItem->name, strlen(rescanItem->name));
393 		close(devFS);
394 #else
395 		// use the private devfs API under Haiku
396 		devfs_rescan_driver(rescanItem->name);
397 #endif
398 
399 		rescan_item *next = rescanItem->link;
400 		delete rescanItem;
401 		rescanItem = next;
402 	}
403 }
404 
405 
406 status_t
407 Stack::RegisterDriver(const char *driverName,
408 	const usb_support_descriptor *descriptors,
409 	size_t descriptorCount, const char *republishDriverName)
410 {
411 	TRACE("register driver \"%s\"\n", driverName);
412 	if (!driverName)
413 		return B_BAD_VALUE;
414 
415 	if (!Lock())
416 		return B_ERROR;
417 
418 	usb_driver_info *element = fDriverList;
419 	while (element) {
420 		if (strcmp(element->driver_name, driverName) == 0) {
421 			// we already have an entry for this driver, just update it
422 			free((char *)element->republish_driver_name);
423 			element->republish_driver_name = strdup(republishDriverName);
424 
425 			free(element->support_descriptors);
426 			size_t descriptorsSize = descriptorCount * sizeof(usb_support_descriptor);
427 			element->support_descriptors = (usb_support_descriptor *)malloc(descriptorsSize);
428 			memcpy(element->support_descriptors, descriptors, descriptorsSize);
429 			element->support_descriptor_count = descriptorCount;
430 
431 			Unlock();
432 			return B_OK;
433 		}
434 
435 		element = element->link;
436 	}
437 
438 	// this is a new driver, add it to the driver list
439 	usb_driver_info *info = new(std::nothrow) usb_driver_info;
440 	if (!info) {
441 		Unlock();
442 		return B_NO_MEMORY;
443 	}
444 
445 	info->driver_name = strdup(driverName);
446 	info->republish_driver_name = strdup(republishDriverName);
447 
448 	size_t descriptorsSize = descriptorCount * sizeof(usb_support_descriptor);
449 	info->support_descriptors = (usb_support_descriptor *)malloc(descriptorsSize);
450 	memcpy(info->support_descriptors, descriptors, descriptorsSize);
451 	info->support_descriptor_count = descriptorCount;
452 
453 	info->notify_hooks.device_added = NULL;
454 	info->notify_hooks.device_removed = NULL;
455 	info->cookies = NULL;
456 	info->link = NULL;
457 
458 	if (fDriverList) {
459 		usb_driver_info *element = fDriverList;
460 		while (element->link)
461 			element = element->link;
462 
463 		element->link = info;
464 	} else
465 		fDriverList = info;
466 
467 	Unlock();
468 	return B_OK;
469 }
470 
471 
472 status_t
473 Stack::InstallNotify(const char *driverName, const usb_notify_hooks *hooks)
474 {
475 	TRACE("installing notify hooks for driver \"%s\"\n", driverName);
476 
477 	usb_driver_info *element = fDriverList;
478 	while (element) {
479 		if (strcmp(element->driver_name, driverName) == 0) {
480 			if (mutex_lock(&fExploreLock) != B_OK)
481 				return B_ERROR;
482 
483 			// inform driver about any already present devices
484 			for (int32 i = 0; i < fBusManagers.Count(); i++) {
485 				Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
486 				if (rootHub) {
487 					// Report device will recurse down the whole tree
488 					rootHub->ReportDevice(element->support_descriptors,
489 						element->support_descriptor_count, hooks,
490 						&element->cookies, true, true);
491 				}
492 			}
493 
494 			element->notify_hooks.device_added = hooks->device_added;
495 			element->notify_hooks.device_removed = hooks->device_removed;
496 			mutex_unlock(&fExploreLock);
497 			return B_OK;
498 		}
499 
500 		element = element->link;
501 	}
502 
503 	return B_NAME_NOT_FOUND;
504 }
505 
506 
507 status_t
508 Stack::UninstallNotify(const char *driverName)
509 {
510 	TRACE("uninstalling notify hooks for driver \"%s\"\n", driverName);
511 
512 	usb_driver_info *element = fDriverList;
513 	while (element) {
514 		if (strcmp(element->driver_name, driverName) == 0) {
515 			if (mutex_lock(&fExploreLock) != B_OK)
516 				return B_ERROR;
517 
518 			// trigger the device removed hook
519 			for (int32 i = 0; i < fBusManagers.Count(); i++) {
520 				Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
521 				if (rootHub)
522 					rootHub->ReportDevice(element->support_descriptors,
523 						element->support_descriptor_count,
524 						&element->notify_hooks, &element->cookies, false, true);
525 			}
526 
527 			element->notify_hooks.device_added = NULL;
528 			element->notify_hooks.device_removed = NULL;
529 			mutex_unlock(&fExploreLock);
530 			return B_OK;
531 		}
532 
533 		element = element->link;
534 	}
535 
536 	return B_NAME_NOT_FOUND;
537 }
538