xref: /haiku/src/add-ons/kernel/drivers/input/usb_hid/Driver.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
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 
24 struct device_cookie {
25 	ProtocolHandler*	handler;
26 	uint32				cookie;
27 };
28 
29 
30 int32 api_version = B_CUR_DRIVER_API_VERSION;
31 usb_module_info *gUSBModule = NULL;
32 DeviceList *gDeviceList = NULL;
33 static int32 sParentCookie = 0;
34 static mutex sDriverLock;
35 static usb_support_descriptor *sSupportDescriptors;
36 
37 
38 usb_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 
54 int32 gBlackListedDeviceCount
55 	= sizeof(gBlackListedDevices) / sizeof(gBlackListedDevices[0]);
56 
57 
58 // #pragma mark - notify hooks
59 
60 
61 status_t
62 usb_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 
188 status_t
189 usb_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 
232 static status_t
233 usb_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 
264 static status_t
265 usb_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 
275 static status_t
276 usb_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 
287 static status_t
288 usb_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 
298 static status_t
299 usb_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 
308 static status_t
309 usb_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 
335 status_t
336 init_hardware()
337 {
338 	TRACE("init_hardware() " __DATE__ " " __TIME__ "\n");
339 	return B_OK;
340 }
341 
342 
343 status_t
344 init_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 
395 void
396 uninit_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 
409 const char **
410 publish_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 
425 device_hooks *
426 find_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