You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2247 lines
84 KiB
2247 lines
84 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
descript.c
|
|
|
|
Abstract:
|
|
This module contains the code for parsing HID descriptors.
|
|
|
|
Environment:
|
|
|
|
Kernel & user mode
|
|
|
|
Revision History:
|
|
|
|
Aug-96 : created by Kenneth Ray
|
|
|
|
--*/
|
|
|
|
#include "wdm.h"
|
|
#include "hidpddi.h"
|
|
#include "hidusage.h"
|
|
|
|
#define FAR
|
|
#include "poclass.h"
|
|
#include "hidparse.h"
|
|
|
|
#define HIDP_LINK_COLLECTION_NODE use internal "private" only
|
|
#define PHIDP_LINK_COLLECTION_NODE use internal "private" only
|
|
|
|
|
|
typedef struct _HIDP_COLLECTION_DESC_LIST
|
|
{
|
|
struct _HIDP_COLLECTION_DESC;
|
|
struct _HIDP_COLLECTION_DESC_LIST * NextCollection;
|
|
} HIDP_COLLECTION_DESC_LIST, *PHIDP_COLLECTION_DESC_LIST;
|
|
|
|
typedef struct _HIDP_PARSE_GLOBAL_PUSH
|
|
{
|
|
USHORT UsagePage;
|
|
USHORT ReportSize, ReportCount;
|
|
USHORT NumGlobalUnknowns;
|
|
|
|
LONG LogicalMin, LogicalMax;
|
|
LONG PhysicalMin, PhysicalMax;
|
|
ULONG UnitExp, Unit;
|
|
HIDP_UNKNOWN_TOKEN GlobalUnknowns [HIDP_MAX_UNKNOWN_ITEMS];
|
|
|
|
struct _HIDP_REPORT_IDS * ReportIDs;
|
|
struct _HIDP_PARSE_GLOBAL_PUSH * Pop;
|
|
} HIDP_PARSE_GLOBAL_PUSH, *PHIDP_PARSE_GLOBAL_PUSH;
|
|
|
|
|
|
typedef struct _HIDP_PARSE_LOCAL_RANGE
|
|
{
|
|
BOOLEAN Range;
|
|
BOOLEAN IsAlias;
|
|
// This usage is an alias (as declaired with a delimiter)
|
|
// An alias of the next LOCAL_RANGE on the LOCAL_RANGE stack
|
|
USHORT UsagePage;
|
|
USHORT Value, Min, Max;
|
|
} HIDP_PARSE_LOCAL_RANGE, *PHIDP_PARSE_LOCAL_RANGE;
|
|
|
|
typedef struct _HIDP_PARSE_LOCAL_RANGE_LIST
|
|
{
|
|
HIDP_PARSE_LOCAL_RANGE;
|
|
UCHAR Depth;
|
|
UCHAR Reserved2[1];
|
|
struct _HIDP_PARSE_LOCAL_RANGE_LIST * Next;
|
|
} HIDP_PARSE_LOCAL_RANGE_LIST, *PHIDP_PARSE_LOCAL_RANGE_LIST;
|
|
|
|
NTSTATUS HidP_AllocateCollections (PHIDP_REPORT_DESCRIPTOR, ULONG, POOL_TYPE, PHIDP_COLLECTION_DESC_LIST *, PULONG, PHIDP_GETCOLDESC_DBG, PHIDP_DEVICE_DESC);
|
|
NTSTATUS HidP_ParseCollections (PHIDP_REPORT_DESCRIPTOR, ULONG, POOL_TYPE, PHIDP_COLLECTION_DESC_LIST, ULONG, PHIDP_GETCOLDESC_DBG, PHIDP_DEVICE_DESC);
|
|
void HidP_AssignDataIndices (PHIDP_PREPARSED_DATA, PHIDP_GETCOLDESC_DBG);
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST HidP_FreeUsageList (PHIDP_PARSE_LOCAL_RANGE_LIST);
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST HidP_PushUsageList (PHIDP_PARSE_LOCAL_RANGE_LIST, POOL_TYPE, BOOLEAN);
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST HidP_PopUsageList (PHIDP_PARSE_LOCAL_RANGE_LIST);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, HidP_AllocateCollections)
|
|
#pragma alloc_text(PAGE, HidP_ParseCollections)
|
|
#pragma alloc_text(PAGE, HidP_AssignDataIndices)
|
|
#pragma alloc_text(PAGE, HidP_GetCollectionDescription)
|
|
#pragma alloc_text(PAGE, HidP_FreeUsageList)
|
|
#pragma alloc_text(PAGE, HidP_PushUsageList)
|
|
#pragma alloc_text(PAGE, HidP_PopUsageList)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
HidP_GetCollectionDescription(
|
|
IN PHIDP_REPORT_DESCRIPTOR ReportDesc,
|
|
IN ULONG DescLength,
|
|
IN POOL_TYPE PoolType,
|
|
OUT PHIDP_DEVICE_DESC DeviceDesc
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
see hidpi.h for a description of this function.
|
|
|
|
GetCollectionDescription is a one time cost.
|
|
The following function and its support functions were put together
|
|
in as straight forward (as HID will allow) manner.
|
|
Not major opt. has been made.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PHIDP_COLLECTION_DESC_LIST collectDesc = 0;
|
|
PHIDP_COLLECTION_DESC_LIST nextCollectDesc = 0;
|
|
ULONG numCols = 0;
|
|
ULONG collectionDescLength = 0;
|
|
|
|
// First Pass allocate memory for the collections.
|
|
|
|
DeviceDesc->Dbg.ErrorCode = HIDP_GETCOLDESC_SUCCESS;
|
|
|
|
RtlZeroMemory (DeviceDesc, sizeof (HIDP_DEVICE_DESC));
|
|
|
|
HidP_KdPrint(0, ("'Preparing to Allocate memory\n"));
|
|
status = HidP_AllocateCollections (ReportDesc,
|
|
DescLength,
|
|
PoolType,
|
|
&collectDesc,
|
|
&numCols,
|
|
&DeviceDesc->Dbg,
|
|
DeviceDesc);
|
|
if (0 == numCols)
|
|
{
|
|
// No collections were reported. That means that this device did not
|
|
// report any top level collections in its report descriptor.
|
|
// This is most bad.
|
|
status = STATUS_NO_DATA_DETECTED;
|
|
goto HIDP_GETCOLLECTIONS_REJECT;
|
|
}
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// Something went wrong in the allocation of the memory.
|
|
goto HIDP_GETCOLLECTIONS_REJECT;
|
|
}
|
|
|
|
// Second Pass fill in the data.
|
|
|
|
HidP_KdPrint(0, ("'Starting Parsing Pass\n"));
|
|
status = HidP_ParseCollections(ReportDesc,
|
|
DescLength,
|
|
PoolType,
|
|
collectDesc,
|
|
numCols,
|
|
&DeviceDesc->Dbg,
|
|
DeviceDesc);
|
|
|
|
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
DeviceDesc->CollectionDesc =
|
|
(PHIDP_COLLECTION_DESC)
|
|
ExAllocatePool (PoolType, numCols * sizeof (HIDP_COLLECTION_DESC));
|
|
|
|
if (! (DeviceDesc->CollectionDesc))
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
HidP_KdPrint(2, ("Insufficitent Resources at VERY END\n"));
|
|
DeviceDesc->Dbg.BreakOffset = DescLength;
|
|
DeviceDesc->Dbg.ErrorCode = HIDP_GETCOLDESC_RESOURCES;
|
|
goto HIDP_GETCOLLECTIONS_REJECT;
|
|
}
|
|
|
|
//
|
|
// Here we flatten out the collection descriptions but we never
|
|
// flatten the PHIDP_PREPARSED_DATA data. We could (should) do that as
|
|
// well if we ever optimize.
|
|
//
|
|
DeviceDesc->CollectionDescLength = numCols;
|
|
numCols = 0;
|
|
while (collectDesc)
|
|
{
|
|
nextCollectDesc = collectDesc->NextCollection;
|
|
RtlCopyMemory (DeviceDesc->CollectionDesc + (numCols++),
|
|
collectDesc,
|
|
sizeof (HIDP_COLLECTION_DESC));
|
|
HidP_AssignDataIndices (collectDesc->PreparsedData, &DeviceDesc->Dbg);
|
|
ExFreePool (collectDesc);
|
|
collectDesc = nextCollectDesc;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
HIDP_GETCOLLECTIONS_REJECT:
|
|
while (collectDesc)
|
|
{
|
|
nextCollectDesc = collectDesc->NextCollection;
|
|
if (collectDesc->PreparsedData)
|
|
{
|
|
ExFreePool (collectDesc->PreparsedData);
|
|
}
|
|
ExFreePool (collectDesc);
|
|
collectDesc = nextCollectDesc;
|
|
}
|
|
|
|
if (DeviceDesc->ReportIDs)
|
|
{
|
|
ExFreePool (DeviceDesc->ReportIDs);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
#define MORE_DATA(_pos_, _len_) \
|
|
if (!((_pos_) < (_len_))) \
|
|
{ \
|
|
DeviceDesc->Dbg.BreakOffset = descIndex; \
|
|
DeviceDesc->Dbg.ErrorCode = HIDP_GETCOLDESC_BUFFER; \
|
|
return STATUS_BUFFER_TOO_SMALL; \
|
|
}
|
|
|
|
NTSTATUS
|
|
HidP_AllocateCollections (
|
|
IN PHIDP_REPORT_DESCRIPTOR RepDesc,
|
|
IN ULONG RepDescLen,
|
|
IN POOL_TYPE PoolType,
|
|
OUT PHIDP_COLLECTION_DESC_LIST * ColsRet,
|
|
OUT PULONG NumCols,
|
|
OUT PHIDP_GETCOLDESC_DBG Dbg,
|
|
OUT PHIDP_DEVICE_DESC DeviceDesc)
|
|
/*++
|
|
Routine Description:
|
|
Allocate a link list of Collection descriptors for use by the preparser.
|
|
Each collection descriptor represents a top level app collection found
|
|
in the given report descriptor, and contains enough memory (scratch space)
|
|
into which to write the preparsed data.
|
|
Return a linked list of such collections.
|
|
|
|
In each collection also allocate enough space for the preparsed data, based
|
|
on the number of channels required.
|
|
|
|
Also allocate memory for the three report ID structures.
|
|
|
|
Parameters:
|
|
Rep The given raw report descriptor.
|
|
RepLen Length of this said descriptor.
|
|
ColsRet The head of the list of collection descriptors.
|
|
NumCols Then number of collection descriptors in said list.
|
|
--*/
|
|
{
|
|
PHIDP_COLLECTION_DESC_LIST preCol = 0;
|
|
PHIDP_COLLECTION_DESC_LIST curCol = 0;
|
|
PHIDP_PREPARSED_DATA preparsed = 0;
|
|
|
|
HIDP_ITEM item;
|
|
ULONG descIndex = 0;
|
|
LONG colDepth = 0; // nested collections
|
|
SHORT usageDepth = 0; // How many usages for each main item
|
|
|
|
USHORT inputChannels = 0;
|
|
USHORT outputChannels = 0;
|
|
USHORT featureChannels = 0;
|
|
USHORT length;
|
|
USHORT numLinkCollections = 0;
|
|
// Link Collections within a top level collection.
|
|
UCHAR tmpBitField = 0;
|
|
|
|
BOOLEAN newReportID = FALSE;
|
|
UCHAR numReports = 0;
|
|
BOOLEAN defaultReportIDUsed = FALSE;
|
|
BOOLEAN noDefaultReportIDAllowed = FALSE;
|
|
//
|
|
// numReports indicates the number of HIDP_REPORT_IDS structures needed
|
|
// to describe this device. If the device has only one top level collection
|
|
// then the report descriptor need not contain a report id declaration, and
|
|
// the given device will not prepend a report ID to the input report packets.
|
|
// newReportID indicates the parser has found no report id declaration thus
|
|
// far in the report descriptor.
|
|
//
|
|
// newReportID is set to TRUE with each entrance of a top level collection,
|
|
// this allocation routine sets this to FALSE when it see a report ID
|
|
// declaration.
|
|
//
|
|
// We start newReportID as FALSE so that we can test for TRUE on entering
|
|
// a top level collection. If, for some reason, we enter an additional top
|
|
// level collection and newReportID is still set to TRUE then we have a
|
|
// violation of the HID spec. `No report may span a top level collection.'
|
|
//
|
|
// Also a report ID of zero is not allowed. If there is no declaration
|
|
// of a report id then (1) all channels will have there report id field set
|
|
// to zero (aka none) (2) only one top level collection may be encountered.
|
|
// We track this with the defaultReportIDUsed noDefaultReportIDAllowed
|
|
// locals.
|
|
//
|
|
|
|
*NumCols = 0;
|
|
// currentTopCollection = 1;
|
|
//
|
|
// each collection returned from the preparser has a unique collection number
|
|
// associated with it. The preparser only concerns itself with top-level
|
|
// collections. This number DOES NOT in any way correspond with the
|
|
// accessor functions, used by the client, described in hidpi.h. The client
|
|
// receives only one collection at a time, and within each top level
|
|
// collection there are subcollections (link collections) which are
|
|
// given another set of numberings.
|
|
// We track the current collection number by the number of collections,
|
|
// argument passed in by the caller.
|
|
//
|
|
|
|
|
|
while (descIndex < RepDescLen)
|
|
{
|
|
item = *(RepDesc + descIndex++);
|
|
switch (item)
|
|
{
|
|
case HIDP_MAIN_COLLECTION:
|
|
MORE_DATA (descIndex, RepDescLen);
|
|
item = *(RepDesc + descIndex++);
|
|
if (1 == ++colDepth)
|
|
{ // We will regard any top level collection as an application
|
|
// collection.
|
|
// We will regard second level collections as a linked collection
|
|
// (or sub collection defined by the HIDP_PRIVATE_LINK_COLLECTION_NODE)
|
|
//
|
|
|
|
inputChannels = outputChannels = featureChannels = 0;
|
|
numLinkCollections = 1;
|
|
// Link collection zero is understood to be the top level
|
|
// collection so we need to start out with at least one node
|
|
// allocated.
|
|
|
|
if (0 == usageDepth) {
|
|
HidP_KdPrint (2, ("No usage for top level collection: %d!\n",
|
|
*NumCols));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_TOP_COLLECTION_USAGE;
|
|
Dbg->Args[0] = *NumCols;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
} else if (1 < usageDepth) {
|
|
HidP_KdPrint (2, ("Multiple usages for top level collection: %d\n",
|
|
*NumCols));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_TOP_COLLECTION_USAGE;
|
|
Dbg->Args[0] = *NumCols;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
|
|
if (newReportID) {
|
|
// This is not the first top collection since this variable is
|
|
// initialized to false.
|
|
// Seeing this set means we have parsed an entire top level
|
|
// collection without seing a report id. This is bad.
|
|
// A device with more than one top level colletion must have
|
|
// more than one report. And the last top level collection
|
|
// declared no such report.
|
|
HidP_KdPrint (2, ("No report ID for collection: %d\n", *NumCols));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_NO_REPORT_ID;
|
|
Dbg->Args[0] = *NumCols;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
|
|
} else if (defaultReportIDUsed) {
|
|
// This is not the first top collection since this variable is
|
|
// initialized to FALSE;
|
|
// So if ever we see this as true we are starting a new top
|
|
// level collection which means there must be report ID from the
|
|
// device and therefore there cannot exist a single channel
|
|
// that has no declared report ID.
|
|
HidP_KdPrint (2, ("Default report ID used inappropriately\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_DEFAULT_ID_ERROR;
|
|
Dbg->Args[0] = *NumCols;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
|
|
}
|
|
|
|
numReports++;
|
|
newReportID = TRUE;
|
|
|
|
(*NumCols)++; // One more top level collection found.
|
|
HidP_KdPrint(2, ("'Top Level Collection %d found\n", *NumCols));
|
|
preCol = curCol;
|
|
curCol = (PHIDP_COLLECTION_DESC_LIST)
|
|
ExAllocatePool (PoolType, sizeof (HIDP_COLLECTION_DESC_LIST));
|
|
if (!curCol) {
|
|
HidP_KdPrint(2, ("No Resources to make Top level collection\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_LINK_RESOURCES;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
RtlZeroMemory (curCol, sizeof (HIDP_COLLECTION_DESC_LIST));
|
|
|
|
if (preCol) {
|
|
preCol->NextCollection = curCol;
|
|
|
|
} else {
|
|
*ColsRet = curCol;
|
|
}
|
|
} else if (1 < colDepth) { // a linked collection
|
|
|
|
HidP_KdPrint(0, ("'Enter Link Collection\n"));
|
|
if (0 == usageDepth) {
|
|
HidP_KdPrint (1, ("***************************************\n"));
|
|
HidP_KdPrint (1, ("Warning! Link collection without usage \n"));
|
|
HidP_KdPrint (1, ("Pos (%d), depth (%d)\n", descIndex, colDepth));
|
|
HidP_KdPrint (1, ("***************************************\n"));
|
|
usageDepth = 1;
|
|
} else if (1 < usageDepth) {
|
|
HidP_KdPrint (1, ("Link Collection with multiple usage decls\n"));
|
|
}
|
|
numLinkCollections += usageDepth;
|
|
}
|
|
usageDepth = 0;
|
|
break;
|
|
|
|
case HIDP_MAIN_ENDCOLLECTION:
|
|
usageDepth = 0;
|
|
if (--colDepth < 0) {
|
|
HidP_KdPrint(2, ("Extra End Collection\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_UNEXP_END_COL;
|
|
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
if (0 < colDepth) {
|
|
HidP_KdPrint(0, ("'Exit Link Collection\n"));
|
|
continue;
|
|
}
|
|
HidP_KdPrint (0, ("'Collection %d exit\n", *NumCols));
|
|
curCol->CollectionNumber = (UCHAR) *NumCols;
|
|
length = sizeof (HIDP_PREPARSED_DATA)
|
|
+ (sizeof (HIDP_CHANNEL_DESC)
|
|
* (inputChannels
|
|
+ outputChannels
|
|
+ featureChannels))
|
|
+ (sizeof (HIDP_PRIVATE_LINK_COLLECTION_NODE))
|
|
* numLinkCollections;
|
|
|
|
curCol->PreparsedDataLength = length;
|
|
curCol->PreparsedData =
|
|
(PHIDP_PREPARSED_DATA) ExAllocatePool (PoolType, length);
|
|
|
|
if (!curCol->PreparsedData) {
|
|
HidP_KdPrint(2, ("Could not allocate space for PreparsedData\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_PREPARSE_RESOURCES;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory (curCol->PreparsedData, curCol->PreparsedDataLength);
|
|
// Set the offsets
|
|
preparsed = curCol->PreparsedData;
|
|
|
|
preparsed->Signature1 = HIDP_PREPARSED_DATA_SIGNATURE1;
|
|
preparsed->Signature2 = HIDP_PREPARSED_DATA_SIGNATURE2;
|
|
preparsed->Input.Index = (UCHAR) preparsed->Input.Offset = 0;
|
|
length = preparsed->Input.Size = inputChannels;
|
|
|
|
preparsed->Output.Index = preparsed->Output.Offset = (UCHAR) length;
|
|
length += (preparsed->Output.Size = outputChannels);
|
|
|
|
preparsed->Feature.Index = preparsed->Feature.Offset = (UCHAR) length;
|
|
length += (preparsed->Feature.Size = featureChannels);
|
|
|
|
preparsed->LinkCollectionArrayOffset =
|
|
length * sizeof (HIDP_CHANNEL_DESC);
|
|
preparsed->LinkCollectionArrayLength = numLinkCollections;
|
|
|
|
break;
|
|
|
|
case HIDP_LOCAL_USAGE_4:
|
|
case HIDP_LOCAL_USAGE_MIN_4:
|
|
descIndex += 2;
|
|
|
|
case HIDP_LOCAL_USAGE_2:
|
|
case HIDP_LOCAL_USAGE_MIN_2:
|
|
descIndex++;
|
|
|
|
case HIDP_LOCAL_USAGE_1:
|
|
case HIDP_LOCAL_USAGE_MIN_1:
|
|
MORE_DATA (descIndex++, RepDescLen);
|
|
usageDepth++;
|
|
break;
|
|
|
|
case HIDP_LOCAL_DELIMITER:
|
|
if (1 != (item = *(RepDesc + descIndex))) {
|
|
HidP_KdPrint (2, ("Delimiter not start %x\n", item));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_MISMATCH_OC_DELIMITER;
|
|
Dbg->Args[0] = item;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
|
|
MORE_DATA (descIndex++, RepDescLen);
|
|
while (TRUE) {
|
|
if (descIndex >= RepDescLen) {
|
|
HidP_KdPrint (2, ("End delimiter NOT found!\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_NO_CLOSE_DELIMITER;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
item = *(RepDesc + descIndex++);
|
|
|
|
if (HIDP_LOCAL_DELIMITER == item) {
|
|
if (0 != (item = *(RepDesc + descIndex))) {
|
|
HidP_KdPrint (2, ("Delimiter not stop %x\n", item));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_MISMATCH_OC_DELIMITER;
|
|
Dbg->Args[0] = item;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
MORE_DATA (descIndex++, RepDescLen);
|
|
break;
|
|
}
|
|
|
|
switch (item) {
|
|
//
|
|
// TODO: kenray
|
|
//
|
|
// Usage Min / Max not yet supported within delimiter.
|
|
//
|
|
// case HIDP_LOCAL_USAGE_MAX_4:
|
|
// descIndex += 2;
|
|
// case HIDP_LOCAL_USAGE_MAX_2:
|
|
// descIndex++;
|
|
// case HIDP_LOCAL_USAGE_MAX_1:
|
|
// descIndex++;
|
|
// break;
|
|
|
|
case HIDP_LOCAL_USAGE_4:
|
|
// case HIDP_LOCAL_USAGE_MIN_4:
|
|
descIndex += 2;
|
|
case HIDP_LOCAL_USAGE_2:
|
|
// case HIDP_LOCAL_USAGE_MIN_2:
|
|
descIndex++;
|
|
case HIDP_LOCAL_USAGE_1:
|
|
// case HIDP_LOCAL_USAGE_MIN_1:
|
|
MORE_DATA (descIndex++, RepDescLen);
|
|
usageDepth++;
|
|
break;
|
|
|
|
default:
|
|
HidP_KdPrint (2, ("Invalid token found within delimiter!\n"));
|
|
HidP_KdPrint (2, ("Only Usages are allowed within a delimiter\n"));
|
|
// HidP_KdPrint (("IE: Only Usage, UsageMin, UsageMax tokens\n"));
|
|
HidP_KdPrint (2, ("IE: Only Usage token allowes (no min or max)\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_NOT_VALID_DELIMITER;
|
|
Dbg->Args[0] = item;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HIDP_MAIN_INPUT_2:
|
|
MORE_DATA (descIndex + 1, RepDescLen);
|
|
tmpBitField = *(RepDesc + descIndex++);
|
|
descIndex++;
|
|
goto HIDP_ALLOC_MAIN_INPUT;
|
|
|
|
case HIDP_MAIN_INPUT_1:
|
|
MORE_DATA (descIndex, RepDescLen);
|
|
tmpBitField = *(RepDesc + descIndex++);
|
|
|
|
HIDP_ALLOC_MAIN_INPUT:
|
|
if (0 == usageDepth) {
|
|
if (HIDP_ISCONST(tmpBitField)) {
|
|
break;
|
|
}
|
|
HidP_KdPrint (2, ("Non constant main item found without usage decl\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_MAIN_ITEM_NO_USAGE;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
|
|
inputChannels += (usageDepth ? usageDepth : 1);
|
|
if (newReportID) {
|
|
if (noDefaultReportIDAllowed) {
|
|
// A report ID declaration was found somewhere earlier in this
|
|
// report descriptor. This means that ALL main items must
|
|
// have a declared report ID.
|
|
HidP_KdPrint (2, ("Default report ID used inappropriately\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_DEFAULT_ID_ERROR;
|
|
Dbg->Args[0] = *NumCols;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
defaultReportIDUsed = TRUE;
|
|
}
|
|
if (0 == colDepth) {
|
|
HidP_KdPrint (2, ("Main item found not in top level collection\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_INVALID_MAIN_ITEM;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
usageDepth = 0;
|
|
break;
|
|
|
|
case HIDP_MAIN_OUTPUT_2:
|
|
MORE_DATA (descIndex + 1, RepDescLen);
|
|
tmpBitField = *(RepDesc + descIndex++);
|
|
descIndex++;
|
|
goto HIDP_ALLOC_MAIN_OUTPUT;
|
|
|
|
case HIDP_MAIN_OUTPUT_1:
|
|
MORE_DATA (descIndex, RepDescLen);
|
|
tmpBitField = *(RepDesc + descIndex++);
|
|
|
|
HIDP_ALLOC_MAIN_OUTPUT:
|
|
if (0 == usageDepth) {
|
|
if (HIDP_ISCONST(tmpBitField)) {
|
|
break;
|
|
}
|
|
HidP_KdPrint (2, ("Non constant main item found without usage decl\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_MAIN_ITEM_NO_USAGE;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
|
|
outputChannels += (usageDepth ? usageDepth : 1);
|
|
if (newReportID) {
|
|
if (noDefaultReportIDAllowed) {
|
|
// A report ID declaration was found somewhere earlier in this
|
|
// report descriptor. This means that ALL main items must
|
|
// have a declared report ID.
|
|
HidP_KdPrint (2, ("Default report ID used inappropriately\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_DEFAULT_ID_ERROR;
|
|
Dbg->Args[0] = *NumCols;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
defaultReportIDUsed = TRUE;
|
|
}
|
|
if (0 == colDepth) {
|
|
HidP_KdPrint (2, ("Main item found not in top level collection\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_INVALID_MAIN_ITEM;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
usageDepth = 0;
|
|
break;
|
|
|
|
case HIDP_MAIN_FEATURE_2:
|
|
MORE_DATA (descIndex + 1, RepDescLen);
|
|
tmpBitField = *(RepDesc + descIndex++);
|
|
descIndex++;
|
|
goto HIDP_ALLOC_MAIN_FEATURE;
|
|
|
|
case HIDP_MAIN_FEATURE_1:
|
|
MORE_DATA (descIndex, RepDescLen);
|
|
tmpBitField = *(RepDesc + descIndex++);
|
|
|
|
HIDP_ALLOC_MAIN_FEATURE:
|
|
if (0 == usageDepth) {
|
|
if (HIDP_ISCONST(tmpBitField)) {
|
|
break;
|
|
}
|
|
HidP_KdPrint (2, ("Non constant main item found without usage decl\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_MAIN_ITEM_NO_USAGE;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
|
|
featureChannels += (usageDepth ? usageDepth : 1);
|
|
if (newReportID) {
|
|
if (noDefaultReportIDAllowed) {
|
|
// A report ID declaration was found somewhere earlier in this
|
|
// report descriptor. This means that ALL main items must
|
|
// have a declared report ID.
|
|
HidP_KdPrint (2, ("Default report ID used inappropriately\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_DEFAULT_ID_ERROR;
|
|
Dbg->Args[0] = *NumCols;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
defaultReportIDUsed = TRUE;
|
|
}
|
|
if (0 == colDepth) {
|
|
HidP_KdPrint (2, ("Main item found not in top level collection\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_INVALID_MAIN_ITEM;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
usageDepth = 0;
|
|
break;
|
|
|
|
case HIDP_GLOBAL_REPORT_ID:
|
|
MORE_DATA (descIndex, RepDescLen);
|
|
item = *(RepDesc + descIndex++);
|
|
|
|
if (0 < colDepth) {
|
|
ASSERT (curCol);
|
|
} else {
|
|
HidP_KdPrint(2, ("Report ID outside of Top level collection\n"));
|
|
HidP_KdPrint(2, ("Reports cannot span more than one top level \n"));
|
|
HidP_KdPrint(2, ("Report ID found: %d", (ULONG) item));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_REPORT_ID;
|
|
Dbg->Args[0] = item;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
|
|
if (newReportID) {
|
|
newReportID = FALSE;
|
|
} else {
|
|
numReports++;
|
|
}
|
|
|
|
noDefaultReportIDAllowed = TRUE;
|
|
if (defaultReportIDUsed) {
|
|
// A report ID declaration was found somewhere earlier in this
|
|
// report descriptor. This means that ALL main items must
|
|
// have a declared report ID.
|
|
HidP_KdPrint (2, ("Default report ID used inappropriately\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_DEFAULT_ID_ERROR;
|
|
Dbg->Args[0] = *NumCols;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
break;
|
|
|
|
case HIDP_ITEM_LONG:
|
|
HidP_KdPrint (2, ("Long Items not supported %x\n", item));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_ITEM_UNKNOWN;
|
|
Dbg->Args[0] = item;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
|
|
default:
|
|
// Bump past the data bytes in the descriptor.
|
|
length = (item & HIDP_ITEM_LENGTH_DATA);
|
|
length = (3 == length) ? 4 : length;
|
|
if (!((descIndex + length) <= RepDescLen)) {
|
|
// OK the lower 2 bits in the item represent the length of the
|
|
// data if this is 3 then there are 4 data bytes following this
|
|
// item. DescPos already points to the next data item.
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_ONE_BYTE;
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
descIndex += length;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// According to the HID spec no report id may span a top level collection.
|
|
// which means that each collection must have at least one report, and there
|
|
// should be at least as many report IDs as collections. Unless there is
|
|
// only one report (therefore only one collection). In this case no report
|
|
// ID will be sent from the device. But in this case we return saying there
|
|
// was indeed one report anyway. The ReportID decsriptor was of length one.
|
|
// Therefore numReports must always be greater than or equal to the number
|
|
// of collections.
|
|
//
|
|
// For output and feature reports, report ids are sent as an extra argument
|
|
// so they will always be present even if they are zero. (Zero means that
|
|
// the device did not list a report ID in the descriptor.)
|
|
//
|
|
// However with input packets the report ID is part of the packet itself:
|
|
// the first byte. UNLESS there is only one report, and then it is not
|
|
// present.
|
|
//
|
|
// __For input packets___
|
|
// the device can have a report ID even if it has only one
|
|
// report. This is odd, as it wastes a byte, but then again who knows the
|
|
// mind of an IHV. For this reason, hidparse must check to see if the
|
|
// reportID list is of length one and the report id itself (in the one and
|
|
// only one space) is zero in order to determine if the device sends no
|
|
// reports ids.
|
|
// If it is zero (the device is not allowed to send report ids of zero)
|
|
// than that report id was simulated meaning the number of bytes in the
|
|
// packet from the device is one less than the number of byte given to the
|
|
// user.
|
|
// If is is non-zero, then the number of bytes from the device is the same
|
|
// as the number of bytes given to the user.
|
|
//
|
|
|
|
if (numReports < *NumCols) {
|
|
HidP_KdPrint (2, ("Report IDS cannot span collections.\n"));
|
|
HidP_KdPrint (2, ("This means that you must have at least one report ID\n"));
|
|
HidP_KdPrint (2, ("For each TOP level collection, unless you have only\n"));
|
|
HidP_KdPrint (2, ("report.\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_NO_REPORT_ID;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
|
|
if (0 < colDepth) {
|
|
HidP_KdPrint(2, ("End Collection not found\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_UNEXP_END_COL;
|
|
return STATUS_COULD_NOT_INTERPRET;
|
|
}
|
|
|
|
//
|
|
// Now that we have seen the entire structure, allocate the structure for
|
|
// holding the report id switch table.
|
|
//
|
|
|
|
if (0 == numReports) {
|
|
HidP_KdPrint (2, ("No top level collections were found! \n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_NO_DATA;
|
|
return STATUS_NO_DATA_DETECTED;
|
|
}
|
|
|
|
DeviceDesc->ReportIDsLength = numReports;
|
|
DeviceDesc->ReportIDs = (PHIDP_REPORT_IDS)
|
|
ExAllocatePool (PoolType, numReports * sizeof (HIDP_REPORT_IDS));
|
|
|
|
if (!DeviceDesc->ReportIDs) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory (DeviceDesc->ReportIDs, numReports * sizeof (HIDP_REPORT_IDS));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST
|
|
HidP_FreeUsageList (
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST Usage
|
|
)
|
|
/*++
|
|
RoutineDescription:
|
|
clear off all the usages in the linked list
|
|
But do not free the first element in the list.
|
|
--*/
|
|
{
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST curUsage;
|
|
while (Usage->Next) {
|
|
curUsage = Usage;
|
|
Usage = curUsage->Next;
|
|
ExFreePool (curUsage);
|
|
}
|
|
RtlZeroMemory (Usage, sizeof (HIDP_PARSE_LOCAL_RANGE_LIST));
|
|
return Usage;
|
|
}
|
|
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST
|
|
HidP_PushUsageList (
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST Usage,
|
|
POOL_TYPE PoolType,
|
|
BOOLEAN WithinDelimiter
|
|
)
|
|
/*++
|
|
RoutineDescription:
|
|
allocate another Usage node and add it to the top O the list.
|
|
--*/
|
|
{
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST newUsage;
|
|
|
|
newUsage = (PHIDP_PARSE_LOCAL_RANGE_LIST)
|
|
ExAllocatePool (PoolType, sizeof (HIDP_PARSE_LOCAL_RANGE_LIST));
|
|
if (newUsage) {
|
|
RtlZeroMemory (newUsage, sizeof (HIDP_PARSE_LOCAL_RANGE_LIST));
|
|
newUsage->Next = Usage;
|
|
if (!WithinDelimiter) {
|
|
newUsage->Depth = Usage->Depth
|
|
+ (Usage->Range ? (Usage->Max - Usage->Min + 1) : 1);
|
|
} else {
|
|
newUsage->Depth = Usage->Depth;
|
|
//
|
|
// Note ranges are not allowed in delimiters therefore we know
|
|
// that all entries in the delimiter are equal and are length 1
|
|
//
|
|
}
|
|
} else {
|
|
HidP_FreeUsageList (Usage);
|
|
}
|
|
return newUsage;
|
|
}
|
|
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST
|
|
HidP_PopUsageList (
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST Usage
|
|
)
|
|
{
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST newUsage;
|
|
|
|
if (Usage->Next) {
|
|
newUsage = Usage->Next;
|
|
ExFreePool (Usage);
|
|
} else {
|
|
newUsage = Usage;
|
|
#if DBG
|
|
RtlFillMemory (newUsage, sizeof (HIDP_PARSE_LOCAL_RANGE_LIST), 0xDB);
|
|
newUsage->Depth = 0;
|
|
#endif
|
|
}
|
|
return newUsage;
|
|
}
|
|
|
|
|
|
#define ONE_BYTE_DATA(_data_, _pos_, _dbg_) \
|
|
if (!((_pos_) < RepDescLen)) { \
|
|
status = STATUS_BUFFER_TOO_SMALL; \
|
|
KdPrint(("More Data Expected\n")); \
|
|
_dbg_->ErrorCode = HIDP_GETCOLDESC_ONE_BYTE; \
|
|
_dbg_->BreakOffset = descIndex; \
|
|
goto HIDP_PARSE_REJECT; \
|
|
} \
|
|
(_data_) = *(RepDesc + (_pos_)++);
|
|
|
|
#define TWO_BYTE_DATA(_data_, _pos_, _dbg_) \
|
|
if (!((_pos_) + 1 < RepDescLen)) { \
|
|
status = STATUS_BUFFER_TOO_SMALL; \
|
|
KdPrint(("More Data Expected\n")); \
|
|
_dbg_->ErrorCode = HIDP_GETCOLDESC_TWO_BYTE; \
|
|
_dbg_->BreakOffset = descIndex; \
|
|
goto HIDP_PARSE_REJECT; \
|
|
} \
|
|
(_data_) = *(RepDesc + (_pos_)++); \
|
|
(_data_) |= *(RepDesc + (_pos_)++) << 8;
|
|
|
|
|
|
#define FOUR_BYTE_DATA(_data_, _pos_, _dbg_) \
|
|
if (!((_pos_) + 3 < RepDescLen)) { \
|
|
status = STATUS_BUFFER_TOO_SMALL; \
|
|
KdPrint(("More Data Expected\n")); \
|
|
_dbg_->ErrorCode = HIDP_GETCOLDESC_FOUR_BYTE; \
|
|
_dbg_->BreakOffset = descIndex; \
|
|
goto HIDP_PARSE_REJECT; \
|
|
} \
|
|
(_data_) = *(RepDesc + (_pos_)++); \
|
|
(_data_) |= *(RepDesc + (_pos_)++) << 8; \
|
|
(_data_) |= *(RepDesc + (_pos_)++) << 16; \
|
|
(_data_) |= *(RepDesc + (_pos_)++) << 24;
|
|
|
|
#define BIT_EXTEND_1(_data_) \
|
|
(_data_) = ((_data_) & 0xFF) \
|
|
| (((_data_) & 0x80) ? 0xFFFFFF00 : 0)
|
|
|
|
#define BIT_EXTEND_2(_data_) \
|
|
(_data_) = ((_data_) & 0xFFFF) \
|
|
| (((_data_) & 0x8000) ? 0xFFFF0000 : 0)
|
|
|
|
|
|
NTSTATUS
|
|
HidP_ParseCollections (
|
|
IN PHIDP_REPORT_DESCRIPTOR RepDesc,
|
|
IN ULONG RepDescLen,
|
|
IN POOL_TYPE PoolType,
|
|
IN OUT PHIDP_COLLECTION_DESC_LIST Cols,
|
|
IN ULONG NumCols,
|
|
OUT PHIDP_GETCOLDESC_DBG Dbg,
|
|
IN OUT PHIDP_DEVICE_DESC DeviceDesc)
|
|
/*++
|
|
Routine Description:
|
|
Given a nice linked list of collection descriptors parse into those
|
|
descriptors the information descerned from the Raw Report Descriptor.
|
|
Each given CollectionDescriptor already has the proper amount of memory
|
|
in the PreparsedData field.
|
|
|
|
Parameters:
|
|
Rep The given raw report descriptor.
|
|
RepLen Length of this said descriptor.
|
|
ColsRet The head of the list of collection descriptors.
|
|
NumCols Then number of collection descriptors in said list.
|
|
--*/
|
|
{
|
|
HIDP_PREPARSED_DATA safeData;
|
|
HIDP_PARSE_GLOBAL_PUSH firstPush = {0,0,0,0,0,0,0,0,0,0,0};
|
|
HIDP_PARSE_LOCAL_RANGE_LIST firstUsage = {0,0,0,0,0};
|
|
HIDP_PARSE_LOCAL_RANGE designator = {0,0,0,0};
|
|
HIDP_PARSE_LOCAL_RANGE string = {0,0,0,0};
|
|
HIDP_PARSE_LOCAL_RANGE zeroLocal = {0,0,0,0};
|
|
|
|
PHIDP_COLLECTION_DESC_LIST appCol = 0;
|
|
PHIDP_PREPARSED_DATA preparsed = &safeData;
|
|
PHIDP_PARSE_GLOBAL_PUSH push = &firstPush;
|
|
PHIDP_PARSE_GLOBAL_PUSH tmpPush = 0;
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST usage = &firstUsage;
|
|
PHIDP_PARSE_LOCAL_RANGE_LIST tmpUsage = 0;
|
|
PHIDP_CHANNEL_DESC channel = 0;
|
|
PHIDP_PRIVATE_LINK_COLLECTION_NODE linkNodeArray = 0;
|
|
PHIDP_PRIVATE_LINK_COLLECTION_NODE parentLCNode = 0;
|
|
PHIDP_PRIVATE_LINK_COLLECTION_NODE currentLCNode = 0;
|
|
struct _HIDP_UNKNOWN_TOKEN * unknownToken;
|
|
USHORT linkNodeIndex = 0;
|
|
|
|
ULONG descIndex = 0;
|
|
ULONG colDepth = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
USHORT bitPos;
|
|
HIDP_ITEM item;
|
|
USHORT tmpBitField = 0;
|
|
USHORT tmpCount = 0;
|
|
USHORT i;
|
|
PUSHORT channelIndex = 0;
|
|
|
|
PHIDP_REPORT_IDS currentReportIDs = DeviceDesc->ReportIDs;
|
|
PHIDP_REPORT_IDS tmpReportIDs;
|
|
BOOLEAN isFirstReportID = TRUE;
|
|
BOOLEAN withinDelimiter = FALSE;
|
|
BOOLEAN firstUsageWithinDelimiter = TRUE;
|
|
BOOLEAN isAlias = FALSE;
|
|
UCHAR collectionType;
|
|
UCHAR tmpID;
|
|
|
|
UNREFERENCED_PARAMETER (NumCols);
|
|
|
|
while (descIndex < RepDescLen)
|
|
{
|
|
item = *(RepDesc + descIndex++);
|
|
switch (item)
|
|
{
|
|
case HIDP_MAIN_COLLECTION:
|
|
ONE_BYTE_DATA (collectionType, descIndex, Dbg);
|
|
if (1 == ++colDepth)
|
|
{
|
|
//
|
|
// We will regard any top level collection as an application
|
|
// collection as approved by the HID committee
|
|
//
|
|
// we will regard second level collections as a link collection.
|
|
//
|
|
|
|
if (appCol)
|
|
{
|
|
appCol = appCol->NextCollection;
|
|
} else
|
|
{
|
|
appCol = Cols;
|
|
}
|
|
ASSERT (appCol);
|
|
|
|
HidP_KdPrint(0, ("'Parse Collection %d \n", appCol->CollectionNumber));
|
|
|
|
preparsed = appCol->PreparsedData;
|
|
ASSERT (preparsed);
|
|
|
|
//
|
|
// Set up the report IDs for this collection
|
|
// There is one report ID array for all top level collections
|
|
//
|
|
push->ReportIDs = currentReportIDs;
|
|
isFirstReportID = TRUE;
|
|
// Make room for the Report ID as the first byte.
|
|
currentReportIDs->InputLength = 8;
|
|
currentReportIDs->OutputLength = 8;
|
|
currentReportIDs->FeatureLength = 8;
|
|
currentReportIDs->ReportID = 0;
|
|
currentReportIDs->CollectionNumber = appCol->CollectionNumber;
|
|
currentReportIDs++;
|
|
|
|
preparsed->UsagePage = appCol->UsagePage = usage->UsagePage ?
|
|
usage->UsagePage :
|
|
push->UsagePage;
|
|
if (usage->Range){
|
|
preparsed->Usage = appCol->Usage = usage->Min;
|
|
} else {
|
|
preparsed->Usage = appCol->Usage = usage->Value;
|
|
}
|
|
designator = string = zeroLocal;
|
|
usage = HidP_FreeUsageList (usage);
|
|
if (0 == appCol->Usage) {
|
|
//
|
|
// Explicitly check for Usage ID (0) which is reserved
|
|
//
|
|
HidP_KdPrint(2, ("Top Level Collection %x defined with Report ID 0! (UP: %x)\n",
|
|
appCol->CollectionNumber,
|
|
appCol->UsagePage));
|
|
#if 0
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_TOP_COLLECTION_USAGE;
|
|
Dbg->Args[0] = appCol->CollectionNumber;
|
|
status = STATUS_COULD_NOT_INTERPRET;
|
|
goto HIDP_PARSE_REJECT;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Initialize the Link node array for this top level collection.
|
|
// There is a link node array for each top level collection
|
|
//
|
|
linkNodeArray = (PHIDP_PRIVATE_LINK_COLLECTION_NODE)
|
|
(preparsed->RawBytes +
|
|
preparsed->LinkCollectionArrayOffset);
|
|
|
|
ASSERT (0 < preparsed->LinkCollectionArrayLength);
|
|
parentLCNode = &(linkNodeArray[0]);
|
|
currentLCNode = &(linkNodeArray[0]);
|
|
linkNodeIndex = 0;
|
|
|
|
parentLCNode->LinkUsagePage = appCol->UsagePage;
|
|
parentLCNode->LinkUsage = appCol->Usage;
|
|
parentLCNode->Parent = 0;
|
|
parentLCNode->NumberOfChildren = 0;
|
|
parentLCNode->NextSibling = 0;
|
|
parentLCNode->FirstChild = 0;
|
|
parentLCNode->CollectionType = collectionType;
|
|
|
|
} else if (1 < colDepth)
|
|
{
|
|
linkNodeIndex++;
|
|
parentLCNode = currentLCNode;
|
|
ASSERT (linkNodeIndex < preparsed->LinkCollectionArrayLength);
|
|
currentLCNode = &linkNodeArray[linkNodeIndex];
|
|
|
|
//
|
|
// Pop of the usage stack all the usages which are aliases, and
|
|
// create a link collection node for each one.
|
|
// Each allias link collection node has the IsAlias bit set.
|
|
// The last one does not have the bit set, and becomes the
|
|
// collection number for all controls list within this aliased
|
|
// link collection.
|
|
//
|
|
//
|
|
|
|
while (TRUE) {
|
|
currentLCNode->LinkUsagePage = usage->UsagePage ?
|
|
usage->UsagePage :
|
|
push->UsagePage;
|
|
currentLCNode->LinkUsage = usage->Range ?
|
|
usage->Min :
|
|
usage->Value;
|
|
currentLCNode->Parent = (USHORT)(parentLCNode - linkNodeArray);
|
|
ASSERT (currentLCNode->Parent < preparsed->LinkCollectionArrayLength);
|
|
currentLCNode->NumberOfChildren = 0;
|
|
currentLCNode->FirstChild = 0;
|
|
currentLCNode->NextSibling = parentLCNode->FirstChild;
|
|
parentLCNode->FirstChild = linkNodeIndex;
|
|
parentLCNode->NumberOfChildren++;
|
|
currentLCNode->CollectionType = collectionType;
|
|
|
|
if (usage->IsAlias) {
|
|
currentLCNode->IsAlias = TRUE;
|
|
linkNodeIndex++;
|
|
ASSERT (linkNodeIndex < preparsed->LinkCollectionArrayLength);
|
|
currentLCNode = &linkNodeArray[linkNodeIndex];
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
designator = string = zeroLocal;
|
|
usage = HidP_FreeUsageList (usage);
|
|
}
|
|
break;
|
|
|
|
case HIDP_MAIN_ENDCOLLECTION:
|
|
if (0 == colDepth--) {
|
|
status = STATUS_COULD_NOT_INTERPRET;
|
|
goto HIDP_PARSE_REJECT;
|
|
|
|
} else if (0 < colDepth) {
|
|
ASSERT ((parentLCNode - linkNodeArray) == currentLCNode->Parent);
|
|
currentLCNode = parentLCNode;
|
|
ASSERT (currentLCNode->Parent < preparsed->LinkCollectionArrayLength);
|
|
parentLCNode = &linkNodeArray[currentLCNode->Parent];
|
|
break;
|
|
}
|
|
|
|
HidP_KdPrint(0, ("'X Parse Collection %d \n", appCol->CollectionNumber));
|
|
|
|
|
|
//
|
|
// Walk the report IDs for this collection
|
|
//
|
|
for (tmpReportIDs = currentReportIDs - 1;
|
|
tmpReportIDs != DeviceDesc->ReportIDs - 1;
|
|
tmpReportIDs--)
|
|
{
|
|
if (tmpReportIDs->CollectionNumber != appCol->CollectionNumber)
|
|
{
|
|
continue;
|
|
}
|
|
if ((0 != (tmpReportIDs->InputLength & 7)) ||
|
|
(0 != (tmpReportIDs->OutputLength & 7)) ||
|
|
(0 != (tmpReportIDs->FeatureLength & 7)))
|
|
{
|
|
HidP_KdPrint(2, ("Col %x Report %x NOT byte alligned!! %x %x %x\n",
|
|
appCol->CollectionNumber,
|
|
tmpReportIDs->ReportID,
|
|
tmpReportIDs->InputLength,
|
|
tmpReportIDs->OutputLength,
|
|
tmpReportIDs->FeatureLength));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_BYTE_ALLIGN;
|
|
Dbg->Args[0] = appCol->CollectionNumber,
|
|
Dbg->Args[1] = tmpReportIDs->ReportID,
|
|
Dbg->Args[2] = tmpReportIDs->InputLength;
|
|
Dbg->Args[3] = tmpReportIDs->OutputLength;
|
|
Dbg->Args[4] = tmpReportIDs->FeatureLength;
|
|
status = STATUS_COULD_NOT_INTERPRET;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
|
|
preparsed->Input.ByteLen = MAX (preparsed->Input.ByteLen,
|
|
tmpReportIDs->InputLength >> 3);
|
|
preparsed->Output.ByteLen = MAX (preparsed->Output.ByteLen,
|
|
tmpReportIDs->OutputLength >> 3);
|
|
preparsed->Feature.ByteLen = MAX (preparsed->Feature.ByteLen,
|
|
tmpReportIDs->FeatureLength >> 3);
|
|
|
|
//
|
|
// We are now done with this report so convert the length to
|
|
// bytes instead of bits, and remove the report id, if the
|
|
// device will not send one.
|
|
//
|
|
|
|
if (0 == tmpReportIDs->ReportID)
|
|
{
|
|
// The report ID was never set; therefore, for input the device
|
|
// will not send a report id.
|
|
tmpReportIDs->InputLength = (tmpReportIDs->InputLength >> 3) - 1;
|
|
tmpReportIDs->OutputLength = (tmpReportIDs->OutputLength >> 3) -1;
|
|
tmpReportIDs->FeatureLength = (tmpReportIDs->FeatureLength >> 3) -1;
|
|
} else
|
|
{
|
|
tmpReportIDs->InputLength = (8 == tmpReportIDs->InputLength)
|
|
? 0
|
|
: tmpReportIDs->InputLength >> 3;
|
|
tmpReportIDs->OutputLength = (8 == tmpReportIDs->OutputLength)
|
|
? 0
|
|
: tmpReportIDs->OutputLength >> 3;
|
|
tmpReportIDs->FeatureLength = (8 == tmpReportIDs->FeatureLength)
|
|
? 0
|
|
: tmpReportIDs->FeatureLength >> 3;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This field is adjusted and always accounts for a space for the
|
|
// included report ID, even if the device itslef has only one report
|
|
// and therefore sends no report ids. (The input report is one byte
|
|
// smaller.)
|
|
//
|
|
// BUT if the length is one, then only the report ID exists.
|
|
// This means that the device has no data to send for that field.
|
|
// Therefore return zero.
|
|
//
|
|
// Remember that the BitLen fields were spiked earlier with values
|
|
// of 8 (one byte).
|
|
//
|
|
// appCol->XXXLength is the length expected from/by the client
|
|
// currentReportID->XxLength == the length expected from/by the device
|
|
//
|
|
if (1 == (appCol->InputLength = preparsed->Input.ByteLen))
|
|
{
|
|
appCol->InputLength = preparsed->Input.ByteLen = 0;
|
|
}
|
|
if (1 == (appCol->OutputLength = preparsed->Output.ByteLen))
|
|
{
|
|
appCol->OutputLength = preparsed->Output.ByteLen = 0;
|
|
}
|
|
if (1 == (appCol->FeatureLength = preparsed->Feature.ByteLen))
|
|
{
|
|
appCol->FeatureLength = preparsed->Feature.ByteLen = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case HIDP_GLOBAL_USAGE_PAGE_1:
|
|
ONE_BYTE_DATA (push->UsagePage, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_USAGE_PAGE_2:
|
|
TWO_BYTE_DATA (push->UsagePage, descIndex, Dbg);
|
|
break;
|
|
|
|
//
|
|
// 16 bits allowed only.
|
|
// case HIDP_GLOBAL_USAGE_PAGE_4:
|
|
// FOUR_BYTE_DATA (push->UsagePage, descIndex, Dbg);
|
|
// break;
|
|
//
|
|
|
|
case HIDP_GLOBAL_LOG_MIN_1:
|
|
ONE_BYTE_DATA (push->LogicalMin, descIndex, Dbg);
|
|
BIT_EXTEND_1 (push->LogicalMin);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_LOG_MIN_2:
|
|
TWO_BYTE_DATA (push->LogicalMin, descIndex, Dbg);
|
|
BIT_EXTEND_2 (push->LogicalMin);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_LOG_MIN_4:
|
|
FOUR_BYTE_DATA (push->LogicalMin, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_LOG_MAX_1:
|
|
ONE_BYTE_DATA (push->LogicalMax, descIndex, Dbg);
|
|
BIT_EXTEND_1 (push->LogicalMax);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_LOG_MAX_2:
|
|
TWO_BYTE_DATA (push->LogicalMax, descIndex, Dbg);
|
|
BIT_EXTEND_2 (push->LogicalMax);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_LOG_MAX_4:
|
|
FOUR_BYTE_DATA (push->LogicalMax, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_PHY_MIN_1:
|
|
ONE_BYTE_DATA (push->PhysicalMin, descIndex, Dbg);
|
|
BIT_EXTEND_1 (push->PhysicalMin);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_PHY_MIN_2:
|
|
TWO_BYTE_DATA (push->PhysicalMin, descIndex, Dbg);
|
|
BIT_EXTEND_2 (push->PhysicalMin);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_PHY_MIN_4:
|
|
FOUR_BYTE_DATA (push->PhysicalMin, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_PHY_MAX_1:
|
|
ONE_BYTE_DATA (push->PhysicalMax, descIndex, Dbg);
|
|
BIT_EXTEND_1 (push->PhysicalMax);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_PHY_MAX_2:
|
|
TWO_BYTE_DATA (push->PhysicalMax, descIndex, Dbg);
|
|
BIT_EXTEND_2 (push->PhysicalMax);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_PHY_MAX_4:
|
|
FOUR_BYTE_DATA (push->PhysicalMax, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_UNIT_EXP_1:
|
|
ONE_BYTE_DATA (push->UnitExp, descIndex, Dbg);
|
|
BIT_EXTEND_1 (push->UnitExp);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_UNIT_EXP_2:
|
|
TWO_BYTE_DATA (push->UnitExp, descIndex, Dbg);
|
|
BIT_EXTEND_2 (push->UnitExp);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_UNIT_EXP_4:
|
|
FOUR_BYTE_DATA (push->UnitExp, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_UNIT_1:
|
|
ONE_BYTE_DATA (push->Unit, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_UNIT_2:
|
|
TWO_BYTE_DATA (push->Unit, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_UNIT_4:
|
|
FOUR_BYTE_DATA (push->Unit, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_REPORT_SIZE:
|
|
ONE_BYTE_DATA (push->ReportSize, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_REPORT_COUNT_1:
|
|
ONE_BYTE_DATA (push->ReportCount, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_REPORT_COUNT_2:
|
|
TWO_BYTE_DATA (push->ReportCount, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_GLOBAL_REPORT_ID:
|
|
//
|
|
// If a device has no report GLOBAL_REPORT_ID token in its descriptor
|
|
// then it will never transmit a report ID in its input reports,
|
|
// and the report ID for each channel will be set to zero.
|
|
//
|
|
// But, if anywhere in the report, a device declares a report ID
|
|
// that device must always transmit a report ID with input reports,
|
|
// AND more importantly that report ID MUST NOT BE ZERO.
|
|
//
|
|
// This means that if we find a report id token, that we can just
|
|
// overwrite the first report ID structure with the given report ID
|
|
// because we know that the first ID structure (initialized to zero
|
|
// and therefore not valid) will not be used for any of the channels.
|
|
//
|
|
ONE_BYTE_DATA (tmpID, descIndex, Dbg);
|
|
|
|
//
|
|
// Search to see if this report id has been used before.
|
|
//
|
|
for (tmpReportIDs = DeviceDesc->ReportIDs;
|
|
tmpReportIDs != currentReportIDs;
|
|
tmpReportIDs++) {
|
|
|
|
if (tmpReportIDs->ReportID == tmpID) {
|
|
//
|
|
// A duplicate!
|
|
// Make sure that it is for this same collection
|
|
//
|
|
if (tmpReportIDs->CollectionNumber != appCol->CollectionNumber) {
|
|
HidP_KdPrint(2, ("Reports cannot span more than one top level \n"));
|
|
HidP_KdPrint(2, ("Report ID %d found in collections [%d %d]",
|
|
(ULONG) tmpID,
|
|
(ULONG) tmpReportIDs->CollectionNumber,
|
|
(ULONG) appCol->CollectionNumber));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_REPORT_ID;
|
|
Dbg->Args[0] = item;
|
|
status = HIDP_STATUS_INVALID_REPORT_TYPE;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
//
|
|
// Use this report ID.
|
|
//
|
|
push->ReportIDs = tmpReportIDs;
|
|
break;
|
|
}
|
|
} // continue looking.
|
|
|
|
if (isFirstReportID) {
|
|
isFirstReportID = FALSE;
|
|
} else if (tmpReportIDs == currentReportIDs) {
|
|
//
|
|
// We have not seen this report ID before.
|
|
// make a new container.
|
|
//
|
|
push->ReportIDs = currentReportIDs;
|
|
// Make room for the Report ID as the first byte.
|
|
currentReportIDs->InputLength = 8;
|
|
currentReportIDs->OutputLength = 8;
|
|
currentReportIDs->FeatureLength = 8;
|
|
currentReportIDs->CollectionNumber = appCol->CollectionNumber;
|
|
currentReportIDs++;
|
|
}
|
|
|
|
push->ReportIDs->ReportID = tmpID;
|
|
|
|
if (0 == push->ReportIDs->ReportID) {
|
|
status = HIDP_STATUS_INVALID_REPORT_TYPE;
|
|
HidP_KdPrint(2, ("Report IDs cannot be zero (0)\n"));
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_BAD_REPORT_ID;
|
|
Dbg->BreakOffset = descIndex;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
break;
|
|
|
|
case HIDP_GLOBAL_PUSH:
|
|
tmpPush = (PHIDP_PARSE_GLOBAL_PUSH)
|
|
ExAllocatePool (PoolType, sizeof (HIDP_PARSE_GLOBAL_PUSH));
|
|
if (!tmpPush)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
HidP_KdPrint(2, ("No Resources to Push global stack\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_PUSH_RESOURCES;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
HidP_KdPrint(0, ("Push Global Stack\n"));
|
|
*tmpPush = *push;
|
|
tmpPush->Pop = push;
|
|
push = tmpPush;
|
|
break;
|
|
|
|
case HIDP_GLOBAL_POP:
|
|
tmpPush = push->Pop;
|
|
ExFreePool (push);
|
|
push = tmpPush;
|
|
HidP_KdPrint(0, ("Pop Global Stack\n"));
|
|
|
|
break;
|
|
|
|
//
|
|
// Local Items
|
|
//
|
|
|
|
//
|
|
// We already verified that only "approved" tokens will be within
|
|
// the open / close of the following delimiter. This simplifies
|
|
// our parsing here tremendously.
|
|
//
|
|
case HIDP_LOCAL_DELIMITER:
|
|
ONE_BYTE_DATA (item, descIndex, Dbg);
|
|
if (1 == item) {
|
|
withinDelimiter = TRUE;
|
|
firstUsageWithinDelimiter = TRUE;
|
|
} else if (0 == item) {
|
|
withinDelimiter = FALSE;
|
|
} else {
|
|
TRAP ();
|
|
}
|
|
break;
|
|
|
|
case HIDP_LOCAL_USAGE_1:
|
|
case HIDP_LOCAL_USAGE_2:
|
|
case HIDP_LOCAL_USAGE_4:
|
|
if ((&firstUsage == usage) || usage->Value || usage->Max || usage->Min) {
|
|
usage = HidP_PushUsageList (usage, PoolType, withinDelimiter);
|
|
if (!usage) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
HidP_KdPrint(2, ("No Resources to Push Usage stack\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_PUSH_RESOURCES;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
}
|
|
usage->Range = FALSE;
|
|
if (HIDP_LOCAL_USAGE_1 == item) {
|
|
ONE_BYTE_DATA (usage->Value, descIndex, Dbg);
|
|
} else if (HIDP_LOCAL_USAGE_2 == item) {
|
|
TWO_BYTE_DATA (usage->Value, descIndex, Dbg);
|
|
} else {
|
|
TWO_BYTE_DATA (usage->Value, descIndex, Dbg);
|
|
TWO_BYTE_DATA (usage->UsagePage, descIndex, Dbg);
|
|
// upper 16 bits overwrite the default usage page.
|
|
}
|
|
|
|
if (withinDelimiter) {
|
|
usage->IsAlias = !firstUsageWithinDelimiter;
|
|
firstUsageWithinDelimiter = FALSE;
|
|
}
|
|
if (0 == usage->Value) {
|
|
//
|
|
// Test to see if they have used Usage ID (0) which is reserved.
|
|
// But instead of breaking just print an error
|
|
//
|
|
HidP_KdPrint(2, ("Usage ID (0) explicitly usaged! This usage is reserved. Offset (%x)\n",
|
|
descIndex));
|
|
}
|
|
break;
|
|
|
|
//
|
|
// NB: before we can add delimiters to usage ranges we must insure
|
|
// that the range is identical for all entries within the delimiter.
|
|
//
|
|
|
|
case HIDP_LOCAL_USAGE_MIN_1:
|
|
case HIDP_LOCAL_USAGE_MIN_2:
|
|
case HIDP_LOCAL_USAGE_MIN_4:
|
|
if ((&firstUsage == usage) || (usage->Min) || (usage->Value)) {
|
|
usage = HidP_PushUsageList (usage, PoolType, FALSE);
|
|
if (!usage) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
HidP_KdPrint(2, ("No Resources to Push Usage stack\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_PUSH_RESOURCES;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
}
|
|
usage->Range = TRUE;
|
|
if (HIDP_LOCAL_USAGE_MIN_1 == item) {
|
|
ONE_BYTE_DATA (usage->Min, descIndex, Dbg);
|
|
} else if (HIDP_LOCAL_USAGE_MIN_2 == item) {
|
|
TWO_BYTE_DATA (usage->Min, descIndex, Dbg);
|
|
} else {
|
|
TWO_BYTE_DATA (usage->Min, descIndex, Dbg);
|
|
TWO_BYTE_DATA (usage->UsagePage, descIndex, Dbg);
|
|
// upper 16 bits overwrite the default usage page.
|
|
}
|
|
break;
|
|
|
|
case HIDP_LOCAL_USAGE_MAX_1:
|
|
case HIDP_LOCAL_USAGE_MAX_2:
|
|
case HIDP_LOCAL_USAGE_MAX_4:
|
|
if ((&firstUsage == usage) || (usage->Max) || (usage->Value)) {
|
|
usage = HidP_PushUsageList (usage, PoolType, FALSE);
|
|
if (!usage) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
HidP_KdPrint(2, ("No Resources to Push Usage stack\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_PUSH_RESOURCES;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
}
|
|
usage->Range = TRUE;
|
|
if (HIDP_LOCAL_USAGE_MAX_1 == item) {
|
|
ONE_BYTE_DATA (usage->Max, descIndex, Dbg);
|
|
} else if (HIDP_LOCAL_USAGE_MAX_2 == item) {
|
|
TWO_BYTE_DATA (usage->Max, descIndex, Dbg);
|
|
} else {
|
|
TWO_BYTE_DATA (usage->Max, descIndex, Dbg);
|
|
TWO_BYTE_DATA (usage->UsagePage, descIndex, Dbg);
|
|
// upper 16 bits overwrite the default usage page.
|
|
}
|
|
break;
|
|
|
|
case HIDP_LOCAL_DESIG_INDEX:
|
|
designator.Range = FALSE;
|
|
ONE_BYTE_DATA (designator.Value, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_LOCAL_DESIG_MIN:
|
|
designator.Range = TRUE;
|
|
ONE_BYTE_DATA (designator.Min, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_LOCAL_DESIG_MAX:
|
|
designator.Range = TRUE;
|
|
ONE_BYTE_DATA (designator.Max, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_LOCAL_STRING_INDEX:
|
|
string.Range = FALSE;
|
|
ONE_BYTE_DATA (string.Value, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_LOCAL_STRING_MIN:
|
|
string.Range = TRUE;
|
|
ONE_BYTE_DATA (string.Min, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_LOCAL_STRING_MAX:
|
|
string.Range = TRUE;
|
|
ONE_BYTE_DATA (string.Max, descIndex, Dbg);
|
|
break;
|
|
|
|
case HIDP_MAIN_INPUT_1:
|
|
tmpReportIDs = push->ReportIDs;
|
|
bitPos = tmpReportIDs->InputLength; // The distance into the report
|
|
HidP_KdPrint(0, ("'Main Offset:%x \n", bitPos));
|
|
tmpReportIDs->InputLength += push->ReportSize * push->ReportCount;
|
|
channelIndex = &(preparsed->Input.Index);
|
|
ONE_BYTE_DATA (tmpBitField, descIndex, Dbg);
|
|
goto HIDP_PARSE_MAIN_ITEM;
|
|
|
|
case HIDP_MAIN_INPUT_2:
|
|
tmpReportIDs = push->ReportIDs;
|
|
bitPos = tmpReportIDs->InputLength; // The distance into the report
|
|
HidP_KdPrint(0, ("'Main2 offset:%x \n", bitPos));
|
|
tmpReportIDs->InputLength += push->ReportSize * push->ReportCount;
|
|
channelIndex = &(preparsed->Input.Index);
|
|
TWO_BYTE_DATA (tmpBitField, descIndex, Dbg);
|
|
goto HIDP_PARSE_MAIN_ITEM;
|
|
|
|
case HIDP_MAIN_OUTPUT_1:
|
|
tmpReportIDs = push->ReportIDs;
|
|
bitPos = tmpReportIDs->OutputLength; // The distance into the report
|
|
HidP_KdPrint(0, ("'Out offset:%x \n", bitPos));
|
|
tmpReportIDs->OutputLength += push->ReportSize * push->ReportCount;
|
|
channelIndex = &(preparsed->Output.Index);
|
|
ONE_BYTE_DATA (tmpBitField, descIndex, Dbg);
|
|
goto HIDP_PARSE_MAIN_ITEM;
|
|
|
|
case HIDP_MAIN_OUTPUT_2:
|
|
tmpReportIDs = push->ReportIDs;
|
|
bitPos = tmpReportIDs->OutputLength; // The distance into the report
|
|
HidP_KdPrint(0, ("'Out2 offset:%x \n", bitPos));
|
|
tmpReportIDs->OutputLength += push->ReportSize * push->ReportCount;
|
|
channelIndex = &(preparsed->Output.Index);
|
|
TWO_BYTE_DATA (tmpBitField, descIndex, Dbg);
|
|
goto HIDP_PARSE_MAIN_ITEM;
|
|
|
|
case HIDP_MAIN_FEATURE_1:
|
|
tmpReportIDs = push->ReportIDs;
|
|
bitPos = tmpReportIDs->FeatureLength; // The distance into the report
|
|
HidP_KdPrint(0, ("'Feature offset:%x \n", bitPos));
|
|
tmpReportIDs->FeatureLength += push->ReportSize * push->ReportCount;
|
|
channelIndex = &(preparsed->Feature.Index);
|
|
ONE_BYTE_DATA (tmpBitField, descIndex, Dbg);
|
|
goto HIDP_PARSE_MAIN_ITEM;
|
|
|
|
case HIDP_MAIN_FEATURE_2:
|
|
tmpReportIDs = push->ReportIDs;
|
|
bitPos = tmpReportIDs->FeatureLength; // The distance into the report
|
|
HidP_KdPrint(0, ("'Feature2 offset:%x \n", bitPos));
|
|
tmpReportIDs->FeatureLength += push->ReportSize * push->ReportCount;
|
|
channelIndex = &(preparsed->Feature.Index);
|
|
TWO_BYTE_DATA (tmpBitField, descIndex, Dbg);
|
|
|
|
HIDP_PARSE_MAIN_ITEM:
|
|
|
|
// You can have a constant field that does return data.
|
|
// so we probably shouldn't skip it.
|
|
// BUT it should NOT be an array style button field.
|
|
if (HIDP_ISARRAY (tmpBitField)) {
|
|
if (HIDP_ISCONST(tmpBitField)) {
|
|
break;
|
|
}
|
|
//
|
|
// Here we have a list of indices that refer to the usages
|
|
// described prior. For each of the prior usages, up to the depth
|
|
// found, we allocate a channel structure to describe the given
|
|
// usages. These channels are linked so that we will later know
|
|
// that they all describe the same filled.
|
|
//
|
|
|
|
//
|
|
// We do no support delimiteres in array declairations.
|
|
// To do so would require a large change to Index2Usage which
|
|
// instead of returning only one usage would have to return
|
|
// several.
|
|
//
|
|
|
|
if (usage->IsAlias) {
|
|
status = STATUS_COULD_NOT_INTERPRET;
|
|
HidP_KdPrint(2, ("Currently this parser does not support\n"));
|
|
HidP_KdPrint(2, ("Delimiters for array declairations\n"));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_UNSUPPORTED;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
|
|
for ( ;
|
|
usage != &firstUsage;
|
|
(*channelIndex)++,
|
|
usage = HidP_PopUsageList (usage)) {
|
|
|
|
channel = &(preparsed->Data[*channelIndex]);
|
|
|
|
channel->BitField = tmpBitField;
|
|
|
|
// field that says this channel is linked
|
|
channel->MoreChannels = TRUE;
|
|
|
|
// say what link collection number we are in.
|
|
channel->LinkCollection = (USHORT)(currentLCNode - linkNodeArray);
|
|
channel->LinkUsage = currentLCNode->LinkUsage;
|
|
channel->LinkUsagePage = currentLCNode->LinkUsagePage;
|
|
|
|
if (usage->UsagePage) {
|
|
// The default usage page been overwritten.
|
|
channel->UsagePage = usage->UsagePage;
|
|
} else {
|
|
channel->UsagePage = push->UsagePage;
|
|
}
|
|
|
|
channel->BitOffset = (UCHAR) bitPos & 7;
|
|
channel->ByteOffset = (USHORT) bitPos >> 3;
|
|
channel->ReportSize = push->ReportSize;
|
|
channel->ReportCount = push->ReportCount;
|
|
|
|
channel->BitLength = push->ReportSize * push->ReportCount;
|
|
channel->ByteEnd = (channel->BitOffset + channel->BitLength);
|
|
channel->ByteEnd = (channel->ByteEnd >> 3)
|
|
+ ((channel->ByteEnd & 7) ? 1 : 0)
|
|
+ channel->ByteOffset;
|
|
|
|
|
|
channel->ReportID = push->ReportIDs->ReportID;
|
|
channel->Units = push->Unit;
|
|
channel->UnitExp = push->UnitExp;
|
|
|
|
channel->IsConst = FALSE;
|
|
|
|
channel->IsButton = TRUE;
|
|
channel->IsAbsolute = HIDP_ISABSOLUTE(tmpBitField);
|
|
channel->button.LogicalMin = push->LogicalMin;
|
|
channel->button.LogicalMax = push->LogicalMax;
|
|
|
|
channel->IsRange = usage->Range;
|
|
channel->IsDesignatorRange = designator.Range;
|
|
channel->IsStringRange = string.Range;
|
|
|
|
if (usage->Range) {
|
|
channel->Range.UsageMin = usage->Min;
|
|
channel->Range.UsageMax = usage->Max;
|
|
} else {
|
|
channel->Range.UsageMin =
|
|
channel->Range.UsageMax = usage->Value;
|
|
}
|
|
if (designator.Range) {
|
|
channel->Range.DesignatorMin = designator.Min;
|
|
channel->Range.DesignatorMax = designator.Max;
|
|
} else {
|
|
channel->Range.DesignatorMin =
|
|
channel->Range.DesignatorMax = designator.Value;
|
|
}
|
|
|
|
if (string.Range) {
|
|
channel->Range.StringMin = string.Min;
|
|
channel->Range.StringMax = string.Max;
|
|
} else {
|
|
channel->Range.StringMin =
|
|
channel->Range.StringMax = string.Value;
|
|
}
|
|
|
|
channel->NumGlobalUnknowns = push->NumGlobalUnknowns;
|
|
|
|
if (push->NumGlobalUnknowns) {
|
|
RtlCopyMemory (channel->GlobalUnknowns,
|
|
push->GlobalUnknowns,
|
|
push->NumGlobalUnknowns
|
|
* sizeof (HIDP_UNKNOWN_TOKEN));
|
|
}
|
|
|
|
//
|
|
// Check for power buttons
|
|
//
|
|
if (HIDP_USAGE_SYSCTL_PAGE == channel->UsagePage) {
|
|
if ((channel->Range.UsageMin <= HIDP_USAGE_SYSCTL_POWER) &&
|
|
(HIDP_USAGE_SYSCTL_POWER <= channel->Range.UsageMax)) {
|
|
preparsed->PowerButtonMask |= SYS_BUTTON_POWER;
|
|
}
|
|
if ((channel->Range.UsageMin <= HIDP_USAGE_SYSCTL_SLEEP) &&
|
|
(HIDP_USAGE_SYSCTL_SLEEP <= channel->Range.UsageMax)) {
|
|
preparsed->PowerButtonMask |= SYS_BUTTON_SLEEP;
|
|
}
|
|
if ((channel->Range.UsageMin <= HIDP_USAGE_SYSCTL_WAKE) &&
|
|
(HIDP_USAGE_SYSCTL_WAKE <= channel->Range.UsageMax)) {
|
|
preparsed->PowerButtonMask |= SYS_BUTTON_WAKE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT (0 == usage->Depth);
|
|
|
|
channel->MoreChannels = FALSE;
|
|
designator = string = zeroLocal;
|
|
break;
|
|
} // end array style channel
|
|
|
|
|
|
channel = &(preparsed->Data[*channelIndex]);
|
|
if (HIDP_ISCONST(tmpBitField)) {
|
|
if ((0 == usage->Depth) ||
|
|
((0 == usage->Value) && (0 == usage->Min)
|
|
&& (0 == usage->Max))) {
|
|
//
|
|
// A constant channel with no usage. Skip it.
|
|
//
|
|
|
|
usage = HidP_FreeUsageList (usage);
|
|
ASSERT (usage == &firstUsage);
|
|
ASSERT (0 == usage->Depth);
|
|
break;
|
|
}
|
|
channel->IsConst = TRUE;
|
|
} else {
|
|
channel->IsConst = FALSE;
|
|
}
|
|
|
|
tmpCount = usage->Depth // - 1
|
|
+ (usage->Range ? (usage->Max - usage->Min) : 0); // + 1
|
|
|
|
#if 0
|
|
while (tmpCount > push->ReportCount) {
|
|
// Get rid of excess usages.
|
|
tmpCount = usage->Depth - 1;
|
|
usage = HidP_PopUsageList (usage);
|
|
|
|
ASSERT (tmpCount == (usage->Depth +
|
|
(usage->Range ? (usage->Max - usage->Min) : 0)));
|
|
}
|
|
#else
|
|
while (tmpCount > push->ReportCount) {
|
|
// Get rid of excess usages.
|
|
|
|
if (tmpCount <= usage->Depth) {
|
|
// We've got enough in the linked usages to fulfill this request
|
|
tmpCount = usage->Depth - 1;
|
|
usage = HidP_PopUsageList (usage);
|
|
|
|
ASSERT (tmpCount ==
|
|
(usage->Depth +
|
|
(usage->Range ? (usage->Max - usage->Min) : 0)));
|
|
} else {
|
|
// We don't have enough in the linked usages, but we've too
|
|
// much in this range. So, adjust the max value of the
|
|
// range so that it won't be too many usages.
|
|
|
|
ASSERT (usage->Range);
|
|
usage->Max = push->ReportCount - usage->Depth + usage->Min;
|
|
|
|
tmpCount = usage->Depth + (usage->Max - usage->Min);
|
|
}
|
|
}
|
|
ASSERT (tmpCount <= push->ReportCount);
|
|
// Now we should no longer have too many usages.
|
|
//
|
|
#endif
|
|
//
|
|
// The last value in the link (aka the top) must be
|
|
// repeated if there are less usages than there are
|
|
// report counts. That particular usage applies to all
|
|
// field in this main item not yet accounted for. In this
|
|
// case a single channel descriptor is allocated and
|
|
// report count is set to the number of fields referenced
|
|
// by this usage.
|
|
//
|
|
// Not the usages are listed in reverse order of there appearence
|
|
// in the report descriptor, so the first usage found in this list
|
|
// is the one that should be repeated.
|
|
//
|
|
// tmpCount is the number of field to which this first usage applies.
|
|
//
|
|
|
|
tmpCount = 1 + push->ReportCount - tmpCount
|
|
+ usage->Max - usage->Min;
|
|
|
|
//
|
|
// The following loop assigns the usage to the fields in this main
|
|
// item in reverse order.
|
|
//
|
|
bitPos += push->ReportSize * (push->ReportCount - tmpCount);
|
|
for (i = 0;
|
|
i < push->ReportCount;
|
|
|
|
i += tmpCount, // Bump i by the number of fields for this channel
|
|
tmpCount = 1 + (usage->Range ? (usage->Max - usage->Min) : 0),
|
|
bitPos -= (push->ReportSize * tmpCount)) {
|
|
|
|
do { // do for all the aliases.
|
|
channel = &(preparsed->Data[(*channelIndex)++]);
|
|
|
|
// set the IsAlias flag now and then clear the last one
|
|
// at the close of this Do while loop.
|
|
channel->IsAlias = TRUE;
|
|
|
|
channel->BitField = tmpBitField;
|
|
channel->MoreChannels = FALSE; // only valid for arrays
|
|
channel->LinkCollection = (USHORT)(currentLCNode - linkNodeArray);
|
|
channel->LinkUsage = currentLCNode->LinkUsage;
|
|
channel->LinkUsagePage = currentLCNode->LinkUsagePage;
|
|
|
|
if (usage->UsagePage) {
|
|
// The default usage page been overwritten.
|
|
channel->UsagePage = usage->UsagePage;
|
|
} else {
|
|
channel->UsagePage = push->UsagePage;
|
|
}
|
|
|
|
channel->BitOffset = (UCHAR) bitPos & 7;
|
|
channel->ByteOffset = (USHORT) bitPos >> 3;
|
|
channel->ReportSize = push->ReportSize;
|
|
channel->ReportCount = tmpCount;
|
|
|
|
channel->BitLength = push->ReportSize * tmpCount;
|
|
channel->ByteEnd = (channel->BitOffset + channel->BitLength);
|
|
channel->ByteEnd = (channel->ByteEnd >> 3)
|
|
+ ((channel->ByteEnd & 7) ? 1 : 0)
|
|
+ channel->ByteOffset;
|
|
|
|
channel->ReportID = push->ReportIDs->ReportID;
|
|
|
|
channel->IsAbsolute = HIDP_ISABSOLUTE(tmpBitField);
|
|
|
|
channel->Units = push->Unit;
|
|
channel->UnitExp = push->UnitExp;
|
|
|
|
if (1 == push->ReportSize) {
|
|
channel->IsButton = TRUE;
|
|
} else {
|
|
channel->IsButton = FALSE;
|
|
channel->Data.HasNull = HIDP_HASNULL(channel->BitField);
|
|
channel->Data.LogicalMin = push->LogicalMin;
|
|
channel->Data.LogicalMax = push->LogicalMax;
|
|
channel->Data.PhysicalMin = push->PhysicalMin;
|
|
channel->Data.PhysicalMax = push->PhysicalMax;
|
|
}
|
|
|
|
channel->IsDesignatorRange = designator.Range;
|
|
channel->IsStringRange = string.Range;
|
|
channel->IsRange = usage->Range;
|
|
if (usage->Range) {
|
|
channel->Range.UsageMin = usage->Min;
|
|
channel->Range.UsageMax = usage->Max;
|
|
} else {
|
|
channel->Range.UsageMin =
|
|
channel->Range.UsageMax = usage->Value;
|
|
}
|
|
|
|
if (designator.Range) {
|
|
channel->Range.DesignatorMin = designator.Min;
|
|
channel->Range.DesignatorMax = designator.Max;
|
|
} else {
|
|
channel->Range.DesignatorMin =
|
|
channel->Range.DesignatorMax = designator.Value;
|
|
}
|
|
|
|
if (string.Range) {
|
|
channel->Range.StringMin = string.Min;
|
|
channel->Range.StringMax = string.Max;
|
|
} else {
|
|
channel->Range.StringMin =
|
|
channel->Range.StringMax = string.Value;
|
|
}
|
|
isAlias = usage->IsAlias;
|
|
usage = HidP_PopUsageList (usage); // discard used usage
|
|
|
|
channel->NumGlobalUnknowns = push->NumGlobalUnknowns;
|
|
if (push->NumGlobalUnknowns) {
|
|
RtlCopyMemory (channel->GlobalUnknowns,
|
|
push->GlobalUnknowns,
|
|
push->NumGlobalUnknowns
|
|
* sizeof (HIDP_UNKNOWN_TOKEN));
|
|
}
|
|
|
|
//
|
|
// Check for power buttons
|
|
//
|
|
if (HIDP_USAGE_SYSCTL_PAGE == channel->UsagePage) {
|
|
if ((channel->Range.UsageMin <= HIDP_USAGE_SYSCTL_POWER) &&
|
|
(HIDP_USAGE_SYSCTL_POWER <= channel->Range.UsageMax)) {
|
|
preparsed->PowerButtonMask |= SYS_BUTTON_POWER;
|
|
}
|
|
if ((channel->Range.UsageMin <= HIDP_USAGE_SYSCTL_SLEEP) &&
|
|
(HIDP_USAGE_SYSCTL_SLEEP <= channel->Range.UsageMax)) {
|
|
preparsed->PowerButtonMask |= SYS_BUTTON_SLEEP;
|
|
}
|
|
if ((channel->Range.UsageMin <= HIDP_USAGE_SYSCTL_WAKE) &&
|
|
(HIDP_USAGE_SYSCTL_WAKE <= channel->Range.UsageMax)) {
|
|
preparsed->PowerButtonMask |= SYS_BUTTON_WAKE;
|
|
}
|
|
}
|
|
|
|
} while (isAlias);
|
|
|
|
channel->IsAlias = FALSE;
|
|
} // for all channels in this main item
|
|
|
|
// Zero out the locals.
|
|
designator = string = zeroLocal;
|
|
|
|
// Hopefully we have used all the local usages now
|
|
ASSERT (usage == &firstUsage);
|
|
break;
|
|
|
|
default:
|
|
#ifdef HIDP_REJECT_UNDEFINED
|
|
HidP_KdPrint (2, ("Item Unknown %x\n", item));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_ITEM_UNKNOWN;
|
|
Dbg->Args[0] = item;
|
|
status = STATUS_ILLEGAL_INSTRUCTION;
|
|
goto HIDP_PARSE_REJECT;
|
|
#else
|
|
if (HIDP_IS_MAIN_ITEM (item)) {
|
|
HidP_KdPrint (2, ("Unknown MAIN item: %x\n", item));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_ITEM_UNKNOWN;
|
|
Dbg->Args[0] = item;
|
|
status = STATUS_ILLEGAL_INSTRUCTION;
|
|
goto HIDP_PARSE_REJECT;
|
|
|
|
} else if (HIDP_IS_GLOBAL_ITEM (item)) {
|
|
if (HIDP_MAX_UNKNOWN_ITEMS == push->NumGlobalUnknowns) {
|
|
push->NumGlobalUnknowns--;
|
|
// overwrite the last entry;
|
|
}
|
|
unknownToken = &push->GlobalUnknowns[push->NumGlobalUnknowns];
|
|
unknownToken->Token = item;
|
|
switch (item & HIDP_ITEM_LENGTH_DATA) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
ONE_BYTE_DATA (unknownToken->BitField, descIndex, Dbg);
|
|
break;
|
|
case 2:
|
|
TWO_BYTE_DATA (unknownToken->BitField, descIndex, Dbg);
|
|
break;
|
|
case 3:
|
|
FOUR_BYTE_DATA (unknownToken->BitField, descIndex, Dbg);
|
|
break;
|
|
}
|
|
push->NumGlobalUnknowns++;
|
|
|
|
} else if (HIDP_IS_LOCAL_ITEM (item)) {
|
|
HidP_KdPrint (2, ("Unknown LOCAL item: %x\n", item));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_ITEM_UNKNOWN;
|
|
Dbg->Args[0] = item;
|
|
status = STATUS_ILLEGAL_INSTRUCTION;
|
|
goto HIDP_PARSE_REJECT;
|
|
|
|
} else if (HIDP_IS_RESERVED_ITEM (item)) {
|
|
HidP_KdPrint (2, ("Unknown RESERVED item: %x\n", item));
|
|
Dbg->BreakOffset = descIndex;
|
|
Dbg->ErrorCode = HIDP_GETCOLDESC_ITEM_UNKNOWN;
|
|
Dbg->Args[0] = item;
|
|
status = STATUS_ILLEGAL_INSTRUCTION;
|
|
goto HIDP_PARSE_REJECT;
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
HidP_FreeUsageList (usage);
|
|
//
|
|
// Since the number of report IDs could be less than the total allocated,
|
|
// due to the fact that some might be repeated, reset the length of the
|
|
// array to reflect the total amount which we found.
|
|
//
|
|
DeviceDesc->ReportIDsLength =
|
|
(ULONG)(currentReportIDs - DeviceDesc->ReportIDs);
|
|
|
|
return status;
|
|
|
|
HIDP_PARSE_REJECT:
|
|
while (push != &firstPush)
|
|
{
|
|
tmpPush = push;
|
|
push = push->Pop;
|
|
ExFreePool (tmpPush);
|
|
}
|
|
if (NULL != usage) {
|
|
//
|
|
// If usage is null, that means that something went wrong. (probably
|
|
// in the push usage routine). In this case the usage memory should
|
|
// have already been freed.
|
|
//
|
|
HidP_FreeUsageList (usage);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
HidP_FreeCollectionDescription (
|
|
IN PHIDP_DEVICE_DESC Desc
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i=0; i < Desc->CollectionDescLength; i++) {
|
|
ExFreePool (Desc->CollectionDesc[i].PreparsedData);
|
|
}
|
|
ExFreePool (Desc->CollectionDesc);
|
|
ExFreePool (Desc->ReportIDs);
|
|
|
|
//
|
|
// Do NOT free Desc itself.
|
|
//
|
|
}
|
|
|
|
#define PHIDP_SYS_POWER_EVENT_BUTTON_LENGTH 0x20
|
|
NTSTATUS
|
|
HidP_SysPowerEvent (
|
|
IN PCHAR HidPacket,
|
|
IN USHORT HidPacketLength,
|
|
IN PHIDP_PREPARSED_DATA Ppd,
|
|
OUT PULONG OutputBuffer
|
|
)
|
|
{
|
|
USAGE buttonList [PHIDP_SYS_POWER_EVENT_BUTTON_LENGTH];
|
|
ULONG length = PHIDP_SYS_POWER_EVENT_BUTTON_LENGTH;
|
|
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
|
ULONG i;
|
|
|
|
*OutputBuffer = 0;
|
|
|
|
if (Ppd->PowerButtonMask) {
|
|
|
|
status = HidP_GetUsages (HidP_Input,
|
|
HIDP_USAGE_SYSCTL_PAGE,
|
|
0,
|
|
buttonList,
|
|
&length,
|
|
Ppd,
|
|
HidPacket,
|
|
HidPacketLength);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
for (i = 0; i < length; i++) {
|
|
|
|
switch (buttonList[i]) {
|
|
case HIDP_USAGE_SYSCTL_POWER:
|
|
*OutputBuffer |= SYS_BUTTON_POWER;
|
|
break;
|
|
|
|
case HIDP_USAGE_SYSCTL_WAKE:
|
|
*OutputBuffer |= SYS_BUTTON_WAKE;
|
|
break;
|
|
|
|
|
|
case HIDP_USAGE_SYSCTL_SLEEP:
|
|
*OutputBuffer |= SYS_BUTTON_SLEEP;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
HidP_SysPowerCaps (
|
|
IN PHIDP_PREPARSED_DATA Ppd,
|
|
OUT PULONG OutputBuffer
|
|
)
|
|
{
|
|
*OutputBuffer = Ppd->PowerButtonMask;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
void
|
|
HidP_AssignDataIndices (
|
|
PHIDP_PREPARSED_DATA Ppd,
|
|
PHIDP_GETCOLDESC_DBG Dbg
|
|
)
|
|
{
|
|
struct _CHANNEL_REPORT_HEADER * iof;
|
|
PHIDP_CHANNEL_DESC channel;
|
|
PHIDP_CHANNEL_DESC scan;
|
|
PHIDP_CHANNEL_DESC end;
|
|
USHORT i;
|
|
USHORT dataIndex;
|
|
|
|
PAGED_CODE();
|
|
UNREFERENCED_PARAMETER (Dbg);
|
|
|
|
iof = &Ppd->Input;
|
|
|
|
while (TRUE) {
|
|
dataIndex = 0;
|
|
|
|
for (i = iof->Offset, channel = &Ppd->Data[iof->Offset];
|
|
i < iof->Index ;
|
|
i++, channel++) {
|
|
|
|
if (!channel->MoreChannels) {
|
|
channel->Range.DataIndexMin = dataIndex;
|
|
dataIndex += channel->Range.UsageMax - channel->Range.UsageMin;
|
|
channel->Range.DataIndexMax = dataIndex;
|
|
dataIndex++;
|
|
} else {
|
|
//
|
|
// An array channel. We must number these backwards.
|
|
//
|
|
|
|
scan = channel;
|
|
|
|
while (scan->MoreChannels) {
|
|
scan++;
|
|
i++;
|
|
}
|
|
end = scan;
|
|
|
|
do {
|
|
scan->Range.DataIndexMin = dataIndex;
|
|
dataIndex += scan->Range.UsageMax
|
|
- scan->Range.UsageMin;
|
|
|
|
scan->Range.DataIndexMax = dataIndex;
|
|
dataIndex++;
|
|
scan--;
|
|
|
|
} while ( channel <= scan );
|
|
channel = end;
|
|
}
|
|
}
|
|
|
|
if (&Ppd->Input == iof) {
|
|
iof = &Ppd->Output;
|
|
} else if (&Ppd->Output == iof) {
|
|
iof = &Ppd->Feature;
|
|
} else {
|
|
ASSERT (&Ppd->Feature == iof);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|