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