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