2 * Copyright 2008-2011 Michael Lotz <mmlr@mlotz.ch>
3 * Distributed under the terms of the MIT license.
7 //! Driver for USB Human Interface Devices.
10 #include "DeviceList.h"
12 #include "HIDDevice.h"
13 #include "ProtocolHandler.h"
14 #include "QuirkyDevices.h"
17 #include <util/AutoLock.h>
24 struct device_cookie
{
25 ProtocolHandler
* handler
;
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
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
)
55 const usb_configuration_info
*config
56 = gUSBModule
->get_nth_configuration(device
, USB_DEFAULT_CONFIGURATION
);
58 TRACE_ALWAYS("cannot get default configuration\n");
62 // ensure default configuration is set
63 status_t result
= gUSBModule
->set_configuration(device
, config
);
65 TRACE_ALWAYS("set_configuration() failed 0x%08" B_PRIx32
"\n", result
);
70 config
= gUSBModule
->get_configuration(device
);
72 TRACE_ALWAYS("cannot get current configuration\n");
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
)) {
108 if (quirkyIndex
>= 0 || interfaceClass
== USB_INTERFACE_CLASS_HID
) {
109 mutex_lock(&sDriverLock
);
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
);
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.
128 char pathBuffer
[128];
129 const char *basePath
= handler
->BasePath();
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
));
139 gDeviceList
->AddDevice(handler
->PublishPath(), handler
);
145 mutex_unlock(&sDriverLock
);
152 *cookie
= (void *)(addr_t
)parentCookie
;
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
);
169 HIDDevice
*device
= handler
->Device();
170 if (device
->ParentCookie() != parentCookie
)
173 // remove all the handlers
174 for (uint32 i
= 0;; i
++) {
175 handler
= device
->ProtocolHandlerAt(i
);
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
192 mutex_unlock(&sDriverLock
);
197 // #pragma mark - driver hooks
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();
209 MutexLocker
locker(sDriverLock
);
211 ProtocolHandler
*handler
= (ProtocolHandler
*)gDeviceList
->FindDevice(name
);
212 TRACE(" name %s: handler %p\n", name
, handler
);
214 cookie
->handler
= handler
;
217 status_t result
= handler
== NULL
? B_ENTRY_NOT_FOUND
: B_OK
;
219 result
= handler
->Open(flags
, &cookie
->cookie
);
221 if (result
!= B_OK
) {
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
);
244 usb_hid_write(void *_cookie
, off_t position
, const void *buffer
,
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
);
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
);
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
);
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
292 mutex_unlock(&sDriverLock
);
299 // #pragma mark - driver API
305 TRACE("init_hardware() " __DATE__
" " __TIME__
"\n");
313 TRACE("init_driver() " __DATE__
" " __TIME__
"\n");
314 if (get_module(B_USB_MODULE_NAME
, (module_info
**)&gUSBModule
) != B_OK
)
317 gDeviceList
= new(std::nothrow
) DeviceList();
318 if (gDeviceList
== NULL
) {
319 put_module(B_USB_MODULE_NAME
);
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
;
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
);
351 // no memory for quirky devices, at least support proper HID
352 gUSBModule
->register_driver(DRIVER_NAME
, &genericHIDSupportDescriptor
,
356 gUSBModule
->install_notify(DRIVER_NAME
, ¬ifyHooks
);
357 TRACE("init_driver() OK\n");
365 TRACE("uninit_driver()\n");
366 gUSBModule
->uninstall_notify(DRIVER_NAME
);
367 put_module(B_USB_MODULE_NAME
);
368 delete[] sSupportDescriptors
;
369 sSupportDescriptors
= NULL
;
372 mutex_destroy(&sDriverLock
);
379 TRACE("publish_devices()\n");
380 const char **publishList
= gDeviceList
->PublishDevices();
383 while (publishList
[index
] != NULL
) {
384 TRACE("publishing %s\n", publishList
[index
]);
393 find_device(const char *name
)
395 static device_hooks hooks
= {
406 TRACE("find_device(%s)\n", name
);
407 if (gDeviceList
->FindDevice(name
) == NULL
) {
408 TRACE_ALWAYS("didn't find device %s\n", name
);