1 /* 2 * Copyright 2008-2009 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 15 #include <lock.h> 16 #include <util/AutoLock.h> 17 18 #include <new> 19 #include <stdio.h> 20 #include <string.h> 21 22 23 struct device_cookie { 24 ProtocolHandler* handler; 25 uint32 cookie; 26 }; 27 28 29 int32 api_version = B_CUR_DRIVER_API_VERSION; 30 usb_module_info *gUSBModule = NULL; 31 DeviceList *gDeviceList = NULL; 32 static int32 sParentCookie = 0; 33 static mutex sDriverLock; 34 35 36 // #pragma mark - notify hooks 37 38 39 status_t 40 usb_hid_device_added(usb_device device, void **cookie) 41 { 42 TRACE("device_added()\n"); 43 const usb_device_descriptor *deviceDescriptor 44 = gUSBModule->get_device_descriptor(device); 45 46 TRACE("vendor id: 0x%04x; product id: 0x%04x\n", 47 deviceDescriptor->vendor_id, deviceDescriptor->product_id); 48 49 // wacom devices are handled by the dedicated wacom driver 50 if (deviceDescriptor->vendor_id == USB_VENDOR_WACOM) 51 return B_ERROR; 52 53 const usb_configuration_info *config 54 = gUSBModule->get_nth_configuration(device, USB_DEFAULT_CONFIGURATION); 55 if (config == NULL) { 56 TRACE_ALWAYS("cannot get default configuration\n"); 57 return B_ERROR; 58 } 59 60 // ensure default configuration is set 61 status_t result = gUSBModule->set_configuration(device, config); 62 if (result != B_OK) { 63 TRACE_ALWAYS("set_configuration() failed 0x%08lx\n", result); 64 return result; 65 } 66 67 // refresh config 68 config = gUSBModule->get_configuration(device); 69 if (config == NULL) { 70 TRACE_ALWAYS("cannot get current configuration\n"); 71 return B_ERROR; 72 } 73 74 bool devicesFound = false; 75 int32 parentCookie = atomic_add(&sParentCookie, 1); 76 for (size_t i = 0; i < config->interface_count; i++) { 77 const usb_interface_info *interface = config->interface[i].active; 78 uint8 interfaceClass = interface->descr->interface_class; 79 TRACE("interface %lu: class: %u; subclass: %u; protocol: %u\n", 80 i, interfaceClass, interface->descr->interface_subclass, 81 interface->descr->interface_protocol); 82 83 if (interfaceClass == USB_INTERFACE_CLASS_HID) { 84 mutex_lock(&sDriverLock); 85 HIDDevice *hidDevice 86 = new(std::nothrow) HIDDevice(device, config, i); 87 88 if (hidDevice != NULL && hidDevice->InitCheck() == B_OK) { 89 hidDevice->SetParentCookie(parentCookie); 90 91 for (uint32 i = 0;; i++) { 92 ProtocolHandler *handler = hidDevice->ProtocolHandlerAt(i); 93 if (handler == NULL) 94 break; 95 96 // As devices can be un- and replugged at will, we cannot 97 // simply rely on a device count. If there is just one 98 // keyboard, this does not mean that it uses the 0 name. 99 // There might have been two keyboards and the one using 0 100 // might have been unplugged. So we just generate names 101 // until we find one that is not currently in use. 102 int32 index = 0; 103 char pathBuffer[128]; 104 const char *basePath = handler->BasePath(); 105 while (true) { 106 sprintf(pathBuffer, "%s%ld", basePath, index++); 107 if (gDeviceList->FindDevice(pathBuffer) == NULL) { 108 // this name is still free, use it 109 handler->SetPublishPath(strdup(pathBuffer)); 110 break; 111 } 112 } 113 114 gDeviceList->AddDevice(handler->PublishPath(), handler); 115 devicesFound = true; 116 } 117 } else 118 delete hidDevice; 119 120 mutex_unlock(&sDriverLock); 121 } 122 } 123 124 if (!devicesFound) 125 return B_ERROR; 126 127 *cookie = (void *)parentCookie; 128 return B_OK; 129 } 130 131 132 status_t 133 usb_hid_device_removed(void *cookie) 134 { 135 mutex_lock(&sDriverLock); 136 int32 parentCookie = (int32)cookie; 137 TRACE("device_removed(%ld)\n", parentCookie); 138 139 for (int32 i = 0; i < gDeviceList->CountDevices(); i++) { 140 ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->DeviceAt(i); 141 if (!handler) 142 continue; 143 144 HIDDevice *device = handler->Device(); 145 if (device->ParentCookie() != parentCookie) 146 continue; 147 148 // this handler's device belongs to the one removed 149 if (device->IsOpen()) { 150 // the device and it's handlers will be deleted in the free hook 151 device->Removed(); 152 break; 153 } 154 155 // remove all the handlers 156 for (uint32 i = 0;; i++) { 157 handler = device->ProtocolHandlerAt(i); 158 if (handler == NULL) 159 break; 160 161 gDeviceList->RemoveDevice(NULL, handler); 162 } 163 164 delete device; 165 break; 166 } 167 168 mutex_unlock(&sDriverLock); 169 return B_OK; 170 } 171 172 173 // #pragma mark - driver hooks 174 175 176 static status_t 177 usb_hid_open(const char *name, uint32 flags, void **_cookie) 178 { 179 TRACE("open(%s, %lu, %p)\n", name, flags, _cookie); 180 181 device_cookie *cookie = new(std::nothrow) device_cookie(); 182 if (cookie == NULL) 183 return B_NO_MEMORY; 184 185 MutexLocker locker(sDriverLock); 186 187 ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->FindDevice(name); 188 TRACE(" name %s: handler %p\n", name, handler); 189 190 cookie->handler = handler; 191 cookie->cookie = 0; 192 193 status_t result = handler == NULL ? B_ENTRY_NOT_FOUND : B_OK; 194 if (result == B_OK) 195 result = handler->Open(flags, &cookie->cookie); 196 197 if (result != B_OK) { 198 delete cookie; 199 return result; 200 } 201 202 *_cookie = cookie; 203 204 return B_OK; 205 } 206 207 208 static status_t 209 usb_hid_read(void *cookie, off_t position, void *buffer, size_t *numBytes) 210 { 211 TRACE_ALWAYS("read on hid device\n"); 212 *numBytes = 0; 213 return B_ERROR; 214 } 215 216 217 static status_t 218 usb_hid_write(void *cookie, off_t position, const void *buffer, 219 size_t *numBytes) 220 { 221 TRACE_ALWAYS("write on hid device\n"); 222 *numBytes = 0; 223 return B_ERROR; 224 } 225 226 227 static status_t 228 usb_hid_control(void *_cookie, uint32 op, void *buffer, size_t length) 229 { 230 device_cookie *cookie = (device_cookie *)_cookie; 231 232 TRACE("control(%p, %lu, %p, %lu)\n", cookie, op, buffer, length); 233 return cookie->handler->Control(&cookie->cookie, op, buffer, length); 234 } 235 236 237 static status_t 238 usb_hid_close(void *_cookie) 239 { 240 device_cookie *cookie = (device_cookie *)_cookie; 241 242 TRACE("close(%p)\n", cookie); 243 return cookie->handler->Close(&cookie->cookie); 244 } 245 246 247 static status_t 248 usb_hid_free(void *_cookie) 249 { 250 device_cookie *cookie = (device_cookie *)_cookie; 251 TRACE("free(%p)\n", cookie); 252 253 mutex_lock(&sDriverLock); 254 255 HIDDevice *device = cookie->handler->Device(); 256 if (device->IsOpen()) { 257 // another handler of this device is still open so we can't free it 258 } else if (device->IsRemoved()) { 259 // the parent device is removed already and none of its handlers are 260 // open anymore so we can free it here 261 for (uint32 i = 0;; i++) { 262 ProtocolHandler *handler = device->ProtocolHandlerAt(i); 263 if (handler == NULL) 264 break; 265 266 gDeviceList->RemoveDevice(NULL, handler); 267 } 268 269 delete device; 270 } 271 272 mutex_unlock(&sDriverLock); 273 274 delete cookie; 275 return B_OK; 276 } 277 278 279 // #pragma mark - driver API 280 281 282 status_t 283 init_hardware() 284 { 285 TRACE("init_hardware() " __DATE__ " " __TIME__ "\n"); 286 return B_OK; 287 } 288 289 290 status_t 291 init_driver() 292 { 293 TRACE("init_driver() " __DATE__ " " __TIME__ "\n"); 294 if (get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule) != B_OK) 295 return B_ERROR; 296 297 gDeviceList = new(std::nothrow) DeviceList(); 298 if (gDeviceList == NULL) { 299 put_module(B_USB_MODULE_NAME); 300 return B_NO_MEMORY; 301 } 302 303 mutex_init(&sDriverLock, "usb hid driver lock"); 304 305 static usb_notify_hooks notifyHooks = { 306 &usb_hid_device_added, 307 &usb_hid_device_removed 308 }; 309 310 static usb_support_descriptor supportDescriptor = { 311 USB_INTERFACE_CLASS_HID, 0, 0, 0, 0 312 }; 313 314 gUSBModule->register_driver(DRIVER_NAME, &supportDescriptor, 1, NULL); 315 gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks); 316 TRACE("init_driver() OK\n"); 317 return B_OK; 318 } 319 320 321 void 322 uninit_driver() 323 { 324 TRACE("uninit_driver()\n"); 325 gUSBModule->uninstall_notify(DRIVER_NAME); 326 put_module(B_USB_MODULE_NAME); 327 delete gDeviceList; 328 gDeviceList = NULL; 329 mutex_destroy(&sDriverLock); 330 } 331 332 333 const char ** 334 publish_devices() 335 { 336 TRACE("publish_devices()\n"); 337 const char **publishList = gDeviceList->PublishDevices(); 338 339 int32 index = 0; 340 while (publishList[index] != NULL) { 341 TRACE("publishing %s\n", publishList[index]); 342 index++; 343 } 344 345 return publishList; 346 } 347 348 349 device_hooks * 350 find_device(const char *name) 351 { 352 static device_hooks hooks = { 353 usb_hid_open, 354 usb_hid_close, 355 usb_hid_free, 356 usb_hid_control, 357 usb_hid_read, 358 usb_hid_write, 359 NULL, /* select */ 360 NULL /* deselect */ 361 }; 362 363 TRACE("find_device(%s)\n", name); 364 if (gDeviceList->FindDevice(name) == NULL) { 365 TRACE_ALWAYS("didn't find device %s\n", name); 366 return NULL; 367 } 368 369 return &hooks; 370 } 371