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