1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** USBX Component */
16 /** */
17 /** HID Class */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22
23 /* Include necessary system files. */
24
25 #define UX_SOURCE_CODE
26
27 #include "ux_api.h"
28 #include "ux_host_class_hid.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_host_class_hid_report_add PORTABLE C */
37 /* 6.3.0 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function adds a report (input/output/feature) to the current */
45 /* parser. */
46 /* */
47 /* INPUT */
48 /* */
49 /* hid Pointer to HID class */
50 /* descriptor Pointer to descriptor */
51 /* item Pointer to item */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* Completion Status */
56 /* */
57 /* CALLS */
58 /* */
59 /* _ux_host_class_hid_item_data_get Get data item */
60 /* _ux_utility_memory_allocate Allocate memory block */
61 /* _ux_utility_memory_copy Copy memory block */
62 /* _ux_utility_memory_free Release memory block */
63 /* */
64 /* CALLED BY */
65 /* */
66 /* HID Class */
67 /* */
68 /* RELEASE HISTORY */
69 /* */
70 /* DATE NAME DESCRIPTION */
71 /* */
72 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
73 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
74 /* verified memset and memcpy */
75 /* cases, */
76 /* resulting in version 6.1 */
77 /* 08-02-2021 Wen Wang Modified comment(s), */
78 /* fixed spelling error, */
79 /* resulting in version 6.1.8 */
80 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
81 /* fixed field managing issue, */
82 /* improved usage handling, */
83 /* resulting in version 6.3.0 */
84 /* */
85 /**************************************************************************/
_ux_host_class_hid_report_add(UX_HOST_CLASS_HID * hid,UCHAR * descriptor,UX_HOST_CLASS_HID_ITEM * item)86 UINT _ux_host_class_hid_report_add(UX_HOST_CLASS_HID *hid, UCHAR *descriptor, UX_HOST_CLASS_HID_ITEM *item)
87 {
88
89 UX_HOST_CLASS_HID_PARSER *hid_parser;
90 ULONG hid_field_value;
91 ULONG hid_field_count;
92 UX_HOST_CLASS_HID_REPORT *new_hid_report;
93 UX_HOST_CLASS_HID_REPORT *hid_report;
94 UX_HOST_CLASS_HID_FIELD *hid_field;
95 UX_HOST_CLASS_HID_FIELD *new_hid_field;
96 ULONG current_field_address;
97
98
99 /* Get the parser structure pointer. */
100 hid_parser = &hid -> ux_host_class_hid_parser;
101
102 /* Obtain the field value from the report. */
103 hid_field_value = _ux_host_class_hid_item_data_get(descriptor, item);
104
105 /* Allocate some memory to store this new report. */
106 new_hid_report = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_HID_REPORT));
107 if (new_hid_report == UX_NULL)
108 return(UX_MEMORY_INSUFFICIENT);
109
110 /* We need to select the report entry based on the type of report. If the entry in the
111 report chain is NULL, this is the first report so update the start of the chain. */
112 switch (item -> ux_host_class_hid_item_report_tag)
113 {
114
115 case UX_HOST_CLASS_HID_MAIN_TAG_INPUT:
116
117 hid_report = hid_parser -> ux_host_class_hid_parser_input_report;
118 if (hid_report == UX_NULL)
119 hid_parser -> ux_host_class_hid_parser_input_report = new_hid_report;
120
121 /* This is a Input report. */
122 new_hid_report -> ux_host_class_hid_report_type = UX_HOST_CLASS_HID_REPORT_TYPE_INPUT;
123 break;
124
125 case UX_HOST_CLASS_HID_MAIN_TAG_OUTPUT:
126
127 hid_report = hid_parser -> ux_host_class_hid_parser_output_report;
128 if (hid_report == UX_NULL)
129 hid_parser -> ux_host_class_hid_parser_output_report = new_hid_report;
130
131 /* This is output report. */
132 new_hid_report -> ux_host_class_hid_report_type = UX_HOST_CLASS_HID_REPORT_TYPE_OUTPUT;
133 break;
134
135 case UX_HOST_CLASS_HID_MAIN_TAG_FEATURE:
136
137 hid_report = hid_parser -> ux_host_class_hid_parser_feature_report;
138 if (hid_report == UX_NULL)
139 hid_parser -> ux_host_class_hid_parser_feature_report = new_hid_report;
140
141 /* This is a Feature report. */
142 new_hid_report -> ux_host_class_hid_report_type = UX_HOST_CLASS_HID_REPORT_TYPE_FEATURE;
143 break;
144
145
146 default:
147
148 _ux_utility_memory_free(new_hid_report);
149
150 /* Error trap. */
151 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR);
152
153 /* If trace is enabled, insert this event into the trace buffer. */
154 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0)
155
156 /* Return an error. */
157 return(UX_HOST_CLASS_HID_REPORT_ERROR);
158 }
159
160 /* If there is a preceding report, locate the end of the report chain. */
161 if (hid_report != UX_NULL)
162 {
163
164 while (hid_report -> ux_host_class_hid_report_next_report != UX_NULL)
165 hid_report = hid_report -> ux_host_class_hid_report_next_report;
166 }
167
168 /* If this report is part of the current global report, use the last report
169 to add the fields. */
170 if ((hid_report != UX_NULL) && (hid_report -> ux_host_class_hid_report_id == hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_id))
171 {
172
173 /* So we did not need a new report after all! */
174 _ux_utility_memory_free(new_hid_report);
175 new_hid_report = hid_report;
176 }
177 else
178 {
179
180 /* We do have to build a new report. Add the new one to the chain. */
181 if (hid_report != UX_NULL)
182 hid_report -> ux_host_class_hid_report_next_report = new_hid_report;
183
184 /* Add the new report ID. */
185 new_hid_report -> ux_host_class_hid_report_id = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_id;
186 }
187
188 /* Compute the size of the report. The size is first calculated in bits. */
189 current_field_address = new_hid_report -> ux_host_class_hid_report_bit_length;
190 new_hid_report -> ux_host_class_hid_report_bit_length += hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_size*
191 hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_count;
192
193 /* Now compute the size in bytes (easier for the end/receive reports functions). */
194 new_hid_report -> ux_host_class_hid_report_byte_length = new_hid_report -> ux_host_class_hid_report_bit_length >> 3;
195
196 /* Take care of the bit padding if necessary. */
197 if (new_hid_report -> ux_host_class_hid_report_bit_length & 7)
198 new_hid_report -> ux_host_class_hid_report_byte_length++;
199
200 /* Get the number of fields for this report. */
201 hid_field_count = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_count;
202
203 /* If the field count is null, this is only padding and there is no field to be allocated to the report. */
204 if (hid_field_count == 0)
205 return(UX_SUCCESS);
206
207 /* Create the field structure. */
208 new_hid_field = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_HID_FIELD));
209 if (new_hid_field == UX_NULL)
210 return(UX_MEMORY_INSUFFICIENT);
211
212 /* From the parser structure, update the new field values. Start with logical values. */
213 new_hid_field -> ux_host_class_hid_field_logical_min = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_logical_min;
214 new_hid_field -> ux_host_class_hid_field_logical_max = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_logical_max;
215
216 /* Then the usage values. Note that these are only used if the item is an array. */
217 new_hid_field -> ux_host_class_hid_field_usage_page = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_usage_page;
218 new_hid_field -> ux_host_class_hid_field_usage_min = hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_min;
219 new_hid_field -> ux_host_class_hid_field_usage_max = hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_max;
220
221 /* Then physical values. */
222 new_hid_field -> ux_host_class_hid_field_physical_min = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_physical_min;
223 new_hid_field -> ux_host_class_hid_field_physical_max = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_physical_max;
224
225 /* Then unit values. */
226 new_hid_field -> ux_host_class_hid_field_unit = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_unit;
227 new_hid_field -> ux_host_class_hid_field_unit_expo = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_unit_expo;
228
229 /* Then report values. */
230 new_hid_field -> ux_host_class_hid_field_report_type = item -> ux_host_class_hid_item_report_tag;
231 new_hid_field -> ux_host_class_hid_field_report_id = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_id;
232 new_hid_field -> ux_host_class_hid_field_report_size = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_size;
233 new_hid_field -> ux_host_class_hid_field_report_count = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_count;
234 new_hid_field -> ux_host_class_hid_field_report_offset = current_field_address;
235
236 /* Save the HID field value. */
237 new_hid_field -> ux_host_class_hid_field_value = hid_field_value;
238
239 /* We need some memory for the values. */
240 new_hid_field -> ux_host_class_hid_field_values = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY,
241 new_hid_field -> ux_host_class_hid_field_report_count, 4);
242
243 /* Check the memory pointer. */
244 if (new_hid_field -> ux_host_class_hid_field_values == UX_NULL)
245 {
246
247 _ux_utility_memory_free(new_hid_field);
248 return(UX_MEMORY_INSUFFICIENT);
249 }
250
251 /* We need some memory for the usages, but only for variable items; usage
252 values for array items can be calculated. */
253 if ((hid_field_value & UX_HOST_CLASS_HID_ITEM_VARIABLE) &&
254 (hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage > 0))
255 {
256
257 /* Allocate memory for the usages. */
258 new_hid_field -> ux_host_class_hid_field_usages = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage, 4);
259 if (new_hid_field -> ux_host_class_hid_field_usages == UX_NULL)
260 {
261
262 _ux_utility_memory_free(new_hid_field -> ux_host_class_hid_field_values);
263 _ux_utility_memory_free(new_hid_field);
264 return(UX_MEMORY_INSUFFICIENT);
265 }
266
267 /* Copy the current usages in the field structure. */
268 _ux_utility_memory_copy(new_hid_field -> ux_host_class_hid_field_usages, hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usages, hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage * 4); /* Use case of memcpy is verified. */
269 }
270
271 /* Save the number of usages. */
272 new_hid_field -> ux_host_class_hid_field_number_usage = hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage;
273
274 /* Attach the new field to the report. The report may already contain a field, if so parse the chain
275 until we reach the end of the chain. */
276 new_hid_report -> ux_host_class_hid_report_number_item += hid_field_count;
277 if (new_hid_report -> ux_host_class_hid_report_field == UX_NULL)
278 {
279
280 /* This is the first field for the report. */
281 new_hid_report -> ux_host_class_hid_report_field = new_hid_field;
282 }
283 else
284 {
285
286 /* We have previous HID fields, so search for the end of the chain. */
287 hid_field = new_hid_report -> ux_host_class_hid_report_field;
288 while(hid_field -> ux_host_class_hid_field_next_field != UX_NULL)
289 hid_field = hid_field -> ux_host_class_hid_field_next_field;
290
291 /* Attach the new field to the end of the chain. */
292 hid_field -> ux_host_class_hid_field_next_field = new_hid_field;
293 }
294
295 /* Return successful completion. */
296 return(UX_SUCCESS);
297 }
298
299