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