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
20Stack::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
104Stack::~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
126status_t
127Stack::InitCheck()
128{
129	if (fBusManagers.Count() == 0)
130		return ENODEV;
131	return B_OK;
132}
133
134
135bool
136Stack::Lock()
137{
138	return (mutex_lock(&fStackLock) == B_OK);
139}
140
141
142void
143Stack::Unlock()
144{
145	mutex_unlock(&fStackLock);
146}
147
148
149usb_id
150Stack::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
174void
175Stack::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
191Object *
192Stack::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
210Object *
211Stack::GetObjectNoLock(usb_id id) const
212{
213	if (id >= fObjectMaxCount)
214		return NULL;
215	return fObjectArray[id];
216}
217
218
219int32
220Stack::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
259void
260Stack::AddBusManager(BusManager *busManager)
261{
262	fBusManagers.PushBack(busManager);
263}
264
265
266int32
267Stack::IndexOfBusManager(BusManager *busManager)
268{
269	return fBusManagers.IndexOf(busManager);
270}
271
272
273BusManager *
274Stack::BusManagerAt(int32 index) const
275{
276	return fBusManagers.ElementAt(index);
277}
278
279
280status_t
281Stack::AllocateChunk(void **logicalAddress, phys_addr_t *physicalAddress,
282	size_t size)
283{
284	return fAllocator->Allocate(size, logicalAddress, physicalAddress);
285}
286
287
288status_t
289Stack::FreeChunk(void *logicalAddress, phys_addr_t physicalAddress,
290	size_t size)
291{
292	return fAllocator->Deallocate(size, logicalAddress, physicalAddress);
293}
294
295
296area_id
297Stack::AllocateArea(void **logicalAddress, phys_addr_t *physicalAddress, size_t size,
298	const char *name)
299{
300	TRACE("allocating %ld bytes for %s\n", size, name);
301
302	void *logAddress;
303	size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
304	area_id area = create_area(name, &logAddress, B_ANY_KERNEL_ADDRESS, size,
305		B_32_BIT_CONTIGUOUS, 0);
306		// TODO: Use B_CONTIGUOUS when the TODOs regarding 64 bit physical
307		// addresses are fixed (if possible).
308
309	if (area < B_OK) {
310		TRACE_ERROR("couldn't allocate area %s\n", name);
311		return B_ERROR;
312	}
313
314	physical_entry physicalEntry;
315	status_t result = get_memory_map(logAddress, size, &physicalEntry, 1);
316	if (result < B_OK) {
317		delete_area(area);
318		TRACE_ERROR("couldn't map area %s\n", name);
319		return B_ERROR;
320	}
321
322	memset(logAddress, 0, size);
323	if (logicalAddress)
324		*logicalAddress = logAddress;
325
326	if (physicalAddress)
327		*physicalAddress = (phys_addr_t)physicalEntry.address;
328
329	TRACE("area = %" B_PRId32 ", size = %" B_PRIuSIZE ", log = %p, phy = %#"
330		B_PRIxPHYSADDR "\n", area, size, logAddress, physicalEntry.address);
331	return area;
332}
333
334
335void
336Stack::NotifyDeviceChange(Device *device, rescan_item **rescanList, bool added)
337{
338	TRACE("device %s\n", added ? "added" : "removed");
339
340	usb_driver_info *element = fDriverList;
341	while (element) {
342		status_t result = device->ReportDevice(element->support_descriptors,
343			element->support_descriptor_count, &element->notify_hooks,
344			&element->cookies, added, false);
345
346		if (result >= B_OK) {
347			const char *driverName = element->driver_name;
348			if (element->republish_driver_name)
349				driverName = element->republish_driver_name;
350
351			bool already = false;
352			rescan_item *rescanItem = *rescanList;
353			while (rescanItem) {
354				if (strcmp(rescanItem->name, driverName) == 0) {
355					// this driver is going to be rescanned already
356					already = true;
357					break;
358				}
359				rescanItem = rescanItem->link;
360			}
361
362			if (!already) {
363				rescanItem = new(std::nothrow) rescan_item;
364				if (!rescanItem)
365					return;
366
367				rescanItem->name = driverName;
368				rescanItem->link = *rescanList;
369				*rescanList = rescanItem;
370			}
371		}
372
373		element = element->link;
374	}
375}
376
377
378void
379Stack::RescanDrivers(rescan_item *rescanItem)
380{
381	while (rescanItem) {
382		// the device is supported by this driver. it either got notified
383		// already by the hooks or it is not loaded at this time. in any
384		// case we will rescan the driver so it either is loaded and can
385		// scan for supported devices or its publish_devices hook will be
386		// called to expose changed devices.
387
388#ifndef HAIKU_TARGET_PLATFORM_HAIKU
389		// the R5 way to republish a device in devfs
390		int devFS = open("/dev", O_WRONLY);
391		write(devFS, rescanItem->name, strlen(rescanItem->name));
392		close(devFS);
393#else
394		// use the private devfs API under Haiku
395		devfs_rescan_driver(rescanItem->name);
396#endif
397
398		rescan_item *next = rescanItem->link;
399		delete rescanItem;
400		rescanItem = next;
401	}
402}
403
404
405status_t
406Stack::RegisterDriver(const char *driverName,
407	const usb_support_descriptor *descriptors,
408	size_t descriptorCount, const char *republishDriverName)
409{
410	TRACE("register driver \"%s\"\n", driverName);
411	if (!driverName)
412		return B_BAD_VALUE;
413
414	if (!Lock())
415		return B_ERROR;
416
417	usb_driver_info *element = fDriverList;
418	while (element) {
419		if (strcmp(element->driver_name, driverName) == 0) {
420			// we already have an entry for this driver, just update it
421			free((char *)element->republish_driver_name);
422			element->republish_driver_name = strdup(republishDriverName);
423
424			free(element->support_descriptors);
425			size_t descriptorsSize = descriptorCount * sizeof(usb_support_descriptor);
426			element->support_descriptors = (usb_support_descriptor *)malloc(descriptorsSize);
427			memcpy(element->support_descriptors, descriptors, descriptorsSize);
428			element->support_descriptor_count = descriptorCount;
429
430			Unlock();
431			return B_OK;
432		}
433
434		element = element->link;
435	}
436
437	// this is a new driver, add it to the driver list
438	usb_driver_info *info = new(std::nothrow) usb_driver_info;
439	if (!info) {
440		Unlock();
441		return B_NO_MEMORY;
442	}
443
444	info->driver_name = strdup(driverName);
445	info->republish_driver_name = strdup(republishDriverName);
446
447	size_t descriptorsSize = descriptorCount * sizeof(usb_support_descriptor);
448	info->support_descriptors = (usb_support_descriptor *)malloc(descriptorsSize);
449	memcpy(info->support_descriptors, descriptors, descriptorsSize);
450	info->support_descriptor_count = descriptorCount;
451
452	info->notify_hooks.device_added = NULL;
453	info->notify_hooks.device_removed = NULL;
454	info->cookies = NULL;
455	info->link = NULL;
456
457	if (fDriverList) {
458		usb_driver_info *element = fDriverList;
459		while (element->link)
460			element = element->link;
461
462		element->link = info;
463	} else
464		fDriverList = info;
465
466	Unlock();
467	return B_OK;
468}
469
470
471status_t
472Stack::InstallNotify(const char *driverName, const usb_notify_hooks *hooks)
473{
474	TRACE("installing notify hooks for driver \"%s\"\n", driverName);
475
476	usb_driver_info *element = fDriverList;
477	while (element) {
478		if (strcmp(element->driver_name, driverName) == 0) {
479			if (mutex_lock(&fExploreLock) != B_OK)
480				return B_ERROR;
481
482			// inform driver about any already present devices
483			for (int32 i = 0; i < fBusManagers.Count(); i++) {
484				Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
485				if (rootHub) {
486					// Report device will recurse down the whole tree
487					rootHub->ReportDevice(element->support_descriptors,
488						element->support_descriptor_count, hooks,
489						&element->cookies, true, true);
490				}
491			}
492
493			element->notify_hooks.device_added = hooks->device_added;
494			element->notify_hooks.device_removed = hooks->device_removed;
495			mutex_unlock(&fExploreLock);
496			return B_OK;
497		}
498
499		element = element->link;
500	}
501
502	return B_NAME_NOT_FOUND;
503}
504
505
506status_t
507Stack::UninstallNotify(const char *driverName)
508{
509	TRACE("uninstalling notify hooks for driver \"%s\"\n", driverName);
510
511	usb_driver_info *element = fDriverList;
512	while (element) {
513		if (strcmp(element->driver_name, driverName) == 0) {
514			if (mutex_lock(&fExploreLock) != B_OK)
515				return B_ERROR;
516
517			// trigger the device removed hook
518			for (int32 i = 0; i < fBusManagers.Count(); i++) {
519				Hub *rootHub = fBusManagers.ElementAt(i)->GetRootHub();
520				if (rootHub)
521					rootHub->ReportDevice(element->support_descriptors,
522						element->support_descriptor_count,
523						&element->notify_hooks, &element->cookies, false, true);
524			}
525
526			element->notify_hooks.device_added = NULL;
527			element->notify_hooks.device_removed = NULL;
528			mutex_unlock(&fExploreLock);
529			return B_OK;
530		}
531
532		element = element->link;
533	}
534
535	return B_NAME_NOT_FOUND;
536}
537