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 Keyboard Client */
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_class_hid_keyboard.h"
30 #include "ux_host_stack.h"
31
32 /* Define USB HID keyboard mapping tables. */
33
34 #ifndef UX_HOST_CLASS_HID_KEYBOARD_REGULAR_ARRAY_DEFAULT
35 #define UX_HOST_CLASS_HID_KEYBOARD_REGULAR_ARRAY_DEFAULT UX_HID_KEYBOARD_REGULAR_ARRAY_US
36 #endif
37 #ifndef UX_HOST_CLASS_HID_KEYBOARD_SHIFT_ARRAY_DEFAULT
38 #define UX_HOST_CLASS_HID_KEYBOARD_SHIFT_ARRAY_DEFAULT UX_HID_KEYBOARD_SHIFT_ARRAY_US
39 #endif
40 #ifndef UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_ON_ARRAY_DEFAULT
41 #define UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_ON_ARRAY_DEFAULT UX_HID_KEYBOARD_NUMLOCK_ON_ARRAY
42 #endif
43 #ifndef UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_OFF_ARRAY_DEFAULT
44 #define UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_OFF_ARRAY_DEFAULT UX_HID_KEYBOARD_NUMLOCK_OFF_ARRAY
45 #endif
46 #ifndef UX_HOST_CLASS_HID_KEYBOARD_KEYS_UPPER_RANGE_DEFAULT
47 #define UX_HOST_CLASS_HID_KEYBOARD_KEYS_UPPER_RANGE_DEFAULT UX_HID_KEYBOARD_KEYS_UPPER_RANGE
48 #endif
49 #ifndef UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_A_DEFAULT
50 #define UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_A_DEFAULT UX_HID_KEYBOARD_KEY_LETTER_A
51 #endif
52 #ifndef UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_Z_DEFAULT
53 #define UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_Z_DEFAULT UX_HID_KEYBOARD_KEY_LETTER_Z
54 #endif
55 #ifndef UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE_DEFAULT
56 #define UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE_DEFAULT UX_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE
57 #endif
58 #ifndef UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE_DEFAULT
59 #define UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE_DEFAULT UX_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE
60 #endif
61
62 UCHAR ux_host_class_hid_keyboard_regular_array[] =
63 {
64 UX_HOST_CLASS_HID_KEYBOARD_REGULAR_ARRAY_DEFAULT
65 };
66
67 UCHAR ux_host_class_hid_keyboard_shift_array[] =
68 {
69 UX_HOST_CLASS_HID_KEYBOARD_SHIFT_ARRAY_DEFAULT
70 };
71
72 UCHAR ux_host_class_hid_keyboard_numlock_on_array[] =
73 {
74 UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_ON_ARRAY_DEFAULT
75 };
76
77 UCHAR ux_host_class_hid_keyboard_numlock_off_array[] =
78 {
79 UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_OFF_ARRAY_DEFAULT
80 };
81
82 UX_HOST_CLASS_HID_KEYBOARD_LAYOUT ux_host_class_hid_keyboard_layout =
83 {
84 ux_host_class_hid_keyboard_regular_array,
85 ux_host_class_hid_keyboard_shift_array,
86 ux_host_class_hid_keyboard_numlock_on_array,
87 ux_host_class_hid_keyboard_numlock_off_array,
88 UX_HOST_CLASS_HID_KEYBOARD_KEYS_UPPER_RANGE_DEFAULT,
89 UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_A_DEFAULT,
90 UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_Z_DEFAULT,
91 UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE_DEFAULT,
92 UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE_DEFAULT,
93 };
94
95 /**************************************************************************/
96 /* */
97 /* FUNCTION RELEASE */
98 /* */
99 /* _ux_host_class_hid_keyboard_activate PORTABLE C */
100 /* 6.1.11 */
101 /* AUTHOR */
102 /* */
103 /* Chaoqiong Xiao, Microsoft Corporation */
104 /* */
105 /* DESCRIPTION */
106 /* */
107 /* This function performs the enumeration of a HID Keyboard Client. */
108 /* */
109 /* INPUT */
110 /* */
111 /* command Pointer to command */
112 /* */
113 /* OUTPUT */
114 /* */
115 /* Completion Status */
116 /* */
117 /* CALLS */
118 /* */
119 /* _ux_host_class_hid_periodic_report_start */
120 /* Start periodic report */
121 /* _ux_host_class_hid_report_callback_register */
122 /* Register callback */
123 /* _ux_host_class_hid_report_id_get Get the report ID */
124 /* _ux_host_class_hid_idle_set Set the idle rate */
125 /* _ux_host_class_hid_report_set Do SET_REPORT */
126 /* _ux_utility_memory_allocate Allocate memory block */
127 /* _ux_utility_memory_free Free memory block */
128 /* _ux_host_semaphore_create Create semaphore */
129 /* _ux_host_semaphore_delete Delete semaphore */
130 /* _ux_utility_thread_create Create thread */
131 /* _ux_utility_thread_delete Delete thread */
132 /* */
133 /* CALLED BY */
134 /* */
135 /* HID Class */
136 /* */
137 /* RELEASE HISTORY */
138 /* */
139 /* DATE NAME DESCRIPTION */
140 /* */
141 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
142 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
143 /* used UX prefix to refer to */
144 /* TX symbols instead of using */
145 /* them directly, */
146 /* resulting in version 6.1 */
147 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
148 /* added standalone support, */
149 /* resulting in version 6.1.10 */
150 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
151 /* fixed clients management, */
152 /* resulting in version 6.1.11 */
153 /* */
154 /**************************************************************************/
_ux_host_class_hid_keyboard_activate(UX_HOST_CLASS_HID_CLIENT_COMMAND * command)155 UINT _ux_host_class_hid_keyboard_activate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command)
156 {
157
158 UX_HOST_CLASS_HID_REPORT_CALLBACK call_back;
159 #if !defined(UX_HOST_STANDALONE)
160 UX_HOST_CLASS_HID_CLIENT_REPORT client_report;
161 #endif
162 UX_HOST_CLASS_HID_REPORT_GET_ID report_id;
163 UX_HOST_CLASS_HID *hid;
164 UX_HOST_CLASS_HID_CLIENT *hid_client;
165 UX_HOST_CLASS_HID_CLIENT_KEYBOARD *client_keyboard;
166 UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance;
167 ULONG event_process_memory_size;
168 UINT status = UX_SUCCESS;
169 #ifdef UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE
170 UX_HOST_CLASS_HID_FIELD *field;
171 #endif
172
173
174 /* Get the instance to the HID class. */
175 hid = command -> ux_host_class_hid_client_command_instance;
176
177 /* Get some memory for both the HID class instance and copy of this client
178 and for the callback. */
179 client_keyboard = (UX_HOST_CLASS_HID_CLIENT_KEYBOARD *)
180 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY,
181 sizeof(UX_HOST_CLASS_HID_CLIENT_KEYBOARD));
182 if (client_keyboard == UX_NULL)
183 return(UX_MEMORY_INSUFFICIENT);
184
185 /* Use allocated memory.
186 * - create a client copy.
187 * - get keyboard instance.
188 */
189 keyboard_instance = &client_keyboard -> ux_host_class_hid_client_keyboard_keyboard;
190 hid_client = &client_keyboard -> ux_host_class_hid_client_keyboard_client;
191 _ux_utility_memory_copy(hid_client, hid -> ux_host_class_hid_client, sizeof(UX_HOST_CLASS_HID_CLIENT)); /* Use case of memcpy is verified. */
192
193 /* Attach the remote control instance to the client instance. */
194 hid_client -> ux_host_class_hid_client_local_instance = (VOID *) keyboard_instance;
195
196 /* Save the HID instance in the client instance. */
197 keyboard_instance -> ux_host_class_hid_keyboard_hid = hid;
198
199 #if defined(UX_HOST_STANDALONE)
200
201 /* Set client task function. */
202 hid_client -> ux_host_class_hid_client_function = _ux_host_class_hid_keyboard_tasks_run;
203
204 /* The instance is mounting now. */
205 keyboard_instance -> ux_host_class_hid_keyboard_state = UX_HOST_CLASS_INSTANCE_MOUNTING;
206 #else
207
208 /* The instance is live now. */
209 keyboard_instance -> ux_host_class_hid_keyboard_state = UX_HOST_CLASS_INSTANCE_LIVE;
210 #endif
211
212 /* Allocate the round-robin buffer that the remote control instance will use
213 * to store the usages as they come in.
214 * Size calculation overflow is checked near where _USAGE_ARRAY_LENGTH is defined.
215 */
216 keyboard_instance -> ux_host_class_hid_keyboard_usage_array = (ULONG *)
217 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_HOST_CLASS_HID_KEYBOARD_USAGE_ARRAY_LENGTH*4);
218 if (keyboard_instance -> ux_host_class_hid_keyboard_usage_array == UX_NULL)
219 status = (UX_MEMORY_INSUFFICIENT);
220
221 /* If we are OK, go on. */
222 if (status == UX_SUCCESS)
223 {
224
225 /* Initialize the head and tail of this array. */
226 keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head = keyboard_instance -> ux_host_class_hid_keyboard_usage_array;
227 keyboard_instance -> ux_host_class_hid_keyboard_usage_array_tail = keyboard_instance -> ux_host_class_hid_keyboard_usage_array;
228
229 /* Get the report ID for the keyboard. The keyboard is a INPUT report.
230 This should be 0 but in case. */
231 report_id.ux_host_class_hid_report_get_report = UX_NULL;
232 report_id.ux_host_class_hid_report_get_type = UX_HOST_CLASS_HID_REPORT_TYPE_INPUT;
233 status = _ux_host_class_hid_report_id_get(hid, &report_id);
234 }
235
236 /* If we are OK, go on. */
237 if (status == UX_SUCCESS)
238 {
239
240 /* Save the keyboard report ID. */
241 keyboard_instance -> ux_host_class_hid_keyboard_id = (USHORT)(report_id.ux_host_class_hid_report_get_id);
242
243 #ifdef UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE
244
245 /* Summarize number of usages to allocate state buffer for keyboard report.
246 We reserve 3 bits for lock status, 8 bits for modifier status.
247 We get number of keys from report of 8-bit array.
248 Key usages bytes then saved if it's down.
249 - 3 Locks - 8 Modifiers - N regular keys ...
250 */
251 keyboard_instance -> ux_host_class_hid_keyboard_key_count = 3 + 8;
252 field = report_id.ux_host_class_hid_report_get_report -> ux_host_class_hid_report_field;
253 while(field)
254 {
255 if (field -> ux_host_class_hid_field_report_size == 8)
256 keyboard_instance -> ux_host_class_hid_keyboard_key_count += field -> ux_host_class_hid_field_report_count;
257 field = field -> ux_host_class_hid_field_next_field;
258 }
259
260 /* Process memory includes:
261 - states for last [usage, value] and new [usage, value], (2 * 2) * max number of keys to log
262 - actions for last and new buffers, 2 * max number of keys to log
263 */
264 UX_UTILITY_MULC_SAFE(keyboard_instance -> ux_host_class_hid_keyboard_key_count, 6, event_process_memory_size, status);
265
266 /* Calculation overflow check. */
267 if (status == UX_SUCCESS)
268 #else
269
270 /* We reserve 3 bytes for lock keys, 3 for processing. */
271 keyboard_instance -> ux_host_class_hid_keyboard_key_count = 3;
272
273 /* key count 3, multiply 2 is int safe. */
274 event_process_memory_size = keyboard_instance -> ux_host_class_hid_keyboard_key_count * 2;
275 #endif
276
277 {
278
279 /* Allocate memory for usages states. */
280 keyboard_instance -> ux_host_class_hid_keyboard_key_state = (UCHAR *)
281 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, event_process_memory_size);
282 if (keyboard_instance -> ux_host_class_hid_keyboard_key_state == UX_NULL)
283 status = UX_MEMORY_INSUFFICIENT;
284 }
285 }
286
287 /* If we are OK, go on. */
288 if (status == UX_SUCCESS)
289 {
290
291 /* Initialize the report callback. */
292 call_back.ux_host_class_hid_report_callback_id = keyboard_instance -> ux_host_class_hid_keyboard_id;
293 call_back.ux_host_class_hid_report_callback_function = _ux_host_class_hid_keyboard_callback;
294 call_back.ux_host_class_hid_report_callback_buffer = UX_NULL;
295 call_back.ux_host_class_hid_report_callback_flags = UX_HOST_CLASS_HID_REPORT_DECOMPRESSED;
296 call_back.ux_host_class_hid_report_callback_length = 0;
297
298 /* Register the report call back when data comes it on this report. */
299 status = _ux_host_class_hid_report_callback_register(hid, &call_back);
300 }
301
302 #if !defined(UX_HOST_STANDALONE)
303
304 /* If we are OK, go on. */
305 if (status == UX_SUCCESS)
306 {
307
308 /* We need a semaphore now. This will be used to synchronize the HID report with the keyboard thread. */
309 status = _ux_host_semaphore_create(&keyboard_instance -> ux_host_class_hid_keyboard_semaphore, "ux_host_class_hid_keyboard_semaphore", 0);
310 if(status != UX_SUCCESS)
311 status = (UX_SEMAPHORE_ERROR);
312 }
313
314 /* If we are OK, go on. */
315 if (status == UX_SUCCESS)
316 {
317
318 /* The HID Keyboard needs a Thread to process LED changes. This process is asynchronous
319 to the callback because it involves using the commands on the Control Pipe which cannot
320 be done during callbacks. First allocate some stack memory. */
321 keyboard_instance -> ux_host_class_hid_keyboard_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE);
322
323 /* Check if stack memory was allocated. */
324 if (keyboard_instance -> ux_host_class_hid_keyboard_thread_stack == UX_NULL)
325 status = (UX_MEMORY_INSUFFICIENT);
326 }
327
328 /* If we are OK, go on. */
329 if (status == UX_SUCCESS)
330
331 /* Then create the actual thread. */
332 status = _ux_utility_thread_create(&keyboard_instance -> ux_host_class_hid_keyboard_thread, "ux_host_stack_keyboard_thread",_ux_host_class_hid_keyboard_thread,
333 (ULONG) (ALIGN_TYPE) keyboard_instance, keyboard_instance -> ux_host_class_hid_keyboard_thread_stack,
334 UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_KEYBOARD,
335 UX_THREAD_PRIORITY_KEYBOARD, UX_NO_TIME_SLICE, UX_AUTO_START);
336
337 #endif
338
339 /* If we are OK, go on. */
340 if (status == UX_SUCCESS)
341 {
342
343 #if !defined(UX_HOST_STANDALONE)
344 UX_THREAD_EXTENSION_PTR_SET(&(keyboard_instance -> ux_host_class_hid_keyboard_thread), keyboard_instance)
345 #endif
346
347 /* Default state of keyboard is with NumLock on. */
348 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= UX_HID_KEYBOARD_STATE_NUM_LOCK;
349
350 /* We need to build the field for the LEDs. */
351 keyboard_instance -> ux_host_class_hid_keyboard_led_mask = keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state &
352 UX_HID_KEYBOARD_STATE_MASK_LOCK;
353
354 #if defined(UX_HOST_STANDALONE)
355
356 /* Initialize the keyboard layout and use it to decode keys. */
357 keyboard_instance -> ux_host_class_hid_keyboard_layout = &ux_host_class_hid_keyboard_layout;
358 keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable = UX_FALSE;
359
360 /* Remaining things will be done in ACTIVATE_WAIT.
361 - SET_REPORT(LEDs)
362 - SET_IDLE(KEYs, 0)
363 - _periodic_report_start() */
364 keyboard_instance -> ux_host_class_hid_keyboard_enum_state = UX_STATE_WAIT;
365
366 /* It's fine, replace client with our copy. */
367 hid -> ux_host_class_hid_client = hid_client;
368 return(status);
369 #else
370
371 /* We need to find the OUTPUT report for the keyboard LEDs. */
372 report_id.ux_host_class_hid_report_get_report = UX_NULL;
373 report_id.ux_host_class_hid_report_get_type = UX_HOST_CLASS_HID_REPORT_TYPE_OUTPUT;
374 status = _ux_host_class_hid_report_id_get(hid, &report_id);
375
376 /* The report ID should exist. If there is an error, we do not proceed. */
377 if (status == UX_SUCCESS)
378 {
379
380 /* Memorize the report pointer. */
381 client_report.ux_host_class_hid_client_report = report_id.ux_host_class_hid_report_get_report;
382
383 /* The report set is raw since the LEDs mask is already in the right format. */
384 client_report.ux_host_class_hid_client_report_flags = UX_HOST_CLASS_HID_REPORT_RAW;
385
386 /* The length of this report is 1 byte. */
387 client_report.ux_host_class_hid_client_report_length = 1;
388
389 /* The output report buffer is the LED mask field. */
390 client_report.ux_host_class_hid_client_report_buffer = &keyboard_instance -> ux_host_class_hid_keyboard_led_mask;
391
392 /* The HID class will perform the SET_REPORT command. Do not check for error here, this is
393 handled by the function itself. */
394 status = _ux_host_class_hid_report_set(hid, &client_report);
395 }
396
397 /* If we are OK, go on. */
398 if (status == UX_SUCCESS)
399 {
400
401 /* Set the idle rate of the keyboard to 0. This way a report is generated only when there is an activity. */
402 status = _ux_host_class_hid_idle_set(hid, 0, keyboard_instance -> ux_host_class_hid_keyboard_id);
403 }
404
405 /* If we are OK, go on. */
406 if (status == UX_SUCCESS)
407 {
408
409 /* Initialize the keyboard layout and use it to decode keys. */
410 keyboard_instance -> ux_host_class_hid_keyboard_layout = &ux_host_class_hid_keyboard_layout;
411 keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable = UX_FALSE;
412
413 /* Start the periodic report. */
414 status = _ux_host_class_hid_periodic_report_start(hid);
415 }
416
417 /* If we are OK, go on. */
418 if (status == UX_SUCCESS)
419 {
420
421 /* It's fine, replace client copy. */
422 hid -> ux_host_class_hid_client = hid_client;
423
424 /* If all is fine and the device is mounted, we may need to inform the application
425 if a function has been programmed in the system structure. */
426 if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
427 {
428
429 /* Call system change function. */
430 _ux_system_host -> ux_system_host_change_function(UX_HID_CLIENT_INSERTION, hid -> ux_host_class_hid_class, (VOID *) hid_client);
431 }
432
433 /* If trace is enabled, insert this event into the trace buffer. */
434 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_KEYBOARD_ACTIVATE, hid, keyboard_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
435
436 /* Return completion status. */
437 return(status);
438 }
439
440 /* There is error, delete thread. */
441 _ux_utility_thread_delete(&keyboard_instance -> ux_host_class_hid_keyboard_thread);
442 #endif
443 }
444
445 /* We are here if there is error. */
446
447 /* Free usage state. */
448 if (keyboard_instance -> ux_host_class_hid_keyboard_key_state)
449 _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_key_state);
450
451 #if !defined(UX_HOST_STANDALONE)
452
453 /* Free stack. */
454 if (keyboard_instance -> ux_host_class_hid_keyboard_thread_stack)
455 _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_thread_stack);
456
457 /* Delete semaphore. */
458 if (keyboard_instance -> ux_host_class_hid_keyboard_semaphore.tx_semaphore_id != UX_EMPTY)
459 _ux_host_semaphore_delete(&keyboard_instance -> ux_host_class_hid_keyboard_semaphore);
460
461 #endif
462
463 /* Free usage array. */
464 if (keyboard_instance -> ux_host_class_hid_keyboard_usage_array)
465 _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_usage_array);
466
467 /* Free instance. */
468 _ux_utility_memory_free(keyboard_instance);
469
470 /* Return completion status. */
471 return(status);
472 }
473