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