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