xref: /haiku/src/add-ons/kernel/bus_managers/usb/Stack.cpp (revision 959ff00ddee8411dabb09211f3bfbd52d87229da)
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(("USB Stack: 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(("USB Stack: 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(("USB Stack: 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(("USB Stack: looking for host controller modules\n"));
70 	for (uint32 i = 0; moduleNames[i]; i++) {
71 		TRACE(("USB Stack: 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(("USB Stack: 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(("USB Stack: module %s successfully loaded\n", moduleNames[i]));
84 	}
85 
86 	if (fBusManagers.Count() == 0) {
87 		TRACE_ERROR(("USB Stack: 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(("USB Stack: 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(("USB Stack: 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(("USB Stack: 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 int32
210 Stack::ExploreThread(void *data)
211 {
212 	Stack *stack = (Stack *)data;
213 
214 	while (!stack->fStopThreads) {
215 		if (mutex_lock(&stack->fExploreLock) != B_OK)
216 			break;
217 
218 		rescan_item *rescanList = NULL;
219 		change_item *changeItem = NULL;
220 		for (int32 i = 0; i < stack->fBusManagers.Count(); i++) {
221 			Hub *rootHub = stack->fBusManagers.ElementAt(i)->GetRootHub();
222 			if (rootHub)
223 				rootHub->Explore(&changeItem);
224 		}
225 
226 		while (changeItem) {
227 			stack->NotifyDeviceChange(changeItem->device, &rescanList, changeItem->added);
228 			if (!changeItem->added) {
229 				// everyone possibly holding a reference is now notified so we
230 				// can delete the device
231 				changeItem->device->GetBusManager()->FreeDevice(changeItem->device);
232 			}
233 
234 			change_item *next = changeItem->link;
235 			delete changeItem;
236 			changeItem = next;
237 		}
238 
239 		stack->fFirstExploreDone = true;
240 		mutex_unlock(&stack->fExploreLock);
241 		stack->RescanDrivers(rescanList);
242 		snooze(USB_DELAY_HUB_EXPLORE);
243 	}
244 
245 	return B_OK;
246 }
247 
248 
249 void
250 Stack::AddBusManager(BusManager *busManager)
251 {
252 	fBusManagers.PushBack(busManager);
253 }
254 
255 
256 int32
257 Stack::IndexOfBusManager(BusManager *busManager)
258 {
259 	return fBusManagers.IndexOf(busManager);
260 }
261 
262 
263 BusManager *
264 Stack::BusManagerAt(int32 index)
265 {
266 	return fBusManagers.ElementAt(index);
267 }
268 
269 
270 status_t
271 Stack::AllocateChunk(void **logicalAddress, void **physicalAddress, size_t size)
272 {
273 	return fAllocator->Allocate(size, logicalAddress, physicalAddress);
274 }
275 
276 
277 status_t
278 Stack::FreeChunk(void *logicalAddress, void *physicalAddress, size_t size)
279 {
280 	return fAllocator->Deallocate(size, logicalAddress, physicalAddress);
281 }
282 
283 
284 area_id
285 Stack::AllocateArea(void **logicalAddress, void **physicalAddress, size_t size,
286 	const char *name)
287 {
288 	TRACE(("USB Stack: allocating %ld bytes for %s\n", size, name));
289 
290 	void *logAddress;
291 	size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
292 	area_id area = create_area(name, &logAddress, B_ANY_KERNEL_ADDRESS, size,
293 		B_CONTIGUOUS, 0);
294 
295 	if (area < B_OK) {
296 		TRACE_ERROR(("USB Stack: couldn't allocate area %s\n", name));
297 		return B_ERROR;
298 	}
299 
300 	physical_entry physicalEntry;
301 	status_t result = get_memory_map(logAddress, size, &physicalEntry, 1);
302 	if (result < B_OK) {
303 		delete_area(area);
304 		TRACE_ERROR(("USB Stack: couldn't map area %s\n", name));
305 		return B_ERROR;
306 	}
307 
308 	memset(logAddress, 0, size);
309 	if (logicalAddress)
310 		*logicalAddress = logAddress;
311 
312 	if (physicalAddress)
313 		*physicalAddress = physicalEntry.address;
314 
315 	TRACE(("USB Stack: area = 0x%08lx, size = %ld, log = 0x%08lx, phy = 0x%08lx\n",
316 		area, size, logAddress, physicalEntry.address));
317 	return area;
318 }
319 
320 
321 void
322 Stack::NotifyDeviceChange(Device *device, rescan_item **rescanList, bool added)
323 {
324 	TRACE(("USB Stack: device %s\n", added ? "added" : "removed"));
325 
326 	usb_driver_info *element = fDriverList;
327 	while (element) {
328 		status_t result = device->ReportDevice(element->support_descriptors,
329 			element->support_descriptor_count, &element->notify_hooks,
330 			&element->cookies, added, false);
331 
332 		if (result >= B_OK) {
333 			const char *driverName = element->driver_name;
334 			if (element->republish_driver_name)
335 				driverName = element->republish_driver_name;
336 
337 			bool already = false;
338 			rescan_item *rescanItem = *rescanList;
339 			while (rescanItem) {
340 				if (strcmp(rescanItem->name, driverName) == 0) {
341 					// this driver is going to be rescanned already
342 					already = true;
343 					break;
344 				}
345 				rescanItem = rescanItem->link;
346 			}
347 
348 			if (!already) {
349 				rescanItem = new(std::nothrow) rescan_item;
350 				if (!rescanItem)
351 					return;
352 
353 				rescanItem->name = driverName;
354 				rescanItem->link = *rescanList;
355 				*rescanList = rescanItem;
356 			}
357 		}
358 
359 		element = element->link;
360 	}
361 }
362 
363 
364 void
365 Stack::RescanDrivers(rescan_item *rescanItem)
366 {
367 	while (rescanItem) {
368 		// the device is supported by this driver. it either got notified
369 		// already by the hooks or it is not loaded at this time. in any
370 		// case we will rescan the driver so it either is loaded and can
371 		// scan for supported devices or its publish_devices hook will be
372 		// called to expose changed devices.
373 
374 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
375 		// the R5 way to republish a device in devfs
376 		int devFS = open("/dev", O_WRONLY);
377 		write(devFS, rescanItem->name, strlen(rescanItem->name));
378 		close(devFS);
379 #else
380 		// use the private devfs API under Haiku
381 		devfs_rescan_driver(rescanItem->name);
382 #endif
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(("USB Stack: 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(("USB Stack: 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(("USB Stack: 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