void JoystickProtocolHandler::AddHandlers(HIDDevice &device, HIDCollection &collection, ProtocolHandler *&handlerList) { if (collection.UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP || (collection.UsageID() != B_HID_UID_GD_JOYSTICK && collection.UsageID() != B_HID_UID_GD_GAMEPAD && collection.UsageID() != B_HID_UID_GD_MULTIAXIS)) { TRACE("collection not a joystick or gamepad\n"); return; } HIDParser &parser = device.Parser(); uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT); if (maxReportCount == 0) return; uint32 inputReportCount = 0; HIDReport *inputReports[maxReportCount]; collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports, inputReportCount); for (uint32 i = 0; i < inputReportCount; i++) { HIDReport *inputReport = inputReports[i]; // try to find at least one axis bool foundAxis = false; for (uint32 j = 0; j < inputReport->CountItems(); j++) { HIDReportItem *item = inputReport->ItemAt(j); if (item == NULL || !item->HasData()) continue; if (item->UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP) continue; if (item->UsageID() >= B_HID_UID_GD_X && item->UsageID() <= B_HID_UID_GD_RZ) { foundAxis = true; break; } } if (!foundAxis) continue; ProtocolHandler *newHandler = new(std::nothrow) JoystickProtocolHandler(*inputReport); if (newHandler == NULL) { TRACE("failed to allocated joystick protocol handler\n"); continue; } newHandler->SetNextHandler(handlerList); handlerList = newHandler; } }
HIDReport * HIDParser::ReportAt(uint8 type, uint8 index) { for (uint8 i = 0; i < fReportCount; i++) { HIDReport *report = fReports[i]; if (report == NULL || (report->Type() & type) == 0) continue; if (index-- == 0) return report; } return NULL; }
HIDReport * HIDParser::FindReport(uint8 type, uint8 id) { for (uint8 i = 0; i < fReportCount; i++) { HIDReport *report = fReports[i]; if (report == NULL) continue; if ((report->Type() & type) != 0 && report->ID() == id) return report; } return NULL; }
uint8 HIDParser::CountReports(uint8 type) { uint8 count = 0; for (uint8 i = 0; i < fReportCount; i++) { HIDReport *report = fReports[i]; if (report == NULL) continue; if (report->Type() & type) count++; } return count; }
size_t HIDParser::MaxReportSize() { size_t maxSize = 0; for (uint32 i = 0; i < fReportCount; i++) { HIDReport *report = fReports[i]; if (report == NULL) continue; if (report->ReportSize() > maxSize) maxSize = report->ReportSize(); } if (fUsesReportIDs) maxSize++; return maxSize; }
JoystickProtocolHandler::JoystickProtocolHandler(HIDReport &report) : ProtocolHandler(report.Device(), "joystick/usb/", 0), fReport(report), fAxisCount(0), fAxis(NULL), fHatCount(0), fHats(NULL), fButtonCount(0), fMaxButton(0), fButtons(NULL), fOpenCount(0), fUpdateThread(-1) { mutex_init(&fUpdateLock, "joystick update lock"); memset(&fJoystickModuleInfo, 0, sizeof(joystick_module_info)); memset(&fCurrentValues, 0, sizeof(variable_joystick)); for (uint32 i = 0; i < report.CountItems(); i++) { HIDReportItem *item = report.ItemAt(i); if (!item->HasData()) continue; switch (item->UsagePage()) { case B_HID_USAGE_PAGE_BUTTON: { if (item->UsageID() > INT16_MAX) break; HIDReportItem **newButtons = (HIDReportItem **)realloc(fButtons, ++fButtonCount * sizeof(HIDReportItem *)); if (newButtons == NULL) { fButtonCount--; break; } fButtons = newButtons; fButtons[fButtonCount - 1] = item; if (fMaxButton < item->UsageID()) fMaxButton = item->UsageID(); break; } case B_HID_USAGE_PAGE_GENERIC_DESKTOP: { if (item->UsageID() == B_HID_UID_GD_HAT_SWITCH) { HIDReportItem **newHats = (HIDReportItem **)realloc(fHats, ++fHatCount * sizeof(HIDReportItem *)); if (newHats == NULL) { fHatCount--; break; } fHats = newHats; fHats[fHatCount - 1] = item; break; } // TODO: "axis" is set but not used! // uint16 axis = 0; if (item->UsageID() >= B_HID_UID_GD_X && item->UsageID() <= B_HID_UID_GD_WHEEL) { // axis = item->UsageID() - B_HID_UID_GD_X; } else if (item->UsageID() >= B_HID_UID_GD_VX && item->UsageID() <= B_HID_UID_GD_VNO) { // axis = item->UsageID() - B_HID_UID_GD_VX; } else break; HIDReportItem **newAxis = (HIDReportItem **)realloc(fAxis, ++fAxisCount * sizeof(HIDReportItem *)); if (newAxis == NULL) { fAxisCount--; break; } fAxis = newAxis; fAxis[fAxisCount - 1] = item; break; } } } fCurrentValues.initialize(fAxisCount, fHatCount, fMaxButton); TRACE("joystick device with %lu buttons, %lu axes and %lu hats\n", fButtonCount, fAxisCount, fHatCount); TRACE("report id: %u\n", report.ID()); }
status_t HIDParser::ParseReportDescriptor(const uint8 *reportDescriptor, size_t descriptorLength) { _Reset(); global_item_state globalState; memset(&globalState, 0, sizeof(global_item_state)); local_item_state localState; memset(&localState, 0, sizeof(local_item_state)); uint32 usageStackUsed = 0; uint32 usageStackSize = 10; usage_value *usageStack = (usage_value *)malloc(usageStackSize * sizeof(usage_value)); if (usageStack == NULL) { TRACE_ALWAYS("no memory to allocate usage stack\n"); return B_NO_MEMORY; } HIDCollection *collection = NULL; const uint8 *pointer = reportDescriptor; const uint8 *end = pointer + descriptorLength; while (pointer < end) { const item_prefix *item = (item_prefix *)pointer; size_t itemSize = sItemSize[item->size]; uint32 data = 0; if (item->type == ITEM_TYPE_LONG) { long_item *longItem = (long_item *)item; itemSize += longItem->data_size; } else { short_item *shortItem = (short_item *)item; switch (itemSize) { case 1: data = shortItem->data.as_uint8[0]; break; case 2: data = shortItem->data.as_uint16[0]; break; case 4: data = shortItem->data.as_uint32; break; default: break; } } TRACE("got item: type: %s; size: %lu; tag: %u; data: %lu\n", item->type == ITEM_TYPE_MAIN ? "main" : item->type == ITEM_TYPE_GLOBAL ? "global" : item->type == ITEM_TYPE_LOCAL ? "local" : "long", itemSize, item->tag, data); switch (item->type) { case ITEM_TYPE_MAIN: { main_item_data *mainData = (main_item_data *)&data; if (item->tag == ITEM_TAG_MAIN_COLLECTION) { HIDCollection *newCollection = new(std::nothrow) HIDCollection(collection, (uint8)data, globalState, localState); if (newCollection == NULL) { TRACE_ALWAYS("no memory to allocate new collection\n"); break; } if (collection == NULL) fRootCollection = newCollection; else collection->AddChild(newCollection); collection = newCollection; } else if (item->tag == ITEM_TAG_MAIN_END_COLLECTION) { if (collection == NULL) { TRACE_ALWAYS("end collection with no open one\n"); break; } collection = collection->Parent(); } else { uint8 reportType = HID_REPORT_TYPE_ANY; switch (item->tag) { case ITEM_TAG_MAIN_INPUT: reportType = HID_REPORT_TYPE_INPUT; break; case ITEM_TAG_MAIN_OUTPUT: reportType = HID_REPORT_TYPE_OUTPUT; break; case ITEM_TAG_MAIN_FEATURE: reportType = HID_REPORT_TYPE_FEATURE; break; default: TRACE_ALWAYS("unknown main item tag: 0x%02x\n", item->tag); break; } if (reportType == HID_REPORT_TYPE_ANY) break; HIDReport *target = _FindOrCreateReport(reportType, globalState.report_id); if (target == NULL) break; // make all usages extended for easier later processing for (uint32 i = 0; i < usageStackUsed; i++) { if (usageStack[i].is_extended) continue; usageStack[i].u.s.usage_page = globalState.usage_page; usageStack[i].is_extended = true; } if (!localState.usage_minimum.is_extended) { // the specs say if one of them is extended they must // both be extended, so if the minimum isn't, the // maximum mustn't either. localState.usage_minimum.u.s.usage_page = localState.usage_maximum.u.s.usage_page = globalState.usage_page; localState.usage_minimum.is_extended = localState.usage_maximum.is_extended = true; } localState.usage_stack = usageStack; localState.usage_stack_used = usageStackUsed; // fill in a sensible default if the index isn't set if (!localState.designator_index_set) { localState.designator_index = localState.designator_minimum; } if (!localState.string_index_set) localState.string_index = localState.string_minimum; target->AddMainItem(globalState, localState, *mainData, collection); } // reset the local item state memset(&localState, 0, sizeof(local_item_state)); usageStackUsed = 0; break; } case ITEM_TYPE_GLOBAL: { switch (item->tag) { case ITEM_TAG_GLOBAL_USAGE_PAGE: globalState.usage_page = data; break; case ITEM_TAG_GLOBAL_LOGICAL_MINIMUM: globalState.logical_minimum = data; break; case ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM: globalState.logical_maximum = data; break; case ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM: globalState.physical_minimum = data; break; case ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM: globalState.physical_maximum = data; break; case ITEM_TAG_GLOBAL_UNIT_EXPONENT: globalState.unit_exponent = data; break; case ITEM_TAG_GLOBAL_UNIT: globalState.unit = data; break; case ITEM_TAG_GLOBAL_REPORT_SIZE: globalState.report_size = data; break; case ITEM_TAG_GLOBAL_REPORT_ID: globalState.report_id = data; fUsesReportIDs = true; break; case ITEM_TAG_GLOBAL_REPORT_COUNT: globalState.report_count = data; break; case ITEM_TAG_GLOBAL_PUSH: { global_item_state *copy = (global_item_state *)malloc( sizeof(global_item_state)); if (copy == NULL) { TRACE_ALWAYS("out of memory for global push\n"); break; } memcpy(copy, &globalState, sizeof(global_item_state)); globalState.link = copy; break; } case ITEM_TAG_GLOBAL_POP: { if (globalState.link == NULL) { TRACE_ALWAYS("global pop without item on stack\n"); break; } global_item_state *link = globalState.link; memcpy(&globalState, link, sizeof(global_item_state)); free(link); break; } default: TRACE_ALWAYS("unknown global item tag: 0x%02x\n", item->tag); break; } break; } case ITEM_TYPE_LOCAL: { switch (item->tag) { case ITEM_TAG_LOCAL_USAGE: { if (usageStackUsed >= usageStackSize) { usageStackSize += 10; usage_value *newUsageStack = (usage_value *)realloc(usageStack, usageStackSize * sizeof(usage_value)); if (newUsageStack == NULL) { TRACE_ALWAYS("no memory when growing usages\n"); usageStackSize -= 10; break; } usageStack = newUsageStack; } usage_value *value = &usageStack[usageStackUsed]; value->is_extended = itemSize == sizeof(uint32); value->u.extended = data; usageStackUsed++; break; } case ITEM_TAG_LOCAL_USAGE_MINIMUM: localState.usage_minimum.u.extended = data; localState.usage_minimum.is_extended = itemSize == sizeof(uint32); localState.usage_minimum_set = true; break; case ITEM_TAG_LOCAL_USAGE_MAXIMUM: localState.usage_maximum.u.extended = data; localState.usage_maximum.is_extended = itemSize == sizeof(uint32); localState.usage_maximum_set = true; break; case ITEM_TAG_LOCAL_DESIGNATOR_INDEX: localState.designator_index = data; localState.designator_index_set = true; break; case ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM: localState.designator_minimum = data; break; case ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM: localState.designator_maximum = data; break; case ITEM_TAG_LOCAL_STRING_INDEX: localState.string_index = data; localState.string_index_set = true; break; case ITEM_TAG_LOCAL_STRING_MINIMUM: localState.string_minimum = data; break; case ITEM_TAG_LOCAL_STRING_MAXIMUM: localState.string_maximum = data; break; default: TRACE_ALWAYS("unknown local item tag: 0x%02x\n", item->tag); break; } break; } case ITEM_TAG_LONG: { long_item *longItem = (long_item *)item; // no long items are defined yet switch (longItem->long_item_tag) { default: TRACE_ALWAYS("unknown long item tag: 0x%02x\n", longItem->long_item_tag); break; } break; } } pointer += itemSize + sizeof(item_prefix); } global_item_state *state = globalState.link; while (state != NULL) { global_item_state *next = state->link; free(state); state = next; } free(usageStack); return B_OK; }
void TabletProtocolHandler::AddHandlers(HIDDevice &device, HIDCollection &collection, ProtocolHandler *&handlerList) { bool supported = false; switch (collection.UsagePage()) { case B_HID_USAGE_PAGE_GENERIC_DESKTOP: { switch (collection.UsageID()) { case B_HID_UID_GD_MOUSE: case B_HID_UID_GD_POINTER: // NOTE: Maybe it is supported if X-axis and Y-axis are // absolute. This is determined below by scanning the // report items for absolute X and Y axis. supported = true; break; } break; } case B_HID_USAGE_PAGE_DIGITIZER: { switch (collection.UsageID()) { case B_HID_UID_DIG_DIGITIZER: case B_HID_UID_DIG_PEN: case B_HID_UID_DIG_LIGHT_PEN: case B_HID_UID_DIG_TOUCH_SCREEN: case B_HID_UID_DIG_TOUCH_PAD: case B_HID_UID_DIG_WHITE_BOARD: TRACE("found tablet/digitizer\n"); supported = true; break; } break; } } if (!supported) { TRACE("collection not a tablet/digitizer\n"); return; } HIDParser &parser = device.Parser(); uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT); if (maxReportCount == 0) return; uint32 inputReportCount = 0; HIDReport *inputReports[maxReportCount]; collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports, inputReportCount); for (uint32 i = 0; i < inputReportCount; i++) { HIDReport *inputReport = inputReports[i]; // try to find at least an absolute x and y axis HIDReportItem *xAxis = inputReport->FindItem( B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_X); if (xAxis == NULL || xAxis->Relative()) continue; HIDReportItem *yAxis = inputReport->FindItem( B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_Y); if (yAxis == NULL || yAxis->Relative()) continue; ProtocolHandler *newHandler = new(std::nothrow) TabletProtocolHandler( *inputReport, *xAxis, *yAxis); if (newHandler == NULL) { TRACE("failed to allocated tablet protocol handler\n"); continue; } newHandler->SetNextHandler(handlerList); handlerList = newHandler; } }
TabletProtocolHandler::TabletProtocolHandler(HIDReport &report, HIDReportItem &xAxis, HIDReportItem &yAxis) : ProtocolHandler(report.Device(), "input/tablet/usb", 0), fReport(report), fXAxis(xAxis), fYAxis(yAxis), fWheel(NULL), fPressure(NULL), fRange(NULL), fTip(NULL), fBarrelSwitch(NULL), fEraser(NULL), fXTilt(NULL), fYTilt(NULL), fLastButtons(0), fClickCount(0), fLastClickTime(0), fClickSpeed(250000) { uint32 buttonCount = 0; for (uint32 i = 0; i < report.CountItems(); i++) { HIDReportItem *item = report.ItemAt(i); if (!item->HasData()) continue; if (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON && item->UsageID() - 1 < B_MAX_MOUSE_BUTTONS) { fButtons[buttonCount++] = item; } } fButtons[buttonCount] = NULL; fWheel = report.FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_WHEEL); fPressure = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, B_HID_UID_DIG_TIP_PRESSURE); fRange = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, B_HID_UID_DIG_IN_RANGE); fTip = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, B_HID_UID_DIG_TIP_SWITCH); fBarrelSwitch = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, B_HID_UID_DIG_BARREL_SWITCH); fEraser = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, B_HID_UID_DIG_ERASER); fXTilt = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, B_HID_UID_DIG_X_TILT); fYTilt = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, B_HID_UID_DIG_Y_TILT); TRACE("tablet device with %lu buttons, %stip, %seraser, " "%spressure, and %stilt\n", buttonCount, fTip == NULL ? "no " : "", fEraser == NULL ? "no " : "", fPressure == NULL ? "no " : "", fXTilt == NULL && fYTilt == NULL ? "no " : ""); TRACE("report id: %u\n", report.ID()); }