diff options
| author | 3gg <3gg@shellblade.net> | 2025-12-27 12:03:39 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-12-27 12:03:39 -0800 |
| commit | 5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch) | |
| tree | 8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/hidapi/windows/hidapi_descriptor_reconstruct.c | |
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/hidapi/windows/hidapi_descriptor_reconstruct.c')
| -rw-r--r-- | contrib/SDL-3.2.8/src/hidapi/windows/hidapi_descriptor_reconstruct.c | 990 |
1 files changed, 990 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/hidapi/windows/hidapi_descriptor_reconstruct.c b/contrib/SDL-3.2.8/src/hidapi/windows/hidapi_descriptor_reconstruct.c new file mode 100644 index 0000000..6697d3c --- /dev/null +++ b/contrib/SDL-3.2.8/src/hidapi/windows/hidapi_descriptor_reconstruct.c | |||
| @@ -0,0 +1,990 @@ | |||
| 1 | /******************************************************* | ||
| 2 | HIDAPI - Multi-Platform library for | ||
| 3 | communication with HID devices. | ||
| 4 | |||
| 5 | libusb/hidapi Team | ||
| 6 | |||
| 7 | Copyright 2022, All Rights Reserved. | ||
| 8 | |||
| 9 | At the discretion of the user of this library, | ||
| 10 | this software may be licensed under the terms of the | ||
| 11 | GNU General Public License v3, a BSD-Style license, or the | ||
| 12 | original HIDAPI license as outlined in the LICENSE.txt, | ||
| 13 | LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt | ||
| 14 | files located at the root of the source distribution. | ||
| 15 | These files may also be found in the public source | ||
| 16 | code repository located at: | ||
| 17 | https://github.com/libusb/hidapi . | ||
| 18 | ********************************************************/ | ||
| 19 | #include "hidapi_descriptor_reconstruct.h" | ||
| 20 | |||
| 21 | /** | ||
| 22 | * @brief References to report descriptor buffer. | ||
| 23 | * | ||
| 24 | */ | ||
| 25 | struct rd_buffer { | ||
| 26 | unsigned char* buf; /* Pointer to the array which stores the reconstructed descriptor */ | ||
| 27 | size_t buf_size; /* Size of the buffer in bytes */ | ||
| 28 | size_t byte_idx; /* Index of the next report byte to write to buf array */ | ||
| 29 | }; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * @brief Function that appends a byte to encoded report descriptor buffer. | ||
| 33 | * | ||
| 34 | * @param[in] byte Single byte to append. | ||
| 35 | * @param rpt_desc Pointer to report descriptor buffer struct. | ||
| 36 | */ | ||
| 37 | static void rd_append_byte(unsigned char byte, struct rd_buffer* rpt_desc) { | ||
| 38 | if (rpt_desc->byte_idx < rpt_desc->buf_size) { | ||
| 39 | rpt_desc->buf[rpt_desc->byte_idx] = byte; | ||
| 40 | rpt_desc->byte_idx++; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | /** | ||
| 45 | * @brief Writes a short report descriptor item according USB HID spec 1.11 chapter 6.2.2.2. | ||
| 46 | * | ||
| 47 | * @param[in] rd_item Enumeration identifying type (Main, Global, Local) and function (e.g Usage or Report Count) of the item. | ||
| 48 | * @param[in] data Data (Size depends on rd_item 0,1,2 or 4bytes). | ||
| 49 | * @param rpt_desc Pointer to report descriptor buffer struct. | ||
| 50 | * | ||
| 51 | * @return Returns 0 if successful, -1 for error. | ||
| 52 | */ | ||
| 53 | static int rd_write_short_item(rd_items rd_item, LONG64 data, struct rd_buffer* rpt_desc) { | ||
| 54 | if (rd_item & 0x03) { | ||
| 55 | // Invalid input data, last to bits are reserved for data size | ||
| 56 | return -1; | ||
| 57 | } | ||
| 58 | |||
| 59 | if (rd_item == rd_main_collection_end) { | ||
| 60 | // Item without data (1Byte prefix only) | ||
| 61 | unsigned char oneBytePrefix = (unsigned char) rd_item + 0x00; | ||
| 62 | rd_append_byte(oneBytePrefix, rpt_desc); | ||
| 63 | } | ||
| 64 | else if ((rd_item == rd_global_logical_minimum) || | ||
| 65 | (rd_item == rd_global_logical_maximum) || | ||
| 66 | (rd_item == rd_global_physical_minimum) || | ||
| 67 | (rd_item == rd_global_physical_maximum)) { | ||
| 68 | // Item with signed integer data | ||
| 69 | if ((data >= -128) && (data <= 127)) { | ||
| 70 | // 1Byte prefix + 1Byte data | ||
| 71 | unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01; | ||
| 72 | char localData = (char)data; | ||
| 73 | rd_append_byte(oneBytePrefix, rpt_desc); | ||
| 74 | rd_append_byte(localData & 0xFF, rpt_desc); | ||
| 75 | } | ||
| 76 | else if ((data >= -32768) && (data <= 32767)) { | ||
| 77 | // 1Byte prefix + 2Byte data | ||
| 78 | unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02; | ||
| 79 | INT16 localData = (INT16)data; | ||
| 80 | rd_append_byte(oneBytePrefix, rpt_desc); | ||
| 81 | rd_append_byte(localData & 0xFF, rpt_desc); | ||
| 82 | rd_append_byte(localData >> 8 & 0xFF, rpt_desc); | ||
| 83 | } | ||
| 84 | else if ((data >= -2147483648LL) && (data <= 2147483647)) { | ||
| 85 | // 1Byte prefix + 4Byte data | ||
| 86 | unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03; | ||
| 87 | INT32 localData = (INT32)data; | ||
| 88 | rd_append_byte(oneBytePrefix, rpt_desc); | ||
| 89 | rd_append_byte(localData & 0xFF, rpt_desc); | ||
| 90 | rd_append_byte(localData >> 8 & 0xFF, rpt_desc); | ||
| 91 | rd_append_byte(localData >> 16 & 0xFF, rpt_desc); | ||
| 92 | rd_append_byte(localData >> 24 & 0xFF, rpt_desc); | ||
| 93 | } | ||
| 94 | else { | ||
| 95 | // Data out of 32 bit signed integer range | ||
| 96 | return -1; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | else { | ||
| 100 | // Item with unsigned integer data | ||
| 101 | if ((data >= 0) && (data <= 0xFF)) { | ||
| 102 | // 1Byte prefix + 1Byte data | ||
| 103 | unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01; | ||
| 104 | unsigned char localData = (unsigned char)data; | ||
| 105 | rd_append_byte(oneBytePrefix, rpt_desc); | ||
| 106 | rd_append_byte(localData & 0xFF, rpt_desc); | ||
| 107 | } | ||
| 108 | else if ((data >= 0) && (data <= 0xFFFF)) { | ||
| 109 | // 1Byte prefix + 2Byte data | ||
| 110 | unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02; | ||
| 111 | UINT16 localData = (UINT16)data; | ||
| 112 | rd_append_byte(oneBytePrefix, rpt_desc); | ||
| 113 | rd_append_byte(localData & 0xFF, rpt_desc); | ||
| 114 | rd_append_byte(localData >> 8 & 0xFF, rpt_desc); | ||
| 115 | } | ||
| 116 | else if ((data >= 0) && (data <= 0xFFFFFFFF)) { | ||
| 117 | // 1Byte prefix + 4Byte data | ||
| 118 | unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03; | ||
| 119 | UINT32 localData = (UINT32)data; | ||
| 120 | rd_append_byte(oneBytePrefix, rpt_desc); | ||
| 121 | rd_append_byte(localData & 0xFF, rpt_desc); | ||
| 122 | rd_append_byte(localData >> 8 & 0xFF, rpt_desc); | ||
| 123 | rd_append_byte(localData >> 16 & 0xFF, rpt_desc); | ||
| 124 | rd_append_byte(localData >> 24 & 0xFF, rpt_desc); | ||
| 125 | } | ||
| 126 | else { | ||
| 127 | // Data out of 32 bit unsigned integer range | ||
| 128 | return -1; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | 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) { | ||
| 135 | struct rd_main_item_node *new_list_node; | ||
| 136 | |||
| 137 | // Determine last node in the list | ||
| 138 | while (*list != NULL) | ||
| 139 | { | ||
| 140 | list = &(*list)->next; | ||
| 141 | } | ||
| 142 | |||
| 143 | new_list_node = malloc(sizeof(*new_list_node)); // Create new list entry | ||
| 144 | new_list_node->FirstBit = first_bit; | ||
| 145 | new_list_node->LastBit = last_bit; | ||
| 146 | new_list_node->TypeOfNode = type_of_node; | ||
| 147 | new_list_node->CapsIndex = caps_index; | ||
| 148 | new_list_node->CollectionIndex = collection_index; | ||
| 149 | new_list_node->MainItemType = main_item_type; | ||
| 150 | new_list_node->ReportID = report_id; | ||
| 151 | new_list_node->next = NULL; // NULL marks last node in the list | ||
| 152 | |||
| 153 | *list = new_list_node; | ||
| 154 | return new_list_node; | ||
| 155 | } | ||
| 156 | |||
| 157 | 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) { | ||
| 158 | // Insert item after the main item node referenced by list | ||
| 159 | struct rd_main_item_node *next_item = (*list)->next; | ||
| 160 | (*list)->next = NULL; | ||
| 161 | rd_append_main_item_node(first_bit, last_bit, type_of_node, caps_index, collection_index, main_item_type, report_id, list); | ||
| 162 | (*list)->next->next = next_item; | ||
| 163 | return (*list)->next; | ||
| 164 | } | ||
| 165 | |||
| 166 | 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) { | ||
| 167 | // Determine first INPUT/OUTPUT/FEATURE main item, where the last bit position is equal or greater than the search bit position | ||
| 168 | |||
| 169 | while (((*list)->next->MainItemType != rd_collection) && | ||
| 170 | ((*list)->next->MainItemType != rd_collection_end) && | ||
| 171 | !(((*list)->next->LastBit >= search_bit) && | ||
| 172 | ((*list)->next->ReportID == report_id) && | ||
| 173 | ((*list)->next->MainItemType == main_item_type)) | ||
| 174 | ) | ||
| 175 | { | ||
| 176 | list = &(*list)->next; | ||
| 177 | } | ||
| 178 | return *list; | ||
| 179 | } | ||
| 180 | |||
| 181 | int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned char *buf, size_t buf_size) | ||
| 182 | { | ||
| 183 | hidp_preparsed_data *pp_data = (hidp_preparsed_data *) preparsed_data; | ||
| 184 | |||
| 185 | // Check if MagicKey is correct, to ensure that pp_data points to an valid preparse data structure | ||
| 186 | if (memcmp(pp_data->MagicKey, "HidP KDR", 8) != 0) { | ||
| 187 | return -1; | ||
| 188 | } | ||
| 189 | |||
| 190 | struct rd_buffer rpt_desc; | ||
| 191 | rpt_desc.buf = buf; | ||
| 192 | rpt_desc.buf_size = buf_size; | ||
| 193 | rpt_desc.byte_idx = 0; | ||
| 194 | |||
| 195 | // Set pointer to the first node of link_collection_nodes | ||
| 196 | phid_pp_link_collection_node link_collection_nodes = (phid_pp_link_collection_node)(((unsigned char*)&pp_data->caps[0]) + pp_data->FirstByteOfLinkCollectionArray); | ||
| 197 | |||
| 198 | // **************************************************************************************************************************** | ||
| 199 | // Create lookup tables for the bit range of each report per collection (position of first bit and last bit in each collection) | ||
| 200 | // coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE] | ||
| 201 | // **************************************************************************************************************************** | ||
| 202 | |||
| 203 | // Allocate memory and initialize lookup table | ||
| 204 | rd_bit_range ****coll_bit_range; | ||
| 205 | coll_bit_range = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_bit_range)); | ||
| 206 | for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { | ||
| 207 | coll_bit_range[collection_node_idx] = malloc(256 * sizeof(*coll_bit_range[0])); // 256 possible report IDs (incl. 0x00) | ||
| 208 | for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { | ||
| 209 | coll_bit_range[collection_node_idx][reportid_idx] = malloc(NUM_OF_HIDP_REPORT_TYPES * sizeof(*coll_bit_range[0][0])); | ||
| 210 | for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { | ||
| 211 | coll_bit_range[collection_node_idx][reportid_idx][rt_idx] = malloc(sizeof(rd_bit_range)); | ||
| 212 | coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = -1; | ||
| 213 | coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = -1; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | // Fill the lookup table where caps exist | ||
| 219 | for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { | ||
| 220 | for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) { | ||
| 221 | int first_bit, last_bit; | ||
| 222 | first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8 | ||
| 223 | + pp_data->caps[caps_idx].BitPosition; | ||
| 224 | last_bit = first_bit + pp_data->caps[caps_idx].ReportSize | ||
| 225 | * pp_data->caps[caps_idx].ReportCount - 1; | ||
| 226 | if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit == -1 || | ||
| 227 | coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit > first_bit) { | ||
| 228 | coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit = first_bit; | ||
| 229 | } | ||
| 230 | if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit < last_bit) { | ||
| 231 | coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit = last_bit; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | // ************************************************************************* | ||
| 237 | // -Determine hierarchy levels of each collections and store it in: | ||
| 238 | // coll_levels[COLLECTION_INDEX] | ||
| 239 | // -Determine number of direct childs of each collections and store it in: | ||
| 240 | // coll_number_of_direct_childs[COLLECTION_INDEX] | ||
| 241 | // ************************************************************************* | ||
| 242 | int max_coll_level = 0; | ||
| 243 | int *coll_levels = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_levels[0])); | ||
| 244 | int *coll_number_of_direct_childs = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_number_of_direct_childs[0])); | ||
| 245 | for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { | ||
| 246 | coll_levels[collection_node_idx] = -1; | ||
| 247 | coll_number_of_direct_childs[collection_node_idx] = 0; | ||
| 248 | } | ||
| 249 | |||
| 250 | { | ||
| 251 | int actual_coll_level = 0; | ||
| 252 | USHORT collection_node_idx = 0; | ||
| 253 | while (actual_coll_level >= 0) { | ||
| 254 | coll_levels[collection_node_idx] = actual_coll_level; | ||
| 255 | if ((link_collection_nodes[collection_node_idx].NumberOfChildren > 0) && | ||
| 256 | (coll_levels[link_collection_nodes[collection_node_idx].FirstChild] == -1)) { | ||
| 257 | actual_coll_level++; | ||
| 258 | coll_levels[collection_node_idx] = actual_coll_level; | ||
| 259 | if (max_coll_level < actual_coll_level) { | ||
| 260 | max_coll_level = actual_coll_level; | ||
| 261 | } | ||
| 262 | coll_number_of_direct_childs[collection_node_idx]++; | ||
| 263 | collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild; | ||
| 264 | } | ||
| 265 | else if (link_collection_nodes[collection_node_idx].NextSibling != 0) { | ||
| 266 | coll_number_of_direct_childs[link_collection_nodes[collection_node_idx].Parent]++; | ||
| 267 | collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling; | ||
| 268 | } | ||
| 269 | else { | ||
| 270 | actual_coll_level--; | ||
| 271 | if (actual_coll_level >= 0) { | ||
| 272 | collection_node_idx = link_collection_nodes[collection_node_idx].Parent; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | // ********************************************************************************* | ||
| 279 | // Propagate the bit range of each report from the child collections to their parent | ||
| 280 | // and store the merged result for the parent | ||
| 281 | // ********************************************************************************* | ||
| 282 | for (int actual_coll_level = max_coll_level - 1; actual_coll_level >= 0; actual_coll_level--) { | ||
| 283 | for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { | ||
| 284 | if (coll_levels[collection_node_idx] == actual_coll_level) { | ||
| 285 | USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild; | ||
| 286 | while (child_idx) { | ||
| 287 | for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { | ||
| 288 | for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { | ||
| 289 | // Merge bit range from childs | ||
| 290 | if ((coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit != -1) && | ||
| 291 | (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit)) { | ||
| 292 | coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit; | ||
| 293 | } | ||
| 294 | if (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit < coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit) { | ||
| 295 | coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit; | ||
| 296 | } | ||
| 297 | child_idx = link_collection_nodes[child_idx].NextSibling; | ||
| 298 | } | ||
| 299 | } | ||
| 300 | } | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | // ************************************************************************************************** | ||
| 306 | // Determine child collection order of the whole hierarchy, based on previously determined bit ranges | ||
| 307 | // and store it this index coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX] | ||
| 308 | // ************************************************************************************************** | ||
| 309 | USHORT **coll_child_order; | ||
| 310 | coll_child_order = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_child_order)); | ||
| 311 | { | ||
| 312 | BOOLEAN *coll_parsed_flag; | ||
| 313 | coll_parsed_flag = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_parsed_flag[0])); | ||
| 314 | for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { | ||
| 315 | coll_parsed_flag[collection_node_idx] = FALSE; | ||
| 316 | } | ||
| 317 | int actual_coll_level = 0; | ||
| 318 | USHORT collection_node_idx = 0; | ||
| 319 | while (actual_coll_level >= 0) { | ||
| 320 | if ((coll_number_of_direct_childs[collection_node_idx] != 0) && | ||
| 321 | (coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] == FALSE)) { | ||
| 322 | coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] = TRUE; | ||
| 323 | coll_child_order[collection_node_idx] = malloc((coll_number_of_direct_childs[collection_node_idx]) * sizeof(*coll_child_order[0])); | ||
| 324 | |||
| 325 | { | ||
| 326 | // Create list of child collection indices | ||
| 327 | // sorted reverse to the order returned to HidP_GetLinkCollectionNodeschild | ||
| 328 | // which seems to match the original order, as long as no bit position needs to be considered | ||
| 329 | USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild; | ||
| 330 | int child_count = coll_number_of_direct_childs[collection_node_idx] - 1; | ||
| 331 | coll_child_order[collection_node_idx][child_count] = child_idx; | ||
| 332 | while (link_collection_nodes[child_idx].NextSibling) { | ||
| 333 | child_count--; | ||
| 334 | child_idx = link_collection_nodes[child_idx].NextSibling; | ||
| 335 | coll_child_order[collection_node_idx][child_count] = child_idx; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | if (coll_number_of_direct_childs[collection_node_idx] > 1) { | ||
| 340 | // Sort child collections indices by bit positions | ||
| 341 | for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { | ||
| 342 | for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { | ||
| 343 | for (int child_idx = 1; child_idx < coll_number_of_direct_childs[collection_node_idx]; child_idx++) { | ||
| 344 | // since the coll_bit_range array is not sorted, we need to reference the collection index in | ||
| 345 | // our sorted coll_child_order array, and look up the corresponding bit ranges for comparing values to sort | ||
| 346 | int prev_coll_idx = coll_child_order[collection_node_idx][child_idx - 1]; | ||
| 347 | int cur_coll_idx = coll_child_order[collection_node_idx][child_idx]; | ||
| 348 | if ((coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) && | ||
| 349 | (coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) && | ||
| 350 | (coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit)) { | ||
| 351 | // Swap position indices of the two compared child collections | ||
| 352 | USHORT idx_latch = coll_child_order[collection_node_idx][child_idx - 1]; | ||
| 353 | coll_child_order[collection_node_idx][child_idx - 1] = coll_child_order[collection_node_idx][child_idx]; | ||
| 354 | coll_child_order[collection_node_idx][child_idx] = idx_latch; | ||
| 355 | } | ||
| 356 | } | ||
| 357 | } | ||
| 358 | } | ||
| 359 | } | ||
| 360 | actual_coll_level++; | ||
| 361 | collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild; | ||
| 362 | } | ||
| 363 | else if (link_collection_nodes[collection_node_idx].NextSibling != 0) { | ||
| 364 | collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling; | ||
| 365 | } | ||
| 366 | else { | ||
| 367 | actual_coll_level--; | ||
| 368 | if (actual_coll_level >= 0) { | ||
| 369 | collection_node_idx = link_collection_nodes[collection_node_idx].Parent; | ||
| 370 | } | ||
| 371 | } | ||
| 372 | } | ||
| 373 | free(coll_parsed_flag); | ||
| 374 | } | ||
| 375 | |||
| 376 | |||
| 377 | // *************************************************************************************** | ||
| 378 | // Create sorted main_item_list containing all the Collection and CollectionEnd main items | ||
| 379 | // *************************************************************************************** | ||
| 380 | struct rd_main_item_node *main_item_list = NULL; // List root | ||
| 381 | // Lookup table to find the Collection items in the list by index | ||
| 382 | struct rd_main_item_node **coll_begin_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_begin_lookup)); | ||
| 383 | struct rd_main_item_node **coll_end_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_end_lookup)); | ||
| 384 | { | ||
| 385 | int *coll_last_written_child = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_last_written_child[0])); | ||
| 386 | for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { | ||
| 387 | coll_last_written_child[collection_node_idx] = -1; | ||
| 388 | } | ||
| 389 | |||
| 390 | int actual_coll_level = 0; | ||
| 391 | USHORT collection_node_idx = 0; | ||
| 392 | struct rd_main_item_node *firstDelimiterNode = NULL; | ||
| 393 | struct rd_main_item_node *delimiterCloseNode = NULL; | ||
| 394 | 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); | ||
| 395 | while (actual_coll_level >= 0) { | ||
| 396 | if ((coll_number_of_direct_childs[collection_node_idx] != 0) && | ||
| 397 | (coll_last_written_child[collection_node_idx] == -1)) { | ||
| 398 | // Collection has child collections, but none is written to the list yet | ||
| 399 | |||
| 400 | coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][0]; | ||
| 401 | collection_node_idx = coll_child_order[collection_node_idx][0]; | ||
| 402 | |||
| 403 | // In a HID Report Descriptor, the first usage declared is the most preferred usage for the control. | ||
| 404 | // While the order in the WIN32 capabiliy strutures is the opposite: | ||
| 405 | // Here the preferred usage is the last aliased usage in the sequence. | ||
| 406 | |||
| 407 | if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) { | ||
| 408 | // Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output) | ||
| 409 | firstDelimiterNode = main_item_list; | ||
| 410 | 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); | ||
| 411 | 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); | ||
| 412 | delimiterCloseNode = main_item_list; | ||
| 413 | } | ||
| 414 | else { | ||
| 415 | // Normal not aliased collection | ||
| 416 | 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); | ||
| 417 | actual_coll_level++; | ||
| 418 | } | ||
| 419 | |||
| 420 | |||
| 421 | } | ||
| 422 | else if ((coll_number_of_direct_childs[collection_node_idx] > 1) && | ||
| 423 | (coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][coll_number_of_direct_childs[collection_node_idx] - 1])) { | ||
| 424 | // Collection has child collections, and this is not the first child | ||
| 425 | |||
| 426 | int nextChild = 1; | ||
| 427 | while (coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][nextChild - 1]) { | ||
| 428 | nextChild++; | ||
| 429 | } | ||
| 430 | coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][nextChild]; | ||
| 431 | collection_node_idx = coll_child_order[collection_node_idx][nextChild]; | ||
| 432 | |||
| 433 | if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) { | ||
| 434 | // Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output) | ||
| 435 | firstDelimiterNode = main_item_list; | ||
| 436 | 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); | ||
| 437 | 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); | ||
| 438 | delimiterCloseNode = main_item_list; | ||
| 439 | } | ||
| 440 | else if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) { | ||
| 441 | 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); | ||
| 442 | } | ||
| 443 | else if (!link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) { | ||
| 444 | 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); | ||
| 445 | 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); | ||
| 446 | firstDelimiterNode = NULL; | ||
| 447 | main_item_list = delimiterCloseNode; | ||
| 448 | delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE | ||
| 449 | } | ||
| 450 | if (!link_collection_nodes[collection_node_idx].IsAlias) { | ||
| 451 | 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); | ||
| 452 | actual_coll_level++; | ||
| 453 | } | ||
| 454 | } | ||
| 455 | else { | ||
| 456 | actual_coll_level--; | ||
| 457 | 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); | ||
| 458 | collection_node_idx = link_collection_nodes[collection_node_idx].Parent; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | free(coll_last_written_child); | ||
| 462 | } | ||
| 463 | |||
| 464 | |||
| 465 | // **************************************************************** | ||
| 466 | // Inserted Input/Output/Feature main items into the main_item_list | ||
| 467 | // in order of reconstructed bit positions | ||
| 468 | // **************************************************************** | ||
| 469 | for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { | ||
| 470 | // Add all value caps to node list | ||
| 471 | struct rd_main_item_node *firstDelimiterNode = NULL; | ||
| 472 | struct rd_main_item_node *delimiterCloseNode = NULL; | ||
| 473 | for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) { | ||
| 474 | struct rd_main_item_node *coll_begin = coll_begin_lookup[pp_data->caps[caps_idx].LinkCollection]; | ||
| 475 | int first_bit, last_bit; | ||
| 476 | first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8 + | ||
| 477 | pp_data->caps[caps_idx].BitPosition; | ||
| 478 | last_bit = first_bit + pp_data->caps[caps_idx].ReportSize * | ||
| 479 | pp_data->caps[caps_idx].ReportCount - 1; | ||
| 480 | |||
| 481 | for (int child_idx = 0; child_idx < coll_number_of_direct_childs[pp_data->caps[caps_idx].LinkCollection]; child_idx++) { | ||
| 482 | // Determine in which section before/between/after child collection the item should be inserted | ||
| 483 | 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) | ||
| 484 | { | ||
| 485 | // Note, that the default value for undefined coll_bit_range is -1, which can't be greater than the bit position | ||
| 486 | break; | ||
| 487 | } | ||
| 488 | coll_begin = coll_end_lookup[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]]; | ||
| 489 | } | ||
| 490 | struct rd_main_item_node *list_node; | ||
| 491 | 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); | ||
| 492 | |||
| 493 | // In a HID Report Descriptor, the first usage declared is the most preferred usage for the control. | ||
| 494 | // While the order in the WIN32 capabiliy strutures is the opposite: | ||
| 495 | // Here the preferred usage is the last aliased usage in the sequence. | ||
| 496 | |||
| 497 | if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode == NULL)) { | ||
| 498 | // Alliased Usage (First node in pp_data->caps -> Last entry in report descriptor output) | ||
| 499 | firstDelimiterNode = list_node; | ||
| 500 | 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); | ||
| 501 | 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); | ||
| 502 | delimiterCloseNode = list_node; | ||
| 503 | } else if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) { | ||
| 504 | 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); | ||
| 505 | } | ||
| 506 | else if (!pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) { | ||
| 507 | // Alliased Collection (Last node in pp_data->caps -> First entry in report descriptor output) | ||
| 508 | 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); | ||
| 509 | 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); | ||
| 510 | firstDelimiterNode = NULL; | ||
| 511 | list_node = delimiterCloseNode; | ||
| 512 | delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE | ||
| 513 | } | ||
| 514 | if (!pp_data->caps[caps_idx].IsAlias) { | ||
| 515 | 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); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | |||
| 521 | // *********************************************************** | ||
| 522 | // Add const main items for padding to main_item_list | ||
| 523 | // -To fill all bit gaps | ||
| 524 | // -At each report end for 8bit padding | ||
| 525 | // Note that information about the padding at the report end, | ||
| 526 | // is not stored in the preparsed data, but in practice all | ||
| 527 | // report descriptors seem to have it, as assumed here. | ||
| 528 | // *********************************************************** | ||
| 529 | { | ||
| 530 | int *last_bit_position[NUM_OF_HIDP_REPORT_TYPES]; | ||
| 531 | struct rd_main_item_node **last_report_item_lookup[NUM_OF_HIDP_REPORT_TYPES]; | ||
| 532 | for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { | ||
| 533 | last_bit_position[rt_idx] = malloc(256 * sizeof(*last_bit_position[rt_idx])); | ||
| 534 | last_report_item_lookup[rt_idx] = malloc(256 * sizeof(*last_report_item_lookup[rt_idx])); | ||
| 535 | for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { | ||
| 536 | last_bit_position[rt_idx][reportid_idx] = -1; | ||
| 537 | last_report_item_lookup[rt_idx][reportid_idx] = NULL; | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | struct rd_main_item_node *list = main_item_list; // List root; | ||
| 542 | |||
| 543 | while (list->next != NULL) | ||
| 544 | { | ||
| 545 | if ((list->MainItemType >= rd_input) && | ||
| 546 | (list->MainItemType <= rd_feature)) { | ||
| 547 | // INPUT, OUTPUT or FEATURE | ||
| 548 | if (list->FirstBit != -1) { | ||
| 549 | if ((last_bit_position[list->MainItemType][list->ReportID] + 1 != list->FirstBit) && | ||
| 550 | (last_report_item_lookup[list->MainItemType][list->ReportID] != NULL) && | ||
| 551 | (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 | ||
| 552 | ) { | ||
| 553 | 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]); | ||
| 554 | 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); | ||
| 555 | } | ||
| 556 | last_bit_position[list->MainItemType][list->ReportID] = list->LastBit; | ||
| 557 | last_report_item_lookup[list->MainItemType][list->ReportID] = list; | ||
| 558 | } | ||
| 559 | } | ||
| 560 | list = list->next; | ||
| 561 | } | ||
| 562 | // Add 8 bit padding at each report end | ||
| 563 | for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { | ||
| 564 | for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { | ||
| 565 | if (last_bit_position[rt_idx][reportid_idx] != -1) { | ||
| 566 | int padding = 8 - ((last_bit_position[rt_idx][reportid_idx] + 1) % 8); | ||
| 567 | if (padding < 8) { | ||
| 568 | // Insert padding item after item referenced in last_report_item_lookup | ||
| 569 | 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]); | ||
| 570 | } | ||
| 571 | } | ||
| 572 | } | ||
| 573 | free(last_bit_position[rt_idx]); | ||
| 574 | free(last_report_item_lookup[rt_idx]); | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | |||
| 579 | // *********************************** | ||
| 580 | // Encode the report descriptor output | ||
| 581 | // *********************************** | ||
| 582 | UCHAR last_report_id = 0; | ||
| 583 | USAGE last_usage_page = 0; | ||
| 584 | 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 | ||
| 585 | LONG last_physical_max = 0; | ||
| 586 | 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 | ||
| 587 | 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 | ||
| 588 | BOOLEAN inhibit_write_of_usage = FALSE; // Needed in case of delimited usage print, before the normal collection or cap | ||
| 589 | int report_count = 0; | ||
| 590 | while (main_item_list != NULL) | ||
| 591 | { | ||
| 592 | int rt_idx = main_item_list->MainItemType; | ||
| 593 | int caps_idx = main_item_list->CapsIndex; | ||
| 594 | if (main_item_list->MainItemType == rd_collection) { | ||
| 595 | if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) { | ||
| 596 | // Write "Usage Page" at the begin of a collection - except it refers the same table as wrote last | ||
| 597 | rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc); | ||
| 598 | last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage; | ||
| 599 | } | ||
| 600 | if (inhibit_write_of_usage) { | ||
| 601 | // Inhibit only once after DELIMITER statement | ||
| 602 | inhibit_write_of_usage = FALSE; | ||
| 603 | } | ||
| 604 | else { | ||
| 605 | // Write "Usage" of collection | ||
| 606 | rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc); | ||
| 607 | } | ||
| 608 | // Write begin of "Collection" | ||
| 609 | rd_write_short_item(rd_main_collection, link_collection_nodes[main_item_list->CollectionIndex].CollectionType, &rpt_desc); | ||
| 610 | } | ||
| 611 | else if (main_item_list->MainItemType == rd_collection_end) { | ||
| 612 | // Write "End Collection" | ||
| 613 | rd_write_short_item(rd_main_collection_end, 0, &rpt_desc); | ||
| 614 | } | ||
| 615 | else if (main_item_list->MainItemType == rd_delimiter_open) { | ||
| 616 | if (main_item_list->CollectionIndex != -1) { | ||
| 617 | // Write "Usage Page" inside of a collection delmiter section | ||
| 618 | if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) { | ||
| 619 | rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc); | ||
| 620 | last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage; | ||
| 621 | } | ||
| 622 | } | ||
| 623 | else if (main_item_list->CapsIndex != 0) { | ||
| 624 | // Write "Usage Page" inside of a main item delmiter section | ||
| 625 | if (pp_data->caps[caps_idx].UsagePage != last_usage_page) { | ||
| 626 | rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc); | ||
| 627 | last_usage_page = pp_data->caps[caps_idx].UsagePage; | ||
| 628 | } | ||
| 629 | } | ||
| 630 | // Write "Delimiter Open" | ||
| 631 | rd_write_short_item(rd_local_delimiter, 1, &rpt_desc); // 1 = open set of aliased usages | ||
| 632 | } | ||
| 633 | else if (main_item_list->MainItemType == rd_delimiter_usage) { | ||
| 634 | if (main_item_list->CollectionIndex != -1) { | ||
| 635 | // Write aliased collection "Usage" | ||
| 636 | rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc); | ||
| 637 | } if (main_item_list->CapsIndex != 0) { | ||
| 638 | // Write aliased main item range from "Usage Minimum" to "Usage Maximum" | ||
| 639 | if (pp_data->caps[caps_idx].IsRange) { | ||
| 640 | rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc); | ||
| 641 | rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc); | ||
| 642 | } | ||
| 643 | else { | ||
| 644 | // Write single aliased main item "Usage" | ||
| 645 | rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc); | ||
| 646 | } | ||
| 647 | } | ||
| 648 | } | ||
| 649 | else if (main_item_list->MainItemType == rd_delimiter_close) { | ||
| 650 | // Write "Delimiter Close" | ||
| 651 | rd_write_short_item(rd_local_delimiter, 0, &rpt_desc); // 0 = close set of aliased usages | ||
| 652 | // Inhibit next usage write | ||
| 653 | inhibit_write_of_usage = TRUE; | ||
| 654 | } | ||
| 655 | else if (main_item_list->TypeOfNode == rd_item_node_padding) { | ||
| 656 | // Padding | ||
| 657 | // The preparsed data doesn't contain any information about padding. Therefore all undefined gaps | ||
| 658 | // in the reports are filled with the same style of constant padding. | ||
| 659 | |||
| 660 | // Write "Report Size" with number of padding bits | ||
| 661 | rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc); | ||
| 662 | |||
| 663 | // Write "Report Count" for padding always as 1 | ||
| 664 | rd_write_short_item(rd_global_report_count, 1, &rpt_desc); | ||
| 665 | |||
| 666 | if (rt_idx == HidP_Input) { | ||
| 667 | // 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 | ||
| 668 | rd_write_short_item(rd_main_input, 0x03, &rpt_desc); // Const / Abs | ||
| 669 | } | ||
| 670 | else if (rt_idx == HidP_Output) { | ||
| 671 | // 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 | ||
| 672 | rd_write_short_item(rd_main_output, 0x03, &rpt_desc); // Const / Abs | ||
| 673 | } | ||
| 674 | else if (rt_idx == HidP_Feature) { | ||
| 675 | // 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 | ||
| 676 | rd_write_short_item(rd_main_feature, 0x03, &rpt_desc); // Const / Abs | ||
| 677 | } | ||
| 678 | report_count = 0; | ||
| 679 | } | ||
| 680 | else if (pp_data->caps[caps_idx].IsButtonCap) { | ||
| 681 | // Button | ||
| 682 | // (The preparsed data contain different data for 1 bit Button caps, than for parametric Value caps) | ||
| 683 | |||
| 684 | if (last_report_id != pp_data->caps[caps_idx].ReportID) { | ||
| 685 | // Write "Report ID" if changed | ||
| 686 | rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc); | ||
| 687 | last_report_id = pp_data->caps[caps_idx].ReportID; | ||
| 688 | } | ||
| 689 | |||
| 690 | // Write "Usage Page" when changed | ||
| 691 | if (pp_data->caps[caps_idx].UsagePage != last_usage_page) { | ||
| 692 | rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc); | ||
| 693 | last_usage_page = pp_data->caps[caps_idx].UsagePage; | ||
| 694 | } | ||
| 695 | |||
| 696 | // Write only local report items for each cap, if ReportCount > 1 | ||
| 697 | if (pp_data->caps[caps_idx].IsRange) { | ||
| 698 | report_count += (pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin); | ||
| 699 | } | ||
| 700 | |||
| 701 | if (inhibit_write_of_usage) { | ||
| 702 | // Inhibit only once after Delimiter - Reset flag | ||
| 703 | inhibit_write_of_usage = FALSE; | ||
| 704 | } | ||
| 705 | else { | ||
| 706 | if (pp_data->caps[caps_idx].IsRange) { | ||
| 707 | // Write range from "Usage Minimum" to "Usage Maximum" | ||
| 708 | rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc); | ||
| 709 | rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc); | ||
| 710 | } | ||
| 711 | else { | ||
| 712 | // Write single "Usage" | ||
| 713 | rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc); | ||
| 714 | } | ||
| 715 | } | ||
| 716 | |||
| 717 | if (pp_data->caps[caps_idx].IsDesignatorRange) { | ||
| 718 | // Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum" | ||
| 719 | rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc); | ||
| 720 | rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc); | ||
| 721 | } | ||
| 722 | else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) { | ||
| 723 | // Designator set 0 is a special descriptor set (of the HID Physical Descriptor), | ||
| 724 | // that specifies the number of additional descriptor sets. | ||
| 725 | // Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it. | ||
| 726 | // Write single "Designator Index" | ||
| 727 | rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc); | ||
| 728 | } | ||
| 729 | |||
| 730 | if (pp_data->caps[caps_idx].IsStringRange) { | ||
| 731 | // Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum" | ||
| 732 | rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc); | ||
| 733 | rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc); | ||
| 734 | } | ||
| 735 | else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) { | ||
| 736 | // String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages, | ||
| 737 | // therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it. | ||
| 738 | // Write single "String Index" | ||
| 739 | rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc); | ||
| 740 | } | ||
| 741 | |||
| 742 | if ((main_item_list->next != NULL) && | ||
| 743 | ((int)main_item_list->next->MainItemType == rt_idx) && | ||
| 744 | (main_item_list->next->TypeOfNode == rd_item_node_cap) && | ||
| 745 | (pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) && | ||
| 746 | (!pp_data->caps[caps_idx].IsRange) && // This node in list is no array | ||
| 747 | (!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array | ||
| 748 | (pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) && | ||
| 749 | (pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) && | ||
| 750 | (pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField) | ||
| 751 | ) { | ||
| 752 | if (main_item_list->next->FirstBit != main_item_list->FirstBit) { | ||
| 753 | // In case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array, the report count should be incremented | ||
| 754 | |||
| 755 | // Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields | ||
| 756 | report_count++; | ||
| 757 | } | ||
| 758 | } | ||
| 759 | else { | ||
| 760 | |||
| 761 | if ((pp_data->caps[caps_idx].Button.LogicalMin == 0) && | ||
| 762 | (pp_data->caps[caps_idx].Button.LogicalMax == 0)) { | ||
| 763 | // While a HID report descriptor must always contain LogicalMinimum and LogicalMaximum, | ||
| 764 | // the preparsed data contain both fields set to zero, for the case of simple buttons | ||
| 765 | // Write "Logical Minimum" set to 0 and "Logical Maximum" set to 1 | ||
| 766 | rd_write_short_item(rd_global_logical_minimum, 0, &rpt_desc); | ||
| 767 | rd_write_short_item(rd_global_logical_maximum, 1, &rpt_desc); | ||
| 768 | } | ||
| 769 | else { | ||
| 770 | // Write logical range from "Logical Minimum" to "Logical Maximum" | ||
| 771 | rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].Button.LogicalMin, &rpt_desc); | ||
| 772 | rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].Button.LogicalMax, &rpt_desc); | ||
| 773 | } | ||
| 774 | |||
| 775 | // Write "Report Size" | ||
| 776 | rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc); | ||
| 777 | |||
| 778 | // Write "Report Count" | ||
| 779 | if (!pp_data->caps[caps_idx].IsRange) { | ||
| 780 | // Variable bit field with one bit per button | ||
| 781 | // In case of multiple usages with the same items, only "Usage" is written per cap, and "Report Count" is incremented | ||
| 782 | rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc); | ||
| 783 | } | ||
| 784 | else { | ||
| 785 | // Button array of "Report Size" x "Report Count | ||
| 786 | rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount, &rpt_desc); | ||
| 787 | } | ||
| 788 | |||
| 789 | |||
| 790 | // Buttons have only 1 bit and therefore no physical limits/units -> Set to undefined state | ||
| 791 | if (last_physical_min != 0) { | ||
| 792 | // Write "Physical Minimum", but only if changed | ||
| 793 | last_physical_min = 0; | ||
| 794 | rd_write_short_item(rd_global_physical_minimum, last_physical_min, &rpt_desc); | ||
| 795 | } | ||
| 796 | if (last_physical_max != 0) { | ||
| 797 | // Write "Physical Maximum", but only if changed | ||
| 798 | last_physical_max = 0; | ||
| 799 | rd_write_short_item(rd_global_physical_maximum, last_physical_max, &rpt_desc); | ||
| 800 | } | ||
| 801 | if (last_unit_exponent != 0) { | ||
| 802 | // Write "Unit Exponent", but only if changed | ||
| 803 | last_unit_exponent = 0; | ||
| 804 | rd_write_short_item(rd_global_unit_exponent, last_unit_exponent, &rpt_desc); | ||
| 805 | } | ||
| 806 | if (last_unit != 0) { | ||
| 807 | // Write "Unit",but only if changed | ||
| 808 | last_unit = 0; | ||
| 809 | rd_write_short_item(rd_global_unit, last_unit, &rpt_desc); | ||
| 810 | } | ||
| 811 | |||
| 812 | // Write "Input" main item | ||
| 813 | if (rt_idx == HidP_Input) { | ||
| 814 | rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc); | ||
| 815 | } | ||
| 816 | // Write "Output" main item | ||
| 817 | else if (rt_idx == HidP_Output) { | ||
| 818 | rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc); | ||
| 819 | } | ||
| 820 | // Write "Feature" main item | ||
| 821 | else if (rt_idx == HidP_Feature) { | ||
| 822 | rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc); | ||
| 823 | } | ||
| 824 | report_count = 0; | ||
| 825 | } | ||
| 826 | } | ||
| 827 | else { | ||
| 828 | |||
| 829 | if (last_report_id != pp_data->caps[caps_idx].ReportID) { | ||
| 830 | // Write "Report ID" if changed | ||
| 831 | rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc); | ||
| 832 | last_report_id = pp_data->caps[caps_idx].ReportID; | ||
| 833 | } | ||
| 834 | |||
| 835 | // Write "Usage Page" if changed | ||
| 836 | if (pp_data->caps[caps_idx].UsagePage != last_usage_page) { | ||
| 837 | rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc); | ||
| 838 | last_usage_page = pp_data->caps[caps_idx].UsagePage; | ||
| 839 | } | ||
| 840 | |||
| 841 | if (inhibit_write_of_usage) { | ||
| 842 | // Inhibit only once after Delimiter - Reset flag | ||
| 843 | inhibit_write_of_usage = FALSE; | ||
| 844 | } | ||
| 845 | else { | ||
| 846 | if (pp_data->caps[caps_idx].IsRange) { | ||
| 847 | // Write usage range from "Usage Minimum" to "Usage Maximum" | ||
| 848 | rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc); | ||
| 849 | rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc); | ||
| 850 | } | ||
| 851 | else { | ||
| 852 | // Write single "Usage" | ||
| 853 | rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc); | ||
| 854 | } | ||
| 855 | } | ||
| 856 | |||
| 857 | if (pp_data->caps[caps_idx].IsDesignatorRange) { | ||
| 858 | // Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum" | ||
| 859 | rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc); | ||
| 860 | rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc); | ||
| 861 | } | ||
| 862 | else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) { | ||
| 863 | // Designator set 0 is a special descriptor set (of the HID Physical Descriptor), | ||
| 864 | // that specifies the number of additional descriptor sets. | ||
| 865 | // Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it. | ||
| 866 | // Write single "Designator Index" | ||
| 867 | rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc); | ||
| 868 | } | ||
| 869 | |||
| 870 | if (pp_data->caps[caps_idx].IsStringRange) { | ||
| 871 | // Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum" | ||
| 872 | rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc); | ||
| 873 | rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc); | ||
| 874 | } | ||
| 875 | else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) { | ||
| 876 | // String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages, | ||
| 877 | // therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it. | ||
| 878 | // Write single "String Index" | ||
| 879 | rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc); | ||
| 880 | } | ||
| 881 | |||
| 882 | if ((pp_data->caps[caps_idx].BitField & 0x02) != 0x02) { | ||
| 883 | // In case of an value array overwrite "Report Count" | ||
| 884 | pp_data->caps[caps_idx].ReportCount = pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin + 1; | ||
| 885 | } | ||
| 886 | |||
| 887 | |||
| 888 | // Print only local report items for each cap, if ReportCount > 1 | ||
| 889 | if ((main_item_list->next != NULL) && | ||
| 890 | ((int) main_item_list->next->MainItemType == rt_idx) && | ||
| 891 | (main_item_list->next->TypeOfNode == rd_item_node_cap) && | ||
| 892 | (!pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) && | ||
| 893 | (!pp_data->caps[caps_idx].IsRange) && // This node in list is no array | ||
| 894 | (!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array | ||
| 895 | (pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) && | ||
| 896 | (pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMin == pp_data->caps[caps_idx].NotButton.LogicalMin) && | ||
| 897 | (pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMax == pp_data->caps[caps_idx].NotButton.LogicalMax) && | ||
| 898 | (pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMin == pp_data->caps[caps_idx].NotButton.PhysicalMin) && | ||
| 899 | (pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMax == pp_data->caps[caps_idx].NotButton.PhysicalMax) && | ||
| 900 | (pp_data->caps[main_item_list->next->CapsIndex].UnitsExp == pp_data->caps[caps_idx].UnitsExp) && | ||
| 901 | (pp_data->caps[main_item_list->next->CapsIndex].Units == pp_data->caps[caps_idx].Units) && | ||
| 902 | (pp_data->caps[main_item_list->next->CapsIndex].ReportSize == pp_data->caps[caps_idx].ReportSize) && | ||
| 903 | (pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) && | ||
| 904 | (pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField) && | ||
| 905 | (pp_data->caps[main_item_list->next->CapsIndex].ReportCount == 1) && | ||
| 906 | (pp_data->caps[caps_idx].ReportCount == 1) | ||
| 907 | ) { | ||
| 908 | // Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields | ||
| 909 | report_count++; | ||
| 910 | } | ||
| 911 | else { | ||
| 912 | // Value | ||
| 913 | |||
| 914 | // Write logical range from "Logical Minimum" to "Logical Maximum" | ||
| 915 | rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].NotButton.LogicalMin, &rpt_desc); | ||
| 916 | rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].NotButton.LogicalMax, &rpt_desc); | ||
| 917 | |||
| 918 | if ((last_physical_min != pp_data->caps[caps_idx].NotButton.PhysicalMin) || | ||
| 919 | (last_physical_max != pp_data->caps[caps_idx].NotButton.PhysicalMax)) { | ||
| 920 | // Write range from "Physical Minimum" to " Physical Maximum", but only if one of them changed | ||
| 921 | rd_write_short_item(rd_global_physical_minimum, pp_data->caps[caps_idx].NotButton.PhysicalMin, &rpt_desc); | ||
| 922 | last_physical_min = pp_data->caps[caps_idx].NotButton.PhysicalMin; | ||
| 923 | rd_write_short_item(rd_global_physical_maximum, pp_data->caps[caps_idx].NotButton.PhysicalMax, &rpt_desc); | ||
| 924 | last_physical_max = pp_data->caps[caps_idx].NotButton.PhysicalMax; | ||
| 925 | } | ||
| 926 | |||
| 927 | |||
| 928 | if (last_unit_exponent != pp_data->caps[caps_idx].UnitsExp) { | ||
| 929 | // Write "Unit Exponent", but only if changed | ||
| 930 | rd_write_short_item(rd_global_unit_exponent, pp_data->caps[caps_idx].UnitsExp, &rpt_desc); | ||
| 931 | last_unit_exponent = pp_data->caps[caps_idx].UnitsExp; | ||
| 932 | } | ||
| 933 | |||
| 934 | if (last_unit != pp_data->caps[caps_idx].Units) { | ||
| 935 | // Write physical "Unit", but only if changed | ||
| 936 | rd_write_short_item(rd_global_unit, pp_data->caps[caps_idx].Units, &rpt_desc); | ||
| 937 | last_unit = pp_data->caps[caps_idx].Units; | ||
| 938 | } | ||
| 939 | |||
| 940 | // Write "Report Size" | ||
| 941 | rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc); | ||
| 942 | |||
| 943 | // Write "Report Count" | ||
| 944 | rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc); | ||
| 945 | |||
| 946 | if (rt_idx == HidP_Input) { | ||
| 947 | // Write "Input" main item | ||
| 948 | rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc); | ||
| 949 | } | ||
| 950 | else if (rt_idx == HidP_Output) { | ||
| 951 | // Write "Output" main item | ||
| 952 | rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc); | ||
| 953 | } | ||
| 954 | else if (rt_idx == HidP_Feature) { | ||
| 955 | // Write "Feature" main item | ||
| 956 | rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc); | ||
| 957 | } | ||
| 958 | report_count = 0; | ||
| 959 | } | ||
| 960 | } | ||
| 961 | |||
| 962 | // Go to next item in main_item_list and free the memory of the actual item | ||
| 963 | struct rd_main_item_node *main_item_list_prev = main_item_list; | ||
| 964 | main_item_list = main_item_list->next; | ||
| 965 | free(main_item_list_prev); | ||
| 966 | } | ||
| 967 | |||
| 968 | // Free multidimensionable array: coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE] | ||
| 969 | // Free multidimensionable array: coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX] | ||
| 970 | for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { | ||
| 971 | for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { | ||
| 972 | for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { | ||
| 973 | free(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]); | ||
| 974 | } | ||
| 975 | free(coll_bit_range[collection_node_idx][reportid_idx]); | ||
| 976 | } | ||
| 977 | free(coll_bit_range[collection_node_idx]); | ||
| 978 | if (coll_number_of_direct_childs[collection_node_idx] != 0) free(coll_child_order[collection_node_idx]); | ||
| 979 | } | ||
| 980 | free(coll_bit_range); | ||
| 981 | free(coll_child_order); | ||
| 982 | |||
| 983 | // Free one dimensional arrays | ||
| 984 | free(coll_begin_lookup); | ||
| 985 | free(coll_end_lookup); | ||
| 986 | free(coll_levels); | ||
| 987 | free(coll_number_of_direct_childs); | ||
| 988 | |||
| 989 | return (int) rpt_desc.byte_idx; | ||
| 990 | } | ||
