A couple small notes inline below.
On Wed, Sep 02, 2015 at 07:01:51AM -0500, Aric Stewart wrote:
> diff --git a/dlls/hidclass.sys/Makefile.in b/dlls/hidclass.sys/Makefile.in
> index 8ed51e6..6e79486 100644
> --- a/dlls/hidclass.sys/Makefile.in
> +++ b/dlls/hidclass.sys/Makefile.in
> @@ -5,6 +5,7 @@ DELAYIMPORTS = setupapi hid
>
> C_SRCS = \
> buffer.c \
> + descriptor.c \
> device.c \
> main.c \
> pnp.c
> diff --git a/dlls/hidclass.sys/descriptor.c b/dlls/hidclass.sys/descriptor.c
> new file mode 100644
> index 0000000..c277dec
> --- /dev/null
> +++ b/dlls/hidclass.sys/descriptor.c
> @@ -0,0 +1,1106 @@
> +/*
> + * HID descriptor parsing
> + *
> + * Copyright (C) 2015 Aric Stewart
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
> + */
> +
> +#include "config.h"
> +
> +#include <stdarg.h>
> +#include <stdlib.h>
> +#define NONAMELESSUNION
> +#include "hid.h"
> +
> +#include "wine/debug.h"
> +#include "wine/list.h"
> +
> +WINE_DEFAULT_DEBUG_CHANNEL(hid);
> +
> +#define USAGE_MAX 10
> +
> +static const char* feature_string[] =
> + { "Input", "Output", "Feature" };
> +
> +struct caps {
> + USAGE UsagePage;
> + LONG LogicalMin;
> + LONG LogicalMax;
> + LONG PhysicalMin;
> + LONG PhysicalMax;
> + ULONG UnitsExp;
> + ULONG Units;
> + USHORT BitSize;
> + UCHAR ReportID;
> + USHORT ReportCount;
> +
> + BOOLEAN IsRange;
> + BOOLEAN IsStringRange;
> + BOOLEAN IsDesignatorRange;
> + int usage_count;
> + union {
> + struct {
> + USAGE UsageMin;
> + USAGE UsageMax;
> + USHORT StringMin;
> + USHORT StringMax;
> + USHORT DesignatorMin;
> + USHORT DesignatorMax;
> + } Range;
> + struct {
> + USAGE Usage[USAGE_MAX];
> + USAGE Reserved1;
> + USHORT StringIndex;
> + USHORT Reserved2;
> + USHORT DesignatorIndex;
> + USHORT Reserved3;
> + } NotRange;
> + } DUMMYUNIONNAME;
> +
> + int Delim;
> +};
> +
> +struct feature {
> + struct list entry;
> + struct list col_entry;
> + struct caps caps;
> +
> + HIDP_REPORT_TYPE type;
> + BOOLEAN isData;
> + BOOLEAN isArray;
> + BOOLEAN IsAbsolute;
> + BOOLEAN Wrap;
> + BOOLEAN Linear;
> + BOOLEAN prefState;
> + BOOLEAN HasNull;
> + BOOLEAN Volatile;
> + BOOLEAN BitField;
> +
> + int index;
> + struct collection *collection;
> +};
> +
> +static const char* collection_string[] = {
> + "Physical",
> + "Application",
> + "Logical",
> + "Report",
> + "Named Array",
> + "Usage Switch",
> + "Usage Modifier",
> +};
> +
> +struct collection {
> + struct list entry;
> + struct caps caps;
> + int index;
> + int type;
> + struct collection *parent;
> + struct list features;
> + struct list collections;
> +};
> +
> +static const char* debugstr_usages(struct caps *caps)
> +{
> + if (!caps->IsRange)
> + {
> + static char out[30];
> + int i;
> + strcpy(out, "[ ");
> + for (i = 0; i < caps->usage_count; i++)
> + strcat(out, wine_dbg_sprintf("%x ", caps->u.NotRange.Usage[i]));
> + strcat(out,"]");
> + return out;
> + }
> + else
> + return wine_dbg_sprintf("[%x - %x]", caps->u.Range.UsageMin, caps->u.Range.UsageMax);
> +}
> +
> +static const char* debugstr_stringindex(struct caps *caps)
> +{
> + if (!caps->IsStringRange)
> + return wine_dbg_sprintf("%i", caps->u.NotRange.StringIndex);
> + else
> + return wine_dbg_sprintf("[%i - %i]", caps->u.Range.StringMin, caps->u.Range.StringMax);
> +}
> +
> +static const char* debugstr_designatorindex(struct caps *caps)
> +{
> + if (!caps->IsDesignatorRange)
> + return wine_dbg_sprintf("%i", caps->u.NotRange.DesignatorIndex);
> + else
> + return wine_dbg_sprintf("[%i - %i]", caps->u.Range.DesignatorMin, caps->u.Range.DesignatorMax);
> +}
> +
> +static const char* debugstr_caps(struct caps *caps)
> +{
> + if (!caps)
> + return "";
> + return wine_dbg_sprintf("(UsagePage %x; LogicalMin %i; LogicalMax %i; PhysicalMin %i; PhysicalMax %i; UnitsExp %i; Units %i; BitSize %i; ReportID %i; ReportCount %i; Usage %s; StringIndex %s; DesignatorIndex %s; Delim %i;)",
> + caps->UsagePage,
> + caps->LogicalMin,
> + caps->LogicalMax,
> + caps->PhysicalMin,
> + caps->PhysicalMax,
> + caps->UnitsExp,
> + caps->Units,
> + caps->BitSize,
> + caps->ReportID,
> + caps->ReportCount,
> + debugstr_usages(caps),
> + debugstr_stringindex(caps),
> + debugstr_designatorindex(caps),
> + caps->Delim);
> +}
> +
> +static void debug_feature(struct feature *feature)
> +{
> + if (!feature)
> + return;
> + TRACE("[\n\t type %s [%i]; %s; %s; %s; %s; %s; %s; %s; %s; %s\n\t %s\n]\n",
> + feature_string[feature->type],
> + feature->index,
> + (feature->isData)?"Data":"Const",
> + (feature->isArray)?"Array":"Var",
> + (feature->IsAbsolute)?"Abs":"Rel",
> + (feature->Wrap)?"Wrap":"NoWrap",
> + (feature->Linear)?"Linear":"NonLinear",
> + (feature->prefState)?"PrefStat":"NoPrefState",
> + (feature->HasNull)?"HasNull":"NoNull",
> + (feature->Volatile)?"Volatile":"NonVolatile",
> + (feature->BitField)?"BitField":"Buffered",
> + debugstr_caps(&feature->caps));
> +}
> +
> +static void debug_collection(struct collection *collection)
> +{
> + struct feature *fentry;
> + struct collection *centry;
> + if (TRACE_ON(hid))
> + {
> + TRACE("{ %s[%i], %p, %i features, %i collections\n\t %s\n", collection_string[collection->type], collection->index, collection->parent, list_count(&collection->features), list_count(&collection->collections), debugstr_caps(&collection->caps));
> + LIST_FOR_EACH_ENTRY(fentry, &collection->features, struct feature, col_entry)
> + debug_feature(fentry);
> + LIST_FOR_EACH_ENTRY(centry, &collection->collections, struct collection, entry)
> + debug_collection(centry);
> + TRACE("}\n");
> + }
> +}
> +
> +static void debug_print_button_cap(const CHAR * type, WINE_HID_ELEMENT *wine_element)
> +{
> + if (!wine_element->caps.button.IsRange)
> + TRACE("%s Button: %x/%04x: ReportId %i, startBit %i/1\n" , type,
> + wine_element->caps.button.UsagePage,
> + wine_element->caps.button.u.NotRange.Usage,
> + wine_element->caps.value.ReportID,
> + wine_element->valueStartBit);
> + else
> + TRACE("%s Button: %x/[%04x-%04x]: ReportId %i, startBit %i/%i\n" ,type,
> + wine_element->caps.button.UsagePage,
> + wine_element->caps.button.u.Range.UsageMin,
> + wine_element->caps.button.u.Range.UsageMax,
> + wine_element->caps.value.ReportID,
> + wine_element->valueStartBit,
> + wine_element->bitCount);
> +}
> +
> +static void debug_print_value_cap(const CHAR * type, WINE_HID_ELEMENT *wine_element)
> +{
> + TRACE("%s Value: %x/%x: ReportId %i, IsAbsolute %i, HasNull %i, "
> + "Bit Size %i, ReportCount %i, UnitsExp %i, Units %i, "
> + "LogicalMin %i, Logical Max %i, PhysicalMin %i, "
> + "PhysicalMax %i -- StartBit %i/%i\n", type,
> + wine_element->caps.value.UsagePage,
> + wine_element->caps.value.u.NotRange.Usage,
> + wine_element->caps.value.ReportID,
> + wine_element->caps.value.IsAbsolute,
> + wine_element->caps.value.HasNull,
> + wine_element->caps.value.BitSize,
> + wine_element->caps.value.ReportCount,
> + wine_element->caps.value.UnitsExp,
> + wine_element->caps.value.Units,
> + wine_element->caps.value.LogicalMin,
> + wine_element->caps.value.LogicalMax,
> + wine_element->caps.value.PhysicalMin,
> + wine_element->caps.value.PhysicalMax,
> + wine_element->valueStartBit,
> + wine_element->bitCount);
> +}
> +
> +static void debug_print_element(const CHAR* type, WINE_HID_ELEMENT *wine_element)
> +{
> + if (wine_element->ElementType == ButtonElement)
> + debug_print_button_cap(type, wine_element);
> + else if (wine_element->ElementType == ValueElement)
> + debug_print_value_cap(type, wine_element);
> + else
> + TRACE("%s: UNKNOWN\n", type);
> +}
> +
> +static void debug_print_report(const char* type, WINE_HID_REPORT *report)
> +{
> + int i;
> + TRACE("%s REPORT:\n"
> + "\tReportID: %i\n"
> + "\tdwSize: %i\n"
> + "\telementCount: %i\n",
> + type,
> + report->reportID,
> + report->dwSize,
> + report->elementCount);
> + for (i = 0; i < report->elementCount; i++)
> + debug_print_element(type, &report->Elements[i]);
> +}
> +
> +static void debug_print_preparsed(WINE_HIDP_PREPARSED_DATA *data)
> +{
> + int i;
> + WINE_HID_REPORT *r;
> + if (TRACE_ON(hid))
> + {
> + TRACE("PREPARSED Data:\n"
> + "\tdwSize: %i\n"
> + "\tCaps:\n"
> + "\t\tUsage: %i\n"
> + "\t\tUsagePage: %i\n"
> + "\t\tInputReportByteLength: %i\n"
> + "\t\tOutputReportByteLength: %i\n"
> + "\t\tFeatureReportByteLength: %i\n"
> + "\t\tNumberLinkCollectionNodes: %i\n"
> + "\t\tNumberInputButtonCaps: %i\n"
> + "\t\tNumberInputValueCaps: %i\n"
> + "\t\tNumberInputDataIndices: %i\n"
> + "\t\tNumberOutputButtonCaps: %i\n"
> + "\t\tNumberOutputValueCaps: %i\n"
> + "\t\tNumberOutputDataIndices: %i\n"
> + "\t\tNumberFeatureButtonCaps: %i\n"
> + "\t\tNumberFeatureValueCaps: %i\n"
> + "\t\tNumberFeatureDataIndices: %i\n"
> + "\tdwInputReportCount: %i\n"
> + "\tdwOutputReportCount: %i\n"
> + "\tdwFeatureReportCount: %i\n"
> + "\tdwOutputReportOffset: %i\n"
> + "\tdwFeatureReportOffset: %i\n",
> + data->dwSize,
> + data->caps.Usage,
> + data->caps.UsagePage,
> + data->caps.InputReportByteLength,
> + data->caps.OutputReportByteLength,
> + data->caps.FeatureReportByteLength,
> + data->caps.NumberLinkCollectionNodes,
> + data->caps.NumberInputButtonCaps,
> + data->caps.NumberInputValueCaps,
> + data->caps.NumberInputDataIndices,
> + data->caps.NumberOutputButtonCaps,
> + data->caps.NumberOutputValueCaps,
> + data->caps.NumberOutputDataIndices,
> + data->caps.NumberFeatureButtonCaps,
> + data->caps.NumberFeatureValueCaps,
> + data->caps.NumberFeatureDataIndices,
> + data->dwInputReportCount,
> + data->dwOutputReportCount,
> + data->dwFeatureReportCount,
> + data->dwOutputReportOffset,
> + data->dwFeatureReportOffset);
> +
> + r = HID_INPUT_REPORTS(data);
> + for (i = 0; i < data->dwInputReportCount; i++)
> + {
> + debug_print_report("INPUT", r);
> + r = HID_NEXT_REPORT(data, r);
> + }
> + r = HID_OUTPUT_REPORTS(data);
> + for (i = 0; i < data->dwOutputReportCount; i++)
> + {
> + debug_print_report("OUTPUT", r);
> + r = HID_NEXT_REPORT(data, r);
> + }
> + r = HID_FEATURE_REPORTS(data);
> + for (i = 0; i < data->dwFeatureReportCount; i++)
> + {
> + debug_print_report("FEATURE", r);
> + r = HID_NEXT_REPORT(data, r);
> + }
> + }
> +}
> +
> +static int getValue(int bsize, int source)
> +{
> + int mask = 0xff;
> + int negative = 0x80;
> + int outofrange = 0x100;
> + int value;
> + int i;
> +
> + if (bsize == 4)
> + return source;
> +
> + for (i = 1; i < bsize; i++)
> + {
> + mask = (mask<<8) + 0xff;
> + negative = (negative<<8);
> + outofrange = (outofrange<<8);
> + }
> + value = (source&mask);
> + if (value&negative)
> + value = -1 * (outofrange - value);
> + return value;
> +}
> +
> +void parse_io_feature(int bSize, int itemVal, int bTag, int *feature_index, struct feature *feature)
> +{
> + if (bSize <= 0)
> + {
> + return;
> + }
> + else
> + {
> + if ((itemVal & 0x01) == 0)
> + feature->isData = TRUE;
> + else
> + feature->isData = FALSE; //Const
> + if ((itemVal & 0x02) == 0)
> + feature->isArray= TRUE;
> + else
> + feature->isArray= TRUE; //Var
> + if ((itemVal & 0x04) == 0)
> + feature->IsAbsolute = TRUE;
> + else
> + feature->IsAbsolute = FALSE; //Rel
> + if ((itemVal & 0x08) == 0)
> + feature->Wrap = FALSE;
> + else
> + feature->Wrap = TRUE;
> + if ((itemVal & 0x10) == 0)
> + feature->Linear = TRUE;
> + else
> + feature->Linear = FALSE;
> + if ((itemVal & 0x20) == 0)
> + feature->prefState = TRUE;
> + else
> + feature->prefState = FALSE;
> + if ((itemVal & 0x40) == 0)
> + feature->HasNull = FALSE;
> + else
> + feature->HasNull = TRUE;
> +
> + if (bTag != 0x08)
> + {
> + if ((itemVal & 0x80) == 0)
> + feature->Volatile = FALSE;
> + else
> + feature->Volatile = TRUE;
> + }
> + if (bSize > 1)
> + {
> + if ((itemVal & 0x100) == 0)
> + feature->BitField = TRUE;
> + else
> + feature->BitField = FALSE; //Buffered Bytes
> + }
> + feature->index = *feature_index;
> + *feature_index = *feature_index + 1;
> + }
> +}
> +
> +void parse_collection(int bSize, int itemVal, struct collection *collection)
> +{
> + if (bSize <= 0)
> + return;
> + else
> + {
> + collection->type = itemVal;
> +
> + if (itemVal >= 0x07 && itemVal <= 0x7F) {
> + ERR(" (Reserved %x )\n", itemVal);
> + }
> + else if (itemVal >= 0x80 && itemVal <= 0xFF) {
> + ERR(" (Vendor Defined %x )\n", itemVal);
> + }
> + }
> +}
> +
> +static void new_caps(struct caps *caps)
> +{
> + caps->IsRange = 0;
> + caps->IsStringRange = 0;
> + caps->IsDesignatorRange = 0;
> + caps->usage_count = 0;
> +}
> +
> +int parse_descriptor(BYTE *descriptor, INT index, INT length, INT *feature_index, INT *collection_index, struct collection *collection, struct caps *caps, struct list *features)
> +{
> + int i;
> + for (i = index; i < length;)
> + {
> + BYTE b0 = descriptor[i++];
> + int bSize = b0 & 0x03;
> + int bType = (b0 >> 2) & 0x03;
> + int bTag = (b0 >> 4) & 0x0F;
> +
> + bSize = (bSize == 3) ? 4 : bSize;
> + if (bType == 0x03 && bTag == 0x0F && bSize == 2 && i + 2 < length)
> + {
> + /* Long data items: Should be unused */
> + ERR("Long Data Item, should be unused\n");
This confused me. Is this a malformed report from the HID or something
Wine doesn't yet support?
> + }
> + else
> + {
> + int bSizeActual = 0;
> + int itemVal = 0;
> + int j;
> +
> + for (j = 0; j < bSize; j++)
> + {
> + if (i + j < length)
> + {
> + itemVal += descriptor[i + j] << (8 * j);
> + bSizeActual++;
> + }
> + }
> + TRACE(" %x[%i], type %i , tag %i, size %i, val %i\n",b0,i-1,bType, bTag, bSize, itemVal );
> +
> + if (bType == 0x00)
> + {
> + struct feature *feature;
> + switch(bTag)
> + {
> + case 0x08:
Here and elsewhere, there's a lot of magic numbers. Do these have
names in the standard? Could you make up names for them? TAG_INPUT or
something. Or is it more useful to have the raw numbers?
> + feature = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*feature));
> + list_add_tail(&collection->features, &feature->col_entry);
> + list_add_tail(features, &feature->entry);
> + feature->type = HidP_Input;
> + parse_io_feature(bSize, itemVal, bTag, feature_index, feature);
> + feature->caps = *caps;
> + feature->collection = collection;
> + new_caps(caps);
> + break;
> + case 0x09:
> + feature = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*feature));
> + list_add_tail(&collection->features, &feature->col_entry);
> + list_add_tail(features, &feature->entry);
> + feature->type = HidP_Output;
> + parse_io_feature(bSize, itemVal, bTag, feature_index, feature);
> + feature->caps = *caps;
> + feature->collection = collection;
> + new_caps(caps);
> + break;
> + case 0x0B:
> + feature = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*feature));
> + list_add_tail(&collection->features, &feature->col_entry);
> + list_add_tail(features, &feature->entry);
> + feature->type = HidP_Feature;
> + parse_io_feature(bSize, itemVal, bTag, feature_index, feature);
> + feature->caps = *caps;
> + feature->collection = collection;
> + new_caps(caps);
> + break;
> + case 0x0A:
Here, the cases are in a different order. That wouldn't matter with
symbols, but it struck me with the raw numbers.
> + {
> + struct collection *subcollection = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct collection));
> + list_add_tail(&collection->collections, &subcollection->entry);
> + subcollection->parent = collection;
> + /* Only set our collection once...
> + We do not properly handle composite devices yet. */
> + if (*collection_index == 0)
> + collection->caps = *caps;
> + subcollection->caps = *caps;
> + subcollection->index = *collection_index;
> + *collection_index = *collection_index + 1;
> + list_init(&subcollection->features);
> + list_init(&subcollection->collections);
> + new_caps(caps);
> +
> + parse_collection(bSize, itemVal, subcollection);
> +
> + i = parse_descriptor(descriptor, i+1, length, feature_index, collection_index, subcollection, caps, features);
> + continue;
> + }
> + case 0x0C:
> + return i;
> + default:
> + ERR("Unknown Tag\n");
Would it be useful to print the tag value here?
> + }
> + }
> + else if (bType == 0x01)
> + {
> + switch(bTag)
> + {
> + case 0x00:
> + caps->UsagePage = getValue(bSize, itemVal);
> + break;
> + case 0x01:
> + caps->LogicalMin = getValue(bSize, itemVal);
> + break;
> + case 0x02:
> + caps->LogicalMax = getValue(bSize, itemVal);
> + break;
> + case 0x03:
> + caps->PhysicalMin = getValue(bSize, itemVal);
> + break;
> + case 0x04:
> + caps->PhysicalMax = getValue(bSize, itemVal);
> + break;
> + case 0x05:
> + caps->UnitsExp = getValue(bSize, itemVal);
> + break;
> + case 0x06:
> + caps->Units = getValue(bSize, itemVal);
> + break;
> + case 0x07:
> + caps->BitSize = getValue(bSize, itemVal);
> + break;
> + case 0x08:
> + caps->ReportID = getValue(bSize, itemVal);
> + break;
> + case 0x09:
> + caps->ReportCount = getValue(bSize, itemVal);
> + break;
> + case 0x0A:
> + ERR("Push\n");
> + break;
> + case 0x0B:
> + ERR("Pop\n");
> + break;
Are these invalid messages from the HID device, or things wine doesn't
yet support? Should they be FIXMEs?
> + default:
> + ERR("Unknown (bTag: %x, bType: %x)\n", bTag, bType);
> + }
> + }
> + else if (bType == 0x02)
> + {
> + switch(bTag)
> + {
> + case 0x00:
> + if (caps->usage_count >= USAGE_MAX)
> + FIXME("More than %i individual usages defined\n",USAGE_MAX);
> + else
> + {
> + caps->u.NotRange.Usage[caps->usage_count++] = getValue(bSize, itemVal);
> + caps->IsRange = FALSE;
> + }
> + break;
> + case 0x01:
> + caps->usage_count = 1;
> + caps->u.Range.UsageMin = getValue(bSize, itemVal);
> + caps->IsRange = TRUE;
> + break;
> + case 0x02:
> + caps->usage_count = 1;
> + caps->u.Range.UsageMax = getValue(bSize, itemVal);
> + caps->IsRange = TRUE;
> + break;
> + case 0x03:
> + caps->u.NotRange.DesignatorIndex = getValue(bSize, itemVal);
> + caps->IsDesignatorRange = FALSE;
> + break;
> + case 0x04:
> + caps->u.Range.DesignatorMin = getValue(bSize, itemVal);
> + caps->IsDesignatorRange = TRUE;
> + break;
> + case 0x05:
> + caps->u.Range.DesignatorMax = getValue(bSize, itemVal);
> + caps->IsDesignatorRange = TRUE;
> + break;
> + case 0x07:
> + caps->u.NotRange.StringIndex = getValue(bSize, itemVal);
> + caps->IsStringRange = FALSE;
> + break;
> + case 0x08:
> + caps->u.Range.StringMin = getValue(bSize, itemVal);
> + caps->IsStringRange = TRUE;
> + break;
> + case 0x09:
> + caps->u.Range.StringMax = getValue(bSize, itemVal);
> + caps->IsStringRange = TRUE;
> + break;
> + case 0x0A:
> + caps->Delim = getValue(bSize, itemVal);
> + break;
> + default:
> + ERR("Unknown (bTag: %x, bType: %x)\n", bTag, bType);
> + }
> + }
> + else
> + ERR("Unknown (bTag: %x, bType: %x)\n", bTag, bType);
> +
> + i += bSize;
> + }
> + }
> + return i;
> +}
> +
> +static inline void new_report(WINE_HID_REPORT *wine_report, struct feature* feature)
> +{
> + wine_report->reportID = feature->caps.ReportID;
> + wine_report->dwSize = sizeof(*wine_report) - sizeof(WINE_HID_ELEMENT);
> + wine_report->elementCount = 0;
> +}
> +
> +static void build_elements(WINE_HID_REPORT *wine_report, struct feature* feature, DWORD *bitOffset)
> +{
> + int i;
> +
> + if (!feature->isData)
> + {
> + *bitOffset = *bitOffset + (feature->caps.BitSize * feature->caps.ReportCount);
> + return;
> + }
> +
> + for (i = 0; i < feature->caps.usage_count; i++)
> + {
> + WINE_HID_ELEMENT *wine_element = &wine_report->Elements[wine_report->elementCount];
> +
> + wine_element->valueStartBit = *bitOffset;
> + if (feature->caps.UsagePage == HID_USAGE_PAGE_BUTTON)
> + {
> + wine_element->ElementType = ButtonElement;
> + wine_element->caps.button.UsagePage = feature->caps.UsagePage;
> + wine_element->caps.button.ReportID = feature->caps.ReportID;
> + wine_element->caps.button.BitField = feature->BitField;
> + wine_element->caps.button.IsRange = feature->caps.IsRange;
> + wine_element->caps.button.IsStringRange = feature->caps.IsStringRange;
> + wine_element->caps.button.IsDesignatorRange = feature->caps.IsDesignatorRange;
> + wine_element->caps.button.IsAbsolute = feature->IsAbsolute;
> + if (wine_element->caps.button.IsRange)
> + {
> + wine_element->bitCount = (feature->caps.u.Range.UsageMax - feature->caps.u.Range.UsageMin) + 1;
> + *bitOffset = *bitOffset + wine_element->bitCount;
> + wine_element->caps.button.u.Range.UsageMin = feature->caps.u.Range.UsageMin;
> + wine_element->caps.button.u.Range.UsageMax = feature->caps.u.Range.UsageMax;
> + wine_element->caps.button.u.Range.StringMin = feature->caps.u.Range.StringMin;
> + wine_element->caps.button.u.Range.StringMax = feature->caps.u.Range.StringMax;
> + wine_element->caps.button.u.Range.DesignatorMin = feature->caps.u.Range.DesignatorMin;
> + wine_element->caps.button.u.Range.DesignatorMax = feature->caps.u.Range.DesignatorMax;
> + }
> + else
> + {
> + *bitOffset = *bitOffset + 1;
> + wine_element->bitCount = 1;
> + wine_element->caps.button.u.NotRange.Usage = feature->caps.u.NotRange.Usage[i];
> + wine_element->caps.button.u.NotRange.StringIndex = feature->caps.u.NotRange.StringIndex;
> + wine_element->caps.button.u.NotRange.DesignatorIndex = feature->caps.u.NotRange.DesignatorIndex;
> + }
> + }
> + else
> + {
> + wine_element->ElementType = ValueElement;
> + wine_element->caps.value.UsagePage = feature->caps.UsagePage;
> + wine_element->caps.value.ReportID = feature->caps.ReportID;
> + wine_element->caps.value.BitField = feature->BitField;
> + wine_element->caps.value.IsRange = feature->caps.IsRange;
> + wine_element->caps.value.IsStringRange = feature->caps.IsStringRange;
> + wine_element->caps.value.IsDesignatorRange = feature->caps.IsDesignatorRange;
> + wine_element->caps.value.IsAbsolute = feature->IsAbsolute;
> + wine_element->caps.value.HasNull = feature->HasNull;
> + wine_element->caps.value.BitSize = feature->caps.BitSize;
> + if (feature->caps.usage_count > 1)
> + wine_element->caps.value.ReportCount = 1;
> + else
> + wine_element->caps.value.ReportCount = feature->caps.ReportCount;
> + wine_element->bitCount = (feature->caps.BitSize * wine_element->caps.value.ReportCount);
> + *bitOffset = *bitOffset + wine_element->bitCount;
> + wine_element->caps.value.UnitsExp = feature->caps.UnitsExp;
> + wine_element->caps.value.Units = feature->caps.Units;
> + wine_element->caps.value.LogicalMin = feature->caps.LogicalMin;
> + wine_element->caps.value.LogicalMax = feature->caps.LogicalMax;
> + wine_element->caps.value.PhysicalMin = feature->caps.PhysicalMin;
> + wine_element->caps.value.PhysicalMax = feature->caps.PhysicalMax;
> + if (wine_element->caps.value.IsRange)
> + {
> + wine_element->caps.value.u.Range.UsageMin = feature->caps.u.Range.UsageMin;
> + wine_element->caps.value.u.Range.UsageMax = feature->caps.u.Range.UsageMax;
> + wine_element->caps.value.u.Range.StringMin = feature->caps.u.Range.StringMin;
> + wine_element->caps.value.u.Range.StringMax = feature->caps.u.Range.StringMax;
> + wine_element->caps.value.u.Range.DesignatorMin = feature->caps.u.Range.DesignatorMin;
> + wine_element->caps.value.u.Range.DesignatorMax = feature->caps.u.Range.DesignatorMax;
> + }
> + else
> + {
> + wine_element->caps.value.u.NotRange.Usage = feature->caps.u.NotRange.Usage[i];
> + wine_element->caps.value.u.NotRange.StringIndex = feature->caps.u.NotRange.StringIndex;
> + wine_element->caps.value.u.NotRange.DesignatorIndex = feature->caps.u.NotRange.DesignatorIndex;
> + }
> + }
> +
> + wine_report->elementCount++;
> + }
> +}
> +
> +static void count_elements(struct feature* feature, USHORT *buttons, USHORT *values)
> +{
> + if (feature->caps.UsagePage == HID_USAGE_PAGE_BUTTON)
> + {
> + if (feature->caps.IsRange)
> + *buttons = *buttons + 1;
> + else
> + *buttons = *buttons + feature->caps.usage_count;
> + }
> + else
> + {
> + if (feature->caps.IsRange)
> + *values = *values + 1;
> + else
> + *values = *values + feature->caps.usage_count;
> + }
> +}
> +
> +WINE_HIDP_PREPARSED_DATA* build_PreparseData(
> + struct feature **features, int feature_count,
> + struct feature **input_features, int i_count,
> + struct feature **output_features, int o_count,
> + struct feature **feature_features, int f_count,
> + struct collection *base_collection)
> +{
> + WINE_HIDP_PREPARSED_DATA *data;
> + WINE_HID_REPORT *wine_report = NULL;
> + DWORD bitOffset, bitLength;
> + int report_count = 1;
> + int i;
> + int element_count;
> + int size = 0;
> +
> + if (features[0]->caps.ReportID != 0)
> + {
> + int *report_ids;
> + int cnt = max(i_count, o_count);
> + cnt = max(cnt, f_count);
> + report_ids = HeapAlloc(GetProcessHeap(), 0 , sizeof(*report_ids) * cnt);
> +
> + if (i_count)
> + {
> + report_ids[0] = input_features[0]->caps.ReportID;
> + for (i = 1; i < i_count; i++)
> + {
> + int j;
> + int found = FALSE;
> + for (j = 0; !found && j < i_count; j++)
> + {
> + if (report_ids[j] == input_features[i]->caps.ReportID)
> + found = TRUE;
> + }
> + if (!found)
> + {
> + report_ids[report_count] = input_features[i]->caps.ReportID;
> + report_count++;
> + }
> + }
> + }
> + if (o_count)
> + {
> + report_count++;
> + report_ids[0] = output_features[0]->caps.ReportID;
> + for (i = 1; i < o_count; i++)
> + {
> + int j;
> + int found = FALSE;
> + for (j = 0; !found && j < o_count; j++)
> + {
> + if (report_ids[j] == output_features[i]->caps.ReportID)
> + found = TRUE;
> + }
> + if (!found)
> + {
> + report_ids[report_count] = output_features[i]->caps.ReportID;
> + report_count++;
> + }
> + }
> + }
> + if (f_count)
> + {
> + report_count++;
> + report_ids[0] = feature_features[0]->caps.ReportID;
> + for (i = 1; i < f_count; i++)
> + {
> + int j;
> + int found = FALSE;
> + for (j = 0; !found && j < f_count; j++)
> + {
> + if (report_ids[j] == feature_features[i]->caps.ReportID)
> + found = TRUE;
> + }
> + if (!found)
> + {
> + report_ids[report_count] = feature_features[i]->caps.ReportID;
> + report_count++;
> + }
> + }
> + }
> + HeapFree(GetProcessHeap(), 0, report_ids);
> + }
> + else
> + {
> + if (o_count) report_count++;
> + if (f_count) report_count++;
> + }
> +
> + element_count = 0;
> + for (i = 0; i < feature_count; i++)
> + element_count += features[i]->caps.usage_count;
> +
> + size = sizeof(WINE_HIDP_PREPARSED_DATA) +
> + (element_count * sizeof(WINE_HID_ELEMENT)) +
> + (report_count * sizeof(WINE_HID_REPORT));
> +
> + TRACE("%i reports %i elements -> size %i\n",report_count, element_count, size);
> +
> + data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
> + data->magic = HID_MAGIC;
> + data->dwSize = size;
> + data->caps.Usage = base_collection->caps.u.NotRange.Usage[0];
> + data->caps.UsagePage = base_collection->caps.UsagePage;
> +
> + wine_report = data->InputReports;
> + if (i_count)
> + {
> + bitLength = 0;
> + new_report(wine_report, input_features[0]);
> + data->dwInputReportCount ++;
> +
> + if (input_features[0]->caps.ReportID != 0)
> + bitOffset = 8;
> + else
> + bitOffset = 0;
> +
> + for (i = 0; i < i_count; i++)
> + {
> + if (input_features[i]->caps.ReportID != wine_report->reportID)
> + {
> + wine_report->dwSize += (sizeof(WINE_HID_ELEMENT) * wine_report->elementCount);
> + wine_report = (WINE_HID_REPORT*)(((BYTE*)wine_report)+wine_report->dwSize);
> + new_report(wine_report, input_features[i]);
> + data->dwInputReportCount ++;
> + bitLength = max(bitOffset, bitLength);
> + if (input_features[i]->caps.ReportID != 0)
> + bitOffset = 8;
> + else
> + bitOffset = 0;
> + }
> + build_elements(wine_report, input_features[i], &bitOffset);
> + count_elements(input_features[i], &data->caps.NumberInputButtonCaps,
> + &data->caps.NumberInputValueCaps);
> + }
> + wine_report->dwSize += (sizeof(WINE_HID_ELEMENT) * wine_report->elementCount);
> + bitLength = max(bitOffset, bitLength);
> + data->caps.InputReportByteLength = ((bitLength + 7) & ~7)/8;
> + }
> +
> + if (o_count)
> + {
> + bitLength = 0;
> + wine_report = (WINE_HID_REPORT*)(((BYTE*)wine_report)+wine_report->dwSize);
> + data->dwOutputReportOffset = (BYTE*)wine_report - (BYTE*)data->InputReports;
> + new_report(wine_report, output_features[0]);
> + data->dwOutputReportCount ++;
> + if (output_features[0]->caps.ReportID != 0)
> + bitOffset = 8;
> + else
> + bitOffset = 0;
> +
> + for (i = 0; i < o_count; i++)
> + {
> + if (output_features[i]->caps.ReportID != wine_report->reportID)
> + {
> + wine_report->dwSize += (sizeof(WINE_HID_ELEMENT) * wine_report->elementCount);
> + wine_report = (WINE_HID_REPORT*)(((BYTE*)wine_report)+wine_report->dwSize);
> + new_report(wine_report, output_features[i]);
> + data->dwOutputReportCount ++;
> + bitLength = max(bitOffset, bitLength);
> + if (output_features[0]->caps.ReportID != 0)
> + bitOffset = 8;
> + else
> + bitOffset = 0;
> + }
> + build_elements(wine_report, output_features[i], &bitOffset);
> + count_elements(output_features[i], &data->caps.NumberOutputButtonCaps,
> + &data->caps.NumberOutputValueCaps);
> + }
> + wine_report->dwSize += (sizeof(WINE_HID_ELEMENT) * wine_report->elementCount);
> + bitLength = max(bitOffset, bitLength);
> + data->caps.OutputReportByteLength = ((bitLength + 7) & ~7)/8;
> + }
> +
> + if (f_count)
> + {
> + bitLength = 0;
> + wine_report = (WINE_HID_REPORT*)(((BYTE*)wine_report)+wine_report->dwSize);
> + data->dwFeatureReportOffset = (BYTE*)wine_report - (BYTE*)data->InputReports;
> + new_report(wine_report, feature_features[0]);
> + data->dwFeatureReportCount ++;
> + if (feature_features[0]->caps.ReportID != 0)
> + bitOffset = 8;
> + else
> + bitOffset = 0;
> +
> + for (i = 0; i < f_count; i++)
> + {
> + if (feature_features[i]->caps.ReportID != wine_report->reportID)
> + {
> + wine_report->dwSize += (sizeof(WINE_HID_ELEMENT) * wine_report->elementCount);
> + wine_report = (WINE_HID_REPORT*)((BYTE*)wine_report)+wine_report->dwSize;
> + new_report(wine_report, feature_features[i]);
> + data->dwFeatureReportCount ++;
> + bitLength = max(bitOffset, bitLength);
> + if (feature_features[0]->caps.ReportID != 0)
> + bitOffset = 8;
> + else
> + bitOffset = 0;
> + }
> + build_elements(wine_report, feature_features[i], &bitOffset);
> + count_elements(feature_features[i], &data->caps.NumberFeatureButtonCaps,
> + &data->caps.NumberFeatureValueCaps);
> + }
> + bitLength = max(bitOffset, bitLength);
> + data->caps.FeatureReportByteLength = ((bitLength + 7) & ~7)/8;
> + }
> +
> + return data;
> +}
> +
> +static void free_collection(struct collection *collection)
> +{
> + struct feature *fentry, *fnext;
> + struct collection *centry, *cnext;
> + LIST_FOR_EACH_ENTRY_SAFE(centry, cnext, &collection->collections, struct collection, entry)
> + {
> + list_remove(¢ry->entry);
> + free_collection(centry);
> + }
> + LIST_FOR_EACH_ENTRY_SAFE(fentry, fnext, &collection->features, struct feature, col_entry)
> + {
> + list_remove(&fentry->col_entry);
> + HeapFree(GetProcessHeap(), 0, fentry);
> + }
> + HeapFree(GetProcessHeap(), 0, collection);
> +}
> +
> +static int compare_reports(const void *a, const void* b)
> +{
> + struct feature *f1 = *(struct feature **)a;
> + struct feature *f2 = *(struct feature **)b;
> + int c = (f1->caps.ReportID - f2->caps.ReportID);
> + if (c) return c;
> + return (f1->index - f2->index);
> +}
> +
> +WINE_HIDP_PREPARSED_DATA* ParseDescriptor(BYTE *descriptor, INT length)
> +{
> + WINE_HIDP_PREPARSED_DATA *data = NULL;
> + struct collection *base;
> + struct caps caps;
> +
> + struct list features;
> +
> + int feature_count = 0;
> + int cidx;
> +
> + if (TRACE_ON(hid))
> + {
> + TRACE("Descriptor[%i]: ", length);
> + for (cidx = 0; cidx < length; cidx++)
> + TRACE("%x ",descriptor[cidx]);
> + TRACE("\n");
> + }
> +
> + list_init(&features);
> +
> + base = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*base));
> + base->index = 1;
> + list_init(&base->features);
> + list_init(&base->collections);
> + memset(&caps, 0, sizeof(caps));
> +
> + cidx = 0;
> + parse_descriptor(descriptor, 0, length, &feature_count, &cidx, base, &caps, &features);
> +
> + debug_collection(base);
> +
> + cidx = 2;
> + if (feature_count)
> + {
> + struct feature *entry;
> + struct feature** sorted_features;
> + struct feature** input_features;
> + struct feature** output_features;
> + struct feature** feature_features;
> + int i_count, o_count, f_count;
> + int i;
> +
> + i_count = o_count = f_count = 0;
> +
> + sorted_features = HeapAlloc(GetProcessHeap(), 0, sizeof(*sorted_features) * feature_count);
> + input_features = HeapAlloc(GetProcessHeap(), 0, sizeof(*input_features) * feature_count);
> + output_features = HeapAlloc(GetProcessHeap(), 0, sizeof(*output_features) * feature_count);
> + feature_features = HeapAlloc(GetProcessHeap(), 0, sizeof(*feature_features) * feature_count);
> +
> + i = 0;
> + LIST_FOR_EACH_ENTRY(entry, &features, struct feature, entry)
> + sorted_features[i++] = entry;
> +
> + /* Sort features base on report if there are multiple reports */
> + if (sorted_features[0]->caps.ReportID != 0)
> + qsort(sorted_features, feature_count, sizeof(struct feature*), compare_reports);
> +
> + for (i = 0; i < feature_count; i++)
> + {
> + switch (sorted_features[i]->type)
> + {
> + case HidP_Input:
> + input_features[i_count] = sorted_features[i];
> + i_count++;
> + break;
> + case HidP_Output:
> + output_features[o_count] = sorted_features[i];
> + o_count++;
> + break;
> + case HidP_Feature:
> + feature_features[f_count] = sorted_features[i];
> + f_count++;
> + break;
> + default:
> + ERR("Unknown type %i\n",sorted_features[i]->type);
> + }
> + }
> +
> + if (TRACE_ON(hid))
> + {
> + TRACE("DUMP FEATURES:\n");
> + TRACE("----INPUT----\n");
> + for (cidx = 0; cidx < i_count; cidx++)
> + debug_feature(input_features[cidx]);
> + TRACE("----OUTPUT----\n");
> + for (cidx = 0; cidx < o_count; cidx++)
> + debug_feature(output_features[cidx]);
> + TRACE("----FEATURE----\n");
> + for (cidx = 0; cidx < f_count; cidx++)
> + debug_feature(feature_features[cidx]);
> + }
> +
> + data = build_PreparseData(sorted_features, feature_count, input_features, i_count, output_features, o_count, feature_features, f_count, base);
> +
> + debug_print_preparsed(data);
> +
> + HeapFree(GetProcessHeap(), 0, sorted_features);
> + HeapFree(GetProcessHeap(), 0, input_features);
> + HeapFree(GetProcessHeap(), 0, output_features);
> + HeapFree(GetProcessHeap(), 0, feature_features);
> + }
> +
> + free_collection(base);
> + /* We do not have to free the list as free_collection does all the work */
> +
> + return data;
> +}
> diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h
> index 8f9e56f..c8cee69 100644
> --- a/dlls/hidclass.sys/hid.h
> +++ b/dlls/hidclass.sys/hid.h
> @@ -81,3 +81,6 @@ void HID_DeleteDevice(HID_MINIDRIVER_REGISTRATION *driver, DEVICE_OBJECT *device
> /* Pseudo-Plug and Play support*/
> NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, void* native) DECLSPEC_HIDDEN;
> void PNP_CleanupPNP() DECLSPEC_HIDDEN;
> +
> +/* Parsing HID Report Descriptors into preparsed data */
> +WINE_HIDP_PREPARSED_DATA* ParseDescriptor(BYTE *descriptor, INT length) DECLSPEC_HIDDEN;
> diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c
> index ca85cb7..5694b8a 100644
> --- a/dlls/hidclass.sys/pnp.c
> +++ b/dlls/hidclass.sys/pnp.c
> @@ -53,6 +53,9 @@ NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, void *native)
> DWORD index = HID_STRING_ID_ISERIALNUMBER;
> NATIVE_DEVICE *tracked_device, *ptr;
> INT interface_index = 1;
> + HID_DESCRIPTOR descriptor;
> + BYTE *reportDescriptor;
> + INT i;
>
> static const WCHAR ig_fmtW[] = {'I','G','_','%','i',0};
> static const WCHAR im_fmtW[] = {'I','M','_','%','i',0};
> @@ -104,6 +107,40 @@ NTSTATUS WINAPI PNP_AddDevice(DRIVER_OBJECT *driver, void *native)
>
> list_add_tail(&tracked_devices, &tracked_device->entry);
>
> + status = call_minidriver(IOCTL_HID_GET_DEVICE_DESCRIPTOR, device, NULL, 0,
> + &descriptor, sizeof(descriptor));
> + if (status != STATUS_SUCCESS)
> + {
> + ERR("Cannot get Device Descriptor(%x)\n",status);
> + HID_DeleteDevice(&minidriver->minidriver, device);
> + return status;
> + }
> + for (i = 0; i < descriptor.bNumDescriptors; i++)
> + if (descriptor.DescriptorList[i].bReportType == HID_REPORT_DESCRIPTOR_TYPE)
> + break;
> +
> + if (i >= descriptor.bNumDescriptors)
> + {
> + ERR("No Report Descriptor found in reply\n");
> + HID_DeleteDevice(&minidriver->minidriver, device);
> + return status;
> + }
> +
> + reportDescriptor = HeapAlloc(GetProcessHeap(), 0, descriptor.DescriptorList[i].wReportLength);
> + status = call_minidriver(IOCTL_HID_GET_REPORT_DESCRIPTOR, device, NULL, 0,
> + reportDescriptor, descriptor.DescriptorList[i].wReportLength);
> + if (status != STATUS_SUCCESS)
> + {
> + ERR("Cannot get Report Descriptor(%x)\n",status);
> + HID_DeleteDevice(&minidriver->minidriver, device);
> + HeapFree(GetProcessHeap(), 0, reportDescriptor);
> + return status;
> + }
> +
> + ext->preparseData = ParseDescriptor(reportDescriptor, descriptor.DescriptorList[0].wReportLength);
> + ext->information.DescriptorSize = ext->preparseData->dwSize;
> + HeapFree(GetProcessHeap(), 0, reportDescriptor);
> +
> status = call_minidriver(IOCTL_HID_GET_STRING, device,
> (void*)index, sizeof(DWORD), serial, sizeof(serial));
>
>
>