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