988 lines
49 KiB
C
988 lines
49 KiB
C
/*******************************************************
|
|
HIDAPI - Multi-Platform library for
|
|
communication with HID devices.
|
|
|
|
libusb/hidapi Team
|
|
|
|
Copyright 2022, All Rights Reserved.
|
|
|
|
At the discretion of the user of this library,
|
|
this software may be licensed under the terms of the
|
|
GNU General Public License v3, a BSD-Style license, or the
|
|
original HIDAPI license as outlined in the LICENSE.txt,
|
|
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
|
files located at the root of the source distribution.
|
|
These files may also be found in the public source
|
|
code repository located at:
|
|
https://github.com/libusb/hidapi .
|
|
********************************************************/
|
|
#include "hidapi_descriptor_reconstruct.h"
|
|
|
|
/**
|
|
* @brief References to report descriptor buffer.
|
|
*
|
|
*/
|
|
struct rd_buffer {
|
|
unsigned char* buf; /* Pointer to the array which stores the reconstructed descriptor */
|
|
size_t buf_size; /* Size of the buffer in bytes */
|
|
size_t byte_idx; /* Index of the next report byte to write to buf array */
|
|
};
|
|
|
|
/**
|
|
* @brief Function that appends a byte to encoded report descriptor buffer.
|
|
*
|
|
* @param[in] byte Single byte to append.
|
|
* @param rpt_desc Pointer to report descriptor buffer struct.
|
|
*/
|
|
static void rd_append_byte(unsigned char byte, struct rd_buffer* rpt_desc) {
|
|
if (rpt_desc->byte_idx < rpt_desc->buf_size) {
|
|
rpt_desc->buf[rpt_desc->byte_idx] = byte;
|
|
rpt_desc->byte_idx++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Writes a short report descriptor item according USB HID spec 1.11 chapter 6.2.2.2.
|
|
*
|
|
* @param[in] rd_item Enumeration identifying type (Main, Global, Local) and function (e.g Usage or Report Count) of the item.
|
|
* @param[in] data Data (Size depends on rd_item 0,1,2 or 4bytes).
|
|
* @param rpt_desc Pointer to report descriptor buffer struct.
|
|
*
|
|
* @return Returns 0 if successful, -1 for error.
|
|
*/
|
|
static int rd_write_short_item(rd_items rd_item, LONG64 data, struct rd_buffer* rpt_desc) {
|
|
if (rd_item & 0x03) {
|
|
// Invalid input data, last to bits are reserved for data size
|
|
return -1;
|
|
}
|
|
|
|
if (rd_item == rd_main_collection_end) {
|
|
// Item without data (1Byte prefix only)
|
|
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x00;
|
|
rd_append_byte(oneBytePrefix, rpt_desc);
|
|
}
|
|
else if ((rd_item == rd_global_logical_minimum) ||
|
|
(rd_item == rd_global_logical_maximum) ||
|
|
(rd_item == rd_global_physical_minimum) ||
|
|
(rd_item == rd_global_physical_maximum)) {
|
|
// Item with signed integer data
|
|
if ((data >= -128) && (data <= 127)) {
|
|
// 1Byte prefix + 1Byte data
|
|
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;
|
|
char localData = (char)data;
|
|
rd_append_byte(oneBytePrefix, rpt_desc);
|
|
rd_append_byte(localData & 0xFF, rpt_desc);
|
|
}
|
|
else if ((data >= -32768) && (data <= 32767)) {
|
|
// 1Byte prefix + 2Byte data
|
|
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;
|
|
INT16 localData = (INT16)data;
|
|
rd_append_byte(oneBytePrefix, rpt_desc);
|
|
rd_append_byte(localData & 0xFF, rpt_desc);
|
|
rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
|
|
}
|
|
else if ((data >= -2147483648LL) && (data <= 2147483647)) {
|
|
// 1Byte prefix + 4Byte data
|
|
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;
|
|
INT32 localData = (INT32)data;
|
|
rd_append_byte(oneBytePrefix, rpt_desc);
|
|
rd_append_byte(localData & 0xFF, rpt_desc);
|
|
rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
|
|
rd_append_byte(localData >> 16 & 0xFF, rpt_desc);
|
|
rd_append_byte(localData >> 24 & 0xFF, rpt_desc);
|
|
}
|
|
else {
|
|
// Data out of 32 bit signed integer range
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
// Item with unsigned integer data
|
|
if ((data >= 0) && (data <= 0xFF)) {
|
|
// 1Byte prefix + 1Byte data
|
|
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;
|
|
unsigned char localData = (unsigned char)data;
|
|
rd_append_byte(oneBytePrefix, rpt_desc);
|
|
rd_append_byte(localData & 0xFF, rpt_desc);
|
|
}
|
|
else if ((data >= 0) && (data <= 0xFFFF)) {
|
|
// 1Byte prefix + 2Byte data
|
|
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;
|
|
UINT16 localData = (UINT16)data;
|
|
rd_append_byte(oneBytePrefix, rpt_desc);
|
|
rd_append_byte(localData & 0xFF, rpt_desc);
|
|
rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
|
|
}
|
|
else if ((data >= 0) && (data <= 0xFFFFFFFF)) {
|
|
// 1Byte prefix + 4Byte data
|
|
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;
|
|
UINT32 localData = (UINT32)data;
|
|
rd_append_byte(oneBytePrefix, rpt_desc);
|
|
rd_append_byte(localData & 0xFF, rpt_desc);
|
|
rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
|
|
rd_append_byte(localData >> 16 & 0xFF, rpt_desc);
|
|
rd_append_byte(localData >> 24 & 0xFF, rpt_desc);
|
|
}
|
|
else {
|
|
// Data out of 32 bit unsigned integer range
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct rd_main_item_node * rd_append_main_item_node(int first_bit, int last_bit, rd_node_type type_of_node, int caps_index, int collection_index, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
|
|
struct rd_main_item_node *new_list_node;
|
|
|
|
// Determine last node in the list
|
|
while (*list != NULL)
|
|
{
|
|
list = &(*list)->next;
|
|
}
|
|
|
|
new_list_node = malloc(sizeof(*new_list_node)); // Create new list entry
|
|
new_list_node->FirstBit = first_bit;
|
|
new_list_node->LastBit = last_bit;
|
|
new_list_node->TypeOfNode = type_of_node;
|
|
new_list_node->CapsIndex = caps_index;
|
|
new_list_node->CollectionIndex = collection_index;
|
|
new_list_node->MainItemType = main_item_type;
|
|
new_list_node->ReportID = report_id;
|
|
new_list_node->next = NULL; // NULL marks last node in the list
|
|
|
|
*list = new_list_node;
|
|
return new_list_node;
|
|
}
|
|
|
|
static struct rd_main_item_node * rd_insert_main_item_node(int first_bit, int last_bit, rd_node_type type_of_node, int caps_index, int collection_index, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
|
|
// Insert item after the main item node referenced by list
|
|
struct rd_main_item_node *next_item = (*list)->next;
|
|
(*list)->next = NULL;
|
|
rd_append_main_item_node(first_bit, last_bit, type_of_node, caps_index, collection_index, main_item_type, report_id, list);
|
|
(*list)->next->next = next_item;
|
|
return (*list)->next;
|
|
}
|
|
|
|
static struct rd_main_item_node * rd_search_main_item_list_for_bit_position(int search_bit, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
|
|
// Determine first INPUT/OUTPUT/FEATURE main item, where the last bit position is equal or greater than the search bit position
|
|
|
|
while (((*list)->next->MainItemType != rd_collection) &&
|
|
((*list)->next->MainItemType != rd_collection_end) &&
|
|
!(((*list)->next->LastBit >= search_bit) &&
|
|
((*list)->next->ReportID == report_id) &&
|
|
((*list)->next->MainItemType == main_item_type))
|
|
)
|
|
{
|
|
list = &(*list)->next;
|
|
}
|
|
return *list;
|
|
}
|
|
|
|
int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned char *buf, size_t buf_size)
|
|
{
|
|
hidp_preparsed_data *pp_data = (hidp_preparsed_data *) preparsed_data;
|
|
|
|
// Check if MagicKey is correct, to ensure that pp_data points to an valid preparse data structure
|
|
if (memcmp(pp_data->MagicKey, "HidP KDR", 8) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
struct rd_buffer rpt_desc = {
|
|
.buf = buf,
|
|
.buf_size = buf_size,
|
|
.byte_idx = 0
|
|
};
|
|
|
|
// Set pointer to the first node of link_collection_nodes
|
|
phid_pp_link_collection_node link_collection_nodes = (phid_pp_link_collection_node)(((unsigned char*)&pp_data->caps[0]) + pp_data->FirstByteOfLinkCollectionArray);
|
|
|
|
// ****************************************************************************************************************************
|
|
// Create lookup tables for the bit range of each report per collection (position of first bit and last bit in each collection)
|
|
// coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
|
|
// ****************************************************************************************************************************
|
|
|
|
// Allocate memory and initialize lookup table
|
|
rd_bit_range ****coll_bit_range;
|
|
coll_bit_range = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_bit_range));
|
|
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
|
coll_bit_range[collection_node_idx] = malloc(256 * sizeof(*coll_bit_range[0])); // 256 possible report IDs (incl. 0x00)
|
|
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
|
coll_bit_range[collection_node_idx][reportid_idx] = malloc(NUM_OF_HIDP_REPORT_TYPES * sizeof(*coll_bit_range[0][0]));
|
|
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
|
coll_bit_range[collection_node_idx][reportid_idx][rt_idx] = malloc(sizeof(rd_bit_range));
|
|
coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = -1;
|
|
coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fill the lookup table where caps exist
|
|
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
|
for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {
|
|
int first_bit, last_bit;
|
|
first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8
|
|
+ pp_data->caps[caps_idx].BitPosition;
|
|
last_bit = first_bit + pp_data->caps[caps_idx].ReportSize
|
|
* pp_data->caps[caps_idx].ReportCount - 1;
|
|
if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit == -1 ||
|
|
coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit > first_bit) {
|
|
coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit = first_bit;
|
|
}
|
|
if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit < last_bit) {
|
|
coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit = last_bit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// *************************************************************************
|
|
// -Determine hierarchy levels of each collections and store it in:
|
|
// coll_levels[COLLECTION_INDEX]
|
|
// -Determine number of direct childs of each collections and store it in:
|
|
// coll_number_of_direct_childs[COLLECTION_INDEX]
|
|
// *************************************************************************
|
|
int max_coll_level = 0;
|
|
int *coll_levels = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_levels[0]));
|
|
int *coll_number_of_direct_childs = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_number_of_direct_childs[0]));
|
|
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
|
coll_levels[collection_node_idx] = -1;
|
|
coll_number_of_direct_childs[collection_node_idx] = 0;
|
|
}
|
|
|
|
{
|
|
int actual_coll_level = 0;
|
|
USHORT collection_node_idx = 0;
|
|
while (actual_coll_level >= 0) {
|
|
coll_levels[collection_node_idx] = actual_coll_level;
|
|
if ((link_collection_nodes[collection_node_idx].NumberOfChildren > 0) &&
|
|
(coll_levels[link_collection_nodes[collection_node_idx].FirstChild] == -1)) {
|
|
actual_coll_level++;
|
|
coll_levels[collection_node_idx] = actual_coll_level;
|
|
if (max_coll_level < actual_coll_level) {
|
|
max_coll_level = actual_coll_level;
|
|
}
|
|
coll_number_of_direct_childs[collection_node_idx]++;
|
|
collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;
|
|
}
|
|
else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {
|
|
coll_number_of_direct_childs[link_collection_nodes[collection_node_idx].Parent]++;
|
|
collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;
|
|
}
|
|
else {
|
|
actual_coll_level--;
|
|
if (actual_coll_level >= 0) {
|
|
collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// *********************************************************************************
|
|
// Propagate the bit range of each report from the child collections to their parent
|
|
// and store the merged result for the parent
|
|
// *********************************************************************************
|
|
for (int actual_coll_level = max_coll_level - 1; actual_coll_level >= 0; actual_coll_level--) {
|
|
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
|
if (coll_levels[collection_node_idx] == actual_coll_level) {
|
|
USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;
|
|
while (child_idx) {
|
|
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
|
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
|
// Merge bit range from childs
|
|
if ((coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
|
|
(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit)) {
|
|
coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit;
|
|
}
|
|
if (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit < coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit) {
|
|
coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit;
|
|
}
|
|
child_idx = link_collection_nodes[child_idx].NextSibling;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// **************************************************************************************************
|
|
// Determine child collection order of the whole hierarchy, based on previously determined bit ranges
|
|
// and store it this index coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
|
|
// **************************************************************************************************
|
|
USHORT **coll_child_order;
|
|
coll_child_order = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_child_order));
|
|
{
|
|
BOOLEAN *coll_parsed_flag;
|
|
coll_parsed_flag = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_parsed_flag[0]));
|
|
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
|
coll_parsed_flag[collection_node_idx] = FALSE;
|
|
}
|
|
int actual_coll_level = 0;
|
|
USHORT collection_node_idx = 0;
|
|
while (actual_coll_level >= 0) {
|
|
if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&
|
|
(coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] == FALSE)) {
|
|
coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] = TRUE;
|
|
coll_child_order[collection_node_idx] = malloc((coll_number_of_direct_childs[collection_node_idx]) * sizeof(*coll_child_order[0]));
|
|
|
|
{
|
|
// Create list of child collection indices
|
|
// sorted reverse to the order returned to HidP_GetLinkCollectionNodeschild
|
|
// which seems to match the original order, as long as no bit position needs to be considered
|
|
USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;
|
|
int child_count = coll_number_of_direct_childs[collection_node_idx] - 1;
|
|
coll_child_order[collection_node_idx][child_count] = child_idx;
|
|
while (link_collection_nodes[child_idx].NextSibling) {
|
|
child_count--;
|
|
child_idx = link_collection_nodes[child_idx].NextSibling;
|
|
coll_child_order[collection_node_idx][child_count] = child_idx;
|
|
}
|
|
}
|
|
|
|
if (coll_number_of_direct_childs[collection_node_idx] > 1) {
|
|
// Sort child collections indices by bit positions
|
|
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
|
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
|
for (int child_idx = 1; child_idx < coll_number_of_direct_childs[collection_node_idx]; child_idx++) {
|
|
// since the coll_bit_range array is not sorted, we need to reference the collection index in
|
|
// our sorted coll_child_order array, and look up the corresponding bit ranges for comparing values to sort
|
|
int prev_coll_idx = coll_child_order[collection_node_idx][child_idx - 1];
|
|
int cur_coll_idx = coll_child_order[collection_node_idx][child_idx];
|
|
if ((coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
|
|
(coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
|
|
(coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit)) {
|
|
// Swap position indices of the two compared child collections
|
|
USHORT idx_latch = coll_child_order[collection_node_idx][child_idx - 1];
|
|
coll_child_order[collection_node_idx][child_idx - 1] = coll_child_order[collection_node_idx][child_idx];
|
|
coll_child_order[collection_node_idx][child_idx] = idx_latch;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
actual_coll_level++;
|
|
collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;
|
|
}
|
|
else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {
|
|
collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;
|
|
}
|
|
else {
|
|
actual_coll_level--;
|
|
if (actual_coll_level >= 0) {
|
|
collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
|
|
}
|
|
}
|
|
}
|
|
free(coll_parsed_flag);
|
|
}
|
|
|
|
|
|
// ***************************************************************************************
|
|
// Create sorted main_item_list containing all the Collection and CollectionEnd main items
|
|
// ***************************************************************************************
|
|
struct rd_main_item_node *main_item_list = NULL; // List root
|
|
// Lookup table to find the Collection items in the list by index
|
|
struct rd_main_item_node **coll_begin_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_begin_lookup));
|
|
struct rd_main_item_node **coll_end_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_end_lookup));
|
|
{
|
|
int *coll_last_written_child = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_last_written_child[0]));
|
|
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
|
coll_last_written_child[collection_node_idx] = -1;
|
|
}
|
|
|
|
int actual_coll_level = 0;
|
|
USHORT collection_node_idx = 0;
|
|
struct rd_main_item_node *firstDelimiterNode = NULL;
|
|
struct rd_main_item_node *delimiterCloseNode = NULL;
|
|
coll_begin_lookup[0] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
|
|
while (actual_coll_level >= 0) {
|
|
if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&
|
|
(coll_last_written_child[collection_node_idx] == -1)) {
|
|
// Collection has child collections, but none is written to the list yet
|
|
|
|
coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][0];
|
|
collection_node_idx = coll_child_order[collection_node_idx][0];
|
|
|
|
// In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
|
|
// While the order in the WIN32 capabiliy strutures is the opposite:
|
|
// Here the preferred usage is the last aliased usage in the sequence.
|
|
|
|
if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {
|
|
// Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
|
|
firstDelimiterNode = main_item_list;
|
|
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &main_item_list);
|
|
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_close, 0, &main_item_list);
|
|
delimiterCloseNode = main_item_list;
|
|
}
|
|
else {
|
|
// Normal not aliased collection
|
|
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
|
|
actual_coll_level++;
|
|
}
|
|
|
|
|
|
}
|
|
else if ((coll_number_of_direct_childs[collection_node_idx] > 1) &&
|
|
(coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][coll_number_of_direct_childs[collection_node_idx] - 1])) {
|
|
// Collection has child collections, and this is not the first child
|
|
|
|
int nextChild = 1;
|
|
while (coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][nextChild - 1]) {
|
|
nextChild++;
|
|
}
|
|
coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][nextChild];
|
|
collection_node_idx = coll_child_order[collection_node_idx][nextChild];
|
|
|
|
if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {
|
|
// Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
|
|
firstDelimiterNode = main_item_list;
|
|
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &main_item_list);
|
|
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_close, 0, &main_item_list);
|
|
delimiterCloseNode = main_item_list;
|
|
}
|
|
else if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {
|
|
coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &firstDelimiterNode);
|
|
}
|
|
else if (!link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {
|
|
coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &firstDelimiterNode);
|
|
coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_open, 0, &firstDelimiterNode);
|
|
firstDelimiterNode = NULL;
|
|
main_item_list = delimiterCloseNode;
|
|
delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE
|
|
}
|
|
if (!link_collection_nodes[collection_node_idx].IsAlias) {
|
|
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
|
|
actual_coll_level++;
|
|
}
|
|
}
|
|
else {
|
|
actual_coll_level--;
|
|
coll_end_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection_end, 0, &main_item_list);
|
|
collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
|
|
}
|
|
}
|
|
free(coll_last_written_child);
|
|
}
|
|
|
|
|
|
// ****************************************************************
|
|
// Inserted Input/Output/Feature main items into the main_item_list
|
|
// in order of reconstructed bit positions
|
|
// ****************************************************************
|
|
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
|
// Add all value caps to node list
|
|
struct rd_main_item_node *firstDelimiterNode = NULL;
|
|
struct rd_main_item_node *delimiterCloseNode = NULL;
|
|
for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {
|
|
struct rd_main_item_node *coll_begin = coll_begin_lookup[pp_data->caps[caps_idx].LinkCollection];
|
|
int first_bit, last_bit;
|
|
first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8 +
|
|
pp_data->caps[caps_idx].BitPosition;
|
|
last_bit = first_bit + pp_data->caps[caps_idx].ReportSize *
|
|
pp_data->caps[caps_idx].ReportCount - 1;
|
|
|
|
for (int child_idx = 0; child_idx < coll_number_of_direct_childs[pp_data->caps[caps_idx].LinkCollection]; child_idx++) {
|
|
// Determine in which section before/between/after child collection the item should be inserted
|
|
if (first_bit < coll_bit_range[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit)
|
|
{
|
|
// Note, that the default value for undefined coll_bit_range is -1, which can't be greater than the bit position
|
|
break;
|
|
}
|
|
coll_begin = coll_end_lookup[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]];
|
|
}
|
|
struct rd_main_item_node *list_node;
|
|
list_node = rd_search_main_item_list_for_bit_position(first_bit, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &coll_begin);
|
|
|
|
// In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
|
|
// While the order in the WIN32 capabiliy strutures is the opposite:
|
|
// Here the preferred usage is the last aliased usage in the sequence.
|
|
|
|
if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode == NULL)) {
|
|
// Alliased Usage (First node in pp_data->caps -> Last entry in report descriptor output)
|
|
firstDelimiterNode = list_node;
|
|
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
|
|
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_close, pp_data->caps[caps_idx].ReportID, &list_node);
|
|
delimiterCloseNode = list_node;
|
|
} else if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {
|
|
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
|
|
}
|
|
else if (!pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {
|
|
// Alliased Collection (Last node in pp_data->caps -> First entry in report descriptor output)
|
|
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
|
|
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_open, pp_data->caps[caps_idx].ReportID, &list_node);
|
|
firstDelimiterNode = NULL;
|
|
list_node = delimiterCloseNode;
|
|
delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE
|
|
}
|
|
if (!pp_data->caps[caps_idx].IsAlias) {
|
|
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &list_node);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***********************************************************
|
|
// Add const main items for padding to main_item_list
|
|
// -To fill all bit gaps
|
|
// -At each report end for 8bit padding
|
|
// Note that information about the padding at the report end,
|
|
// is not stored in the preparsed data, but in practice all
|
|
// report descriptors seem to have it, as assumed here.
|
|
// ***********************************************************
|
|
{
|
|
int last_bit_position[NUM_OF_HIDP_REPORT_TYPES][256];
|
|
struct rd_main_item_node *last_report_item_lookup[NUM_OF_HIDP_REPORT_TYPES][256];
|
|
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
|
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
|
last_bit_position[rt_idx][reportid_idx] = -1;
|
|
last_report_item_lookup[rt_idx][reportid_idx] = NULL;
|
|
}
|
|
}
|
|
|
|
struct rd_main_item_node *list = main_item_list; // List root;
|
|
|
|
while (list->next != NULL)
|
|
{
|
|
if ((list->MainItemType >= rd_input) &&
|
|
(list->MainItemType <= rd_feature)) {
|
|
// INPUT, OUTPUT or FEATURE
|
|
if (list->FirstBit != -1) {
|
|
if ((last_bit_position[list->MainItemType][list->ReportID] + 1 != list->FirstBit) &&
|
|
(last_report_item_lookup[list->MainItemType][list->ReportID] != NULL) &&
|
|
(last_report_item_lookup[list->MainItemType][list->ReportID]->FirstBit != list->FirstBit) // Happens in case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array
|
|
) {
|
|
struct rd_main_item_node *list_node = rd_search_main_item_list_for_bit_position(last_bit_position[list->MainItemType][list->ReportID], list->MainItemType, list->ReportID, &last_report_item_lookup[list->MainItemType][list->ReportID]);
|
|
rd_insert_main_item_node(last_bit_position[list->MainItemType][list->ReportID] + 1, list->FirstBit - 1, rd_item_node_padding, -1, 0, list->MainItemType, list->ReportID, &list_node);
|
|
}
|
|
last_bit_position[list->MainItemType][list->ReportID] = list->LastBit;
|
|
last_report_item_lookup[list->MainItemType][list->ReportID] = list;
|
|
}
|
|
}
|
|
list = list->next;
|
|
}
|
|
// Add 8 bit padding at each report end
|
|
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
|
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
|
if (last_bit_position[rt_idx][reportid_idx] != -1) {
|
|
int padding = 8 - ((last_bit_position[rt_idx][reportid_idx] + 1) % 8);
|
|
if (padding < 8) {
|
|
// Insert padding item after item referenced in last_report_item_lookup
|
|
rd_insert_main_item_node(last_bit_position[rt_idx][reportid_idx] + 1, last_bit_position[rt_idx][reportid_idx] + padding, rd_item_node_padding, -1, 0, (rd_main_items) rt_idx, (unsigned char) reportid_idx, &last_report_item_lookup[rt_idx][reportid_idx]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***********************************
|
|
// Encode the report descriptor output
|
|
// ***********************************
|
|
UCHAR last_report_id = 0;
|
|
USAGE last_usage_page = 0;
|
|
LONG last_physical_min = 0;// If both, Physical Minimum and Physical Maximum are 0, the logical limits should be taken as physical limits according USB HID spec 1.11 chapter 6.2.2.7
|
|
LONG last_physical_max = 0;
|
|
ULONG last_unit_exponent = 0; // If Unit Exponent is Undefined it should be considered as 0 according USB HID spec 1.11 chapter 6.2.2.7
|
|
ULONG last_unit = 0; // If the first nibble is 7, or second nibble of Unit is 0, the unit is None according USB HID spec 1.11 chapter 6.2.2.7
|
|
BOOLEAN inhibit_write_of_usage = FALSE; // Needed in case of delimited usage print, before the normal collection or cap
|
|
int report_count = 0;
|
|
while (main_item_list != NULL)
|
|
{
|
|
int rt_idx = main_item_list->MainItemType;
|
|
int caps_idx = main_item_list->CapsIndex;
|
|
if (main_item_list->MainItemType == rd_collection) {
|
|
if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {
|
|
// Write "Usage Page" at the begin of a collection - except it refers the same table as wrote last
|
|
rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);
|
|
last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;
|
|
}
|
|
if (inhibit_write_of_usage) {
|
|
// Inhibit only once after DELIMITER statement
|
|
inhibit_write_of_usage = FALSE;
|
|
}
|
|
else {
|
|
// Write "Usage" of collection
|
|
rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);
|
|
}
|
|
// Write begin of "Collection"
|
|
rd_write_short_item(rd_main_collection, link_collection_nodes[main_item_list->CollectionIndex].CollectionType, &rpt_desc);
|
|
}
|
|
else if (main_item_list->MainItemType == rd_collection_end) {
|
|
// Write "End Collection"
|
|
rd_write_short_item(rd_main_collection_end, 0, &rpt_desc);
|
|
}
|
|
else if (main_item_list->MainItemType == rd_delimiter_open) {
|
|
if (main_item_list->CollectionIndex != -1) {
|
|
// Write "Usage Page" inside of a collection delmiter section
|
|
if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {
|
|
rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);
|
|
last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;
|
|
}
|
|
}
|
|
else if (main_item_list->CapsIndex != 0) {
|
|
// Write "Usage Page" inside of a main item delmiter section
|
|
if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
|
|
rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
|
|
last_usage_page = pp_data->caps[caps_idx].UsagePage;
|
|
}
|
|
}
|
|
// Write "Delimiter Open"
|
|
rd_write_short_item(rd_local_delimiter, 1, &rpt_desc); // 1 = open set of aliased usages
|
|
}
|
|
else if (main_item_list->MainItemType == rd_delimiter_usage) {
|
|
if (main_item_list->CollectionIndex != -1) {
|
|
// Write aliased collection "Usage"
|
|
rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);
|
|
} if (main_item_list->CapsIndex != 0) {
|
|
// Write aliased main item range from "Usage Minimum" to "Usage Maximum"
|
|
if (pp_data->caps[caps_idx].IsRange) {
|
|
rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
|
|
rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
|
|
}
|
|
else {
|
|
// Write single aliased main item "Usage"
|
|
rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
|
|
}
|
|
}
|
|
}
|
|
else if (main_item_list->MainItemType == rd_delimiter_close) {
|
|
// Write "Delimiter Close"
|
|
rd_write_short_item(rd_local_delimiter, 0, &rpt_desc); // 0 = close set of aliased usages
|
|
// Inhibit next usage write
|
|
inhibit_write_of_usage = TRUE;
|
|
}
|
|
else if (main_item_list->TypeOfNode == rd_item_node_padding) {
|
|
// Padding
|
|
// The preparsed data doesn't contain any information about padding. Therefore all undefined gaps
|
|
// in the reports are filled with the same style of constant padding.
|
|
|
|
// Write "Report Size" with number of padding bits
|
|
rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc);
|
|
|
|
// Write "Report Count" for padding always as 1
|
|
rd_write_short_item(rd_global_report_count, 1, &rpt_desc);
|
|
|
|
if (rt_idx == HidP_Input) {
|
|
// Write "Input" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
|
|
rd_write_short_item(rd_main_input, 0x03, &rpt_desc); // Const / Abs
|
|
}
|
|
else if (rt_idx == HidP_Output) {
|
|
// Write "Output" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
|
|
rd_write_short_item(rd_main_output, 0x03, &rpt_desc); // Const / Abs
|
|
}
|
|
else if (rt_idx == HidP_Feature) {
|
|
// Write "Feature" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
|
|
rd_write_short_item(rd_main_feature, 0x03, &rpt_desc); // Const / Abs
|
|
}
|
|
report_count = 0;
|
|
}
|
|
else if (pp_data->caps[caps_idx].IsButtonCap) {
|
|
// Button
|
|
// (The preparsed data contain different data for 1 bit Button caps, than for parametric Value caps)
|
|
|
|
if (last_report_id != pp_data->caps[caps_idx].ReportID) {
|
|
// Write "Report ID" if changed
|
|
rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);
|
|
last_report_id = pp_data->caps[caps_idx].ReportID;
|
|
}
|
|
|
|
// Write "Usage Page" when changed
|
|
if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
|
|
rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
|
|
last_usage_page = pp_data->caps[caps_idx].UsagePage;
|
|
}
|
|
|
|
// Write only local report items for each cap, if ReportCount > 1
|
|
if (pp_data->caps[caps_idx].IsRange) {
|
|
report_count += (pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin);
|
|
}
|
|
|
|
if (inhibit_write_of_usage) {
|
|
// Inhibit only once after Delimiter - Reset flag
|
|
inhibit_write_of_usage = FALSE;
|
|
}
|
|
else {
|
|
if (pp_data->caps[caps_idx].IsRange) {
|
|
// Write range from "Usage Minimum" to "Usage Maximum"
|
|
rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
|
|
rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
|
|
}
|
|
else {
|
|
// Write single "Usage"
|
|
rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
|
|
}
|
|
}
|
|
|
|
if (pp_data->caps[caps_idx].IsDesignatorRange) {
|
|
// Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
|
|
rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);
|
|
rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);
|
|
}
|
|
else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {
|
|
// Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
|
|
// that specifies the number of additional descriptor sets.
|
|
// Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
|
|
// Write single "Designator Index"
|
|
rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);
|
|
}
|
|
|
|
if (pp_data->caps[caps_idx].IsStringRange) {
|
|
// Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
|
|
rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);
|
|
rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);
|
|
}
|
|
else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {
|
|
// String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
|
|
// therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
|
|
// Write single "String Index"
|
|
rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);
|
|
}
|
|
|
|
if ((main_item_list->next != NULL) &&
|
|
((int)main_item_list->next->MainItemType == rt_idx) &&
|
|
(main_item_list->next->TypeOfNode == rd_item_node_cap) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&
|
|
(!pp_data->caps[caps_idx].IsRange) && // This node in list is no array
|
|
(!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array
|
|
(pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField)
|
|
) {
|
|
if (main_item_list->next->FirstBit != main_item_list->FirstBit) {
|
|
// In case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array, the report count should be incremented
|
|
|
|
// Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
|
|
report_count++;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if ((pp_data->caps[caps_idx].Button.LogicalMin == 0) &&
|
|
(pp_data->caps[caps_idx].Button.LogicalMax == 0)) {
|
|
// While a HID report descriptor must always contain LogicalMinimum and LogicalMaximum,
|
|
// the preparsed data contain both fields set to zero, for the case of simple buttons
|
|
// Write "Logical Minimum" set to 0 and "Logical Maximum" set to 1
|
|
rd_write_short_item(rd_global_logical_minimum, 0, &rpt_desc);
|
|
rd_write_short_item(rd_global_logical_maximum, 1, &rpt_desc);
|
|
}
|
|
else {
|
|
// Write logical range from "Logical Minimum" to "Logical Maximum"
|
|
rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].Button.LogicalMin, &rpt_desc);
|
|
rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].Button.LogicalMax, &rpt_desc);
|
|
}
|
|
|
|
// Write "Report Size"
|
|
rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);
|
|
|
|
// Write "Report Count"
|
|
if (!pp_data->caps[caps_idx].IsRange) {
|
|
// Variable bit field with one bit per button
|
|
// In case of multiple usages with the same items, only "Usage" is written per cap, and "Report Count" is incremented
|
|
rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);
|
|
}
|
|
else {
|
|
// Button array of "Report Size" x "Report Count
|
|
rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount, &rpt_desc);
|
|
}
|
|
|
|
|
|
// Buttons have only 1 bit and therefore no physical limits/units -> Set to undefined state
|
|
if (last_physical_min != 0) {
|
|
// Write "Physical Minimum", but only if changed
|
|
last_physical_min = 0;
|
|
rd_write_short_item(rd_global_physical_minimum, last_physical_min, &rpt_desc);
|
|
}
|
|
if (last_physical_max != 0) {
|
|
// Write "Physical Maximum", but only if changed
|
|
last_physical_max = 0;
|
|
rd_write_short_item(rd_global_physical_maximum, last_physical_max, &rpt_desc);
|
|
}
|
|
if (last_unit_exponent != 0) {
|
|
// Write "Unit Exponent", but only if changed
|
|
last_unit_exponent = 0;
|
|
rd_write_short_item(rd_global_unit_exponent, last_unit_exponent, &rpt_desc);
|
|
}
|
|
if (last_unit != 0) {
|
|
// Write "Unit",but only if changed
|
|
last_unit = 0;
|
|
rd_write_short_item(rd_global_unit, last_unit, &rpt_desc);
|
|
}
|
|
|
|
// Write "Input" main item
|
|
if (rt_idx == HidP_Input) {
|
|
rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
|
}
|
|
// Write "Output" main item
|
|
else if (rt_idx == HidP_Output) {
|
|
rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
|
}
|
|
// Write "Feature" main item
|
|
else if (rt_idx == HidP_Feature) {
|
|
rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
|
}
|
|
report_count = 0;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (last_report_id != pp_data->caps[caps_idx].ReportID) {
|
|
// Write "Report ID" if changed
|
|
rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);
|
|
last_report_id = pp_data->caps[caps_idx].ReportID;
|
|
}
|
|
|
|
// Write "Usage Page" if changed
|
|
if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
|
|
rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
|
|
last_usage_page = pp_data->caps[caps_idx].UsagePage;
|
|
}
|
|
|
|
if (inhibit_write_of_usage) {
|
|
// Inhibit only once after Delimiter - Reset flag
|
|
inhibit_write_of_usage = FALSE;
|
|
}
|
|
else {
|
|
if (pp_data->caps[caps_idx].IsRange) {
|
|
// Write usage range from "Usage Minimum" to "Usage Maximum"
|
|
rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
|
|
rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
|
|
}
|
|
else {
|
|
// Write single "Usage"
|
|
rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
|
|
}
|
|
}
|
|
|
|
if (pp_data->caps[caps_idx].IsDesignatorRange) {
|
|
// Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
|
|
rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);
|
|
rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);
|
|
}
|
|
else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {
|
|
// Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
|
|
// that specifies the number of additional descriptor sets.
|
|
// Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
|
|
// Write single "Designator Index"
|
|
rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);
|
|
}
|
|
|
|
if (pp_data->caps[caps_idx].IsStringRange) {
|
|
// Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
|
|
rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);
|
|
rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);
|
|
}
|
|
else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {
|
|
// String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
|
|
// therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
|
|
// Write single "String Index"
|
|
rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);
|
|
}
|
|
|
|
if ((pp_data->caps[caps_idx].BitField & 0x02) != 0x02) {
|
|
// In case of an value array overwrite "Report Count"
|
|
pp_data->caps[caps_idx].ReportCount = pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin + 1;
|
|
}
|
|
|
|
|
|
// Print only local report items for each cap, if ReportCount > 1
|
|
if ((main_item_list->next != NULL) &&
|
|
((int) main_item_list->next->MainItemType == rt_idx) &&
|
|
(main_item_list->next->TypeOfNode == rd_item_node_cap) &&
|
|
(!pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&
|
|
(!pp_data->caps[caps_idx].IsRange) && // This node in list is no array
|
|
(!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array
|
|
(pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMin == pp_data->caps[caps_idx].NotButton.LogicalMin) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMax == pp_data->caps[caps_idx].NotButton.LogicalMax) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMin == pp_data->caps[caps_idx].NotButton.PhysicalMin) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMax == pp_data->caps[caps_idx].NotButton.PhysicalMax) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].UnitsExp == pp_data->caps[caps_idx].UnitsExp) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].Units == pp_data->caps[caps_idx].Units) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].ReportSize == pp_data->caps[caps_idx].ReportSize) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField) &&
|
|
(pp_data->caps[main_item_list->next->CapsIndex].ReportCount == 1) &&
|
|
(pp_data->caps[caps_idx].ReportCount == 1)
|
|
) {
|
|
// Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
|
|
report_count++;
|
|
}
|
|
else {
|
|
// Value
|
|
|
|
// Write logical range from "Logical Minimum" to "Logical Maximum"
|
|
rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].NotButton.LogicalMin, &rpt_desc);
|
|
rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].NotButton.LogicalMax, &rpt_desc);
|
|
|
|
if ((last_physical_min != pp_data->caps[caps_idx].NotButton.PhysicalMin) ||
|
|
(last_physical_max != pp_data->caps[caps_idx].NotButton.PhysicalMax)) {
|
|
// Write range from "Physical Minimum" to " Physical Maximum", but only if one of them changed
|
|
rd_write_short_item(rd_global_physical_minimum, pp_data->caps[caps_idx].NotButton.PhysicalMin, &rpt_desc);
|
|
last_physical_min = pp_data->caps[caps_idx].NotButton.PhysicalMin;
|
|
rd_write_short_item(rd_global_physical_maximum, pp_data->caps[caps_idx].NotButton.PhysicalMax, &rpt_desc);
|
|
last_physical_max = pp_data->caps[caps_idx].NotButton.PhysicalMax;
|
|
}
|
|
|
|
|
|
if (last_unit_exponent != pp_data->caps[caps_idx].UnitsExp) {
|
|
// Write "Unit Exponent", but only if changed
|
|
rd_write_short_item(rd_global_unit_exponent, pp_data->caps[caps_idx].UnitsExp, &rpt_desc);
|
|
last_unit_exponent = pp_data->caps[caps_idx].UnitsExp;
|
|
}
|
|
|
|
if (last_unit != pp_data->caps[caps_idx].Units) {
|
|
// Write physical "Unit", but only if changed
|
|
rd_write_short_item(rd_global_unit, pp_data->caps[caps_idx].Units, &rpt_desc);
|
|
last_unit = pp_data->caps[caps_idx].Units;
|
|
}
|
|
|
|
// Write "Report Size"
|
|
rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);
|
|
|
|
// Write "Report Count"
|
|
rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);
|
|
|
|
if (rt_idx == HidP_Input) {
|
|
// Write "Input" main item
|
|
rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
|
}
|
|
else if (rt_idx == HidP_Output) {
|
|
// Write "Output" main item
|
|
rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
|
}
|
|
else if (rt_idx == HidP_Feature) {
|
|
// Write "Feature" main item
|
|
rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
|
}
|
|
report_count = 0;
|
|
}
|
|
}
|
|
|
|
// Go to next item in main_item_list and free the memory of the actual item
|
|
struct rd_main_item_node *main_item_list_prev = main_item_list;
|
|
main_item_list = main_item_list->next;
|
|
free(main_item_list_prev);
|
|
}
|
|
|
|
// Free multidimensionable array: coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
|
|
// Free multidimensionable array: coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
|
|
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
|
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
|
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
|
free(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]);
|
|
}
|
|
free(coll_bit_range[collection_node_idx][reportid_idx]);
|
|
}
|
|
free(coll_bit_range[collection_node_idx]);
|
|
if (coll_number_of_direct_childs[collection_node_idx] != 0) free(coll_child_order[collection_node_idx]);
|
|
}
|
|
free(coll_bit_range);
|
|
free(coll_child_order);
|
|
|
|
// Free one dimensional arrays
|
|
free(coll_begin_lookup);
|
|
free(coll_end_lookup);
|
|
free(coll_levels);
|
|
free(coll_number_of_direct_childs);
|
|
|
|
return (int) rpt_desc.byte_idx;
|
|
}
|