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