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