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