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