Adding a complete HID parser and report handling facility. This is generic code,

so should be reusable for bluethooth HID as well (which is the same). The only
missing part so far is the logical collections that would allow nicer
enumeration of the report structure but is otherwise not useful. It should
support all of the HID specs except for usage aliases (even long items that
aren't actually defined should just work if they ever are). Not integrated into
the USB specific device framework and there are no actual drivers making use
of provided functionallity. The parsing was tested and works for all of the 3
devices I had available, but actual interpretation of data is not tested as the
driver side is missing. Will close that gap as a next step and then port the
mouse and keyboard drivers to that framework. Eventually a generic driver that
makes unknown fields available to userland apps in some way should be fairly
easy to implement with that.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30664 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2009-05-07 22:16:50 +00:00
parent 99d1bfb5de
commit e7fcf08d76
9 changed files with 1171 additions and 0 deletions

View File

@ -0,0 +1,74 @@
#include "Driver.h"
#include "HIDCollection.h"
#include <new>
#include <stdlib.h>
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
}

View File

@ -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

View File

@ -0,0 +1,158 @@
#ifndef HID_DATA_TYPES_H
#define HID_DATA_TYPES_H
#include <lendian_bitfield.h>
#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

View File

@ -0,0 +1,514 @@
#include "Driver.h"
#include "HIDCollection.h"
#include "HIDParser.h"
#include "HIDReport.h"
#include <new>
#include <stdlib.h>
#include <string.h>
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;
}

View File

@ -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

View File

@ -0,0 +1,201 @@
#include "Driver.h"
#include "HIDReport.h"
#include "HIDReportItem.h"
#include <new>
#include <stdlib.h>
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;
}
}

View File

@ -0,0 +1,55 @@
#ifndef HID_REPORT_H
#define HID_REPORT_H
#include "HIDParser.h"
#include <condition_variable.h>
#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

View File

@ -0,0 +1,51 @@
#include "HIDReportItem.h"
#include "HIDReport.h"
#include <string.h>
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;
}

View File

@ -0,0 +1,45 @@
#ifndef HID_REPORT_ITEM_H
#define HID_REPORT_ITEM_H
#include <SupportDefs.h>
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