1/*
2 * Copyright 2008-2011 Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
4 */
5
6
7//!	Driver for USB Human Interface Devices.
8
9
10#include "DeviceList.h"
11#include "Driver.h"
12#include "HIDDevice.h"
13#include "ProtocolHandler.h"
14#include "QuirkyDevices.h"
15
16#include <lock.h>
17#include <util/AutoLock.h>
18
19#include <new>
20#include <stdio.h>
21#include <string.h>
22
23
24struct device_cookie {
25	ProtocolHandler*	handler;
26	uint32				cookie;
27};
28
29
30int32 api_version = B_CUR_DRIVER_API_VERSION;
31usb_module_info *gUSBModule = NULL;
32DeviceList *gDeviceList = NULL;
33static int32 sParentCookie = 0;
34static mutex sDriverLock;
35static usb_support_descriptor *sSupportDescriptors;
36
37
38// #pragma mark - notify hooks
39
40
41status_t
42usb_hid_device_added(usb_device device, void **cookie)
43{
44	TRACE("device_added()\n");
45	const usb_device_descriptor *deviceDescriptor
46		= gUSBModule->get_device_descriptor(device);
47
48	TRACE("vendor id: 0x%04x; product id: 0x%04x\n",
49		deviceDescriptor->vendor_id, deviceDescriptor->product_id);
50
51	// wacom devices are handled by the dedicated wacom driver
52	if (deviceDescriptor->vendor_id == USB_VENDOR_WACOM)
53		return B_ERROR;
54
55	const usb_configuration_info *config
56		= gUSBModule->get_nth_configuration(device, USB_DEFAULT_CONFIGURATION);
57	if (config == NULL) {
58		TRACE_ALWAYS("cannot get default configuration\n");
59		return B_ERROR;
60	}
61
62	// ensure default configuration is set
63	status_t result = gUSBModule->set_configuration(device, config);
64	if (result != B_OK) {
65		TRACE_ALWAYS("set_configuration() failed 0x%08" B_PRIx32 "\n", result);
66		return result;
67	}
68
69	// refresh config
70	config = gUSBModule->get_configuration(device);
71	if (config == NULL) {
72		TRACE_ALWAYS("cannot get current configuration\n");
73		return B_ERROR;
74	}
75
76	bool devicesFound = false;
77	int32 parentCookie = atomic_add(&sParentCookie, 1);
78	for (size_t i = 0; i < config->interface_count; i++) {
79		const usb_interface_info *interface = config->interface[i].active;
80		uint8 interfaceClass = interface->descr->interface_class;
81		TRACE("interface %" B_PRIuSIZE ": class: %u; subclass: %u; protocol: "
82			"%u\n",	i, interfaceClass, interface->descr->interface_subclass,
83			interface->descr->interface_protocol);
84
85		// check for quirky devices first
86		int32 quirkyIndex = -1;
87		for (int32 j = 0; j < gQuirkyDeviceCount; j++) {
88			usb_hid_quirky_device &quirky = gQuirkyDevices[j];
89			if ((quirky.vendor_id != 0
90					&& deviceDescriptor->vendor_id != quirky.vendor_id)
91				|| (quirky.product_id != 0
92					&& deviceDescriptor->product_id != quirky.product_id)
93				|| (quirky.device_class != 0
94					&& interfaceClass != quirky.device_class)
95				|| (quirky.device_subclass != 0
96					&& interface->descr->interface_subclass
97						!= quirky.device_subclass)
98				|| (quirky.device_protocol != 0
99					&& interface->descr->interface_protocol
100						!= quirky.device_protocol)) {
101				continue;
102			}
103
104			quirkyIndex = j;
105			break;
106		}
107
108		if (quirkyIndex >= 0 || interfaceClass == USB_INTERFACE_CLASS_HID) {
109			mutex_lock(&sDriverLock);
110			HIDDevice *hidDevice
111				= new(std::nothrow) HIDDevice(device, config, i, quirkyIndex);
112
113			if (hidDevice != NULL && hidDevice->InitCheck() == B_OK) {
114				hidDevice->SetParentCookie(parentCookie);
115
116				for (uint32 i = 0;; i++) {
117					ProtocolHandler *handler = hidDevice->ProtocolHandlerAt(i);
118					if (handler == NULL)
119						break;
120
121					// As devices can be un- and replugged at will, we cannot
122					// simply rely on a device count. If there is just one
123					// keyboard, this does not mean that it uses the 0 name.
124					// There might have been two keyboards and the one using 0
125					// might have been unplugged. So we just generate names
126					// until we find one that is not currently in use.
127					int32 index = 0;
128					char pathBuffer[128];
129					const char *basePath = handler->BasePath();
130					while (true) {
131						sprintf(pathBuffer, "%s%" B_PRId32, basePath, index++);
132						if (gDeviceList->FindDevice(pathBuffer) == NULL) {
133							// this name is still free, use it
134							handler->SetPublishPath(strdup(pathBuffer));
135							break;
136						}
137					}
138
139					gDeviceList->AddDevice(handler->PublishPath(), handler);
140					devicesFound = true;
141				}
142			} else
143				delete hidDevice;
144
145			mutex_unlock(&sDriverLock);
146		}
147	}
148
149	if (!devicesFound)
150		return B_ERROR;
151
152	*cookie = (void *)(addr_t)parentCookie;
153	return B_OK;
154}
155
156
157status_t
158usb_hid_device_removed(void *cookie)
159{
160	mutex_lock(&sDriverLock);
161	int32 parentCookie = (int32)(addr_t)cookie;
162	TRACE("device_removed(%" B_PRId32 ")\n", parentCookie);
163
164	for (int32 i = 0; i < gDeviceList->CountDevices(); i++) {
165		ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->DeviceAt(i);
166		if (!handler)
167			continue;
168
169		HIDDevice *device = handler->Device();
170		if (device->ParentCookie() != parentCookie)
171			continue;
172
173		// this handler's device belongs to the one removed
174		if (device->IsOpen()) {
175			// the device and it's handlers will be deleted in the free hook
176			device->Removed();
177			break;
178		}
179
180		// remove all the handlers
181		for (uint32 i = 0;; i++) {
182			handler = device->ProtocolHandlerAt(i);
183			if (handler == NULL)
184				break;
185
186			gDeviceList->RemoveDevice(NULL, handler);
187		}
188
189		delete device;
190		break;
191	}
192
193	mutex_unlock(&sDriverLock);
194	return B_OK;
195}
196
197
198// #pragma mark - driver hooks
199
200
201static status_t
202usb_hid_open(const char *name, uint32 flags, void **_cookie)
203{
204	TRACE("open(%s, %lu, %p)\n", name, flags, _cookie);
205
206	device_cookie *cookie = new(std::nothrow) device_cookie();
207	if (cookie == NULL)
208		return B_NO_MEMORY;
209
210	MutexLocker locker(sDriverLock);
211
212	ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->FindDevice(name);
213	TRACE("  name %s: handler %p\n", name, handler);
214
215	cookie->handler = handler;
216	cookie->cookie = 0;
217
218	status_t result = handler == NULL ? B_ENTRY_NOT_FOUND : B_OK;
219	if (result == B_OK)
220		result = handler->Open(flags, &cookie->cookie);
221
222	if (result != B_OK) {
223		delete cookie;
224		return result;
225	}
226
227	*_cookie = cookie;
228
229	return B_OK;
230}
231
232
233static status_t
234usb_hid_read(void *_cookie, off_t position, void *buffer, size_t *numBytes)
235{
236	device_cookie *cookie = (device_cookie *)_cookie;
237
238	TRACE("read(%p, %llu, %p, %p (%lu)\n", cookie, position, buffer, numBytes,
239		numBytes != NULL ? *numBytes : 0);
240	return cookie->handler->Read(&cookie->cookie, position, buffer, numBytes);
241}
242
243
244static status_t
245usb_hid_write(void *_cookie, off_t position, const void *buffer,
246	size_t *numBytes)
247{
248	device_cookie *cookie = (device_cookie *)_cookie;
249
250	TRACE("write(%p, %llu, %p, %p (%lu)\n", cookie, position, buffer, numBytes,
251		numBytes != NULL ? *numBytes : 0);
252	return cookie->handler->Write(&cookie->cookie, position, buffer, numBytes);
253}
254
255
256static status_t
257usb_hid_control(void *_cookie, uint32 op, void *buffer, size_t length)
258{
259	device_cookie *cookie = (device_cookie *)_cookie;
260
261	TRACE("control(%p, %lu, %p, %lu)\n", cookie, op, buffer, length);
262	return cookie->handler->Control(&cookie->cookie, op, buffer, length);
263}
264
265
266static status_t
267usb_hid_close(void *_cookie)
268{
269	device_cookie *cookie = (device_cookie *)_cookie;
270
271	TRACE("close(%p)\n", cookie);
272	return cookie->handler->Close(&cookie->cookie);
273}
274
275
276static status_t
277usb_hid_free(void *_cookie)
278{
279	device_cookie *cookie = (device_cookie *)_cookie;
280	TRACE("free(%p)\n", cookie);
281
282	mutex_lock(&sDriverLock);
283
284	HIDDevice *device = cookie->handler->Device();
285	if (device->IsOpen()) {
286		// another handler of this device is still open so we can't free it
287	} else if (device->IsRemoved()) {
288		// the parent device is removed already and none of its handlers are
289		// open anymore so we can free it here
290		for (uint32 i = 0;; i++) {
291			ProtocolHandler *handler = device->ProtocolHandlerAt(i);
292			if (handler == NULL)
293				break;
294
295			gDeviceList->RemoveDevice(NULL, handler);
296		}
297
298		delete device;
299	}
300
301	mutex_unlock(&sDriverLock);
302
303	delete cookie;
304	return B_OK;
305}
306
307
308//	#pragma mark - driver API
309
310
311status_t
312init_hardware()
313{
314	TRACE("init_hardware() " __DATE__ " " __TIME__ "\n");
315	return B_OK;
316}
317
318
319status_t
320init_driver()
321{
322	TRACE("init_driver() " __DATE__ " " __TIME__ "\n");
323	if (get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule) != B_OK)
324		return B_ERROR;
325
326	gDeviceList = new(std::nothrow) DeviceList();
327	if (gDeviceList == NULL) {
328		put_module(B_USB_MODULE_NAME);
329		return B_NO_MEMORY;
330	}
331
332	mutex_init(&sDriverLock, "usb hid driver lock");
333
334	static usb_notify_hooks notifyHooks = {
335		&usb_hid_device_added,
336		&usb_hid_device_removed
337	};
338
339	static usb_support_descriptor genericHIDSupportDescriptor = {
340		USB_INTERFACE_CLASS_HID, 0, 0, 0, 0
341	};
342
343	int32 supportDescriptorCount = 1 + gQuirkyDeviceCount;
344	sSupportDescriptors
345		= new(std::nothrow) usb_support_descriptor[supportDescriptorCount];
346	if (sSupportDescriptors != NULL) {
347		sSupportDescriptors[0] = genericHIDSupportDescriptor;
348		for (int32 i = 0; i < gQuirkyDeviceCount; i++) {
349			usb_support_descriptor &descriptor = sSupportDescriptors[i + 1];
350			descriptor.dev_class = gQuirkyDevices[i].device_class;
351			descriptor.dev_subclass = gQuirkyDevices[i].device_subclass;
352			descriptor.dev_protocol = gQuirkyDevices[i].device_protocol;
353			descriptor.vendor = gQuirkyDevices[i].vendor_id;
354			descriptor.product = gQuirkyDevices[i].product_id;
355		}
356
357		gUSBModule->register_driver(DRIVER_NAME, sSupportDescriptors,
358			supportDescriptorCount, NULL);
359	} else {
360		// no memory for quirky devices, at least support proper HID
361		gUSBModule->register_driver(DRIVER_NAME, &genericHIDSupportDescriptor,
362			1, NULL);
363	}
364
365	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
366	TRACE("init_driver() OK\n");
367	return B_OK;
368}
369
370
371void
372uninit_driver()
373{
374	TRACE("uninit_driver()\n");
375	gUSBModule->uninstall_notify(DRIVER_NAME);
376	put_module(B_USB_MODULE_NAME);
377	delete[] sSupportDescriptors;
378	sSupportDescriptors = NULL;
379	delete gDeviceList;
380	gDeviceList = NULL;
381	mutex_destroy(&sDriverLock);
382}
383
384
385const char **
386publish_devices()
387{
388	TRACE("publish_devices()\n");
389	const char **publishList = gDeviceList->PublishDevices();
390
391	int32 index = 0;
392	while (publishList[index] != NULL) {
393		TRACE("publishing %s\n", publishList[index]);
394		index++;
395	}
396
397	return publishList;
398}
399
400
401device_hooks *
402find_device(const char *name)
403{
404	static device_hooks hooks = {
405		usb_hid_open,
406		usb_hid_close,
407		usb_hid_free,
408		usb_hid_control,
409		usb_hid_read,
410		usb_hid_write,
411		NULL,				/* select */
412		NULL				/* deselect */
413	};
414
415	TRACE("find_device(%s)\n", name);
416	if (gDeviceList->FindDevice(name) == NULL) {
417		TRACE_ALWAYS("didn't find device %s\n", name);
418		return NULL;
419	}
420
421	return &hooks;
422}
423