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 /** USBX Component                                                        */
14 /**                                                                       */
15 /**   Device HID Class                                                    */
16 /**                                                                       */
17 /**************************************************************************/
18 /**************************************************************************/
19 
20 #define UX_SOURCE_CODE
21 
22 
23 /* Include necessary system files.  */
24 
25 #include "ux_api.h"
26 #include "ux_device_class_hid.h"
27 #include "ux_device_stack.h"
28 
29 
30 /**************************************************************************/
31 /*                                                                        */
32 /*  FUNCTION                                               RELEASE        */
33 /*                                                                        */
34 /*    _ux_device_class_hid_initialize                     PORTABLE C      */
35 /*                                                           6.3.0        */
36 /*  AUTHOR                                                                */
37 /*                                                                        */
38 /*    Chaoqiong Xiao, Microsoft Corporation                               */
39 /*                                                                        */
40 /*  DESCRIPTION                                                           */
41 /*                                                                        */
42 /*    This function initializes the USB HID device.                       */
43 /*    This function is called by the class register function. It is only  */
44 /*    done once.                                                          */
45 /*                                                                        */
46 /*  INPUT                                                                 */
47 /*                                                                        */
48 /*    command                              Pointer to hid command         */
49 /*                                                                        */
50 /*  OUTPUT                                                                */
51 /*                                                                        */
52 /*    Completion Status                                                   */
53 /*                                                                        */
54 /*  CALLS                                                                 */
55 /*                                                                        */
56 /*    _ux_utility_memory_allocate           Allocate memory               */
57 /*    _ux_utility_memory_free               Free memory                   */
58 /*    _ux_device_thread_create              Create thread                 */
59 /*    _ux_device_thread_delete              Delete thread                 */
60 /*    _ux_utility_event_flags_create        Create event flags group      */
61 /*                                                                        */
62 /*  CALLED BY                                                             */
63 /*                                                                        */
64 /*    USBX Source Code                                                    */
65 /*                                                                        */
66 /*  RELEASE HISTORY                                                       */
67 /*                                                                        */
68 /*    DATE              NAME                      DESCRIPTION             */
69 /*                                                                        */
70 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
71 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
72 /*                                            used UX prefix to refer to  */
73 /*                                            TX symbols instead of using */
74 /*                                            them directly,              */
75 /*                                            resulting in version 6.1    */
76 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
77 /*                                            added standalone support,   */
78 /*                                            added interrupt OUT support,*/
79 /*                                            resulting in version 6.1.10 */
80 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
81 /*                                            resulting in version 6.1.11 */
82 /*  07-29-2022     Chaoqiong Xiao           Modified comment(s),          */
83 /*                                            added standalone receiver,  */
84 /*                                            fixed parameter/variable    */
85 /*                                            names conflict C++ keyword, */
86 /*                                            resulting in version 6.1.12 */
87 /*  10-31-2022     Chaoqiong Xiao           Modified comment(s),          */
88 /*                                            fixed compile warnings,     */
89 /*                                            resulting in version 6.2.0  */
90 /*  10-31-2023     Chaoqiong Xiao           Modified comment(s),          */
91 /*                                            added zero copy support,    */
92 /*                                            added a new mode to manage  */
93 /*                                            endpoint buffer in classes, */
94 /*                                            checked compile options,    */
95 /*                                            resulting in version 6.3.0  */
96 /*                                                                        */
97 /**************************************************************************/
_ux_device_class_hid_initialize(UX_SLAVE_CLASS_COMMAND * command)98 UINT  _ux_device_class_hid_initialize(UX_SLAVE_CLASS_COMMAND *command)
99 {
100 
101 UX_SLAVE_CLASS_HID                      *hid;
102 UX_SLAVE_CLASS_HID_PARAMETER            *hid_parameter;
103 UX_SLAVE_CLASS                          *class_ptr;
104 UINT                                    status = UX_SUCCESS;
105 ULONG                                   array_memory_size;
106 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_HID_ZERO_COPY)
107 UINT                                    i;
108 UCHAR                                   *buffer;
109 #endif
110 
111 
112     /* Compile option checks.  */
113     UX_ASSERT(UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH <= UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH);
114 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 0
115     UX_ASSERT(UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH <= UX_SLAVE_REQUEST_DATA_MAX_LENGTH);
116 #endif
117 
118 
119     /* Get the pointer to the application parameters for the hid class.  */
120     hid_parameter =  command -> ux_slave_class_command_parameter;
121 
122     /* Get the class container.  */
123     class_ptr =  command -> ux_slave_class_command_class_ptr;
124 
125     /* Create an instance of the device hid class.  */
126     hid =  _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_HID));
127 
128     /* Check for successful allocation.  */
129     if (hid == UX_NULL)
130         return(UX_MEMORY_INSUFFICIENT);
131 
132     /* Save the address of the HID instance inside the HID container.  */
133     class_ptr -> ux_slave_class_instance = (VOID *) hid;
134 
135 #if defined(UX_DEVICE_CLASS_HID_OWN_ENDPOINT_BUFFER)
136 
137     /* Allocate buffer(s) for endpoint(s).  */
138     UX_ASSERT(!UX_DEVICE_CLASS_HID_ENDPOINT_BUFFER_SIZE_CALC_OVERFLOW);
139     hid -> ux_device_class_hid_endpoint_buffer = _ux_utility_memory_allocate(
140                             UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY,
141                             UX_DEVICE_CLASS_HID_ENDPOINT_BUFFER_SIZE);
142     if (hid -> ux_device_class_hid_endpoint_buffer == UX_NULL)
143     {
144         _ux_utility_memory_free(hid);
145         return(UX_MEMORY_INSUFFICIENT);
146     }
147 #endif
148 
149 #if !defined(UX_DEVICE_STANDALONE)
150 
151     /* Allocate some memory for the thread stack. */
152     class_ptr -> ux_slave_class_thread_stack =
153             _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_DEVICE_CLASS_HID_THREAD_STACK_SIZE);
154 
155     /* Check for successful allocation.  */
156     if (class_ptr -> ux_slave_class_thread_stack == UX_NULL)
157         status = UX_MEMORY_INSUFFICIENT;
158 
159     /* This instance needs to be running in a different thread. So start
160        a new thread. We pass a pointer to the class to the new thread.  This thread
161        does not start until we have a instance of the class. */
162     if (status == UX_SUCCESS)
163         status =  _ux_device_thread_create(&class_ptr -> ux_slave_class_thread, "ux_slave_hid_thread",
164                     _ux_device_class_hid_interrupt_thread,
165                     (ULONG) (ALIGN_TYPE) class_ptr, (VOID *) class_ptr -> ux_slave_class_thread_stack,
166                     UX_DEVICE_CLASS_HID_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS,
167                     UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_DONT_START);
168 #else
169 
170 #if defined(UX_DEVICE_CLASS_HID_FLEXIBLE_EVENTS_QUEUE)
171 
172     /* Set event buffer.  */
173     hid -> ux_device_class_hid_event.ux_device_class_hid_event_buffer =
174                                     UX_DEVICE_CLASS_HID_INTERRUPTIN_BUFFER(hid);
175 #endif
176 
177     /* Set task function.  */
178     class_ptr -> ux_slave_class_task_function = _ux_device_class_hid_tasks_run;
179 #endif
180 
181     /* Check the creation of this thread.  */
182     if (status == UX_SUCCESS)
183     {
184 
185 #if !defined(UX_DEVICE_STANDALONE)
186         UX_THREAD_EXTENSION_PTR_SET(&(class_ptr -> ux_slave_class_thread), class_ptr)
187 #endif
188 
189 
190         /* Store all the application parameter information about the report.  */
191         hid -> ux_device_class_hid_report_address             = hid_parameter -> ux_device_class_hid_parameter_report_address;
192         hid -> ux_device_class_hid_report_length              = hid_parameter -> ux_device_class_hid_parameter_report_length;
193         hid -> ux_device_class_hid_report_id                  = hid_parameter -> ux_device_class_hid_parameter_report_id;
194 
195         /* Store the callback function.  */
196         hid -> ux_device_class_hid_callback                   = hid_parameter -> ux_device_class_hid_parameter_callback;
197         hid -> ux_device_class_hid_get_callback               = hid_parameter -> ux_device_class_hid_parameter_get_callback;
198 
199 #if defined(UX_DEVICE_CLASS_HID_FLEXIBLE_EVENTS_QUEUE)
200 
201         /* If event length is invalid, UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH is used.  */
202         if (UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter) == 0 ||
203             UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter) > UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH)
204             UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter) = UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH;
205 
206         /* If event queue size is invalid, UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE is used.  */
207         if (UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter) < 2 ||
208             UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter) > UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE)
209             UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter) = UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE;
210 
211         /* Save event size.  */
212         UX_DEVICE_CLASS_HID_EVENT_MAX_LENGTH(hid) = UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter);
213 #endif
214 
215         /* Create the event array.  */
216         UX_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(
217                     UX_DEVICE_CLASS_HID_EVENT_QUEUE_ITEM_SIZE(hid),
218                     UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter)));
219         array_memory_size = UX_DEVICE_CLASS_HID_EVENT_QUEUE_ITEM_SIZE(hid) * UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter);
220         hid -> ux_device_class_hid_event_array =  _ux_utility_memory_allocate(UX_NO_ALIGN,
221                                         UX_REGULAR_MEMORY, array_memory_size);
222 
223         /* Do we need event buffer?
224          * 1. Even zero copy, report copy is kept to avoid keep buffers in application.
225          * 2. Other cases, buffer must be allocated.
226          */
227         /* Allocate buffer if needed.  */
228         {
229 
230 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_HID_ZERO_COPY)
231 
232             /* Allocate cache safe event buffers.  */
233             buffer = _ux_utility_memory_allocate_mulv_safe(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY,
234                         UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter),
235                         UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter));
236 
237             /* Allocation error check.  */
238             if (buffer == UX_NULL)
239             {
240                 if (hid -> ux_device_class_hid_event_array != UX_NULL)
241                 {
242                     _ux_utility_memory_free(hid -> ux_device_class_hid_event_array);
243                     hid -> ux_device_class_hid_event_array = UX_NULL;
244                 }
245             }
246             else
247             {
248 
249                 /* Assign event buffers.  */
250                 for (i = 0; i < UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter); i ++)
251                 {
252                     hid -> ux_device_class_hid_event_array[i].ux_device_class_hid_event_buffer = buffer;
253                     buffer += UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter);
254                 }
255             }
256 #else
257 
258             /* Regular event place data following id,type and length.  */
259 #endif
260         }
261 
262         /* Check for successful allocation.  */
263         if (hid -> ux_device_class_hid_event_array != UX_NULL)
264         {
265 
266             /* Initialize the head and tail of the notification round robin buffers.
267                At first, the head and tail are pointing to the beginning of the array.  */
268             hid -> ux_device_class_hid_event_array_head =  hid -> ux_device_class_hid_event_array;
269             hid -> ux_device_class_hid_event_array_tail =  hid -> ux_device_class_hid_event_array;
270             hid -> ux_device_class_hid_event_array_end  =  (UX_DEVICE_CLASS_HID_EVENT*)((UCHAR*)hid -> ux_device_class_hid_event_array + array_memory_size);
271 
272             /* Store the start and stop signals if needed by the application.  */
273             hid -> ux_slave_class_hid_instance_activate = hid_parameter -> ux_slave_class_hid_instance_activate;
274             hid -> ux_slave_class_hid_instance_deactivate = hid_parameter -> ux_slave_class_hid_instance_deactivate;
275 
276             /* By default no event wait timeout.  */
277             hid -> ux_device_class_hid_event_wait_timeout = UX_WAIT_FOREVER;
278 
279 #if !defined(UX_DEVICE_STANDALONE)
280 
281             /* Create a event flag group for the hid class to synchronize with the event interrupt thread.  */
282             status =  _ux_utility_event_flags_create(&hid -> ux_device_class_hid_event_flags_group, "ux_device_class_hid_event_flag");
283 
284             /* Check status.  */
285             if (status != UX_SUCCESS)
286                 status = UX_EVENT_ERROR;
287             else
288 #endif
289             {
290 #if defined(UX_DEVICE_CLASS_HID_INTERRUPT_OUT_SUPPORT)
291 
292 #if !defined(UX_DEVICE_STANDALONE)
293 
294                 /* Create a mutex for reading reentry check.  */
295                 status = _ux_utility_mutex_create(&hid -> ux_device_class_hid_read_mutex,
296                                                   "ux_device_class_hid_read_mutex");
297                 if (status == UX_SUCCESS)
298                 {
299 #endif
300 
301                     /* If receiver is enabled by parameter, initialize it.  */
302                     if (hid_parameter -> ux_device_class_hid_parameter_receiver_initialize)
303                     {
304 
305                         /* Allocate buffer for receiver and receiver events.  */
306                         status = hid_parameter ->
307                                 ux_device_class_hid_parameter_receiver_initialize(hid,
308                                                 hid_parameter,
309                                                 &hid -> ux_device_class_hid_receiver);
310                     }
311 
312                     /* Done success, return.  */
313                     if (status == UX_SUCCESS)
314                         return(status);
315 
316 #if !defined(UX_DEVICE_STANDALONE)
317 
318                     /* There is error, delete mutex.  */
319                     _ux_device_mutex_delete(&hid -> ux_device_class_hid_read_mutex);
320                 }
321                 else
322                     status = UX_MUTEX_ERROR;
323 
324                 /* There is error, delete event flags.  */
325                 _ux_utility_event_flags_delete(&hid -> ux_device_class_hid_event_flags_group);
326 #endif
327 #else
328                 return(status);
329 #endif
330 
331             }
332 
333 #if !defined(UX_DEVICE_STANDALONE) || defined(UX_DEVICE_CLASS_HID_INTERRUPT_OUT_SUPPORT)
334 
335             /* There is still initialization activities after array creation,
336              * and some error occurs in this stage.  */
337             /* Free allocated event array memory.  */
338 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_HID_ZERO_COPY)
339             _ux_utility_memory_free(hid -> ux_device_class_hid_event_array -> ux_device_class_hid_event_buffer);
340 #endif
341             _ux_utility_memory_free(hid -> ux_device_class_hid_event_array);
342 #endif
343 
344         }
345         else
346             status =  UX_MEMORY_INSUFFICIENT;
347 
348 #if !defined(UX_DEVICE_STANDALONE)
349 
350         /* Delete thread.  */
351         _ux_device_thread_delete(&class_ptr -> ux_slave_class_thread);
352 #endif
353     }
354     else
355         status = (UX_THREAD_ERROR);
356 
357 #if !defined(UX_DEVICE_STANDALONE)
358 
359     /* Free stack. */
360     if (class_ptr -> ux_slave_class_thread_stack)
361         _ux_utility_memory_free(class_ptr -> ux_slave_class_thread_stack);
362 #endif
363 
364 #if defined(UX_DEVICE_CLASS_HID_OWN_ENDPOINT_BUFFER)
365     _ux_utility_memory_free(hid -> ux_device_class_hid_endpoint_buffer);
366 #endif
367 
368     /* Unmount instance. */
369     class_ptr -> ux_slave_class_instance =  UX_NULL;
370 
371     /* Free HID instance. */
372     _ux_utility_memory_free(hid);
373 
374     /* Return completion status.  */
375     return(status);
376 }
377 
378 
379 /**************************************************************************/
380 /*                                                                        */
381 /*  FUNCTION                                               RELEASE        */
382 /*                                                                        */
383 /*    _uxe_device_class_hid_initialize                    PORTABLE C      */
384 /*                                                           6.3.0        */
385 /*  AUTHOR                                                                */
386 /*                                                                        */
387 /*    Chaoqiong Xiao, Microsoft Corporation                               */
388 /*                                                                        */
389 /*  DESCRIPTION                                                           */
390 /*                                                                        */
391 /*    This function checks errors in HID initialize function call.        */
392 /*                                                                        */
393 /*  INPUT                                                                 */
394 /*                                                                        */
395 /*    command                               Pointer to hid command        */
396 /*                                                                        */
397 /*  OUTPUT                                                                */
398 /*                                                                        */
399 /*    None                                                                */
400 /*                                                                        */
401 /*  CALLS                                                                 */
402 /*                                                                        */
403 /*    _ux_device_class_hid_initialize       Initialize HID instance       */
404 /*                                                                        */
405 /*  CALLED BY                                                             */
406 /*                                                                        */
407 /*    Device Stack                                                        */
408 /*                                                                        */
409 /*  RELEASE HISTORY                                                       */
410 /*                                                                        */
411 /*    DATE              NAME                      DESCRIPTION             */
412 /*                                                                        */
413 /*  10-31-2023     Chaoqiong Xiao           Initial Version 6.3.0         */
414 /*                                                                        */
415 /**************************************************************************/
_uxe_device_class_hid_initialize(UX_SLAVE_CLASS_COMMAND * command)416 UINT  _uxe_device_class_hid_initialize(UX_SLAVE_CLASS_COMMAND *command)
417 {
418 
419 UX_SLAVE_CLASS_HID_PARAMETER            *hid_parameter;
420 
421     /* Get the pointer to the application parameters for the hid class.  */
422     hid_parameter =  command -> ux_slave_class_command_parameter;
423 
424     /* Check input parameters.  */
425     if ((hid_parameter -> ux_device_class_hid_parameter_report_address == UX_NULL) ||
426         (hid_parameter -> ux_device_class_hid_parameter_report_length == 0) ||
427         (hid_parameter -> ux_device_class_hid_parameter_report_length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH))
428     {
429         return(UX_INVALID_PARAMETER);
430     }
431 
432     /* Invoke initialize function.  */
433     return(_ux_device_class_hid_initialize(command));
434 }
435