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 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _ux_host_class_hid_keyboard_callback PORTABLE C */
38 /* 6.1.10 */
39 /* AUTHOR */
40 /* */
41 /* Chaoqiong Xiao, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function is the callback mechanism for a report registration. */
46 /* */
47 /* INPUT */
48 /* */
49 /* callback Pointer to callback */
50 /* */
51 /* OUTPUT */
52 /* */
53 /* None */
54 /* */
55 /* CALLS */
56 /* */
57 /* _ux_utility_memory_copy Copy memory */
58 /* _ux_utility_memory_set Set memory */
59 /* */
60 /* CALLED BY */
61 /* */
62 /* HID Class when a report is generated */
63 /* */
64 /* RELEASE HISTORY */
65 /* */
66 /* DATE NAME DESCRIPTION */
67 /* */
68 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
69 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
70 /* verified memset and memcpy */
71 /* cases, */
72 /* resulting in version 6.1 */
73 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
74 /* added standalone support, */
75 /* resulting in version 6.1.10 */
76 /* */
77 /**************************************************************************/
_ux_host_class_hid_keyboard_callback(UX_HOST_CLASS_HID_REPORT_CALLBACK * callback)78 VOID _ux_host_class_hid_keyboard_callback(UX_HOST_CLASS_HID_REPORT_CALLBACK *callback)
79 {
80
81 /* This array contains the bit for each alternate key (modifier or lock key)
82 that we report to the application. For example, if you wanted to set the
83 bit for the CAPS_LOCK key, you would do:
84 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= alternate_key_bits[0];
85 Index 0 is used since it corresponds to the CAPS_LOCK bit in the array. Note
86 that _alternate_key_state is what we report to the application. */
87 const ULONG alternate_key_bits[] = {
88 UX_HID_KEYBOARD_STATE_CAPS_LOCK,
89 UX_HID_KEYBOARD_STATE_NUM_LOCK,
90 UX_HID_KEYBOARD_STATE_SCROLL_LOCK,
91 UX_HID_KEYBOARD_STATE_LEFT_CTRL,
92 UX_HID_KEYBOARD_STATE_LEFT_SHIFT,
93 UX_HID_KEYBOARD_STATE_LEFT_ALT,
94 UX_HID_KEYBOARD_STATE_LEFT_GUI,
95 UX_HID_KEYBOARD_STATE_RIGHT_CTRL,
96 UX_HID_KEYBOARD_STATE_RIGHT_SHIFT,
97 UX_HID_KEYBOARD_STATE_RIGHT_ALT,
98 UX_HID_KEYBOARD_STATE_RIGHT_GUI,
99 };
100
101 /* Define the indices for each alternate key in the alternate key bits array. */
102
103 #define ALTERNATE_KEY_BITS_IDX_CAPS_LOCK ( 0)
104 #define ALTERNATE_KEY_BITS_IDX_NUM_LOCK ( 1)
105 #define ALTERNATE_KEY_BITS_IDX_SCROLL_LOCK ( 2)
106
107 #define ALTERNATE_KEY_BITS_IDX_LEFT_CTRL ( 3)
108 #define ALTERNATE_KEY_BITS_IDX_LEFT_SHIFT ( 4)
109 #define ALTERNATE_KEY_BITS_IDX_LEFT_ALT ( 5)
110 #define ALTERNATE_KEY_BITS_IDX_LEFT_GUI ( 6)
111 #define ALTERNATE_KEY_BITS_IDX_RIGHT_CTRL ( 7)
112 #define ALTERNATE_KEY_BITS_IDX_RIGHT_SHIFT ( 8)
113 #define ALTERNATE_KEY_BITS_IDX_RIGHT_ALT ( 9)
114 #define ALTERNATE_KEY_BITS_IDX_RIGHT_GUI (10)
115
116 /* Define a macro to get the index of a modifier key in the alternate key bits array. */
117 #define GET_ALTERNATE_KEY_BITS_IDX(usage) ((usage) - UX_HID_MODIFIER_KEY_LEFT_CONTROL + ALTERNATE_KEY_BITS_IDX_LEFT_CTRL)
118
119 /* Define key states. */
120 #define KEY_STATE_REGULAR (11)
121 #define KEY_STATE_NO_KEY (12)
122
123 #define KEY_UP ( 0)
124 #define KEY_KEEP ( 1)
125 #define KEY_DOWN ( 2)
126 #define KEY_DEL ( 3)
127
128
129 UX_HOST_CLASS_HID_CLIENT *hid_client;
130 UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance;
131 UX_HOST_CLASS_HID_KEYBOARD_LAYOUT *keyboard_layout;
132 UCHAR *keypad_array;
133 ULONG *array_head;
134 ULONG *array_tail;
135 ULONG *array_end;
136 ULONG *array_start;
137 ULONG *report_buffer;
138 ULONG *report_buffer_end;
139 ULONG keyboard_char = 0;
140 ULONG shift_on;
141 ULONG capslock_on;
142 ULONG numlock_on;
143 ULONG key_usage;
144 ULONG key_value;
145
146 /* This variable either contains an index into the alternate key bit array,
147 or a value that describes the current key i.e. regular key or no key. */
148 UINT key_state;
149
150 #if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE)
151 UCHAR *previous_lock_key_states;
152 UCHAR *current_lock_key_states;
153 #else
154 UINT i, i_save, new_count;
155 UCHAR *state_usage;
156 UCHAR *state_value;
157 UCHAR *state_action;
158 #endif
159
160
161 /* Get the HID client instance that issued the callback. */
162 hid_client = callback -> ux_host_class_hid_report_callback_client;
163
164 /* Get the keyboard local instance. */
165 keyboard_instance = (UX_HOST_CLASS_HID_KEYBOARD *) hid_client -> ux_host_class_hid_client_local_instance;
166
167 /* Get the report buffer. */
168 report_buffer = (ULONG *)callback -> ux_host_class_hid_report_callback_buffer;
169
170 /* Get the end of report buffer. */
171 report_buffer_end = &report_buffer[callback -> ux_host_class_hid_report_callback_actual_length];
172
173 #if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE)
174
175 /* Get the previous states of the lock keys. */
176 previous_lock_key_states = &keyboard_instance -> ux_host_class_hid_keyboard_key_state[0];
177
178 /* Get the current states of the lock keys and immediately initialize them to zero;
179 if a lock key is not pressed in this report, it will remain zero (not pressed). */
180 current_lock_key_states = &keyboard_instance -> ux_host_class_hid_keyboard_key_state[3];
181 _ux_utility_memory_set(current_lock_key_states, 0, 3); /* Use case of memset is verified. */
182
183 /* Scan the report buffer and decode it. */
184 while(report_buffer < report_buffer_end)
185 {
186
187 /* Get usage and value from buffer. */
188 key_usage = *report_buffer ++;
189 key_value = *report_buffer ++;
190
191 /* Analyze the usage we have received. We eliminate the page from the usage. */
192 key_usage &= 0xFF;
193 #else
194
195 /* Initialize key states for report processing. */
196 state_usage = keyboard_instance -> ux_host_class_hid_keyboard_key_state;
197 state_value = state_usage + keyboard_instance -> ux_host_class_hid_keyboard_key_count * 2;
198 state_action = state_value + keyboard_instance -> ux_host_class_hid_keyboard_key_count * 2;
199
200 /* Reset state actions to DEL(not received). */
201 _ux_utility_memory_set(state_usage + keyboard_instance -> ux_host_class_hid_keyboard_key_count, 0, keyboard_instance -> ux_host_class_hid_keyboard_key_count); /* Use case of memset is verified. */
202 _ux_utility_memory_set(state_value + keyboard_instance -> ux_host_class_hid_keyboard_key_count, 0, keyboard_instance -> ux_host_class_hid_keyboard_key_count); /* Use case of memset is verified. */
203 _ux_utility_memory_set(state_action, KEY_DEL, keyboard_instance -> ux_host_class_hid_keyboard_key_count * 2); /* Use case of memset is verified. */
204
205 new_count = keyboard_instance -> ux_host_class_hid_keyboard_key_count;
206 while(report_buffer < report_buffer_end)
207 {
208
209 /* Get usage and value from buffer. */
210 key_usage = *report_buffer ++;
211 key_value = *report_buffer ++;
212
213 /* Analyze the usage we have received. We eliminate the page from the usage. */
214 key_usage &= 0xFF;
215 key_value &= 0xFF;
216
217 /* If there is no key or in phantom state (roll over), skip. */
218 if (key_usage <= UX_HID_KEYBOARD_PHANTOM_STATE)
219 continue;
220
221 /* Check if the key is previously reported. */
222 for (i = 0; i < keyboard_instance -> ux_host_class_hid_keyboard_key_count; i ++)
223 {
224
225 /* Check if it's modified. */
226 if (state_usage[i] == key_usage)
227 {
228
229 /* Replace action state. */
230 state_action[i] = (state_value[i] == key_value) ? KEY_KEEP : (key_value ? KEY_DOWN : KEY_UP);
231
232 /* Replace key value. */
233 state_value[i] = key_value;
234 break;
235 }
236 }
237
238 /* When there is new key, add to new key list. */
239 if (i == keyboard_instance -> ux_host_class_hid_keyboard_key_count)
240 {
241
242 /* Add key value. */
243 state_usage [new_count] = key_usage;
244 state_value [new_count] = key_value;
245
246 /* Add key action. */
247 state_action[new_count] = key_value ? KEY_DOWN : KEY_KEEP;
248
249 new_count ++;
250 }
251 } /* while(report_buffer < report_buffer_end) */
252
253 /* Process pending key states. */
254 i_save = 0;
255 for (i = 0; i < new_count; i ++)
256 {
257
258 /* Get state value from buffer. */
259 key_usage = state_usage[i];
260 key_value = state_value[i];
261 key_state = state_action[i];
262
263 /* If no key, just skip. */
264 if (key_usage == 0)
265 continue;
266
267 /* If key not reported, add up event if it's enabled. */
268 if (key_state == KEY_DEL)
269 {
270
271 /* Clear state, do not save. */
272 state_usage[i] = 0;
273 state_action[i] = KEY_UP;
274 }
275
276 /* Key reported, process it. */
277 else
278 {
279
280 /* We need to save key anyway. */
281 if (i_save < i)
282 {
283 state_usage[i_save] = key_usage;
284 state_value[i_save] = key_value;
285 }
286 i_save ++;
287 }
288
289 /* Skip keep keys. */
290 if (state_action[i] == KEY_KEEP)
291 continue;
292
293 /* Now handle key event. */
294 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state &= ~(UX_HID_KEYBOARD_STATE_FUNCTION | UX_HID_KEYBOARD_STATE_KEY_UP);
295 #endif /* UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE */
296
297 /* Determine what key this is. */
298 switch(key_usage)
299 {
300
301 /* This is the usage of a modifier key. Set or clear the appropriate
302 bits in the alternate key state bitmap. */
303 case UX_HID_MODIFIER_KEY_LEFT_SHIFT :
304 case UX_HID_MODIFIER_KEY_RIGHT_SHIFT :
305 case UX_HID_MODIFIER_KEY_LEFT_ALT :
306 case UX_HID_MODIFIER_KEY_RIGHT_ALT :
307 case UX_HID_MODIFIER_KEY_RIGHT_CONTROL :
308 case UX_HID_MODIFIER_KEY_LEFT_CONTROL :
309 case UX_HID_MODIFIER_KEY_RIGHT_GUI :
310 case UX_HID_MODIFIER_KEY_LEFT_GUI :
311
312 key_state = GET_ALTERNATE_KEY_BITS_IDX(key_usage);
313
314 /* We have received a modifier Key. Remember the state. */
315 if (key_value > 0)
316 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= alternate_key_bits[key_state];
317 else
318 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state &= ~alternate_key_bits[key_state];
319
320 break;
321
322 /* This is the usage of a LOCK key. Just save the its index in the alternate
323 key bit array. */
324
325 case UX_HID_LED_KEY_CAPS_LOCK :
326 key_state = ALTERNATE_KEY_BITS_IDX_CAPS_LOCK;
327 break;
328
329 case UX_HID_LED_KEY_NUM_LOCK :
330 key_state = ALTERNATE_KEY_BITS_IDX_NUM_LOCK;
331 break;
332
333 case UX_HID_LED_KEY_SCROLL_LOCK :
334 key_state = ALTERNATE_KEY_BITS_IDX_SCROLL_LOCK;
335 break;
336
337 #if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE)
338
339 /* Usage no key. */
340 case UX_HID_KEYBOARD_NO_KEY :
341 case UX_HID_KEYBOARD_PHANTOM_STATE :
342 key_state = KEY_STATE_NO_KEY;
343 break;
344 #endif
345
346 /* This is the usage of a regular key. Here, we just get the decoded
347 value; we will add it to the queue later. */
348 default :
349
350 /* By default the key will be saved. */
351 key_state = KEY_STATE_REGULAR;
352
353 /* Skip decode if decode is disabled. */
354 if (keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable == UX_TRUE)
355 {
356
357 /* Use raw data (scan code) as key code. */
358 keyboard_char = key_value;
359 break;
360 }
361
362 /* Get keyboard layout instance. */
363 keyboard_layout = keyboard_instance -> ux_host_class_hid_keyboard_layout;
364
365 /* Is this key outside the valid range? */
366 if (key_value > keyboard_layout -> ux_host_class_hid_keyboard_layout_keys_upper_range)
367 {
368
369 /* Set a flag to discard it. */
370 key_state = KEY_STATE_NO_KEY;
371 break;
372 }
373
374 /* We have received a regular key. Depending on the state of the shift or numlock status, the key should be mapped into
375 one of the translation tables. We verify if the key is within our mapping range. */
376
377 /* Get SHIFT, CAPS_LOCK and NUM_LOCK states. */
378 shift_on = (keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & UX_HID_KEYBOARD_STATE_SHIFT) ? UX_TRUE : UX_FALSE;
379 capslock_on = (keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & UX_HID_KEYBOARD_STATE_CAPS_LOCK) ? UX_TRUE : UX_FALSE;
380 numlock_on = (keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & UX_HID_KEYBOARD_STATE_NUM_LOCK) ? UX_TRUE : UX_FALSE;
381
382 /* Check if we have letters ('a' to 'z'). */
383 if (key_value >= keyboard_layout -> ux_host_class_hid_keyboard_layout_letters_lower_range &&
384 key_value <= keyboard_layout -> ux_host_class_hid_keyboard_layout_letters_upper_range)
385 {
386
387 /* We have letters, check the Shift and CapsLock state. */
388 if (shift_on != capslock_on)
389
390 /* Shift and CapsLock in different state: upper case. */
391 keyboard_char = keyboard_layout -> ux_host_class_hid_keyboard_layout_shift_array[key_value];
392 else
393
394 /* Lower case. */
395 keyboard_char = keyboard_layout -> ux_host_class_hid_keyboard_layout_regular_array[key_value];
396
397 break; /* default: */
398 }
399
400 /* Check if we have received a keypad key. They may be multiplexed. */
401 if (key_value >= keyboard_layout -> ux_host_class_hid_keyboard_layout_keypad_lower_range &&
402 key_value <= keyboard_layout -> ux_host_class_hid_keyboard_layout_keypad_upper_range)
403 {
404
405 /* We have a keypad key. Check the NumLock state. */
406 if (numlock_on)
407
408 /* Numlock is on. */
409 keypad_array = keyboard_layout -> ux_host_class_hid_keyboard_layout_numlock_on_array;
410
411 else
412
413 /* Numlock is off. */
414 keypad_array = keyboard_layout -> ux_host_class_hid_keyboard_layout_numlock_off_array;
415
416 /* Decode the keypad key. */
417 keyboard_char = keypad_array[key_value -
418 keyboard_layout -> ux_host_class_hid_keyboard_layout_keypad_lower_range];
419
420 break; /* default: */
421 }
422
423 /* Check the state of the shift. */
424 if (shift_on)
425
426 /* We get the key from the shifted array. */
427 keyboard_char = keyboard_layout -> ux_host_class_hid_keyboard_layout_shift_array[key_value];
428
429 else
430
431 /* We get the key from the regular array. */
432 keyboard_char = keyboard_layout -> ux_host_class_hid_keyboard_layout_regular_array[key_value];
433
434 break; /* default: */
435
436 } /* switch(key_usage) */
437
438 #if defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE)
439
440 if (state_action[i] == KEY_UP)
441
442 #if defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_KEY_DOWN_ONLY)
443
444 /* Skip save. */
445 continue;
446 #else
447
448 /* Save key up state. */
449 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= UX_HID_KEYBOARD_STATE_KEY_UP;
450 #endif
451 #endif
452
453 /* If there is no key, just try next. */
454 if (key_state == KEY_STATE_NO_KEY)
455 continue;
456
457 /* Is this a LOCK key (i.e. caps lock, scroll lock or num lock)? */
458 if (key_state <= ALTERNATE_KEY_BITS_IDX_SCROLL_LOCK)
459 {
460
461 /* Skip decode if decode is disabled. */
462 if (keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable == UX_TRUE)
463 {
464
465 /* Use raw data (scan code) as key code. */
466 keyboard_char = key_value;
467 }
468 else
469 {
470
471 #if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE)
472
473 /* Reflect the press in the current lock key state. */
474 current_lock_key_states[key_state] = (UCHAR)key_value;
475
476 /* Take action only if key state changes from up to down (pressed).
477 Remember that the nothing happens when lock keys are released. */
478 if (previous_lock_key_states[key_state] == 0)
479 #elif !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_KEY_DOWN_ONLY)
480
481 /* Take action only if key state changes from up to down (pressed). */
482 if (state_action[i] == KEY_DOWN)
483 #endif
484 {
485
486 /* Reflect the change in the keyboard state. The state should be inverted. */
487 if (keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & alternate_key_bits[key_state])
488 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state &= (ULONG)~alternate_key_bits[key_state];
489 else
490 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= alternate_key_bits[key_state];
491
492 #if defined(UX_HOST_STANDALONE)
493
494 /* Let background task to set LED status. */
495 keyboard_instance -> ux_host_class_hid_keyboard_out_state = UX_STATE_WAIT;
496 #else
497
498 /* Wake up the keyboard thread semaphore. */
499 _ux_host_semaphore_put(&keyboard_instance -> ux_host_class_hid_keyboard_semaphore);
500 #endif
501 }
502
503 #if defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) && defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_LOCK_KEYS)
504
505 /* Use usage and UX_HID_KEYBOARD_STATE_FUNCTION. */
506 keyboard_char = key_usage;
507 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= UX_HID_KEYBOARD_STATE_FUNCTION;
508
509 #else
510
511 /* Check next usage & value. */
512 continue;
513 #endif
514 }
515 }
516
517 /* If it's modifier, check next usage & value. */
518 else if (key_state < KEY_STATE_REGULAR)
519 {
520 #if defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) && defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_MODIFIER_KEYS)
521
522 /* Use usage and UX_HID_KEYBOARD_STATE_FUNCTION. */
523 keyboard_char = key_usage;
524 keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= UX_HID_KEYBOARD_STATE_FUNCTION;
525 #else
526
527 /* Check next usage & value. */
528 continue;
529 #endif
530 }
531
532 /* If we get here, then we have a regular key. Now it's time to save
533 raw/decoded key in the key queue. */
534
535 /* This key should now be inserted in the circular array for the application to retrieve it. */
536 array_start = keyboard_instance -> ux_host_class_hid_keyboard_usage_array;
537 array_end = array_start + UX_HOST_CLASS_HID_KEYBOARD_USAGE_ARRAY_LENGTH;
538 array_head = keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head;
539 array_tail = keyboard_instance -> ux_host_class_hid_keyboard_usage_array_tail;
540
541 /* We have a single usage/value. We have to store it into the array. If the array overflows,
542 there is no mechanism for flow control here so we ignore the usage/value until the
543 applications makes more room in the array. */
544
545 /* Is the head at the end of the array and need to loop back? */
546 if ((array_head + 2) >= array_end)
547 array_head = array_start;
548 else
549 array_head += 2;
550
551 /* Do we have enough space to store the new usage? */
552 if (array_head != array_tail)
553 {
554
555 /* Yes, we have some space. */
556 *keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head = keyboard_char;
557 *(keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head + 1) = keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state;
558
559 /* Now update the array head. */
560 keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head = array_head;
561 }
562 else
563
564 /* Error trap. */
565 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW);
566
567 } /* while(report_buffer < report_buffer_end) */
568
569 #if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE)
570
571 /* Copy the current lock key states to the previous states. Note that if
572 a lock key wasn't down in this report, its current state would have
573 remained zero (not pressed). */
574 _ux_utility_memory_copy(previous_lock_key_states, current_lock_key_states, 3); /* Use case of memcpy is verified. */
575 #else
576
577 /* Clear redundant data after last saved key. */
578 _ux_utility_memory_set(state_usage + i_save, 0, keyboard_instance -> ux_host_class_hid_keyboard_key_count - i_save); /* Use case of memset is verified. */
579 #endif
580
581 /* Return to caller. */
582 return;
583 }
584
585