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