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