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 #if !defined(UX_HOST_STANDALONE)
33
34 /* Inline the function to let compiler optimize. */
35 static inline
36 #else
37
38 /* Prototype the function to remove warnings. */
39 UINT ux_host_class_hid_report_set_buffer_allocate(
40 UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report,
41 UCHAR **allocated);
42 #endif
ux_host_class_hid_report_set_buffer_allocate(UX_HOST_CLASS_HID * hid,UX_HOST_CLASS_HID_CLIENT_REPORT * client_report,UCHAR ** allocated)43 UINT ux_host_class_hid_report_set_buffer_allocate(
44 UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report,
45 UCHAR **allocated)
46 {
47
48 UX_HOST_CLASS_HID_REPORT *hid_report;
49 UCHAR *report_buffer;
50 UCHAR *current_report_buffer;
51
52 /* Get the report pointer from the caller. */
53 hid_report = client_report -> ux_host_class_hid_client_report;
54
55 /* Ensure this is NOT an INPUT report. */
56 if (hid_report -> ux_host_class_hid_report_type == UX_HOST_CLASS_HID_REPORT_TYPE_INPUT)
57 {
58
59 /* Return error code. */
60 return(UX_HOST_CLASS_HID_REPORT_ERROR);
61 }
62
63 /* Get some memory for sending the report. */
64 if (hid_report -> ux_host_class_hid_report_id == 0)
65 report_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, hid_report -> ux_host_class_hid_report_byte_length);
66 else
67 report_buffer = _ux_utility_memory_allocate_add_safe(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, hid_report -> ux_host_class_hid_report_byte_length, 1);
68 if (report_buffer == UX_NULL)
69 {
70
71 /* Return error code. */
72 return(UX_MEMORY_INSUFFICIENT);
73 }
74
75 /* Memorize the start of the real report buffer. */
76 current_report_buffer = report_buffer;
77
78 /* Check if there is a report ID to be inserted in front of the buffer. */
79 if (hid_report -> ux_host_class_hid_report_id != 0)
80 *current_report_buffer++ = (UCHAR)(hid_report -> ux_host_class_hid_report_id);
81
82 /* The report is in the client's buffer. It may be raw or or decompressed. If decompressed,
83 we need to create the report. */
84 if (client_report -> ux_host_class_hid_client_report_flags & UX_HOST_CLASS_HID_REPORT_RAW)
85 {
86
87 /* Ensure the user is not trying to overflow the report buffer. */
88 if (hid_report -> ux_host_class_hid_report_byte_length >= client_report -> ux_host_class_hid_client_report_length)
89 {
90
91 /* We have enough memory to store the raw buffer. */
92 _ux_utility_memory_copy(current_report_buffer, client_report -> ux_host_class_hid_client_report_buffer, hid_report -> ux_host_class_hid_report_byte_length); /* Use case of memcpy is verified. */
93 }
94 else
95 {
96
97 /* Free allocated buffer. */
98 _ux_utility_memory_free(report_buffer);
99
100 /* Return error code. */
101 return(UX_HOST_CLASS_HID_REPORT_OVERFLOW);
102 }
103 }
104 else
105 {
106
107 /* The report buffer has to be compressed. */
108 _ux_host_class_hid_report_compress(hid, client_report, current_report_buffer, client_report -> ux_host_class_hid_client_report_length);
109 }
110
111 /* Buffer is allocated and report data copied. */
112 *allocated = report_buffer;
113 return(UX_SUCCESS);
114 }
115
116 /**************************************************************************/
117 /* */
118 /* FUNCTION RELEASE */
119 /* */
120 /* _ux_host_class_hid_report_set PORTABLE C */
121 /* 6.1.10 */
122 /* AUTHOR */
123 /* */
124 /* Chaoqiong Xiao, Microsoft Corporation */
125 /* */
126 /* DESCRIPTION */
127 /* */
128 /* This function sets a report (input/output/feature) to the device. */
129 /* The report can be either decompressed by the stack or raw. */
130 /* */
131 /* INPUT */
132 /* */
133 /* hid Pointer to HID class */
134 /* client_report Pointer to client report */
135 /* */
136 /* OUTPUT */
137 /* */
138 /* Completion Status */
139 /* */
140 /* CALLS */
141 /* */
142 /* _ux_host_class_hid_report_compress Compress HID report */
143 /* _ux_host_stack_class_instance_verify Verify the instance is valid */
144 /* _ux_host_stack_transfer_request Process transfer request */
145 /* _ux_utility_memory_allocate Allocate memory block */
146 /* _ux_utility_memory_copy Copy memory block */
147 /* _ux_utility_memory_free Release memory block */
148 /* _ux_host_semaphore_get Get protection semaphore */
149 /* _ux_host_semaphore_put Release protection semaphore */
150 /* */
151 /* CALLED BY */
152 /* */
153 /* Application */
154 /* HID Class */
155 /* */
156 /* RELEASE HISTORY */
157 /* */
158 /* DATE NAME DESCRIPTION */
159 /* */
160 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
161 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
162 /* verified memset and memcpy */
163 /* cases, */
164 /* resulting in version 6.1 */
165 /* 01-31-2022 Xiuwen Cai, CQ Xiao Modified comment(s), */
166 /* added interrupt OUT support,*/
167 /* added standalone support, */
168 /* refined code sequence, */
169 /* resulting in version 6.1.10 */
170 /* */
171 /**************************************************************************/
_ux_host_class_hid_report_set(UX_HOST_CLASS_HID * hid,UX_HOST_CLASS_HID_CLIENT_REPORT * client_report)172 UINT _ux_host_class_hid_report_set(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report)
173 {
174 #if defined(UX_HOST_STANDALONE)
175 UINT status;
176 do
177 {
178 status = _ux_host_class_hid_report_set_run(hid, client_report);
179 } while(status == UX_STATE_WAIT || status == UX_STATE_LOCK);
180 return(hid -> ux_host_class_hid_status);
181 #else
182
183 UX_ENDPOINT *control_endpoint;
184 UX_TRANSFER *transfer_request;
185 UCHAR *report_buffer;
186 UX_HOST_CLASS_HID_REPORT *hid_report;
187 UINT status;
188
189 /* If trace is enabled, insert this event into the trace buffer. */
190 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_REPORT_SET, hid, client_report, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
191
192 /* Ensure the instance is valid. */
193 if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS)
194 {
195
196 /* Error trap. */
197 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN);
198
199 /* If trace is enabled, insert this event into the trace buffer. */
200 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0)
201
202 return(UX_HOST_CLASS_INSTANCE_UNKNOWN);
203 }
204
205 /* Protect thread reentry to this instance. */
206 _ux_host_class_hid_lock_fail_return(hid);
207
208 /* Get the report pointer from the caller. */
209 hid_report = client_report -> ux_host_class_hid_client_report;
210 #if defined(UX_HOST_CLASS_HID_INTERRUPT_OUT_SUPPORT)
211
212 /* Check if there is an interrupt OUT endpoint. */
213 if (hid -> ux_host_class_hid_interrupt_out_endpoint != UX_NULL)
214 {
215
216 /* Transfer the report by using the interrupt OUT endpoint. */
217 transfer_request = &hid -> ux_host_class_hid_interrupt_out_endpoint -> ux_endpoint_transfer_request;
218 }
219 else
220 {
221 #endif
222 /* We need to get the default control endpoint transfer request pointer. */
223 control_endpoint = &hid -> ux_host_class_hid_device -> ux_device_control_endpoint;
224 transfer_request = &control_endpoint -> ux_endpoint_transfer_request;
225
226 /* Protect the control endpoint semaphore here. It will be unprotected in the
227 transfer request function. */
228 status = _ux_host_semaphore_get(&hid -> ux_host_class_hid_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER);
229
230 /* Check for status. */
231 if (status != UX_SUCCESS)
232 {
233
234 /* Something went wrong. */
235 /* Unprotect thread reentry to this instance. */
236 _ux_host_class_hid_unlock(hid);
237 return(status);
238 }
239 #if defined(UX_HOST_CLASS_HID_INTERRUPT_OUT_SUPPORT)
240 }
241 #endif
242 /* Build the report buffer for SET_REPORT request. */
243 status = ux_host_class_hid_report_set_buffer_allocate(hid, client_report, &report_buffer);
244 if (status != UX_SUCCESS)
245 {
246
247 /* Unlock device control endpoint. */
248 _ux_host_semaphore_put(&hid -> ux_host_class_hid_device -> ux_device_protection_semaphore);
249
250 /* Unprotect thread reentry to this instance. */
251 _ux_host_class_hid_unlock(hid);
252
253 /* Error trap. */
254 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
255
256 /* If trace is enabled, insert this event into the trace buffer. */
257 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, status, hid, 0, 0, UX_TRACE_ERRORS, 0, 0)
258
259 return(status);
260 }
261
262 /* Create a transfer request for the SET_REPORT request. */
263 transfer_request -> ux_transfer_request_data_pointer = report_buffer;
264 transfer_request -> ux_transfer_request_requested_length = hid_report -> ux_host_class_hid_report_byte_length;
265 transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_HID_SET_REPORT;
266 transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE;
267 transfer_request -> ux_transfer_request_value = (UINT)((USHORT) hid_report -> ux_host_class_hid_report_id | (USHORT) hid_report -> ux_host_class_hid_report_type << 8);
268 transfer_request -> ux_transfer_request_index = hid -> ux_host_class_hid_interface -> ux_interface_descriptor.bInterfaceNumber;
269
270 /* Send request to HCD layer. */
271 status = _ux_host_stack_transfer_request(transfer_request);
272 #if defined(UX_HOST_CLASS_HID_INTERRUPT_OUT_SUPPORT)
273
274 /* Check if interrupt OUT endpoint is used. */
275 if ((hid -> ux_host_class_hid_interrupt_out_endpoint != UX_NULL) && (status == UX_SUCCESS))
276 {
277
278 /* We need to wait for the completion of the transfer request. */
279 status = _ux_host_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_MS_TO_TICK(UX_HOST_CLASS_HID_REPORT_TRANSFER_TIMEOUT));
280 }
281 #endif
282
283 /* Check for correct transfer and the actual transfer length. */
284 if ((status != UX_SUCCESS) || (transfer_request -> ux_transfer_request_actual_length != hid_report -> ux_host_class_hid_report_byte_length))
285 {
286 #if defined(UX_HOST_CLASS_HID_INTERRUPT_OUT_SUPPORT)
287
288 /* Check if interrupt OUT endpoint is used. */
289 if ((hid -> ux_host_class_hid_interrupt_out_endpoint != UX_NULL) && (status != UX_SUCCESS))
290 {
291
292 /* We need to abort the transfer request. */
293 _ux_host_stack_transfer_request_abort(transfer_request);
294 }
295 #endif
296
297 /* Error trap. */
298 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR);
299
300 /* If trace is enabled, insert this event into the trace buffer. */
301 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0)
302
303 status = UX_HOST_CLASS_HID_REPORT_ERROR;
304 }
305
306 /* Free all resources. */
307 _ux_utility_memory_free(report_buffer);
308
309 /* Unprotect thread reentry to this instance. */
310 _ux_host_class_hid_unlock(hid);
311
312 /* Return the function status */
313 return(status);
314 #endif
315 }
316
317 /**************************************************************************/
318 /* */
319 /* FUNCTION RELEASE */
320 /* */
321 /* _uxe_host_class_hid_report_set PORTABLE C */
322 /* 6.3.0 */
323 /* AUTHOR */
324 /* */
325 /* Chaoqiong Xiao, Microsoft Corporation */
326 /* */
327 /* DESCRIPTION */
328 /* */
329 /* This function checks errors in HID report set function call. */
330 /* */
331 /* INPUT */
332 /* */
333 /* hid Pointer to HID class */
334 /* client_report Pointer to client report */
335 /* */
336 /* OUTPUT */
337 /* */
338 /* Status */
339 /* */
340 /* CALLS */
341 /* */
342 /* _ux_host_class_hid_report_set Set a report */
343 /* */
344 /* CALLED BY */
345 /* */
346 /* Application */
347 /* */
348 /* RELEASE HISTORY */
349 /* */
350 /* DATE NAME DESCRIPTION */
351 /* */
352 /* 10-31-2023 Chaoqiong Xiao Initial Version 6.3.0 */
353 /* */
354 /**************************************************************************/
_uxe_host_class_hid_report_set(UX_HOST_CLASS_HID * hid,UX_HOST_CLASS_HID_CLIENT_REPORT * client_report)355 UINT _uxe_host_class_hid_report_set(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report)
356 {
357
358 /* Sanity checks. */
359 if ((hid == UX_NULL) || (client_report == UX_NULL))
360 return(UX_INVALID_PARAMETER);
361
362 /* Invoke report set function. */
363 return(_ux_host_class_hid_report_set(hid, client_report));
364 }
365