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_JOYSTICK_SET_DEVICE_MODULE: 271 { 272 if (length < sizeof(joystick_module_info)) 273 return B_BAD_VALUE; 274 275 status_t result = mutex_lock(&fUpdateLock); 276 if (result != B_OK) 277 return result; 278 279 if (!IS_USER_ADDRESS(buffer) 280 || user_memcpy(&fJoystickModuleInfo, buffer, 281 sizeof(joystick_module_info)) != B_OK) { 282 return B_BAD_ADDRESS; 283 } 284 285 bool supportsVariable = (fJoystickModuleInfo.flags 286 & js_flag_variable_size_reads) != 0; 287 if (!supportsVariable) { 288 // We revert to a structure that we can support using only 289 // the data available in an extended_joystick structure. 290 free(fCurrentValues.data); 291 fCurrentValues.initialize_to_extended_joystick(); 292 if (fAxisCount > MAX_AXES) 293 fAxisCount = MAX_AXES; 294 if (fHatCount > MAX_HATS) 295 fHatCount = MAX_HATS; 296 if (fMaxButton > MAX_BUTTONS) 297 fMaxButton = MAX_BUTTONS; 298 299 TRACE_ALWAYS("using joystick in extended_joystick mode\n"); 300 } else { 301 TRACE_ALWAYS("using joystick in variable mode\n"); 302 } 303 304 fJoystickModuleInfo.num_axes = fAxisCount; 305 fJoystickModuleInfo.num_buttons = fMaxButton; 306 fJoystickModuleInfo.num_hats = fHatCount; 307 fJoystickModuleInfo.num_sticks = 1; 308 fJoystickModuleInfo.config_size = 0; 309 mutex_unlock(&fUpdateLock); 310 return B_OK; 311 } 312 313 case B_JOYSTICK_GET_DEVICE_MODULE: 314 if (length < sizeof(joystick_module_info)) 315 return B_BAD_VALUE; 316 317 if (!IS_USER_ADDRESS(buffer) 318 || user_memcpy(buffer, &fJoystickModuleInfo, 319 sizeof(joystick_module_info)) != B_OK) { 320 return B_BAD_ADDRESS; 321 } 322 return B_OK; 323 } 324 325 return B_ERROR; 326 } 327 328 329 int32 330 JoystickProtocolHandler::_UpdateThread(void *data) 331 { 332 JoystickProtocolHandler *handler = (JoystickProtocolHandler *)data; 333 while (handler->fUpdateThread == find_thread(NULL)) { 334 status_t result = handler->_Update(); 335 if (result != B_OK) 336 return result; 337 } 338 339 return B_OK; 340 } 341 342 343 status_t 344 JoystickProtocolHandler::_Update() 345 { 346 status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT); 347 if (result != B_OK) { 348 if (fReport.Device()->IsRemoved()) { 349 TRACE("device has been removed\n"); 350 return B_DEV_NOT_READY; 351 } 352 353 if (result == B_CANCELED) 354 return B_CANCELED; 355 356 if (result != B_INTERRUPTED) { 357 // interrupts happen when other reports come in on the same 358 // input as ours 359 TRACE_ALWAYS("error waiting for report: %s\n", strerror(result)); 360 } 361 362 // signal that we simply want to try again 363 return B_OK; 364 } 365 366 result = mutex_lock(&fUpdateLock); 367 if (result != B_OK) { 368 fReport.DoneProcessing(); 369 return result; 370 } 371 372 memset(fCurrentValues.data, 0, fCurrentValues.data_size); 373 374 for (uint32 i = 0; i < fAxisCount; i++) { 375 if (fAxis[i] == NULL) 376 continue; 377 378 if (fAxis[i]->Extract() == B_OK && fAxis[i]->Valid()) 379 fCurrentValues.axes[i] = (int16)fAxis[i]->ScaledData(16, true); 380 } 381 382 for (uint32 i = 0; i < fHatCount; i++) { 383 HIDReportItem *hat = fHats[i]; 384 if (hat == NULL) 385 continue; 386 387 if (hat->Extract() != B_OK || !hat->Valid()) 388 continue; 389 390 fCurrentValues.hats[i] = hat->ScaledRangeData(1, 8); 391 } 392 393 for (uint32 i = 0; i < fButtonCount; i++) { 394 HIDReportItem *button = fButtons[i]; 395 if (button == NULL) 396 break; 397 398 uint16 index = button->UsageID() - 1; 399 if (index >= fMaxButton) 400 continue; 401 402 if (button->Extract() == B_OK && button->Valid()) { 403 fCurrentValues.buttons[index / 32] 404 |= (button->Data() & 1) << (index % 32); 405 } 406 } 407 408 fReport.DoneProcessing(); 409 TRACE("got joystick report\n"); 410 411 *fCurrentValues.timestamp = system_time(); 412 mutex_unlock(&fUpdateLock); 413 return B_OK; 414 } 415