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