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_transfer_request_completed PORTABLE C */
37 /* 6.2.1 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function is called by the completion thread when a transfer */
45 /* request has been completed either because the transfer is */
46 /* successful or there was an error. */
47 /* */
48 /* INPUT */
49 /* */
50 /* transfer_request Pointer to transfer request */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* None */
55 /* */
56 /* CALLS */
57 /* */
58 /* (ux_host_class_hid_report_callback_function) */
59 /* Callback function for report */
60 /* _ux_host_class_hid_report_decompress Decompress HID report */
61 /* _ux_host_stack_transfer_request Process transfer request */
62 /* _ux_utility_memory_allocate Allocate memory block */
63 /* _ux_utility_memory_free Release memory block */
64 /* */
65 /* CALLED BY */
66 /* */
67 /* HID Class */
68 /* */
69 /* RELEASE HISTORY */
70 /* */
71 /* DATE NAME DESCRIPTION */
72 /* */
73 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
74 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
75 /* resulting in version 6.1 */
76 /* 03-08-2023 Chaoqiong Xiao Modified comment(s), */
77 /* supported report IDs, */
78 /* resulting in version 6.2.1 */
79 /* */
80 /**************************************************************************/
_ux_host_class_hid_transfer_request_completed(UX_TRANSFER * transfer_request)81 VOID _ux_host_class_hid_transfer_request_completed(UX_TRANSFER *transfer_request)
82 {
83
84 UX_HOST_CLASS_HID *hid;
85 UX_HOST_CLASS_HID_CLIENT *hid_client;
86 UX_HOST_CLASS_HID_REPORT *hid_report;
87 UINT status;
88 VOID *report_buffer;
89 UX_HOST_CLASS_HID_REPORT_CALLBACK callback;
90 UX_HOST_CLASS_HID_CLIENT_REPORT client_report;
91 ULONG *client_buffer;
92 UX_HOST_CLASS_HID_FIELD *hid_field;
93 ULONG field_report_count;
94
95 /* Set Status to success. Optimistic view. */
96 status = UX_SUCCESS;
97
98 /* Get the class instance for this transfer request. */
99 hid = (UX_HOST_CLASS_HID *) transfer_request -> ux_transfer_request_class_instance;
100
101 /* Check the state of the transfer. If there is an error, we do not proceed with this report. */
102 if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS)
103 {
104
105 /* We have an error. We do not rehook another transfer if the device instance is shutting down or
106 if the transfer was aborted by the class. */
107 if ((hid -> ux_host_class_hid_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) ||
108 (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_ABORT))
109
110 /* We do not proceed. */
111 return;
112
113 else
114 {
115
116 /* Reactivate the HID interrupt pipe. */
117 _ux_host_stack_transfer_request(transfer_request);
118
119 /* We do not proceed. */
120 return;
121 }
122 }
123
124 /* Get the client instance attached to the HID. */
125 hid_client = hid -> ux_host_class_hid_client;
126
127 /* Get the pointer to the report buffer in the transfer request. */
128 report_buffer = transfer_request -> ux_transfer_request_data_pointer;
129
130 /* We know this incoming report is for the Input report. */
131 hid_report = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_input_report;
132
133 /* If there are multiple HID reports, report ID must be checked. */
134 if (hid_report -> ux_host_class_hid_report_next_report != UX_NULL)
135 {
136
137 /* Scan the reports to find the report expected. */
138 while(1)
139 {
140
141 /* Check report ID at buffer start. */
142 if (*(UCHAR*)report_buffer == hid_report -> ux_host_class_hid_report_id)
143 break;
144
145 /* If there is no more report, it's done. */
146 if (hid_report -> ux_host_class_hid_report_next_report == UX_NULL)
147 break;
148
149 /* There is more reports, next. */
150 hid_report = hid_report -> ux_host_class_hid_report_next_report;
151 }
152
153 /* Check if the report is what we expected. */
154 if (*(UCHAR*)report_buffer != hid_report -> ux_host_class_hid_report_id)
155
156 /* Report not found. */
157 hid_report = UX_NULL;
158 }
159
160 /* For this report to be used, the HID client must have registered
161 the report. We check the call back function. */
162 if ((hid_report != UX_NULL) &&
163 (hid_report -> ux_host_class_hid_report_callback_function != UX_NULL))
164 {
165
166 /* initialize some of the callback structure which are generic to any
167 reporting method. */
168 callback.ux_host_class_hid_report_callback_client = hid_client;
169 callback.ux_host_class_hid_report_callback_id = hid_report -> ux_host_class_hid_report_id;
170
171 /* The report is now in memory in a raw format the application may desire to handle it that way! */
172 if (hid_report -> ux_host_class_hid_report_callback_flags & UX_HOST_CLASS_HID_REPORT_RAW)
173 {
174
175 /* Put the length of the report in raw form in the callers callback structure. */
176 callback.ux_host_class_hid_report_callback_actual_length = transfer_request -> ux_transfer_request_actual_length;
177 callback.ux_host_class_hid_report_callback_buffer = report_buffer;
178
179 /* Build the callback structure status. */
180 callback.ux_host_class_hid_report_callback_status = status;
181
182 /* Set the flags to indicate the type of report. */
183 callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags;
184
185 /* Call the report owner. */
186 hid_report -> ux_host_class_hid_report_callback_function(&callback);
187 }
188 else
189 {
190
191 /* The report may be decompressed, buffer length is based on number of items in report.
192 Each item is a pair of words (usage and the value itself), so the required decompress memory for each
193 item is 4 (word size) * 2 (number of word) = 8 bytes.
194 To accelerate we shift number of item by 3 to get the result. */
195 if (UX_OVERFLOW_CHECK_MULC_ULONG(hid_report->ux_host_class_hid_report_number_item, 8))
196 client_buffer = UX_NULL;
197 else
198 {
199 client_report.ux_host_class_hid_client_report_length = hid_report->ux_host_class_hid_report_number_item << 3;
200
201 /* We need to allocate some memory to build the decompressed report. */
202 client_buffer = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, client_report.ux_host_class_hid_client_report_length);
203 }
204
205 /* Check completion status. */
206 if (client_buffer == UX_NULL)
207 {
208 /* We have an error of memory, do not proceed */
209 status = UX_MEMORY_INSUFFICIENT;
210 }
211 else
212 {
213
214 /* We need to build a client structure to be used by the decompression engine. */
215 client_report.ux_host_class_hid_client_report_buffer = client_buffer;
216 client_report.ux_host_class_hid_client_report_actual_length = 0;
217 client_report.ux_host_class_hid_client_report = hid_report;
218
219 /* The report buffer must be parsed and decompressed into the local buffer. */
220 _ux_host_class_hid_report_decompress(hid, &client_report, report_buffer, transfer_request -> ux_transfer_request_actual_length);
221
222 /* The report may be decompressed and returned as individual Usages. */
223 if (hid_report -> ux_host_class_hid_report_callback_flags & UX_HOST_CLASS_HID_REPORT_INDIVIDUAL_USAGE)
224 {
225
226 /* Now, we need to call the HID client call back function with a usage/value couple. */
227 hid_field = hid_report -> ux_host_class_hid_report_field;
228
229 /* Set the flags to indicate the type of report (usage/value couple). */
230 callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags;
231
232 /* The length of the buffer is irrelevant here so we reset it. */
233 callback.ux_host_class_hid_report_callback_actual_length = 0;
234
235 /* Scan all the fields and send each usage/value. */
236 while(hid_field != UX_NULL)
237 {
238
239 /* Build each report item. */
240 for (field_report_count = 0; field_report_count < hid_field -> ux_host_class_hid_field_report_count; field_report_count++)
241 {
242
243 /* Insert the usage and the report value into the callback structure. */
244 callback.ux_host_class_hid_report_callback_usage = *client_report.ux_host_class_hid_client_report_buffer++;
245 callback.ux_host_class_hid_report_callback_value = *client_report.ux_host_class_hid_client_report_buffer++;
246
247 /* Build the callback structure status. */
248 callback.ux_host_class_hid_report_callback_status = status;
249
250 /* Call the report owner */
251 hid_report -> ux_host_class_hid_report_callback_function(&callback);
252 }
253
254 /* Get the next field. */
255 hid_field = hid_field -> ux_host_class_hid_field_next_field;
256 }
257 }
258 else
259 {
260
261 /* Add the length actually valid in the caller's buffer. */
262 callback.ux_host_class_hid_report_callback_actual_length = client_report.ux_host_class_hid_client_report_actual_length;
263
264 /* Add the caller's buffer address. */
265 callback.ux_host_class_hid_report_callback_buffer = client_report.ux_host_class_hid_client_report_buffer;
266
267 /* Build the callback structure status. */
268 callback.ux_host_class_hid_report_callback_status = status;
269
270 /* Set the flags to indicate the type of report. */
271 callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags;
272
273 /* Call the report owner. */
274 hid_report -> ux_host_class_hid_report_callback_function(&callback);
275 }
276
277 /* Free the memory resource we used. */
278 _ux_utility_memory_free(client_buffer);
279 }
280 }
281 }
282
283 /* Check latest status. */
284 if (status != UX_SUCCESS)
285
286 /* Error trap. */
287 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
288
289 /* Reactivate the HID interrupt pipe. */
290 status = _ux_host_stack_transfer_request(transfer_request);
291
292 /* Check latest status. */
293 if (status != UX_SUCCESS)
294
295 /* Error trap. */
296 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
297
298 /* Return to caller. */
299 return;
300 }
301
302