1 /**
2  * @file lv_uefi_indev.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "../../lvgl.h"
11 #include "../../stdlib/lv_mem.h"
12 #include "../../misc/lv_types.h"
13 #include "../../misc/lv_text.h"
14 
15 #if LV_USE_UEFI
16 
17 #include "lv_uefi_indev.h"
18 #include "lv_uefi_private.h"
19 
20 /*********************
21  *      DEFINES
22  *********************/
23 
24 #define SIMPLE_TEXT_INPUT_INDEV_SIGNATURE 0x53495449
25 
26 /**********************
27  *      TYPEDEFS
28  **********************/
29 
30 typedef struct _lv_uefi_simple_text_input_key_cache_t {
31     uint32_t key;   /**< Key code*/
32     bool pressed;   /**< If true this is a pressed entry if false this is a release entry*/
33 } lv_uefi_simple_text_input_key_cache_t;
34 
35 typedef struct _lv_uefi_simple_text_input_handle_context_t {
36     EFI_HANDLE handle;
37     EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface;
38 } lv_uefi_simple_text_input_handle_context_t;
39 
40 typedef struct _lv_uefi_simple_text_input_context_t {
41     uint32_t signature; /**< Has to be checked to avoid access to a different indev*/
42     lv_ll_t handles;
43     lv_ll_t key_cache;
44 } lv_uefi_simple_text_input_context_t;
45 
46 /**********************
47  *  STATIC PROTOTYPES
48  **********************/
49 
50 static void _simple_text_input_event_cb(lv_event_t * e);
51 static void _simple_text_input_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
52 
53 static void _simple_text_input_handle_context_free(void * ptr);
54 static void _simple_text_input_context_free(lv_uefi_simple_text_input_context_t * indev_ctx);
55 
56 static bool _simple_text_input_interface_is_valid(const EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface);
57 
58 static void _simple_text_input_read(lv_uefi_simple_text_input_context_t * indev_ctx,
59                                     lv_uefi_simple_text_input_handle_context_t * handle_ctx);
60 
61 static uint32_t _utf8_from_unicode(UINT32 unicode);
62 static uint32_t _key_from_uefi_key(const EFI_KEY_DATA * key);
63 
64 /**********************
65  *  STATIC VARIABLES
66  **********************/
67 
68 static EFI_GUID _uefi_guid_simple_text_input = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
69 
70 /**********************
71  *      MACROS
72  **********************/
73 
74 /**********************
75  *   GLOBAL FUNCTIONS
76  **********************/
77 
78 /**
79  * @brief Create an indev object.
80  * @return The created LVGL indev object.
81 */
lv_uefi_simple_text_input_indev_create(void)82 lv_indev_t * lv_uefi_simple_text_input_indev_create(void)
83 {
84     lv_indev_t * indev = NULL;
85     lv_uefi_simple_text_input_context_t * indev_ctx = NULL;
86 
87     indev_ctx = lv_calloc(1, sizeof(lv_uefi_simple_text_input_context_t));
88     LV_ASSERT_MALLOC(indev_ctx);
89 
90     indev_ctx->signature = SIMPLE_TEXT_INPUT_INDEV_SIGNATURE;
91 
92     lv_ll_init(&indev_ctx->handles, sizeof(lv_uefi_simple_text_input_handle_context_t));
93     lv_ll_init(&indev_ctx->key_cache, sizeof(lv_uefi_simple_text_input_key_cache_t));
94 
95     indev = lv_indev_create();
96     lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
97     lv_indev_set_user_data(indev, indev_ctx);
98     lv_indev_add_event_cb(indev, _simple_text_input_event_cb, LV_EVENT_DELETE, indev);
99     lv_indev_set_read_cb(indev, _simple_text_input_read_cb);
100 
101     return indev;
102 }
103 
104 /**
105  * @brief Add an EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL interface to the indev.
106  * @param indev Indev that was created with lv_uefi_simple_text_input_indev_create.
107  * @param handle The handle on which an instance of the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol is installed.
108  * @return True if the interface was added.
109 */
lv_uefi_simple_text_input_indev_add_handle(lv_indev_t * indev,EFI_HANDLE handle)110 bool lv_uefi_simple_text_input_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle)
111 {
112     EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface = NULL;
113     lv_uefi_simple_text_input_handle_context_t * handle_ctx = NULL;
114 
115     lv_uefi_simple_text_input_context_t * indev_ctx = (lv_uefi_simple_text_input_context_t *)lv_indev_get_user_data(indev);
116     LV_ASSERT_NULL(indev_ctx);
117 
118     if(indev_ctx->signature != SIMPLE_TEXT_INPUT_INDEV_SIGNATURE) return false;
119 
120     interface = (EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *)lv_uefi_protocol_open(handle, &_uefi_guid_simple_text_input);
121     if(!_simple_text_input_interface_is_valid(interface)) {
122         lv_uefi_protocol_close(handle, &_uefi_guid_simple_text_input);
123         LV_LOG_WARN("[lv_uefi] The SIMPLE_TEXT_INPUT interface is not valid.");
124         return false;
125     }
126 
127     handle_ctx = (lv_uefi_simple_text_input_handle_context_t *) lv_ll_ins_head(&indev_ctx->handles);
128     LV_ASSERT_MALLOC(handle_ctx);
129 
130     handle_ctx->handle = handle;
131     handle_ctx->interface = interface;
132 
133     return true;
134 }
135 
136 /**
137  * @brief Add all available EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL interfaces to the indev.
138  * @param indev Indev that was created with lv_uefi_simple_text_input_indev_create.
139 */
lv_uefi_simple_text_input_indev_add_all(lv_indev_t * indev)140 void lv_uefi_simple_text_input_indev_add_all(lv_indev_t * indev)
141 {
142     EFI_STATUS status;
143     EFI_HANDLE * handles = NULL;
144     UINTN no_handles;
145     UINTN index;
146 
147     lv_uefi_simple_text_input_context_t * indev_ctx = (lv_uefi_simple_text_input_context_t *)lv_indev_get_user_data(indev);
148     LV_ASSERT_NULL(indev_ctx);
149 
150     if(indev_ctx->signature != SIMPLE_TEXT_INPUT_INDEV_SIGNATURE) return;
151 
152     status = gLvEfiBS->LocateHandleBuffer(ByProtocol, &_uefi_guid_simple_text_input, NULL, &no_handles, &handles);
153     if(status != EFI_SUCCESS) {
154         LV_LOG_ERROR("[lv_uefi] LocateHandleBuffer(SIMPLE_TEXT_INPUT_EX) failed with error code %llx.", status);
155         return;
156     }
157 
158     for(index = 0; index < no_handles; index++) {
159         lv_uefi_simple_text_input_indev_add_handle(indev, handles[index]);
160     }
161 
162     if(handles != NULL) gLvEfiBS->FreePool(handles);
163 }
164 
165 /**********************
166  *   STATIC FUNCTIONS
167  **********************/
168 
_simple_text_input_event_cb(lv_event_t * e)169 static void _simple_text_input_event_cb(lv_event_t * e)
170 {
171     lv_indev_t * indev;
172     lv_uefi_simple_text_input_context_t * indev_ctx;
173 
174     if(lv_event_get_code(e) != LV_EVENT_DELETE) return;
175 
176     indev = (lv_indev_t *)lv_event_get_user_data(e);
177     if(indev == NULL) return;
178 
179     indev_ctx = (lv_uefi_simple_text_input_context_t *)lv_indev_get_user_data(indev);
180     lv_indev_set_user_data(indev, NULL);
181 
182     if(indev_ctx != NULL) _simple_text_input_context_free(indev_ctx);
183 }
184 
_simple_text_input_read_cb(lv_indev_t * indev,lv_indev_data_t * data)185 static void _simple_text_input_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
186 {
187     lv_uefi_simple_text_input_handle_context_t * handle_ctx = NULL;
188     lv_uefi_simple_text_input_key_cache_t * key_cache = NULL;
189     void * node = NULL;
190 
191     lv_uefi_simple_text_input_context_t * indev_ctx = (lv_uefi_simple_text_input_context_t *)lv_indev_get_user_data(indev);
192     LV_ASSERT_NULL(indev_ctx);
193 
194     /* Empty the buffer before reading new values */
195     if(lv_ll_is_empty(&indev_ctx->key_cache)) {
196         // Read from all registered devices
197         for(node = lv_ll_get_head(&indev_ctx->handles); node != NULL; node = lv_ll_get_next(&indev_ctx->handles, node)) {
198             handle_ctx = (lv_uefi_simple_text_input_handle_context_t *) node;
199             _simple_text_input_read(indev_ctx, handle_ctx);
200         }
201     }
202 
203     /* Return the first value */
204     node = lv_ll_get_head(&indev_ctx->key_cache);
205     if(node != NULL) {
206         key_cache = (lv_uefi_simple_text_input_key_cache_t *)node;
207         data->state = key_cache->pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
208         data->key = key_cache->key;
209         lv_ll_remove(&indev_ctx->key_cache, node);
210         lv_free(key_cache);
211     }
212 
213     /* Continue reading if there are more values in the buffer */
214     data->continue_reading = !lv_ll_is_empty(&indev_ctx->key_cache);
215 }
216 
_simple_text_input_context_free(lv_uefi_simple_text_input_context_t * indev_ctx)217 static void _simple_text_input_context_free(lv_uefi_simple_text_input_context_t * indev_ctx)
218 {
219     if(indev_ctx == NULL) return;
220     lv_ll_clear_custom(&indev_ctx->handles, _simple_text_input_handle_context_free);
221     lv_ll_clear(&indev_ctx->key_cache);
222     lv_free(indev_ctx);
223 }
224 
_simple_text_input_interface_is_valid(const EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface)225 static bool _simple_text_input_interface_is_valid(const EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * interface)
226 {
227     if(interface == NULL) return FALSE;
228     return TRUE;
229 }
230 
_simple_text_input_handle_context_free(void * ptr)231 static void _simple_text_input_handle_context_free(void * ptr)
232 {
233     lv_uefi_simple_text_input_handle_context_t * handle_ctx = (lv_uefi_simple_text_input_handle_context_t *)ptr;
234 
235     if(handle_ctx == NULL) return;
236     if(handle_ctx->interface) lv_uefi_protocol_close(handle_ctx->handle, &_uefi_guid_simple_text_input);
237     lv_free(handle_ctx);
238 }
239 
_simple_text_input_read(lv_uefi_simple_text_input_context_t * indev_ctx,lv_uefi_simple_text_input_handle_context_t * handle_ctx)240 static void _simple_text_input_read(lv_uefi_simple_text_input_context_t * indev_ctx,
241                                     lv_uefi_simple_text_input_handle_context_t * handle_ctx)
242 {
243     EFI_STATUS status;
244     EFI_KEY_DATA state;
245     uint32_t key;
246     lv_uefi_simple_text_input_key_cache_t * cache = NULL;
247 
248     LV_ASSERT_NULL(indev_ctx);
249     LV_ASSERT_NULL(handle_ctx);
250 
251     status = handle_ctx->interface->ReadKeyStrokeEx(
252                      handle_ctx->interface,
253                      &state);
254     if(status == EFI_NOT_READY) return;
255     if(status != EFI_SUCCESS) {
256         LV_LOG_ERROR("[lv_uefi] ReadKeyStrokeEx failed.");
257         return;
258     }
259 
260     key = _key_from_uefi_key(&state);
261 
262     /* insert the press */
263     cache = (lv_uefi_simple_text_input_key_cache_t *) lv_ll_ins_tail(&indev_ctx->key_cache);
264     LV_ASSERT_MALLOC(cache);
265     cache->key = key;
266     cache->pressed = true;
267 
268     /* insert the release */
269     cache = (lv_uefi_simple_text_input_key_cache_t *) lv_ll_ins_tail(&indev_ctx->key_cache);
270     LV_ASSERT_MALLOC(cache);
271     cache->key = key;
272     cache->pressed = false;
273 }
274 
_utf8_from_unicode(UINT32 unicode)275 static uint32_t _utf8_from_unicode(UINT32 unicode)
276 {
277     uint8_t bytes[4] = {0, 0, 0, 0};
278 
279     /* unicode < 128 -> 1 byte */
280     if(unicode < 128) {
281         bytes[0] |= unicode;
282     }
283     /* unicode < 2048 -> 2 byte */
284     else if(unicode < 2048) {
285         bytes[0] = 0xC0;
286         bytes[0] |= unicode >> 6;
287         bytes[1] = 0x80;
288         bytes[1] |= (unicode & 0x003F);
289     }
290     /* unicode < 65536 -> 3 byte */
291     else if(unicode < 65536) {
292         bytes[0] = 0xE0;
293         bytes[0] |= unicode >> 12;
294         bytes[1] = 0x80;
295         bytes[1] |= ((unicode >> 6) & 0x003F);
296         bytes[2] = 0x80;
297         bytes[2] |= (unicode & 0x003F);
298     }
299 
300     return *((uint32_t *)bytes);
301 }
302 
_key_from_uefi_key(const EFI_KEY_DATA * key)303 static uint32_t _key_from_uefi_key(const EFI_KEY_DATA * key)
304 {
305     LV_ASSERT_NULL(key);
306 
307     switch(key->Key.ScanCode) {
308         case 0x01:
309             return LV_KEY_UP;
310         case 0x02:
311             return LV_KEY_DOWN;
312         case 0x04:
313             return LV_KEY_LEFT;
314         case 0x03:
315             return LV_KEY_RIGHT;
316         case 0x08:
317             return LV_KEY_DEL;
318         case 0x05:
319             return LV_KEY_HOME;
320         case 0x06:
321             return LV_KEY_END;
322         case 0x17:
323             return LV_KEY_ESC;
324         /* ignore all other scan codes */
325         default:
326             break;
327     }
328 
329     switch(key->Key.UnicodeChar) {
330         case 0x09:
331             return (key->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) &&
332                    (key->KeyState.KeyShiftState & (EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED)) ?
333                    LV_KEY_PREV :
334                    LV_KEY_NEXT;
335         case 0x08:
336             return LV_KEY_BACKSPACE;
337         case 0x0D:
338             return LV_KEY_ENTER;
339         case 0x18:
340             return LV_KEY_ESC;
341         default:
342             return _utf8_from_unicode(key->Key.UnicodeChar);
343     }
344 }
345 
346 #endif