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