diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp b/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp new file mode 100644 index 0000000000..123815bf72 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.cpp @@ -0,0 +1,74 @@ +#include "Driver.h" +#include "HIDCollection.h" + +#include +#include + + +HIDCollection::HIDCollection(HIDCollection *parent, uint8 type, + global_item_state &globalState, local_item_state &localState) + : fParent(parent), + fType(type), + fStringID(localState.string_index), + fPhysicalID(localState.designator_index), + fChildCount(0), + fChildren(NULL) +{ + usage_value usageValue; + usageValue.u.s.usage_page = globalState.usage_page; + if (localState.usage_stack != NULL && localState.usage_stack_used > 0) { + if (localState.usage_stack[0].is_extended) + usageValue.u.extended = localState.usage_stack[0].u.extended; + else + usageValue.u.s.usage_id = localState.usage_stack[0].u.s.usage_id; + } else if (localState.usage_minimum_set) { + if (localState.usage_minimum.is_extended) + usageValue.u.extended = localState.usage_minimum.u.extended; + else + usageValue.u.s.usage_id = localState.usage_minimum.u.s.usage_id; + } + + fUsage = usageValue.u.extended; +} + + +HIDCollection::~HIDCollection() +{ + for (uint32 i = 0; i < fChildCount; i++) + delete fChildren[i]; + free(fChildren); +} + + +status_t +HIDCollection::AddChild(HIDCollection *child) +{ + HIDCollection **newChildren = (HIDCollection **)realloc(fChildren, + (fChildCount + 1) * sizeof(HIDCollection *)); + if (newChildren == NULL) { + TRACE_ALWAYS("no memory when trying to resize collection child list\n"); + return B_NO_MEMORY; + } + + fChildren = newChildren; + fChildren[fChildCount++] = child; + return B_OK; +} + + +HIDCollection * +HIDCollection::ChildAt(uint32 index) +{ + if (index >= fChildCount) + return NULL; + + return fChildren[index]; +} + + +void +HIDCollection::AddMainItem(global_item_state &globalState, + local_item_state &localState, main_item_data &mainData) +{ + // TODO: implement +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h b/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h new file mode 100644 index 0000000000..d6c24ed5e0 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDCollection.h @@ -0,0 +1,35 @@ +#ifndef HID_COLLECTION_H +#define HID_COLLECTION_H + +#include "HIDParser.h" + +class HIDCollection { +public: + HIDCollection(HIDCollection *parent, + uint8 type, global_item_state &globalState, + local_item_state &localState); + ~HIDCollection(); + + HIDCollection * Parent() { return fParent; }; + + status_t AddChild(HIDCollection *child); + uint32 CountChildren() { return fChildCount; }; + HIDCollection * ChildAt(uint32 index); + + void AddMainItem(global_item_state &globalState, + local_item_state &localState, + main_item_data &mainData); + +private: + HIDCollection * fParent; + + uint8 fType; + uint32 fUsage; + uint8 fStringID; + uint8 fPhysicalID; + + uint32 fChildCount; + HIDCollection ** fChildren; +}; + +#endif // HID_COLLECTION_H diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h b/src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h new file mode 100644 index 0000000000..636e774c15 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDDataTypes.h @@ -0,0 +1,158 @@ +#ifndef HID_DATA_TYPES_H +#define HID_DATA_TYPES_H + +#include + +#define ITEM_TYPE_MAIN 0x0 +#define ITEM_TYPE_GLOBAL 0x1 +#define ITEM_TYPE_LOCAL 0x2 +#define ITEM_TYPE_LONG 0x3 + +#define ITEM_TAG_MAIN_INPUT 0x8 +#define ITEM_TAG_MAIN_OUTPUT 0x9 +#define ITEM_TAG_MAIN_FEATURE 0xb +#define ITEM_TAG_MAIN_COLLECTION 0xa +#define ITEM_TAG_MAIN_END_COLLECTION 0xc + +#define ITEM_TAG_GLOBAL_USAGE_PAGE 0x0 +#define ITEM_TAG_GLOBAL_LOGICAL_MINIMUM 0x1 +#define ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM 0x2 +#define ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM 0x3 +#define ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM 0x4 +#define ITEM_TAG_GLOBAL_UNIT_EXPONENT 0x5 +#define ITEM_TAG_GLOBAL_UNIT 0x6 +#define ITEM_TAG_GLOBAL_REPORT_SIZE 0x7 +#define ITEM_TAG_GLOBAL_REPORT_ID 0x8 +#define ITEM_TAG_GLOBAL_REPORT_COUNT 0x9 +#define ITEM_TAG_GLOBAL_PUSH 0xa +#define ITEM_TAG_GLOBAL_POP 0xb + +#define ITEM_TAG_LOCAL_USAGE 0x0 +#define ITEM_TAG_LOCAL_USAGE_MINIMUM 0x1 +#define ITEM_TAG_LOCAL_USAGE_MAXIMUM 0x2 +#define ITEM_TAG_LOCAL_DESIGNATOR_INDEX 0x3 +#define ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM 0x4 +#define ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM 0x5 +#define ITEM_TAG_LOCAL_STRING_INDEX 0x7 +#define ITEM_TAG_LOCAL_STRING_MINIMUM 0x8 +#define ITEM_TAG_LOCAL_STRING_MAXIMUM 0x9 +#define ITEM_TAG_LOCAL_DELIMITER 0xa + +#define ITEM_TAG_LONG 0xf + +#define COLLECTION_PHYSICAL 0x00 +#define COLLECTION_APPLICATION 0x01 +#define COLLECTION_LOGICAL 0x02 +#define COLLECTION_REPORT 0x03 +#define COLLECTION_NAMED_ARRAY 0x04 +#define COLLECTION_USAGE_SWITCH 0x05 +#define COLLECTION_USAGE_MODIFIER 0x06 + +#define UNIT_SYSTEM 0x0 +#define UNIT_LENGTH 0x1 +#define UNIT_MASS 0x2 +#define UNIT_TIME 0x3 +#define UNIT_TEMPERATURE 0x4 +#define UNIT_CURRENT 0x5 +#define UNIT_LUMINOUS_INTENSITY 0x6 + +#define USAGE_PAGE_SHIFT 16 +#define USAGE_PAGE_MASK 0xffff +#define USAGE_ID_SHIFT 0 +#define USAGE_ID_MASK 0xffff + + +typedef struct item_prefix { + LBITFIELD8_3( + size : 2, + type : 2, + tag : 4 + ); +} _PACKED item_prefix; + + +typedef struct short_item { + item_prefix prefix; + + union { + uint8 as_uint8[4]; + int8 as_int8[4]; + uint16 as_uint16[2]; + int16 as_int16[2]; + uint32 as_uint32; + int32 as_int32; + } data; +} _PACKED short_item; + + +typedef struct long_item { + item_prefix prefix; + uint8 data_size; + uint8 long_item_tag; + uint8 data[0]; +} _PACKED long_item; + + +typedef struct main_item_data { + LBITFIELD9( + data_constant : 1, + array_variable : 1, + relative : 1, + wrap : 1, + non_linear : 1, + no_preferred : 1, + null_state : 1, + is_volatile : 1, + bits_bytes : 1 + ); + + //uint8 reserved[2]; +} _PACKED main_item_data; + + +typedef struct usage_value { + union { + struct { + uint16 usage_id; + uint16 usage_page; + } _PACKED s; + uint32 extended; + } u; + + bool is_extended; +} usage_value; + + +typedef struct global_item_state { + uint16 usage_page; + uint32 logical_minimum; + uint32 logical_maximum; + uint32 physical_minimum; + uint32 physical_maximum; + uint8 unit_exponent; + uint8 unit; + uint32 report_size; + uint32 report_count; + uint8 report_id; + global_item_state *link; +} global_item_state; + + +typedef struct local_item_state { + usage_value * usage_stack; + uint32 usage_stack_used; + usage_value usage_minimum; + usage_value usage_maximum; + bool usage_minimum_set; + bool usage_maximum_set; + uint32 designator_index; + bool designator_index_set; + uint32 designator_minimum; + uint32 designator_maximum; + uint8 string_index; + bool string_index_set; + uint8 string_minimum; + uint8 string_maximum; +} local_item_state; + +#endif // HID_DATA_TYPES_H diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp b/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp new file mode 100644 index 0000000000..8cf9f9150d --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.cpp @@ -0,0 +1,514 @@ +#include "Driver.h" +#include "HIDCollection.h" +#include "HIDParser.h" +#include "HIDReport.h" + +#include +#include +#include + + +static uint8 sItemSize[4] = { 0, 1, 2, 4 }; +static int8 sUnitExponent[16] = { + // really just a 4 bit signed value + 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 +}; + + +HIDParser::HIDParser() + : fReportCount(0), + fReports(NULL), + fRootCollection(NULL) +{ +} + + +HIDParser::~HIDParser() +{ + _Reset(); +} + + +status_t +HIDParser::ParseReportDescriptor(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; + uint8 *pointer = reportDescriptor; + uint8 *end = pointer + descriptorLength; + + while (pointer < end) { + 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); + if (collection != NULL) { + collection->AddMainItem(globalState, localState, + *mainData); + } else + TRACE_ALWAYS("main item not part of a collection\n"); + } + + // 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; +} + + +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; +} + + +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; +} + + +status_t +HIDParser::SetReport(uint8 *report, size_t length) +{ + HIDReport *target = fReports[0]; + if (fUsesReportIDs) { + target = FindReport(HID_REPORT_TYPE_INPUT, report[0]); + report++; + length--; + } + + if (target == NULL) { + TRACE_ALWAYS("got report buffer but found no report to handle it\n"); + return B_ENTRY_NOT_FOUND; + } + + return target->SetReport(report, length); +} + + +HIDReport * +HIDParser::_FindOrCreateReport(uint8 type, uint8 id) +{ + HIDReport *report = FindReport(type, id); + if (report != NULL) + return report; + + report = new(std::nothrow) HIDReport(this, type, id); + if (report == NULL) { + TRACE_ALWAYS("no memory when allocating report\n"); + return NULL; + } + + HIDReport **newReports = (HIDReport **)realloc(fReports, + (fReportCount + 1) * sizeof(HIDReport *)); + if (newReports == NULL) { + TRACE_ALWAYS("no memory when growing report list\n"); + delete report; + return NULL; + } + + fReports = newReports; + fReports[fReportCount++] = report; + return report; +} + + +float +HIDParser::_CalculateResolution(global_item_state *state) +{ + int64 physicalMinimum = state->physical_minimum; + int64 physicalMaximum = state->physical_maximum; + if (physicalMinimum == 0 && physicalMaximum == 0) { + physicalMinimum = state->logical_minimum; + physicalMaximum = state->logical_maximum; + } + + int8 unitExponent = sUnitExponent[state->unit_exponent]; + + float power = 1; + if (unitExponent < 0) { + while (unitExponent++ < 0) + power /= 10; + } else { + while (unitExponent-- > 0) + power *= 10; + } + + float divisor = (physicalMaximum - physicalMinimum) * power; + if (divisor == 0.0) + return 0.0; + + return (state->logical_maximum - state->logical_minimum) / divisor; +} + + +void +HIDParser::_Reset() +{ + for (uint8 i = 0; i < fReportCount; i++) + delete fReports[i]; + + delete fRootCollection; + free(fReports); + + fUsesReportIDs = false; + fReportCount = 0; + fReports = NULL; + fRootCollection = NULL; +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.h b/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.h new file mode 100644 index 0000000000..dea4993e42 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDParser.h @@ -0,0 +1,38 @@ +#ifndef HID_PARSER_H +#define HID_PARSER_H + +#include "HIDDataTypes.h" + +class HIDReport; +class HIDCollection; + +class HIDParser { +public: + HIDParser(); + ~HIDParser(); + + status_t ParseReportDescriptor(uint8 *reportDescriptor, + size_t descriptorLength); + + bool UsesReportIDs() { return fUsesReportIDs; }; + + HIDReport * FindReport(uint8 type, uint8 id); + uint8 CountReports(uint8 type); + HIDReport * ReportAt(uint8 type, uint8 index); + + HIDCollection * RootCollection() { return fRootCollection; }; + + status_t SetReport(uint8 *report, size_t length); + +private: + HIDReport * _FindOrCreateReport(uint8 type, uint8 id); + float _CalculateResolution(global_item_state *state); + void _Reset(); + + bool fUsesReportIDs; + uint8 fReportCount; + HIDReport ** fReports; + HIDCollection * fRootCollection; +}; + +#endif // HID_PARSER_H diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.cpp b/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.cpp new file mode 100644 index 0000000000..f83be1c6bd --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.cpp @@ -0,0 +1,201 @@ +#include "Driver.h" +#include "HIDReport.h" +#include "HIDReportItem.h" + +#include +#include + + +HIDReport::HIDReport(HIDParser *parser, uint8 type, uint8 id) + : fParser(parser), + fType(type), + fReportID(id), + fReportSize(0), + fItemsUsed(0), + fItemsAllocated(0), + fItems(NULL), + fCurrentReport(NULL), + fBusyCount(0) +{ + fConditionVariable.Init(this, "hid report"); +} + + +HIDReport::~HIDReport() +{ + free(fItems); +} + + +void +HIDReport::AddMainItem(global_item_state &globalState, + local_item_state &localState, main_item_data &mainData) +{ + TRACE("adding main item to report of type 0x%02x with id 0x%02x\n", + fType, fReportID); + TRACE("\tmain data:\n"); + TRACE("\t\t%s\n", mainData.data_constant ? "constant" : "data"); + TRACE("\t\t%s\n", mainData.array_variable ? "variable" : "array"); + TRACE("\t\t%s\n", mainData.relative ? "relative" : "absolute"); + TRACE("\t\t%swrap\n", mainData.wrap ? "" : "no-"); + TRACE("\t\t%slinear\n", mainData.non_linear ? "non-" : ""); + TRACE("\t\t%spreferred state\n", mainData.no_preferred ? "no " : ""); + TRACE("\t\t%s null\n", mainData.null_state ? "has" : "no"); + TRACE("\t\t%svolatile\n", mainData.is_volatile ? "" : "non-"); + TRACE("\t\t%s\n", mainData.bits_bytes ? "bit array" : "buffered bytes"); + + uint32 logicalMinimum = globalState.logical_minimum; + uint32 logicalMaximum = globalState.logical_maximum; + if (logicalMinimum > logicalMaximum) + _SignExtend(logicalMinimum, logicalMaximum); + + uint32 physicalMinimum = globalState.physical_minimum; + uint32 physicalMaximum = globalState.physical_maximum; + if (physicalMinimum > logicalMaximum) + _SignExtend(physicalMinimum, physicalMaximum); + + TRACE("\tglobal state:\n"); + TRACE("\t\tusage_page: 0x%x\n", globalState.usage_page); + TRACE("\t\tlogical_minimum: %ld\n", logicalMinimum); + TRACE("\t\tlogical_maximum: %ld\n", logicalMaximum); + TRACE("\t\tphysical_minimum: %ld\n", physicalMinimum); + TRACE("\t\tphysical_maximum: %ld\n", physicalMaximum); + TRACE("\t\tunit_exponent: %d\n", globalState.unit_exponent); + TRACE("\t\tunit: %d\n", globalState.unit); + TRACE("\t\treport_size: %lu\n", globalState.report_size); + TRACE("\t\treport_count: %lu\n", globalState.report_count); + TRACE("\t\treport_id: %u\n", globalState.report_id); + + TRACE("\tlocal state:\n"); + TRACE("\t\tusage stack (%lu)\n", localState.usage_stack_used); + for (uint32 i = 0; i < localState.usage_stack_used; i++) { + TRACE("\t\t\t0x%08lx\n", localState.usage_stack[i].u.extended); + } + + TRACE("\t\tusage_minimum: 0x%08lx\n", localState.usage_minimum.u.extended); + TRACE("\t\tusage_maximum: 0x%08lx\n", localState.usage_maximum.u.extended); + TRACE("\t\tdesignator_index: %lu\n", localState.designator_index); + TRACE("\t\tdesignator_minimum: %lu\n", localState.designator_minimum); + TRACE("\t\tdesignator_maximum: %lu\n", localState.designator_maximum); + TRACE("\t\tstring_index: %u\n", localState.string_index); + TRACE("\t\tstring_minimum: %u\n", localState.string_minimum); + TRACE("\t\tstring_maximum: %u\n", localState.string_maximum); + + uint32 usageMinimum = 0, usageMaximum = 0; + if (mainData.array_variable == 0) { + usageMinimum = localState.usage_minimum.u.extended; + usageMaximum = localState.usage_maximum.u.extended; + } + + uint32 usageRangeIndex = 0; + for (uint32 i = 0; i < globalState.report_count; i++) { + if (fItemsUsed >= fItemsAllocated) { + fItemsAllocated += 10; + HIDReportItem **newItems = (HIDReportItem **)realloc(fItems, + sizeof(HIDReportItem **) * fItemsAllocated); + if (newItems == NULL) { + TRACE_ALWAYS("no memory when growing report item list\n"); + fItemsAllocated -= 10; + return; + } + + fItems = newItems; + } + + if (mainData.array_variable == 1) { + usage_value usage; + if (i < localState.usage_stack_used) + usage = localState.usage_stack[i]; + else { + usage = localState.usage_minimum; + usage.u.extended += usageRangeIndex++; + if (usage.u.extended > localState.usage_maximum.u.extended) + usage.u.extended = localState.usage_maximum.u.extended; + } + + usageMinimum = usageMaximum = usage.u.extended; + } + + fItems[fItemsUsed++] = new(std::nothrow) HIDReportItem(this, + fReportSize, globalState.report_size, mainData.data_constant == 0, + mainData.array_variable == 0, mainData.relative != 0, + logicalMinimum, logicalMaximum, usageMinimum, usageMaximum); + } +} + + +status_t +HIDReport::SetReport(uint8 *report, size_t length) +{ + if (report == NULL) { + fCurrentReport = NULL; + return B_OK; + } + + if (length * 8 < fReportSize) { + TRACE_ALWAYS("report of %lu bits too small, expected %lu bits\n", + length * 8, fReportSize); + return B_ERROR; + } + + fConditionVariable.NotifyAll(); + fCurrentReport = report; + return B_OK; +} + + +HIDReportItem * +HIDReport::ItemAt(uint32 index) +{ + if (index >= fItemsUsed) + return NULL; + return fItems[index]; +} + + +status_t +HIDReport::WaitForReport(bigtime_t timeout) +{ + while (atomic_or(&fBusyCount, 0) != 0) + snooze(1000); + + ConditionVariableEntry conditionVariableEntry; + fConditionVariable.Add(&conditionVariableEntry); + //status_t result = fParser->Device()->MaybeScheduleTransfer(); + if (result != B_OK) { + conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, 0); + return result; + } + + result = conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, timeout); + if (result != B_OK) + return result; + + atomic_add(&fBusyCount, 1); + return B_OK; +} + + +void +HIDReport::DoneProcessing() +{ + atomic_add(&fBusyCount, -1); +} + + +void +HIDReport::_SignExtend(uint32 &minimum, uint32 &maximum) +{ + uint32 mask = 0x80000000; + for (uint8 i = 0; i < 4; i++) { + if (minimum & mask) { + minimum |= mask; + if (maximum & mask) + maximum |= mask; + return; + } + + mask >>= 8; + mask |= 0xff000000; + } +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.h b/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.h new file mode 100644 index 0000000000..f757aca14a --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDReport.h @@ -0,0 +1,55 @@ +#ifndef HID_REPORT_H +#define HID_REPORT_H + +#include "HIDParser.h" + +#include + +#define HID_REPORT_TYPE_INPUT 0x01 +#define HID_REPORT_TYPE_OUTPUT 0x02 +#define HID_REPORT_TYPE_FEATURE 0x04 +#define HID_REPORT_TYPE_ANY 0x07 + +class HIDReportItem; + +class HIDReport { +public: + HIDReport(HIDParser *parser, uint8 type, + uint8 id); + ~HIDReport(); + + uint8 Type() { return fType; }; + uint8 ID() { return fReportID; }; + + void AddMainItem(global_item_state &globalState, + local_item_state &localState, + main_item_data &mainData); + + status_t SetReport(uint8 *report, size_t length); + uint8 * CurrentReport() { return fCurrentReport; }; + + uint32 CountItems() { return fItemsUsed; }; + HIDReportItem * ItemAt(uint32 index); + + status_t WaitForReport(bigtime_t timeout); + void DoneProcessing(); + +private: + void _SignExtend(uint32 &minimum, uint32 &maximum); + + HIDParser * fParser; + + uint8 fType; + uint8 fReportID; + uint32 fReportSize; + + uint32 fItemsUsed; + uint32 fItemsAllocated; + HIDReportItem ** fItems; + + uint8 * fCurrentReport; + int32 fBusyCount; + ConditionVariable fConditionVariable; +}; + +#endif // HID_REPORT_H diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.cpp b/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.cpp new file mode 100644 index 0000000000..492e41c9fe --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.cpp @@ -0,0 +1,51 @@ +#include "HIDReportItem.h" +#include "HIDReport.h" + +#include + +HIDReportItem::HIDReportItem(HIDReport *report, uint32 bitOffset, + uint8 bitLength, bool hasData, bool isArray, bool isRelative, + uint32 minimum, uint32 maximum, uint32 usageMinimum, uint32 usageMaximum) + : fReport(report), + fByteOffset(bitOffset / 8), + fShift(bitOffset % 8), + fMask(~(0xffffffff << bitLength)), + fHasData(hasData), + fArray(isArray), + fRelative(isRelative), + fMinimum(minimum), + fMaximum(maximum), + fUsageMinimum(usageMinimum), + fUsageMaximum(usageMaximum), + fData(0), + fValid(false) +{ +} + + +status_t +HIDReportItem::Extract() +{ + // The specs restrict items to span at most across 4 bytes, which means + // that we can always just byte-align, copy four bytes and then shift and + // mask as needed. + uint8 *report = fReport->CurrentReport(); + if (report == NULL) + return B_NO_INIT; + + memcpy(&fData, report + fByteOffset, sizeof(uint32)); + fData >>= fShift; + fData &= fMask; + + if (Signed()) { + // sign extend if needed. + if ((fData & ~(fMask >> 1)) != 0) + fData |= ~fMask; + + fValid = (int32)fData >= (int32)fMinimum + && (int32)fData <= (int32)fMaximum; + } else + fValid = fData >= fMinimum && fData <= fMaximum; + + return B_OK; +} diff --git a/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h b/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h new file mode 100644 index 0000000000..c33bc0fe01 --- /dev/null +++ b/src/add-ons/kernel/drivers/input/usb_hid/HIDReportItem.h @@ -0,0 +1,45 @@ +#ifndef HID_REPORT_ITEM_H +#define HID_REPORT_ITEM_H + +#include + +class HIDReport; + +class HIDReportItem { +public: + HIDReportItem(HIDReport *report, + uint32 bitOffset, uint8 bitLength, + bool hasData, bool isArray, bool isRelative, + uint32 minimum, uint32 maximum, + uint32 usageMinimum, uint32 usageMaximum); + + bool HasData() { return fHasData; }; + bool Relative() { return fRelative; }; + bool Array() { return fArray; }; + bool Signed() { return fMinimum > fMaximum; }; + + uint32 UsageMinimum() { return fUsageMinimum; }; + uint32 UsageMaximum() { return fUsageMaximum; }; + + status_t Extract(); + bool Valid() { return fValid; }; + uint32 Data() { return fData; }; + +private: + HIDReport * fReport; + uint32 fByteOffset; + uint8 fShift; + uint32 fMask; + bool fHasData; + bool fArray; + bool fRelative; + uint32 fMinimum; + uint32 fMaximum; + uint32 fUsageMinimum; + uint32 fUsageMaximum; + + uint32 fData; + bool fValid; +}; + +#endif // HID_REPORT_ITEM_H