1 /* 2 * Copyright 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 "Driver.h" 11 #include "JoystickProtocolHandler.h" 12 13 #include "HIDCollection.h" 14 #include "HIDDevice.h" 15 #include "HIDReport.h" 16 #include "HIDReportItem.h" 17 18 #include <new> 19 #include <string.h> 20 #include <usb/USB_hid.h> 21 22 #include <kernel.h> 23 #include <util/AutoLock.h> 24 25 26 JoystickProtocolHandler::JoystickProtocolHandler(HIDReport &report) 27 : 28 ProtocolHandler(report.Device(), "joystick/" DEVICE_PATH_SUFFIX "/", 0), 29 fReport(report), 30 fAxisCount(0), 31 fAxis(NULL), 32 fHatCount(0), 33 fHats(NULL), 34 fButtonCount(0), 35 fMaxButton(0), 36 fButtons(NULL), 37 fOpenCount(0), 38 fUpdateThread(-1) 39 { 40 mutex_init(&fUpdateLock, "joystick update lock"); 41 memset(&fJoystickModuleInfo, 0, sizeof(joystick_module_info)); 42 memset(&fCurrentValues, 0, sizeof(variable_joystick)); 43 44 for (uint32 i = 0; i < report.CountItems(); i++) { 45 HIDReportItem *item = report.ItemAt(i); 46 if (!item->HasData()) 47 continue; 48 49 switch (item->UsagePage()) { 50 case B_HID_USAGE_PAGE_BUTTON: 51 { 52 if (item->UsageID() > INT16_MAX) 53 break; 54 55 HIDReportItem **newButtons = (HIDReportItem **)realloc(fButtons, 56 ++fButtonCount * sizeof(HIDReportItem *)); 57 if (newButtons == NULL) { 58 fButtonCount--; 59 break; 60 } 61 62 fButtons = newButtons; 63 fButtons[fButtonCount - 1] = item; 64 65 if (fMaxButton < item->UsageID()) 66 fMaxButton = item->UsageID(); 67 break; 68 } 69 70 case B_HID_USAGE_PAGE_GENERIC_DESKTOP: 71 { 72 if (item->UsageID() == B_HID_UID_GD_HAT_SWITCH) { 73 HIDReportItem **newHats = (HIDReportItem **)realloc(fHats, 74 ++fHatCount * sizeof(HIDReportItem *)); 75 if (newHats == NULL) { 76 fHatCount--; 77 break; 78 } 79 80 fHats = newHats; 81 fHats[fHatCount - 1] = item; 82 break; 83 } 84 85 // TODO: "axis" is set but not used! 86 // uint16 axis = 0; 87 if (item->UsageID() >= B_HID_UID_GD_X 88 && item->UsageID() <= B_HID_UID_GD_WHEEL) { 89 // axis = item->UsageID() - B_HID_UID_GD_X; 90 } else if (item->UsageID() >= B_HID_UID_GD_VX 91 && item->UsageID() <= B_HID_UID_GD_VNO) { 92 // axis = item->UsageID() - B_HID_UID_GD_VX; 93 } else 94 break; 95 96 HIDReportItem **newAxis = (HIDReportItem **)realloc(fAxis, 97 ++fAxisCount * sizeof(HIDReportItem *)); 98 if (newAxis == NULL) { 99 fAxisCount--; 100 break; 101 } 102 103 fAxis = newAxis; 104 fAxis[fAxisCount - 1] = item; 105 break; 106 } 107 } 108 } 109 110 111 fCurrentValues.initialize(fAxisCount, fHatCount, fMaxButton); 112 113 TRACE("joystick device with %" B_PRIu32 " buttons, %" B_PRIu32 114 " axes and %" B_PRIu32 " hats\n", 115 fButtonCount, fAxisCount, fHatCount); 116 TRACE("report id: %u\n", report.ID()); 117 } 118 119 120 JoystickProtocolHandler::~JoystickProtocolHandler() 121 { 122 free(fCurrentValues.data); 123 free(fAxis); 124 free(fHats); 125 free(fButtons); 126 } 127 128 129 void 130 JoystickProtocolHandler::AddHandlers(HIDDevice &device, 131 HIDCollection &collection, ProtocolHandler *&handlerList) 132 { 133 if (collection.UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP 134 || (collection.UsageID() != B_HID_UID_GD_JOYSTICK 135 && collection.UsageID() != B_HID_UID_GD_GAMEPAD 136 && collection.UsageID() != B_HID_UID_GD_MULTIAXIS)) { 137 TRACE("collection not a joystick or gamepad\n"); 138 return; 139 } 140 141 HIDParser &parser = device.Parser(); 142 uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT); 143 if (maxReportCount == 0) 144 return; 145 146 uint32 inputReportCount = 0; 147 HIDReport *inputReports[maxReportCount]; 148 collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports, 149 inputReportCount); 150 151 for (uint32 i = 0; i < inputReportCount; i++) { 152 HIDReport *inputReport = inputReports[i]; 153 154 // try to find at least one axis 155 bool foundAxis = false; 156 for (uint32 j = 0; j < inputReport->CountItems(); j++) { 157 HIDReportItem *item = inputReport->ItemAt(j); 158 if (item == NULL || !item->HasData()) 159 continue; 160 161 if (item->UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP) 162 continue; 163 164 if (item->UsageID() >= B_HID_UID_GD_X 165 && item->UsageID() <= B_HID_UID_GD_RZ) { 166 foundAxis = true; 167 break; 168 } 169 } 170 171 if (!foundAxis) 172 continue; 173 174 ProtocolHandler *newHandler 175 = new(std::nothrow) JoystickProtocolHandler(*inputReport); 176 if (newHandler == NULL) { 177 TRACE("failed to allocated joystick protocol handler\n"); 178 continue; 179 } 180 181 newHandler->SetNextHandler(handlerList); 182 handlerList = newHandler; 183 } 184 } 185 186 187 status_t 188 JoystickProtocolHandler::Open(uint32 flags, uint32 *cookie) 189 { 190 if (fCurrentValues.data == NULL) 191 return B_NO_INIT; 192 193 status_t result = mutex_lock(&fUpdateLock); 194 if (result != B_OK) 195 return result; 196 197 if (fUpdateThread < 0) { 198 fUpdateThread = spawn_kernel_thread(_UpdateThread, "joystick update", 199 B_NORMAL_PRIORITY, (void *)this); 200 201 if (fUpdateThread < 0) 202 result = fUpdateThread; 203 else 204 resume_thread(fUpdateThread); 205 } 206 207 if (result == B_OK) 208 fOpenCount++; 209 210 mutex_unlock(&fUpdateLock); 211 if (result != B_OK) 212 return result; 213 214 return ProtocolHandler::Open(flags, cookie); 215 } 216 217 218 status_t 219 JoystickProtocolHandler::Close(uint32 *cookie) 220 { 221 status_t result = mutex_lock(&fUpdateLock); 222 if (result == B_OK) { 223 if (--fOpenCount == 0) 224 fUpdateThread = -1; 225 mutex_unlock(&fUpdateLock); 226 } 227 228 return ProtocolHandler::Close(cookie); 229 } 230 231 232 233 status_t 234 JoystickProtocolHandler::Read(uint32 *cookie, off_t position, void *buffer, 235 size_t *numBytes) 236 { 237 if (*numBytes < fCurrentValues.data_size) 238 return B_BUFFER_OVERFLOW; 239 240 // this is a polling interface, we just return the current value 241 MutexLocker locker(fUpdateLock); 242 if (!locker.IsLocked()) { 243 *numBytes = 0; 244 return B_ERROR; 245 } 246 247 if (!IS_USER_ADDRESS(buffer) || user_memcpy(buffer, fCurrentValues.data, 248 fCurrentValues.data_size) != B_OK) 249 return B_BAD_ADDRESS; 250 251 *numBytes = fCurrentValues.data_size; 252 return B_OK; 253 } 254 255 256 status_t 257 JoystickProtocolHandler::Write(uint32 *cookie, off_t position, 258 const void *buffer, size_t *numBytes) 259 { 260 *numBytes = 0; 261 return B_NOT_SUPPORTED; 262 } 263 264 265 status_t 266 JoystickProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer, 267 size_t length) 268 { 269 switch (op) { 270 case B_GET_DEVICE_NAME: 271 { 272 const char name[] = DEVICE_NAME" Joystick"; 273 return IOGetDeviceName(name, buffer, length); 274 } 275 276 case B_JOYSTICK_SET_DEVICE_MODULE: 277 { 278 if (length < sizeof(joystick_module_info)) 279 return B_BAD_VALUE; 280 281 status_t result = mutex_lock(&fUpdateLock); 282 if (result != B_OK) 283 return result; 284 285 if (!IS_USER_ADDRESS(buffer) 286 || user_memcpy(&fJoystickModuleInfo, buffer, 287 sizeof(joystick_module_info)) != B_OK) { 288 return B_BAD_ADDRESS; 289 } 290 291 bool supportsVariable = (fJoystickModuleInfo.flags 292 & js_flag_variable_size_reads) != 0; 293 if (!supportsVariable) { 294 // We revert to a structure that we can support using only 295 // the data available in an extended_joystick structure. 296 free(fCurrentValues.data); 297 fCurrentValues.initialize_to_extended_joystick(); 298 if (fAxisCount > MAX_AXES) 299 fAxisCount = MAX_AXES; 300 if (fHatCount > MAX_HATS) 301 fHatCount = MAX_HATS; 302 if (fMaxButton > MAX_BUTTONS) 303 fMaxButton = MAX_BUTTONS; 304 305 TRACE_ALWAYS("using joystick in extended_joystick mode\n"); 306 } else { 307 TRACE_ALWAYS("using joystick in variable mode\n"); 308 } 309 310 fJoystickModuleInfo.num_axes = fAxisCount; 311 fJoystickModuleInfo.num_buttons = fMaxButton; 312 fJoystickModuleInfo.num_hats = fHatCount; 313 fJoystickModuleInfo.num_sticks = 1; 314 fJoystickModuleInfo.config_size = 0; 315 mutex_unlock(&fUpdateLock); 316 return B_OK; 317 } 318 319 case B_JOYSTICK_GET_DEVICE_MODULE: 320 if (length < sizeof(joystick_module_info)) 321 return B_BAD_VALUE; 322 323 if (!IS_USER_ADDRESS(buffer) 324 || user_memcpy(buffer, &fJoystickModuleInfo, 325 sizeof(joystick_module_info)) != B_OK) { 326 return B_BAD_ADDRESS; 327 } 328 return B_OK; 329 } 330 331 return B_ERROR; 332 } 333 334 335 int32 336 JoystickProtocolHandler::_UpdateThread(void *data) 337 { 338 JoystickProtocolHandler *handler = (JoystickProtocolHandler *)data; 339 while (handler->fUpdateThread == find_thread(NULL)) { 340 status_t result = handler->_Update(); 341 if (result != B_OK) 342 return result; 343 } 344 345 return B_OK; 346 } 347 348 349 status_t 350 JoystickProtocolHandler::_Update() 351 { 352 status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT); 353 if (result != B_OK) { 354 if (fReport.Device()->IsRemoved()) { 355 TRACE("device has been removed\n"); 356 return B_DEV_NOT_READY; 357 } 358 359 if (result == B_CANCELED) 360 return B_CANCELED; 361 362 if (result != B_INTERRUPTED) { 363 // interrupts happen when other reports come in on the same 364 // input as ours 365 TRACE_ALWAYS("error waiting for report: %s\n", strerror(result)); 366 } 367 368 // signal that we simply want to try again 369 return B_OK; 370 } 371 372 result = mutex_lock(&fUpdateLock); 373 if (result != B_OK) { 374 fReport.DoneProcessing(); 375 return result; 376 } 377 378 memset(fCurrentValues.data, 0, fCurrentValues.data_size); 379 380 for (uint32 i = 0; i < fAxisCount; i++) { 381 if (fAxis[i] == NULL) 382 continue; 383 384 if (fAxis[i]->Extract() == B_OK && fAxis[i]->Valid()) 385 fCurrentValues.axes[i] = (int16)fAxis[i]->ScaledData(16, true); 386 } 387 388 for (uint32 i = 0; i < fHatCount; i++) { 389 HIDReportItem *hat = fHats[i]; 390 if (hat == NULL) 391 continue; 392 393 if (hat->Extract() != B_OK || !hat->Valid()) 394 continue; 395 396 fCurrentValues.hats[i] = hat->ScaledRangeData(1, 8); 397 } 398 399 for (uint32 i = 0; i < fButtonCount; i++) { 400 HIDReportItem *button = fButtons[i]; 401 if (button == NULL) 402 break; 403 404 uint16 index = button->UsageID() - 1; 405 if (index >= fMaxButton) 406 continue; 407 408 if (button->Extract() == B_OK && button->Valid()) { 409 fCurrentValues.buttons[index / 32] 410 |= (button->Data() & 1) << (index % 32); 411 } 412 } 413 414 fReport.DoneProcessing(); 415 TRACE("got joystick report\n"); 416 417 *fCurrentValues.timestamp = system_time(); 418 mutex_unlock(&fUpdateLock); 419 return B_OK; 420 } 421