Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / commands / i386 / pc / sendkey.c
blob17f648d78cbb57ff305f4852a76f56b811265bcf
1 /* sendkey.c - fake keystroke. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2009 Free Software Foundation, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <grub/types.h>
22 #include <grub/misc.h>
23 #include <grub/mm.h>
24 #include <grub/err.h>
25 #include <grub/dl.h>
26 #include <grub/extcmd.h>
27 #include <grub/cpu/io.h>
28 #include <grub/loader.h>
29 #include <grub/i18n.h>
31 GRUB_MOD_LICENSE ("GPLv2+");
33 static char sendkey[0x20];
34 /* Length of sendkey. */
35 static int keylen = 0;
36 static int noled = 0;
37 static const struct grub_arg_option options[] =
39 {"num", 'n', 0, N_("set numlock mode"), "[on|off]", ARG_TYPE_STRING},
40 {"caps", 'c', 0, N_("set capslock mode"), "[on|off]", ARG_TYPE_STRING},
41 {"scroll", 's', 0, N_("set scrolllock mode"), "[on|off]", ARG_TYPE_STRING},
42 {"insert", 0, 0, N_("set insert mode"), "[on|off]", ARG_TYPE_STRING},
43 {"pause", 0, 0, N_("set pause mode"), "[on|off]", ARG_TYPE_STRING},
44 {"left-shift", 0, 0, N_("press left shift"), "[on|off]", ARG_TYPE_STRING},
45 {"right-shift", 0, 0, N_("press right shift"), "[on|off]", ARG_TYPE_STRING},
46 {"sysrq", 0, 0, N_("press SysRq"), "[on|off]", ARG_TYPE_STRING},
47 {"numkey", 0, 0, N_("press NumLock key"), "[on|off]", ARG_TYPE_STRING},
48 {"capskey", 0, 0, N_("press CapsLock key"), "[on|off]", ARG_TYPE_STRING},
49 {"scrollkey", 0, 0, N_("press ScrollLock key"), "[on|off]", ARG_TYPE_STRING},
50 {"insertkey", 0, 0, N_("press Insert key"), "[on|off]", ARG_TYPE_STRING},
51 {"left-alt", 0, 0, N_("press left alt"), "[on|off]", ARG_TYPE_STRING},
52 {"right-alt", 0, 0, N_("press right alt"), "[on|off]", ARG_TYPE_STRING},
53 {"left-ctrl", 0, 0, N_("press left ctrl"), "[on|off]", ARG_TYPE_STRING},
54 {"right-ctrl", 0, 0, N_("press right ctrl"), "[on|off]", ARG_TYPE_STRING},
55 {"no-led", 0, 0, N_("don't update LED state"), 0, 0},
56 {0, 0, 0, 0, 0, 0}
58 static int simple_flag_offsets[]
59 = {5, 6, 4, 7, 11, 1, 0, 10, 13, 14, 12, 15, 9, 3, 8, 2};
61 static grub_uint32_t andmask = 0xffffffff, ormask = 0;
63 struct
64 keysym
66 const char *unshifted_name; /* the name in unshifted state */
67 const char *shifted_name; /* the name in shifted state */
68 unsigned char unshifted_ascii; /* the ascii code in unshifted state */
69 unsigned char shifted_ascii; /* the ascii code in shifted state */
70 unsigned char keycode; /* keyboard scancode */
73 /* The table for key symbols. If the "shifted" member of an entry is
74 NULL, the entry does not have shifted state. Copied from GRUB Legacy setkey fuction */
75 static struct keysym keysym_table[] =
77 {"escape", 0, 0x1b, 0, 0x01},
78 {"1", "exclam", '1', '!', 0x02},
79 {"2", "at", '2', '@', 0x03},
80 {"3", "numbersign", '3', '#', 0x04},
81 {"4", "dollar", '4', '$', 0x05},
82 {"5", "percent", '5', '%', 0x06},
83 {"6", "caret", '6', '^', 0x07},
84 {"7", "ampersand", '7', '&', 0x08},
85 {"8", "asterisk", '8', '*', 0x09},
86 {"9", "parenleft", '9', '(', 0x0a},
87 {"0", "parenright", '0', ')', 0x0b},
88 {"minus", "underscore", '-', '_', 0x0c},
89 {"equal", "plus", '=', '+', 0x0d},
90 {"backspace", 0, '\b', 0, 0x0e},
91 {"tab", 0, '\t', 0, 0x0f},
92 {"q", "Q", 'q', 'Q', 0x10},
93 {"w", "W", 'w', 'W', 0x11},
94 {"e", "E", 'e', 'E', 0x12},
95 {"r", "R", 'r', 'R', 0x13},
96 {"t", "T", 't', 'T', 0x14},
97 {"y", "Y", 'y', 'Y', 0x15},
98 {"u", "U", 'u', 'U', 0x16},
99 {"i", "I", 'i', 'I', 0x17},
100 {"o", "O", 'o', 'O', 0x18},
101 {"p", "P", 'p', 'P', 0x19},
102 {"bracketleft", "braceleft", '[', '{', 0x1a},
103 {"bracketright", "braceright", ']', '}', 0x1b},
104 {"enter", 0, '\r', 0, 0x1c},
105 {"control", 0, 0, 0, 0x1d},
106 {"a", "A", 'a', 'A', 0x1e},
107 {"s", "S", 's', 'S', 0x1f},
108 {"d", "D", 'd', 'D', 0x20},
109 {"f", "F", 'f', 'F', 0x21},
110 {"g", "G", 'g', 'G', 0x22},
111 {"h", "H", 'h', 'H', 0x23},
112 {"j", "J", 'j', 'J', 0x24},
113 {"k", "K", 'k', 'K', 0x25},
114 {"l", "L", 'l', 'L', 0x26},
115 {"semicolon", "colon", ';', ':', 0x27},
116 {"quote", "doublequote", '\'', '"', 0x28},
117 {"backquote", "tilde", '`', '~', 0x29},
118 {"shift", 0, 0, 0, 0x2a},
119 {"backslash", "bar", '\\', '|', 0x2b},
120 {"z", "Z", 'z', 'Z', 0x2c},
121 {"x", "X", 'x', 'X', 0x2d},
122 {"c", "C", 'c', 'C', 0x2e},
123 {"v", "V", 'v', 'V', 0x2f},
124 {"b", "B", 'b', 'B', 0x30},
125 {"n", "N", 'n', 'N', 0x31},
126 {"m", "M", 'm', 'M', 0x32},
127 {"comma", "less", ',', '<', 0x33},
128 {"period", "greater", '.', '>', 0x34},
129 {"slash", "question", '/', '?', 0x35},
130 {"rshift", 0, 0, 0, 0x36},
131 {"numasterisk", 0, '*', 0, 0x37},
132 {"alt", 0, 0, 0, 0x38},
133 {"space", 0, ' ', 0, 0x39},
134 {"capslock", 0, 0, 0, 0x3a},
135 {"F1", 0, 0, 0, 0x3b},
136 {"F2", 0, 0, 0, 0x3c},
137 {"F3", 0, 0, 0, 0x3d},
138 {"F4", 0, 0, 0, 0x3e},
139 {"F5", 0, 0, 0, 0x3f},
140 {"F6", 0, 0, 0, 0x40},
141 {"F7", 0, 0, 0, 0x41},
142 {"F8", 0, 0, 0, 0x42},
143 {"F9", 0, 0, 0, 0x43},
144 {"F10", 0, 0, 0, 0x44},
145 {"num7", "numhome", '7', 0, 0x47},
146 {"num8", "numup", '8', 0, 0x48},
147 {"num9", "numpgup", '9', 0, 0x49},
148 {"numminus", 0, '-', 0, 0x4a},
149 {"num4", "numleft", '4', 0, 0x4b},
150 {"num5", "numcenter", '5', 0, 0x4c},
151 {"num6", "numright", '6', 0, 0x4d},
152 {"numplus", 0, '-', 0, 0x4e},
153 {"num1", "numend", '1', 0, 0x4f},
154 {"num2", "numdown", '2', 0, 0x50},
155 {"num3", "numpgdown", '3', 0, 0x51},
156 {"num0", "numinsert", '0', 0, 0x52},
157 {"numperiod", "numdelete", 0, 0x7f, 0x53},
158 {"F11", 0, 0, 0, 0x57},
159 {"F12", 0, 0, 0, 0x58},
160 {"numenter", 0, '\r', 0, 0xe0},
161 {"numslash", 0, '/', 0, 0xe0},
162 {"delete", 0, 0x7f, 0, 0xe0},
163 {"insert", 0, 0xe0, 0, 0x52},
164 {"home", 0, 0xe0, 0, 0x47},
165 {"end", 0, 0xe0, 0, 0x4f},
166 {"pgdown", 0, 0xe0, 0, 0x51},
167 {"pgup", 0, 0xe0, 0, 0x49},
168 {"down", 0, 0xe0, 0, 0x50},
169 {"up", 0, 0xe0, 0, 0x48},
170 {"left", 0, 0xe0, 0, 0x4b},
171 {"right", 0, 0xe0, 0, 0x4d}
174 /* Set a simple flag in flags variable
175 OUTOFFSET - offset of flag in FLAGS,
176 OP - action id
178 static void
179 grub_sendkey_set_simple_flag (int outoffset, int op)
181 if (op == 2)
183 andmask |= (1 << outoffset);
184 ormask &= ~(1 << outoffset);
186 else
188 andmask &= (~(1 << outoffset));
189 if (op == 1)
190 ormask |= (1 << outoffset);
191 else
192 ormask &= ~(1 << outoffset);
196 static int
197 grub_sendkey_parse_op (struct grub_arg_list state)
199 if (! state.set)
200 return 2;
202 if (grub_strcmp (state.arg, "off") == 0 || grub_strcmp (state.arg, "0") == 0
203 || grub_strcmp (state.arg, "unpress") == 0)
204 return 0;
206 if (grub_strcmp (state.arg, "on") == 0 || grub_strcmp (state.arg, "1") == 0
207 || grub_strcmp (state.arg, "press") == 0)
208 return 1;
210 return 2;
213 static grub_uint32_t oldflags;
215 static grub_err_t
216 grub_sendkey_postboot (void)
218 /* For convention: pointer to flags. */
219 grub_uint32_t *flags = (grub_uint32_t *) 0x417;
221 *flags = oldflags;
223 *((char *) 0x41a) = 0x1e;
224 *((char *) 0x41c) = 0x1e;
226 return GRUB_ERR_NONE;
229 /* Set keyboard buffer to our sendkey */
230 static grub_err_t
231 grub_sendkey_preboot (int noret __attribute__ ((unused)))
233 /* For convention: pointer to flags. */
234 grub_uint32_t *flags = (grub_uint32_t *) 0x417;
236 oldflags = *flags;
238 /* Set the sendkey. */
239 *((char *) 0x41a) = 0x1e;
240 *((char *) 0x41c) = keylen + 0x1e;
241 grub_memcpy ((char *) 0x41e, sendkey, 0x20);
243 /* Transform "any ctrl" to "right ctrl" flag. */
244 if (*flags & (1 << 8))
245 *flags &= ~(1 << 2);
247 /* Transform "any alt" to "right alt" flag. */
248 if (*flags & (1 << 9))
249 *flags &= ~(1 << 3);
251 *flags = (*flags & andmask) | ormask;
253 /* Transform "right ctrl" to "any ctrl" flag. */
254 if (*flags & (1 << 8))
255 *flags |= (1 << 2);
257 /* Transform "right alt" to "any alt" flag. */
258 if (*flags & (1 << 9))
259 *flags |= (1 << 3);
261 /* Write new LED state */
262 if (!noled)
264 int value = 0;
265 int failed;
266 /* Try 5 times */
267 for (failed = 0; failed < 5; failed++)
269 value = 0;
270 /* Send command change LEDs */
271 grub_outb (0xed, 0x60);
273 /* Wait */
275 value = grub_inb (0x60);
276 while ((value != 0xfa) && (value != 0xfe));
278 if (value == 0xfa)
280 /* Set new LEDs*/
281 grub_outb ((*flags >> 4) & 7, 0x60);
282 break;
286 return GRUB_ERR_NONE;
289 static grub_err_t
290 grub_cmd_sendkey (grub_extcmd_context_t ctxt, int argc, char **args)
292 struct grub_arg_list *state = ctxt->state;
294 auto int find_key_code (char *key);
295 auto int find_ascii_code (char *key);
297 int find_key_code (char *key)
299 unsigned i;
301 for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
303 if (keysym_table[i].unshifted_name
304 && grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
305 return keysym_table[i].keycode;
306 else if (keysym_table[i].shifted_name
307 && grub_strcmp (key, keysym_table[i].shifted_name) == 0)
308 return keysym_table[i].keycode;
311 return 0;
314 int find_ascii_code (char *key)
316 unsigned i;
318 for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
320 if (keysym_table[i].unshifted_name
321 && grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
322 return keysym_table[i].unshifted_ascii;
323 else if (keysym_table[i].shifted_name
324 && grub_strcmp (key, keysym_table[i].shifted_name) == 0)
325 return keysym_table[i].shifted_ascii;
328 return 0;
331 andmask = 0xffffffff;
332 ormask = 0;
335 int i;
337 keylen = 0;
339 for (i = 0; i < argc && keylen < 0x20; i++)
341 int key_code;
343 key_code = find_key_code (args[i]);
344 if (key_code)
346 sendkey[keylen++] = find_ascii_code (args[i]);
347 sendkey[keylen++] = key_code;
353 unsigned i;
354 for (i = 0; i < sizeof (simple_flag_offsets)
355 / sizeof (simple_flag_offsets[0]); i++)
356 grub_sendkey_set_simple_flag (simple_flag_offsets[i],
357 grub_sendkey_parse_op(state[i]));
360 /* Set noled. */
361 noled = (state[sizeof (simple_flag_offsets)
362 / sizeof (simple_flag_offsets[0])].set);
364 return GRUB_ERR_NONE;
367 static grub_extcmd_t cmd;
368 static struct grub_preboot *preboot_hook;
370 GRUB_MOD_INIT (sendkey)
372 cmd = grub_register_extcmd ("sendkey", grub_cmd_sendkey, 0,
373 N_("[KEYSTROKE1] [KEYSTROKE2] ..."),
374 /* TRANSLATORS: It can emulate multiple
375 keypresses. */
376 N_("Emulate a keystroke sequence"), options);
378 preboot_hook
379 = grub_loader_register_preboot_hook (grub_sendkey_preboot,
380 grub_sendkey_postboot,
381 GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
384 GRUB_MOD_FINI (sendkey)
386 grub_unregister_extcmd (cmd);
387 grub_loader_unregister_preboot_hook (preboot_hook);