Path: blob/master/thirdparty/sdl/hidapi/windows/hidapi_descriptor_reconstruct.c
9917 views
/*******************************************************1HIDAPI - Multi-Platform library for2communication with HID devices.34libusb/hidapi Team56Copyright 2022, All Rights Reserved.78At the discretion of the user of this library,9this software may be licensed under the terms of the10GNU General Public License v3, a BSD-Style license, or the11original HIDAPI license as outlined in the LICENSE.txt,12LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt13files located at the root of the source distribution.14These files may also be found in the public source15code repository located at:16https://github.com/libusb/hidapi .17********************************************************/18#include "hidapi_descriptor_reconstruct.h"1920/**21* @brief References to report descriptor buffer.22*23*/24struct rd_buffer {25unsigned char* buf; /* Pointer to the array which stores the reconstructed descriptor */26size_t buf_size; /* Size of the buffer in bytes */27size_t byte_idx; /* Index of the next report byte to write to buf array */28};2930/**31* @brief Function that appends a byte to encoded report descriptor buffer.32*33* @param[in] byte Single byte to append.34* @param rpt_desc Pointer to report descriptor buffer struct.35*/36static void rd_append_byte(unsigned char byte, struct rd_buffer* rpt_desc) {37if (rpt_desc->byte_idx < rpt_desc->buf_size) {38rpt_desc->buf[rpt_desc->byte_idx] = byte;39rpt_desc->byte_idx++;40}41}4243/**44* @brief Writes a short report descriptor item according USB HID spec 1.11 chapter 6.2.2.2.45*46* @param[in] rd_item Enumeration identifying type (Main, Global, Local) and function (e.g Usage or Report Count) of the item.47* @param[in] data Data (Size depends on rd_item 0,1,2 or 4bytes).48* @param rpt_desc Pointer to report descriptor buffer struct.49*50* @return Returns 0 if successful, -1 for error.51*/52static int rd_write_short_item(rd_items rd_item, LONG64 data, struct rd_buffer* rpt_desc) {53if (rd_item & 0x03) {54// Invalid input data, last to bits are reserved for data size55return -1;56}5758if (rd_item == rd_main_collection_end) {59// Item without data (1Byte prefix only)60unsigned char oneBytePrefix = (unsigned char) rd_item + 0x00;61rd_append_byte(oneBytePrefix, rpt_desc);62}63else if ((rd_item == rd_global_logical_minimum) ||64(rd_item == rd_global_logical_maximum) ||65(rd_item == rd_global_physical_minimum) ||66(rd_item == rd_global_physical_maximum)) {67// Item with signed integer data68if ((data >= -128) && (data <= 127)) {69// 1Byte prefix + 1Byte data70unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;71char localData = (char)data;72rd_append_byte(oneBytePrefix, rpt_desc);73rd_append_byte(localData & 0xFF, rpt_desc);74}75else if ((data >= -32768) && (data <= 32767)) {76// 1Byte prefix + 2Byte data77unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;78INT16 localData = (INT16)data;79rd_append_byte(oneBytePrefix, rpt_desc);80rd_append_byte(localData & 0xFF, rpt_desc);81rd_append_byte(localData >> 8 & 0xFF, rpt_desc);82}83else if ((data >= -2147483648LL) && (data <= 2147483647)) {84// 1Byte prefix + 4Byte data85unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;86INT32 localData = (INT32)data;87rd_append_byte(oneBytePrefix, rpt_desc);88rd_append_byte(localData & 0xFF, rpt_desc);89rd_append_byte(localData >> 8 & 0xFF, rpt_desc);90rd_append_byte(localData >> 16 & 0xFF, rpt_desc);91rd_append_byte(localData >> 24 & 0xFF, rpt_desc);92}93else {94// Data out of 32 bit signed integer range95return -1;96}97}98else {99// Item with unsigned integer data100if ((data >= 0) && (data <= 0xFF)) {101// 1Byte prefix + 1Byte data102unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;103unsigned char localData = (unsigned char)data;104rd_append_byte(oneBytePrefix, rpt_desc);105rd_append_byte(localData & 0xFF, rpt_desc);106}107else if ((data >= 0) && (data <= 0xFFFF)) {108// 1Byte prefix + 2Byte data109unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;110UINT16 localData = (UINT16)data;111rd_append_byte(oneBytePrefix, rpt_desc);112rd_append_byte(localData & 0xFF, rpt_desc);113rd_append_byte(localData >> 8 & 0xFF, rpt_desc);114}115else if ((data >= 0) && (data <= 0xFFFFFFFF)) {116// 1Byte prefix + 4Byte data117unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;118UINT32 localData = (UINT32)data;119rd_append_byte(oneBytePrefix, rpt_desc);120rd_append_byte(localData & 0xFF, rpt_desc);121rd_append_byte(localData >> 8 & 0xFF, rpt_desc);122rd_append_byte(localData >> 16 & 0xFF, rpt_desc);123rd_append_byte(localData >> 24 & 0xFF, rpt_desc);124}125else {126// Data out of 32 bit unsigned integer range127return -1;128}129}130return 0;131}132133static 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) {134struct rd_main_item_node *new_list_node;135136// Determine last node in the list137while (*list != NULL)138{139list = &(*list)->next;140}141142new_list_node = malloc(sizeof(*new_list_node)); // Create new list entry143new_list_node->FirstBit = first_bit;144new_list_node->LastBit = last_bit;145new_list_node->TypeOfNode = type_of_node;146new_list_node->CapsIndex = caps_index;147new_list_node->CollectionIndex = collection_index;148new_list_node->MainItemType = main_item_type;149new_list_node->ReportID = report_id;150new_list_node->next = NULL; // NULL marks last node in the list151152*list = new_list_node;153return new_list_node;154}155156static 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) {157// Insert item after the main item node referenced by list158struct rd_main_item_node *next_item = (*list)->next;159(*list)->next = NULL;160rd_append_main_item_node(first_bit, last_bit, type_of_node, caps_index, collection_index, main_item_type, report_id, list);161(*list)->next->next = next_item;162return (*list)->next;163}164165static 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) {166// Determine first INPUT/OUTPUT/FEATURE main item, where the last bit position is equal or greater than the search bit position167168while (((*list)->next->MainItemType != rd_collection) &&169((*list)->next->MainItemType != rd_collection_end) &&170!(((*list)->next->LastBit >= search_bit) &&171((*list)->next->ReportID == report_id) &&172((*list)->next->MainItemType == main_item_type))173)174{175list = &(*list)->next;176}177return *list;178}179180int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned char *buf, size_t buf_size)181{182hidp_preparsed_data *pp_data = (hidp_preparsed_data *) preparsed_data;183184// Check if MagicKey is correct, to ensure that pp_data points to an valid preparse data structure185if (memcmp(pp_data->MagicKey, "HidP KDR", 8) != 0) {186return -1;187}188189struct rd_buffer rpt_desc;190rpt_desc.buf = buf;191rpt_desc.buf_size = buf_size;192rpt_desc.byte_idx = 0;193194// Set pointer to the first node of link_collection_nodes195phid_pp_link_collection_node link_collection_nodes = (phid_pp_link_collection_node)(((unsigned char*)&pp_data->caps[0]) + pp_data->FirstByteOfLinkCollectionArray);196197// ****************************************************************************************************************************198// Create lookup tables for the bit range of each report per collection (position of first bit and last bit in each collection)199// coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]200// ****************************************************************************************************************************201202// Allocate memory and initialize lookup table203rd_bit_range ****coll_bit_range;204coll_bit_range = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_bit_range));205for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {206coll_bit_range[collection_node_idx] = malloc(256 * sizeof(*coll_bit_range[0])); // 256 possible report IDs (incl. 0x00)207for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {208coll_bit_range[collection_node_idx][reportid_idx] = malloc(NUM_OF_HIDP_REPORT_TYPES * sizeof(*coll_bit_range[0][0]));209for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {210coll_bit_range[collection_node_idx][reportid_idx][rt_idx] = malloc(sizeof(rd_bit_range));211coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = -1;212coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = -1;213}214}215}216217// Fill the lookup table where caps exist218for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {219for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {220int first_bit, last_bit;221first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8222+ pp_data->caps[caps_idx].BitPosition;223last_bit = first_bit + pp_data->caps[caps_idx].ReportSize224* pp_data->caps[caps_idx].ReportCount - 1;225if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit == -1 ||226coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit > first_bit) {227coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit = first_bit;228}229if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit < last_bit) {230coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit = last_bit;231}232}233}234235// *************************************************************************236// -Determine hierarchy levels of each collections and store it in:237// coll_levels[COLLECTION_INDEX]238// -Determine number of direct childs of each collections and store it in:239// coll_number_of_direct_childs[COLLECTION_INDEX]240// *************************************************************************241int max_coll_level = 0;242int *coll_levels = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_levels[0]));243int *coll_number_of_direct_childs = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_number_of_direct_childs[0]));244for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {245coll_levels[collection_node_idx] = -1;246coll_number_of_direct_childs[collection_node_idx] = 0;247}248249{250int actual_coll_level = 0;251USHORT collection_node_idx = 0;252while (actual_coll_level >= 0) {253coll_levels[collection_node_idx] = actual_coll_level;254if ((link_collection_nodes[collection_node_idx].NumberOfChildren > 0) &&255(coll_levels[link_collection_nodes[collection_node_idx].FirstChild] == -1)) {256actual_coll_level++;257coll_levels[collection_node_idx] = actual_coll_level;258if (max_coll_level < actual_coll_level) {259max_coll_level = actual_coll_level;260}261coll_number_of_direct_childs[collection_node_idx]++;262collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;263}264else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {265coll_number_of_direct_childs[link_collection_nodes[collection_node_idx].Parent]++;266collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;267}268else {269actual_coll_level--;270if (actual_coll_level >= 0) {271collection_node_idx = link_collection_nodes[collection_node_idx].Parent;272}273}274}275}276277// *********************************************************************************278// Propagate the bit range of each report from the child collections to their parent279// and store the merged result for the parent280// *********************************************************************************281for (int actual_coll_level = max_coll_level - 1; actual_coll_level >= 0; actual_coll_level--) {282for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {283if (coll_levels[collection_node_idx] == actual_coll_level) {284USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;285while (child_idx) {286for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {287for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {288// Merge bit range from childs289if ((coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit != -1) &&290(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit)) {291coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit;292}293if (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit < coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit) {294coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit;295}296child_idx = link_collection_nodes[child_idx].NextSibling;297}298}299}300}301}302}303304// **************************************************************************************************305// Determine child collection order of the whole hierarchy, based on previously determined bit ranges306// and store it this index coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]307// **************************************************************************************************308USHORT **coll_child_order;309coll_child_order = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_child_order));310{311BOOLEAN *coll_parsed_flag;312coll_parsed_flag = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_parsed_flag[0]));313for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {314coll_parsed_flag[collection_node_idx] = FALSE;315}316int actual_coll_level = 0;317USHORT collection_node_idx = 0;318while (actual_coll_level >= 0) {319if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&320(coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] == FALSE)) {321coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] = TRUE;322coll_child_order[collection_node_idx] = malloc((coll_number_of_direct_childs[collection_node_idx]) * sizeof(*coll_child_order[0]));323324{325// Create list of child collection indices326// sorted reverse to the order returned to HidP_GetLinkCollectionNodeschild327// which seems to match the original order, as long as no bit position needs to be considered328USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;329int child_count = coll_number_of_direct_childs[collection_node_idx] - 1;330coll_child_order[collection_node_idx][child_count] = child_idx;331while (link_collection_nodes[child_idx].NextSibling) {332child_count--;333child_idx = link_collection_nodes[child_idx].NextSibling;334coll_child_order[collection_node_idx][child_count] = child_idx;335}336}337338if (coll_number_of_direct_childs[collection_node_idx] > 1) {339// Sort child collections indices by bit positions340for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {341for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {342for (int child_idx = 1; child_idx < coll_number_of_direct_childs[collection_node_idx]; child_idx++) {343// since the coll_bit_range array is not sorted, we need to reference the collection index in344// our sorted coll_child_order array, and look up the corresponding bit ranges for comparing values to sort345int prev_coll_idx = coll_child_order[collection_node_idx][child_idx - 1];346int cur_coll_idx = coll_child_order[collection_node_idx][child_idx];347if ((coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&348(coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&349(coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit)) {350// Swap position indices of the two compared child collections351USHORT idx_latch = coll_child_order[collection_node_idx][child_idx - 1];352coll_child_order[collection_node_idx][child_idx - 1] = coll_child_order[collection_node_idx][child_idx];353coll_child_order[collection_node_idx][child_idx] = idx_latch;354}355}356}357}358}359actual_coll_level++;360collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;361}362else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {363collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;364}365else {366actual_coll_level--;367if (actual_coll_level >= 0) {368collection_node_idx = link_collection_nodes[collection_node_idx].Parent;369}370}371}372free(coll_parsed_flag);373}374375376// ***************************************************************************************377// Create sorted main_item_list containing all the Collection and CollectionEnd main items378// ***************************************************************************************379struct rd_main_item_node *main_item_list = NULL; // List root380// Lookup table to find the Collection items in the list by index381struct rd_main_item_node **coll_begin_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_begin_lookup));382struct rd_main_item_node **coll_end_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_end_lookup));383{384int *coll_last_written_child = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_last_written_child[0]));385for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {386coll_last_written_child[collection_node_idx] = -1;387}388389int actual_coll_level = 0;390USHORT collection_node_idx = 0;391struct rd_main_item_node *firstDelimiterNode = NULL;392struct rd_main_item_node *delimiterCloseNode = NULL;393coll_begin_lookup[0] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);394while (actual_coll_level >= 0) {395if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&396(coll_last_written_child[collection_node_idx] == -1)) {397// Collection has child collections, but none is written to the list yet398399coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][0];400collection_node_idx = coll_child_order[collection_node_idx][0];401402// In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.403// While the order in the WIN32 capabiliy strutures is the opposite:404// Here the preferred usage is the last aliased usage in the sequence.405406if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {407// Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)408firstDelimiterNode = main_item_list;409coll_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);410coll_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);411delimiterCloseNode = main_item_list;412}413else {414// Normal not aliased collection415coll_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);416actual_coll_level++;417}418419420}421else if ((coll_number_of_direct_childs[collection_node_idx] > 1) &&422(coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][coll_number_of_direct_childs[collection_node_idx] - 1])) {423// Collection has child collections, and this is not the first child424425int nextChild = 1;426while (coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][nextChild - 1]) {427nextChild++;428}429coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][nextChild];430collection_node_idx = coll_child_order[collection_node_idx][nextChild];431432if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {433// Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)434firstDelimiterNode = main_item_list;435coll_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);436coll_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);437delimiterCloseNode = main_item_list;438}439else if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {440coll_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);441}442else if (!link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {443coll_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);444coll_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);445firstDelimiterNode = NULL;446main_item_list = delimiterCloseNode;447delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE448}449if (!link_collection_nodes[collection_node_idx].IsAlias) {450coll_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);451actual_coll_level++;452}453}454else {455actual_coll_level--;456coll_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);457collection_node_idx = link_collection_nodes[collection_node_idx].Parent;458}459}460free(coll_last_written_child);461}462463464// ****************************************************************465// Inserted Input/Output/Feature main items into the main_item_list466// in order of reconstructed bit positions467// ****************************************************************468for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {469// Add all value caps to node list470struct rd_main_item_node *firstDelimiterNode = NULL;471struct rd_main_item_node *delimiterCloseNode = NULL;472for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {473struct rd_main_item_node *coll_begin = coll_begin_lookup[pp_data->caps[caps_idx].LinkCollection];474int first_bit, last_bit;475first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8 +476pp_data->caps[caps_idx].BitPosition;477last_bit = first_bit + pp_data->caps[caps_idx].ReportSize *478pp_data->caps[caps_idx].ReportCount - 1;479480for (int child_idx = 0; child_idx < coll_number_of_direct_childs[pp_data->caps[caps_idx].LinkCollection]; child_idx++) {481// Determine in which section before/between/after child collection the item should be inserted482if (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)483{484// Note, that the default value for undefined coll_bit_range is -1, which can't be greater than the bit position485break;486}487coll_begin = coll_end_lookup[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]];488}489struct rd_main_item_node *list_node;490list_node = rd_search_main_item_list_for_bit_position(first_bit, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &coll_begin);491492// In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.493// While the order in the WIN32 capabiliy strutures is the opposite:494// Here the preferred usage is the last aliased usage in the sequence.495496if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode == NULL)) {497// Alliased Usage (First node in pp_data->caps -> Last entry in report descriptor output)498firstDelimiterNode = list_node;499rd_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);500rd_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);501delimiterCloseNode = list_node;502} else if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {503rd_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);504}505else if (!pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {506// Alliased Collection (Last node in pp_data->caps -> First entry in report descriptor output)507rd_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);508rd_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);509firstDelimiterNode = NULL;510list_node = delimiterCloseNode;511delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE512}513if (!pp_data->caps[caps_idx].IsAlias) {514rd_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);515}516}517}518519520// ***********************************************************521// Add const main items for padding to main_item_list522// -To fill all bit gaps523// -At each report end for 8bit padding524// Note that information about the padding at the report end,525// is not stored in the preparsed data, but in practice all526// report descriptors seem to have it, as assumed here.527// ***********************************************************528{529int *last_bit_position[NUM_OF_HIDP_REPORT_TYPES];530struct rd_main_item_node **last_report_item_lookup[NUM_OF_HIDP_REPORT_TYPES];531for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {532last_bit_position[rt_idx] = malloc(256 * sizeof(*last_bit_position[rt_idx]));533last_report_item_lookup[rt_idx] = malloc(256 * sizeof(*last_report_item_lookup[rt_idx]));534for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {535last_bit_position[rt_idx][reportid_idx] = -1;536last_report_item_lookup[rt_idx][reportid_idx] = NULL;537}538}539540struct rd_main_item_node *list = main_item_list; // List root;541542while (list->next != NULL)543{544if ((list->MainItemType >= rd_input) &&545(list->MainItemType <= rd_feature)) {546// INPUT, OUTPUT or FEATURE547if (list->FirstBit != -1) {548if ((last_bit_position[list->MainItemType][list->ReportID] + 1 != list->FirstBit) &&549(last_report_item_lookup[list->MainItemType][list->ReportID] != NULL) &&550(last_report_item_lookup[list->MainItemType][list->ReportID]->FirstBit != list->FirstBit) // Happens in case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array551) {552struct 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]);553rd_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);554}555last_bit_position[list->MainItemType][list->ReportID] = list->LastBit;556last_report_item_lookup[list->MainItemType][list->ReportID] = list;557}558}559list = list->next;560}561// Add 8 bit padding at each report end562for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {563for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {564if (last_bit_position[rt_idx][reportid_idx] != -1) {565int padding = 8 - ((last_bit_position[rt_idx][reportid_idx] + 1) % 8);566if (padding < 8) {567// Insert padding item after item referenced in last_report_item_lookup568rd_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]);569}570}571}572free(last_bit_position[rt_idx]);573free(last_report_item_lookup[rt_idx]);574}575}576577578// ***********************************579// Encode the report descriptor output580// ***********************************581UCHAR last_report_id = 0;582USAGE last_usage_page = 0;583LONG 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.7584LONG last_physical_max = 0;585ULONG 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.7586ULONG 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.7587BOOLEAN inhibit_write_of_usage = FALSE; // Needed in case of delimited usage print, before the normal collection or cap588int report_count = 0;589while (main_item_list != NULL)590{591int rt_idx = main_item_list->MainItemType;592int caps_idx = main_item_list->CapsIndex;593if (main_item_list->MainItemType == rd_collection) {594if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {595// Write "Usage Page" at the begin of a collection - except it refers the same table as wrote last596rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);597last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;598}599if (inhibit_write_of_usage) {600// Inhibit only once after DELIMITER statement601inhibit_write_of_usage = FALSE;602}603else {604// Write "Usage" of collection605rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);606}607// Write begin of "Collection"608rd_write_short_item(rd_main_collection, link_collection_nodes[main_item_list->CollectionIndex].CollectionType, &rpt_desc);609}610else if (main_item_list->MainItemType == rd_collection_end) {611// Write "End Collection"612rd_write_short_item(rd_main_collection_end, 0, &rpt_desc);613}614else if (main_item_list->MainItemType == rd_delimiter_open) {615if (main_item_list->CollectionIndex != -1) {616// Write "Usage Page" inside of a collection delmiter section617if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {618rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);619last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;620}621}622else if (main_item_list->CapsIndex != 0) {623// Write "Usage Page" inside of a main item delmiter section624if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {625rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);626last_usage_page = pp_data->caps[caps_idx].UsagePage;627}628}629// Write "Delimiter Open"630rd_write_short_item(rd_local_delimiter, 1, &rpt_desc); // 1 = open set of aliased usages631}632else if (main_item_list->MainItemType == rd_delimiter_usage) {633if (main_item_list->CollectionIndex != -1) {634// Write aliased collection "Usage"635rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);636} if (main_item_list->CapsIndex != 0) {637// Write aliased main item range from "Usage Minimum" to "Usage Maximum"638if (pp_data->caps[caps_idx].IsRange) {639rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);640rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);641}642else {643// Write single aliased main item "Usage"644rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);645}646}647}648else if (main_item_list->MainItemType == rd_delimiter_close) {649// Write "Delimiter Close"650rd_write_short_item(rd_local_delimiter, 0, &rpt_desc); // 0 = close set of aliased usages651// Inhibit next usage write652inhibit_write_of_usage = TRUE;653}654else if (main_item_list->TypeOfNode == rd_item_node_padding) {655// Padding656// The preparsed data doesn't contain any information about padding. Therefore all undefined gaps657// in the reports are filled with the same style of constant padding.658659// Write "Report Size" with number of padding bits660rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc);661662// Write "Report Count" for padding always as 1663rd_write_short_item(rd_global_report_count, 1, &rpt_desc);664665if (rt_idx == HidP_Input) {666// Write "Input" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const667rd_write_short_item(rd_main_input, 0x03, &rpt_desc); // Const / Abs668}669else if (rt_idx == HidP_Output) {670// Write "Output" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const671rd_write_short_item(rd_main_output, 0x03, &rpt_desc); // Const / Abs672}673else if (rt_idx == HidP_Feature) {674// Write "Feature" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const675rd_write_short_item(rd_main_feature, 0x03, &rpt_desc); // Const / Abs676}677report_count = 0;678}679else if (pp_data->caps[caps_idx].IsButtonCap) {680// Button681// (The preparsed data contain different data for 1 bit Button caps, than for parametric Value caps)682683if (last_report_id != pp_data->caps[caps_idx].ReportID) {684// Write "Report ID" if changed685rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);686last_report_id = pp_data->caps[caps_idx].ReportID;687}688689// Write "Usage Page" when changed690if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {691rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);692last_usage_page = pp_data->caps[caps_idx].UsagePage;693}694695// Write only local report items for each cap, if ReportCount > 1696if (pp_data->caps[caps_idx].IsRange) {697report_count += (pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin);698}699700if (inhibit_write_of_usage) {701// Inhibit only once after Delimiter - Reset flag702inhibit_write_of_usage = FALSE;703}704else {705if (pp_data->caps[caps_idx].IsRange) {706// Write range from "Usage Minimum" to "Usage Maximum"707rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);708rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);709}710else {711// Write single "Usage"712rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);713}714}715716if (pp_data->caps[caps_idx].IsDesignatorRange) {717// Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"718rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);719rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);720}721else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {722// Designator set 0 is a special descriptor set (of the HID Physical Descriptor),723// that specifies the number of additional descriptor sets.724// Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.725// Write single "Designator Index"726rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);727}728729if (pp_data->caps[caps_idx].IsStringRange) {730// Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"731rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);732rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);733}734else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {735// String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,736// therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.737// Write single "String Index"738rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);739}740741if ((main_item_list->next != NULL) &&742((int)main_item_list->next->MainItemType == rt_idx) &&743(main_item_list->next->TypeOfNode == rd_item_node_cap) &&744(pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&745(!pp_data->caps[caps_idx].IsRange) && // This node in list is no array746(!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array747(pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&748(pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&749(pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField)750) {751if (main_item_list->next->FirstBit != main_item_list->FirstBit) {752// In case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array, the report count should be incremented753754// Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields755report_count++;756}757}758else {759760if ((pp_data->caps[caps_idx].Button.LogicalMin == 0) &&761(pp_data->caps[caps_idx].Button.LogicalMax == 0)) {762// While a HID report descriptor must always contain LogicalMinimum and LogicalMaximum,763// the preparsed data contain both fields set to zero, for the case of simple buttons764// Write "Logical Minimum" set to 0 and "Logical Maximum" set to 1765rd_write_short_item(rd_global_logical_minimum, 0, &rpt_desc);766rd_write_short_item(rd_global_logical_maximum, 1, &rpt_desc);767}768else {769// Write logical range from "Logical Minimum" to "Logical Maximum"770rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].Button.LogicalMin, &rpt_desc);771rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].Button.LogicalMax, &rpt_desc);772}773774// Write "Report Size"775rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);776777// Write "Report Count"778if (!pp_data->caps[caps_idx].IsRange) {779// Variable bit field with one bit per button780// In case of multiple usages with the same items, only "Usage" is written per cap, and "Report Count" is incremented781rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);782}783else {784// Button array of "Report Size" x "Report Count785rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount, &rpt_desc);786}787788789// Buttons have only 1 bit and therefore no physical limits/units -> Set to undefined state790if (last_physical_min != 0) {791// Write "Physical Minimum", but only if changed792last_physical_min = 0;793rd_write_short_item(rd_global_physical_minimum, last_physical_min, &rpt_desc);794}795if (last_physical_max != 0) {796// Write "Physical Maximum", but only if changed797last_physical_max = 0;798rd_write_short_item(rd_global_physical_maximum, last_physical_max, &rpt_desc);799}800if (last_unit_exponent != 0) {801// Write "Unit Exponent", but only if changed802last_unit_exponent = 0;803rd_write_short_item(rd_global_unit_exponent, last_unit_exponent, &rpt_desc);804}805if (last_unit != 0) {806// Write "Unit",but only if changed807last_unit = 0;808rd_write_short_item(rd_global_unit, last_unit, &rpt_desc);809}810811// Write "Input" main item812if (rt_idx == HidP_Input) {813rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);814}815// Write "Output" main item816else if (rt_idx == HidP_Output) {817rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);818}819// Write "Feature" main item820else if (rt_idx == HidP_Feature) {821rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);822}823report_count = 0;824}825}826else {827828if (last_report_id != pp_data->caps[caps_idx].ReportID) {829// Write "Report ID" if changed830rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);831last_report_id = pp_data->caps[caps_idx].ReportID;832}833834// Write "Usage Page" if changed835if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {836rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);837last_usage_page = pp_data->caps[caps_idx].UsagePage;838}839840if (inhibit_write_of_usage) {841// Inhibit only once after Delimiter - Reset flag842inhibit_write_of_usage = FALSE;843}844else {845if (pp_data->caps[caps_idx].IsRange) {846// Write usage range from "Usage Minimum" to "Usage Maximum"847rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);848rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);849}850else {851// Write single "Usage"852rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);853}854}855856if (pp_data->caps[caps_idx].IsDesignatorRange) {857// Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"858rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);859rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);860}861else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {862// Designator set 0 is a special descriptor set (of the HID Physical Descriptor),863// that specifies the number of additional descriptor sets.864// Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.865// Write single "Designator Index"866rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);867}868869if (pp_data->caps[caps_idx].IsStringRange) {870// Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"871rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);872rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);873}874else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {875// String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,876// therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.877// Write single "String Index"878rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);879}880881if ((pp_data->caps[caps_idx].BitField & 0x02) != 0x02) {882// In case of an value array overwrite "Report Count"883pp_data->caps[caps_idx].ReportCount = pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin + 1;884}885886887// Print only local report items for each cap, if ReportCount > 1888if ((main_item_list->next != NULL) &&889((int) main_item_list->next->MainItemType == rt_idx) &&890(main_item_list->next->TypeOfNode == rd_item_node_cap) &&891(!pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&892(!pp_data->caps[caps_idx].IsRange) && // This node in list is no array893(!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array894(pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&895(pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMin == pp_data->caps[caps_idx].NotButton.LogicalMin) &&896(pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMax == pp_data->caps[caps_idx].NotButton.LogicalMax) &&897(pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMin == pp_data->caps[caps_idx].NotButton.PhysicalMin) &&898(pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMax == pp_data->caps[caps_idx].NotButton.PhysicalMax) &&899(pp_data->caps[main_item_list->next->CapsIndex].UnitsExp == pp_data->caps[caps_idx].UnitsExp) &&900(pp_data->caps[main_item_list->next->CapsIndex].Units == pp_data->caps[caps_idx].Units) &&901(pp_data->caps[main_item_list->next->CapsIndex].ReportSize == pp_data->caps[caps_idx].ReportSize) &&902(pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&903(pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField) &&904(pp_data->caps[main_item_list->next->CapsIndex].ReportCount == 1) &&905(pp_data->caps[caps_idx].ReportCount == 1)906) {907// Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields908report_count++;909}910else {911// Value912913// Write logical range from "Logical Minimum" to "Logical Maximum"914rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].NotButton.LogicalMin, &rpt_desc);915rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].NotButton.LogicalMax, &rpt_desc);916917if ((last_physical_min != pp_data->caps[caps_idx].NotButton.PhysicalMin) ||918(last_physical_max != pp_data->caps[caps_idx].NotButton.PhysicalMax)) {919// Write range from "Physical Minimum" to " Physical Maximum", but only if one of them changed920rd_write_short_item(rd_global_physical_minimum, pp_data->caps[caps_idx].NotButton.PhysicalMin, &rpt_desc);921last_physical_min = pp_data->caps[caps_idx].NotButton.PhysicalMin;922rd_write_short_item(rd_global_physical_maximum, pp_data->caps[caps_idx].NotButton.PhysicalMax, &rpt_desc);923last_physical_max = pp_data->caps[caps_idx].NotButton.PhysicalMax;924}925926927if (last_unit_exponent != pp_data->caps[caps_idx].UnitsExp) {928// Write "Unit Exponent", but only if changed929rd_write_short_item(rd_global_unit_exponent, pp_data->caps[caps_idx].UnitsExp, &rpt_desc);930last_unit_exponent = pp_data->caps[caps_idx].UnitsExp;931}932933if (last_unit != pp_data->caps[caps_idx].Units) {934// Write physical "Unit", but only if changed935rd_write_short_item(rd_global_unit, pp_data->caps[caps_idx].Units, &rpt_desc);936last_unit = pp_data->caps[caps_idx].Units;937}938939// Write "Report Size"940rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);941942// Write "Report Count"943rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);944945if (rt_idx == HidP_Input) {946// Write "Input" main item947rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);948}949else if (rt_idx == HidP_Output) {950// Write "Output" main item951rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);952}953else if (rt_idx == HidP_Feature) {954// Write "Feature" main item955rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);956}957report_count = 0;958}959}960961// Go to next item in main_item_list and free the memory of the actual item962struct rd_main_item_node *main_item_list_prev = main_item_list;963main_item_list = main_item_list->next;964free(main_item_list_prev);965}966967// Free multidimensionable array: coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]968// Free multidimensionable array: coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]969for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {970for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {971for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {972free(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]);973}974free(coll_bit_range[collection_node_idx][reportid_idx]);975}976free(coll_bit_range[collection_node_idx]);977if (coll_number_of_direct_childs[collection_node_idx] != 0) free(coll_child_order[collection_node_idx]);978}979free(coll_bit_range);980free(coll_child_order);981982// Free one dimensional arrays983free(coll_begin_lookup);984free(coll_end_lookup);985free(coll_levels);986free(coll_number_of_direct_childs);987988return (int) rpt_desc.byte_idx;989}990991992