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