BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / input / usb_hid / Driver.cpp
blobf194056bf37cc316db1cf95a1cc7dd470e6f6867
1 /*
2 * Copyright 2008-2011 Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
4 */
7 //! Driver for USB Human Interface Devices.
10 #include "DeviceList.h"
11 #include "Driver.h"
12 #include "HIDDevice.h"
13 #include "ProtocolHandler.h"
14 #include "QuirkyDevices.h"
16 #include <lock.h>
17 #include <util/AutoLock.h>
19 #include <new>
20 #include <stdio.h>
21 #include <string.h>
24 struct device_cookie {
25 ProtocolHandler* handler;
26 uint32 cookie;
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;
38 // #pragma mark - notify hooks
41 status_t
42 usb_hid_device_added(usb_device device, void **cookie)
44 TRACE("device_added()\n");
45 const usb_device_descriptor *deviceDescriptor
46 = gUSBModule->get_device_descriptor(device);
48 TRACE("vendor id: 0x%04x; product id: 0x%04x\n",
49 deviceDescriptor->vendor_id, deviceDescriptor->product_id);
51 // wacom devices are handled by the dedicated wacom driver
52 if (deviceDescriptor->vendor_id == USB_VENDOR_WACOM)
53 return B_ERROR;
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;
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;
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;
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);
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;
104 quirkyIndex = j;
105 break;
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);
113 if (hidDevice != NULL && hidDevice->InitCheck() == B_OK) {
114 hidDevice->SetParentCookie(parentCookie);
116 for (uint32 i = 0;; i++) {
117 ProtocolHandler *handler = hidDevice->ProtocolHandlerAt(i);
118 if (handler == NULL)
119 break;
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;
139 gDeviceList->AddDevice(handler->PublishPath(), handler);
140 devicesFound = true;
142 } else
143 delete hidDevice;
145 mutex_unlock(&sDriverLock);
149 if (!devicesFound)
150 return B_ERROR;
152 *cookie = (void *)(addr_t)parentCookie;
153 return B_OK;
157 status_t
158 usb_hid_device_removed(void *cookie)
160 mutex_lock(&sDriverLock);
161 int32 parentCookie = (int32)(addr_t)cookie;
162 TRACE("device_removed(%" B_PRId32 ")\n", parentCookie);
164 for (int32 i = 0; i < gDeviceList->CountDevices(); i++) {
165 ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->DeviceAt(i);
166 if (!handler)
167 continue;
169 HIDDevice *device = handler->Device();
170 if (device->ParentCookie() != parentCookie)
171 continue;
173 // remove all the handlers
174 for (uint32 i = 0;; i++) {
175 handler = device->ProtocolHandlerAt(i);
176 if (handler == NULL)
177 break;
179 gDeviceList->RemoveDevice(NULL, handler);
182 // this handler's device belongs to the one removed
183 if (device->IsOpen()) {
184 // the device and it's handlers will be deleted in the free hook
185 device->Removed();
186 } else
187 delete device;
189 break;
192 mutex_unlock(&sDriverLock);
193 return B_OK;
197 // #pragma mark - driver hooks
200 static status_t
201 usb_hid_open(const char *name, uint32 flags, void **_cookie)
203 TRACE("open(%s, %lu, %p)\n", name, flags, _cookie);
205 device_cookie *cookie = new(std::nothrow) device_cookie();
206 if (cookie == NULL)
207 return B_NO_MEMORY;
209 MutexLocker locker(sDriverLock);
211 ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->FindDevice(name);
212 TRACE(" name %s: handler %p\n", name, handler);
214 cookie->handler = handler;
215 cookie->cookie = 0;
217 status_t result = handler == NULL ? B_ENTRY_NOT_FOUND : B_OK;
218 if (result == B_OK)
219 result = handler->Open(flags, &cookie->cookie);
221 if (result != B_OK) {
222 delete cookie;
223 return result;
226 *_cookie = cookie;
228 return B_OK;
232 static status_t
233 usb_hid_read(void *_cookie, off_t position, void *buffer, size_t *numBytes)
235 device_cookie *cookie = (device_cookie *)_cookie;
237 TRACE("read(%p, %llu, %p, %p (%lu)\n", cookie, position, buffer, numBytes,
238 numBytes != NULL ? *numBytes : 0);
239 return cookie->handler->Read(&cookie->cookie, position, buffer, numBytes);
243 static status_t
244 usb_hid_write(void *_cookie, off_t position, const void *buffer,
245 size_t *numBytes)
247 device_cookie *cookie = (device_cookie *)_cookie;
249 TRACE("write(%p, %llu, %p, %p (%lu)\n", cookie, position, buffer, numBytes,
250 numBytes != NULL ? *numBytes : 0);
251 return cookie->handler->Write(&cookie->cookie, position, buffer, numBytes);
255 static status_t
256 usb_hid_control(void *_cookie, uint32 op, void *buffer, size_t length)
258 device_cookie *cookie = (device_cookie *)_cookie;
260 TRACE("control(%p, %lu, %p, %lu)\n", cookie, op, buffer, length);
261 return cookie->handler->Control(&cookie->cookie, op, buffer, length);
265 static status_t
266 usb_hid_close(void *_cookie)
268 device_cookie *cookie = (device_cookie *)_cookie;
270 TRACE("close(%p)\n", cookie);
271 return cookie->handler->Close(&cookie->cookie);
275 static status_t
276 usb_hid_free(void *_cookie)
278 device_cookie *cookie = (device_cookie *)_cookie;
279 TRACE("free(%p)\n", cookie);
281 mutex_lock(&sDriverLock);
283 HIDDevice *device = cookie->handler->Device();
284 if (device->IsOpen()) {
285 // another handler of this device is still open so we can't free it
286 } else if (device->IsRemoved()) {
287 // the parent device is removed already and none of its handlers are
288 // open anymore so we can free it here
289 delete device;
292 mutex_unlock(&sDriverLock);
294 delete cookie;
295 return B_OK;
299 // #pragma mark - driver API
302 status_t
303 init_hardware()
305 TRACE("init_hardware() " __DATE__ " " __TIME__ "\n");
306 return B_OK;
310 status_t
311 init_driver()
313 TRACE("init_driver() " __DATE__ " " __TIME__ "\n");
314 if (get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule) != B_OK)
315 return B_ERROR;
317 gDeviceList = new(std::nothrow) DeviceList();
318 if (gDeviceList == NULL) {
319 put_module(B_USB_MODULE_NAME);
320 return B_NO_MEMORY;
323 mutex_init(&sDriverLock, "usb hid driver lock");
325 static usb_notify_hooks notifyHooks = {
326 &usb_hid_device_added,
327 &usb_hid_device_removed
330 static usb_support_descriptor genericHIDSupportDescriptor = {
331 USB_INTERFACE_CLASS_HID, 0, 0, 0, 0
334 int32 supportDescriptorCount = 1 + gQuirkyDeviceCount;
335 sSupportDescriptors
336 = new(std::nothrow) usb_support_descriptor[supportDescriptorCount];
337 if (sSupportDescriptors != NULL) {
338 sSupportDescriptors[0] = genericHIDSupportDescriptor;
339 for (int32 i = 0; i < gQuirkyDeviceCount; i++) {
340 usb_support_descriptor &descriptor = sSupportDescriptors[i + 1];
341 descriptor.dev_class = gQuirkyDevices[i].device_class;
342 descriptor.dev_subclass = gQuirkyDevices[i].device_subclass;
343 descriptor.dev_protocol = gQuirkyDevices[i].device_protocol;
344 descriptor.vendor = gQuirkyDevices[i].vendor_id;
345 descriptor.product = gQuirkyDevices[i].product_id;
348 gUSBModule->register_driver(DRIVER_NAME, sSupportDescriptors,
349 supportDescriptorCount, NULL);
350 } else {
351 // no memory for quirky devices, at least support proper HID
352 gUSBModule->register_driver(DRIVER_NAME, &genericHIDSupportDescriptor,
353 1, NULL);
356 gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
357 TRACE("init_driver() OK\n");
358 return B_OK;
362 void
363 uninit_driver()
365 TRACE("uninit_driver()\n");
366 gUSBModule->uninstall_notify(DRIVER_NAME);
367 put_module(B_USB_MODULE_NAME);
368 delete[] sSupportDescriptors;
369 sSupportDescriptors = NULL;
370 delete gDeviceList;
371 gDeviceList = NULL;
372 mutex_destroy(&sDriverLock);
376 const char **
377 publish_devices()
379 TRACE("publish_devices()\n");
380 const char **publishList = gDeviceList->PublishDevices();
382 int32 index = 0;
383 while (publishList[index] != NULL) {
384 TRACE("publishing %s\n", publishList[index]);
385 index++;
388 return publishList;
392 device_hooks *
393 find_device(const char *name)
395 static device_hooks hooks = {
396 usb_hid_open,
397 usb_hid_close,
398 usb_hid_free,
399 usb_hid_control,
400 usb_hid_read,
401 usb_hid_write,
402 NULL, /* select */
403 NULL /* deselect */
406 TRACE("find_device(%s)\n", name);
407 if (gDeviceList->FindDevice(name) == NULL) {
408 TRACE_ALWAYS("didn't find device %s\n", name);
409 return NULL;
412 return &hooks;