Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | // SPDX-License-Identifier: GPL-2.0+ /* * EFI-keyboard input driver * * Uses EFI's Simple Text Input Protocol, polling keystrokes and providing them * to stdio */ #define LOG_CATEGORY LOGC_EFI #include <dm.h> #include <efi_loader.h> #include <efi.h> #include <keyboard.h> #include <log.h> #include <stdio_dev.h> #include <linux/delay.h> /* * struct efi_kbd_priv - private information for the keyboard * * @ex_con */ struct efi_kbd_priv { struct efi_simple_text_input_ex_protocol *ex_con; struct efi_simple_text_input_protocol *con_in; struct efi_input_key key; struct efi_key_data exkey; bool have_key; }; /** * efi_kbd_tstc() - Test for a character from EFI * * @dev: keyboard device * Return: 1 if a character is available, 0 otherwise. */ static int efi_kbd_tstc(struct udevice *dev) { struct efi_kbd_priv *priv = dev_get_priv(dev); efi_status_t status; /* If we already have a key from a previous check, report it's available */ if (priv->have_key) return 1; /* wait until we don't see EFI_NOT_READY */ if (priv->ex_con) { status = priv->ex_con->read_key_stroke_ex(priv->ex_con, &priv->exkey); } else { status = priv->con_in->read_key_stroke(priv->con_in, &priv->key); } if (!status) { priv->have_key = true; return 1; } return 0; } /** * efi_kbd_getc() - Get a character from EFI * * Waits until a key is available and returns the associated character * * @dev: stdio device pointer * Return: character code, or 0 if none */ static int efi_kbd_getc(struct udevice *dev) { struct efi_kbd_priv *priv = dev_get_priv(dev); if (!efi_kbd_tstc(dev)) return 0; priv->have_key = false; if (priv->ex_con) { struct efi_input_key *exkey = &priv->exkey.key; log_debug("got exkey %x scan %x\n", exkey->unicode_char, exkey->scan_code); return efi_decode_key(exkey); } else { struct efi_input_key *key = &priv->key; log_debug("got key %x\n", key->unicode_char); return efi_decode_key(key); } return 0; } /** * efi_kbd_start() - Start the driver * * Reset the keyboard ready for use * * Return: 0 on success (always) */ static int efi_kbd_start(struct udevice *dev) { struct efi_kbd_priv *priv = dev_get_priv(dev); log_debug("keyboard start\n"); /* reset keyboard to drop anything pressed during UEFI startup */ priv->con_in->reset(priv->con_in, true); if (priv->ex_con) priv->ex_con->reset(priv->ex_con, true); priv->have_key = false; return 0; } static int efi_kbd_probe(struct udevice *dev) { struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); struct efi_system_table *systab = efi_get_sys_table(); struct stdio_dev *sdev = &uc_priv->sdev; struct efi_kbd_priv *priv = dev_get_priv(dev); efi_status_t ret_efi; int ret; log_debug("keyboard probe '%s'\n", dev->name); priv->con_in = systab->con_in; /* Try to get the EFI Simple Text Input EX protocol from console handle */ if (systab->con_in_handle) { efi_guid_t ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; ret_efi = efi_get_boot()->open_protocol(systab->con_in_handle, &ex_guid, (void **)&priv->ex_con, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (ret_efi != EFI_SUCCESS) { log_debug("Extended input protocol not available\n"); priv->ex_con = NULL; } } strcpy(sdev->name, "efi-kbd"); ret = input_stdio_register(sdev); if (ret) { log_err("Failed to register\n"); return ret; } return 0; } static const struct keyboard_ops efi_kbd_ops = { .start = efi_kbd_start, .tstc = efi_kbd_tstc, .getc = efi_kbd_getc, }; static const struct udevice_id efi_kbd_ids[] = { { .compatible = "efi-keyboard" }, { } }; U_BOOT_DRIVER(efi_kbd) = { .name = "efi_kbd", .id = UCLASS_KEYBOARD, .of_match = efi_kbd_ids, .ops = &efi_kbd_ops, .priv_auto = sizeof(struct efi_kbd_priv), .probe = efi_kbd_probe, }; |