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