sendkey.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /* sendkey.c - fake keystroke. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2009 Free Software Foundation, Inc.
  5. *
  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.
  10. *
  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.
  15. *
  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.
  19. */
  20. #include <grub/types.h>
  21. #include <grub/misc.h>
  22. #include <grub/mm.h>
  23. #include <grub/err.h>
  24. #include <grub/dl.h>
  25. #include <grub/extcmd.h>
  26. #include <grub/cpu/io.h>
  27. #include <grub/loader.h>
  28. #include <grub/i18n.h>
  29. GRUB_MOD_LICENSE ("GPLv2+");
  30. static char sendkey[0x20];
  31. /* Length of sendkey. */
  32. static int keylen = 0;
  33. static int noled = 0;
  34. static const struct grub_arg_option options[] =
  35. {
  36. {"num", 'n', 0, N_("set numlock mode"), "[on|off]", ARG_TYPE_STRING},
  37. {"caps", 'c', 0, N_("set capslock mode"), "[on|off]", ARG_TYPE_STRING},
  38. {"scroll", 's', 0, N_("set scrolllock mode"), "[on|off]", ARG_TYPE_STRING},
  39. {"insert", 0, 0, N_("set insert mode"), "[on|off]", ARG_TYPE_STRING},
  40. {"pause", 0, 0, N_("set pause mode"), "[on|off]", ARG_TYPE_STRING},
  41. {"left-shift", 0, 0, N_("press left shift"), "[on|off]", ARG_TYPE_STRING},
  42. {"right-shift", 0, 0, N_("press right shift"), "[on|off]", ARG_TYPE_STRING},
  43. {"sysrq", 0, 0, N_("press SysRq"), "[on|off]", ARG_TYPE_STRING},
  44. {"numkey", 0, 0, N_("press NumLock key"), "[on|off]", ARG_TYPE_STRING},
  45. {"capskey", 0, 0, N_("press CapsLock key"), "[on|off]", ARG_TYPE_STRING},
  46. {"scrollkey", 0, 0, N_("press ScrollLock key"), "[on|off]", ARG_TYPE_STRING},
  47. {"insertkey", 0, 0, N_("press Insert key"), "[on|off]", ARG_TYPE_STRING},
  48. {"left-alt", 0, 0, N_("press left alt"), "[on|off]", ARG_TYPE_STRING},
  49. {"right-alt", 0, 0, N_("press right alt"), "[on|off]", ARG_TYPE_STRING},
  50. {"left-ctrl", 0, 0, N_("press left ctrl"), "[on|off]", ARG_TYPE_STRING},
  51. {"right-ctrl", 0, 0, N_("press right ctrl"), "[on|off]", ARG_TYPE_STRING},
  52. {"no-led", 0, 0, N_("don't update LED state"), 0, 0},
  53. {0, 0, 0, 0, 0, 0}
  54. };
  55. static int simple_flag_offsets[]
  56. = {5, 6, 4, 7, 11, 1, 0, 10, 13, 14, 12, 15, 9, 3, 8, 2};
  57. static grub_uint32_t andmask = 0xffffffff, ormask = 0;
  58. struct
  59. keysym
  60. {
  61. const char *unshifted_name; /* the name in unshifted state */
  62. const char *shifted_name; /* the name in shifted state */
  63. unsigned char unshifted_ascii; /* the ascii code in unshifted state */
  64. unsigned char shifted_ascii; /* the ascii code in shifted state */
  65. unsigned char keycode; /* keyboard scancode */
  66. };
  67. /* The table for key symbols. If the "shifted" member of an entry is
  68. NULL, the entry does not have shifted state. Copied from GRUB Legacy setkey fuction */
  69. static struct keysym keysym_table[] =
  70. {
  71. {"escape", 0, 0x1b, 0, 0x01},
  72. {"1", "exclam", '1', '!', 0x02},
  73. {"2", "at", '2', '@', 0x03},
  74. {"3", "numbersign", '3', '#', 0x04},
  75. {"4", "dollar", '4', '$', 0x05},
  76. {"5", "percent", '5', '%', 0x06},
  77. {"6", "caret", '6', '^', 0x07},
  78. {"7", "ampersand", '7', '&', 0x08},
  79. {"8", "asterisk", '8', '*', 0x09},
  80. {"9", "parenleft", '9', '(', 0x0a},
  81. {"0", "parenright", '0', ')', 0x0b},
  82. {"minus", "underscore", '-', '_', 0x0c},
  83. {"equal", "plus", '=', '+', 0x0d},
  84. {"backspace", 0, '\b', 0, 0x0e},
  85. {"tab", 0, '\t', 0, 0x0f},
  86. {"q", "Q", 'q', 'Q', 0x10},
  87. {"w", "W", 'w', 'W', 0x11},
  88. {"e", "E", 'e', 'E', 0x12},
  89. {"r", "R", 'r', 'R', 0x13},
  90. {"t", "T", 't', 'T', 0x14},
  91. {"y", "Y", 'y', 'Y', 0x15},
  92. {"u", "U", 'u', 'U', 0x16},
  93. {"i", "I", 'i', 'I', 0x17},
  94. {"o", "O", 'o', 'O', 0x18},
  95. {"p", "P", 'p', 'P', 0x19},
  96. {"bracketleft", "braceleft", '[', '{', 0x1a},
  97. {"bracketright", "braceright", ']', '}', 0x1b},
  98. {"enter", 0, '\r', 0, 0x1c},
  99. {"control", 0, 0, 0, 0x1d},
  100. {"a", "A", 'a', 'A', 0x1e},
  101. {"s", "S", 's', 'S', 0x1f},
  102. {"d", "D", 'd', 'D', 0x20},
  103. {"f", "F", 'f', 'F', 0x21},
  104. {"g", "G", 'g', 'G', 0x22},
  105. {"h", "H", 'h', 'H', 0x23},
  106. {"j", "J", 'j', 'J', 0x24},
  107. {"k", "K", 'k', 'K', 0x25},
  108. {"l", "L", 'l', 'L', 0x26},
  109. {"semicolon", "colon", ';', ':', 0x27},
  110. {"quote", "doublequote", '\'', '"', 0x28},
  111. {"backquote", "tilde", '`', '~', 0x29},
  112. {"shift", 0, 0, 0, 0x2a},
  113. {"backslash", "bar", '\\', '|', 0x2b},
  114. {"z", "Z", 'z', 'Z', 0x2c},
  115. {"x", "X", 'x', 'X', 0x2d},
  116. {"c", "C", 'c', 'C', 0x2e},
  117. {"v", "V", 'v', 'V', 0x2f},
  118. {"b", "B", 'b', 'B', 0x30},
  119. {"n", "N", 'n', 'N', 0x31},
  120. {"m", "M", 'm', 'M', 0x32},
  121. {"comma", "less", ',', '<', 0x33},
  122. {"period", "greater", '.', '>', 0x34},
  123. {"slash", "question", '/', '?', 0x35},
  124. {"rshift", 0, 0, 0, 0x36},
  125. {"numasterisk", 0, '*', 0, 0x37},
  126. {"alt", 0, 0, 0, 0x38},
  127. {"space", 0, ' ', 0, 0x39},
  128. {"capslock", 0, 0, 0, 0x3a},
  129. {"F1", 0, 0, 0, 0x3b},
  130. {"F2", 0, 0, 0, 0x3c},
  131. {"F3", 0, 0, 0, 0x3d},
  132. {"F4", 0, 0, 0, 0x3e},
  133. {"F5", 0, 0, 0, 0x3f},
  134. {"F6", 0, 0, 0, 0x40},
  135. {"F7", 0, 0, 0, 0x41},
  136. {"F8", 0, 0, 0, 0x42},
  137. {"F9", 0, 0, 0, 0x43},
  138. {"F10", 0, 0, 0, 0x44},
  139. {"num7", "numhome", '7', 0, 0x47},
  140. {"num8", "numup", '8', 0, 0x48},
  141. {"num9", "numpgup", '9', 0, 0x49},
  142. {"numminus", 0, '-', 0, 0x4a},
  143. {"num4", "numleft", '4', 0, 0x4b},
  144. {"num5", "numcenter", '5', 0, 0x4c},
  145. {"num6", "numright", '6', 0, 0x4d},
  146. {"numplus", 0, '-', 0, 0x4e},
  147. {"num1", "numend", '1', 0, 0x4f},
  148. {"num2", "numdown", '2', 0, 0x50},
  149. {"num3", "numpgdown", '3', 0, 0x51},
  150. {"num0", "numinsert", '0', 0, 0x52},
  151. {"numperiod", "numdelete", 0, 0x7f, 0x53},
  152. {"F11", 0, 0, 0, 0x57},
  153. {"F12", 0, 0, 0, 0x58},
  154. {"numenter", 0, '\r', 0, 0xe0},
  155. {"numslash", 0, '/', 0, 0xe0},
  156. {"delete", 0, 0x7f, 0, 0xe0},
  157. {"insert", 0, 0xe0, 0, 0x52},
  158. {"home", 0, 0xe0, 0, 0x47},
  159. {"end", 0, 0xe0, 0, 0x4f},
  160. {"pgdown", 0, 0xe0, 0, 0x51},
  161. {"pgup", 0, 0xe0, 0, 0x49},
  162. {"down", 0, 0xe0, 0, 0x50},
  163. {"up", 0, 0xe0, 0, 0x48},
  164. {"left", 0, 0xe0, 0, 0x4b},
  165. {"right", 0, 0xe0, 0, 0x4d}
  166. };
  167. /* Set a simple flag in flags variable
  168. OUTOFFSET - offset of flag in FLAGS,
  169. OP - action id
  170. */
  171. static void
  172. grub_sendkey_set_simple_flag (int outoffset, int op)
  173. {
  174. if (op == 2)
  175. {
  176. andmask |= (1 << outoffset);
  177. ormask &= ~(1 << outoffset);
  178. }
  179. else
  180. {
  181. andmask &= (~(1 << outoffset));
  182. if (op == 1)
  183. ormask |= (1 << outoffset);
  184. else
  185. ormask &= ~(1 << outoffset);
  186. }
  187. }
  188. static int
  189. grub_sendkey_parse_op (struct grub_arg_list state)
  190. {
  191. if (! state.set)
  192. return 2;
  193. if (grub_strcmp (state.arg, "off") == 0 || grub_strcmp (state.arg, "0") == 0
  194. || grub_strcmp (state.arg, "unpress") == 0)
  195. return 0;
  196. if (grub_strcmp (state.arg, "on") == 0 || grub_strcmp (state.arg, "1") == 0
  197. || grub_strcmp (state.arg, "press") == 0)
  198. return 1;
  199. return 2;
  200. }
  201. static grub_uint32_t oldflags;
  202. static grub_err_t
  203. grub_sendkey_postboot (void)
  204. {
  205. /* For convention: pointer to flags. */
  206. grub_uint32_t *flags = grub_absolute_pointer (0x417);
  207. *flags = oldflags;
  208. *((volatile char *) grub_absolute_pointer (0x41a)) = 0x1e;
  209. *((volatile char *) grub_absolute_pointer (0x41c)) = 0x1e;
  210. return GRUB_ERR_NONE;
  211. }
  212. /* Set keyboard buffer to our sendkey */
  213. static grub_err_t
  214. grub_sendkey_preboot (int noret __attribute__ ((unused)))
  215. {
  216. /* For convention: pointer to flags. */
  217. grub_uint32_t *flags = grub_absolute_pointer (0x417);
  218. oldflags = *flags;
  219. /* Set the sendkey. */
  220. *((volatile char *) grub_absolute_pointer (0x41a)) = 0x1e;
  221. *((volatile char *) grub_absolute_pointer (0x41c)) = keylen + 0x1e;
  222. grub_memcpy ((char *) 0x41e, sendkey, 0x20);
  223. /* Transform "any ctrl" to "right ctrl" flag. */
  224. if (*flags & (1 << 8))
  225. *flags &= ~(1 << 2);
  226. /* Transform "any alt" to "right alt" flag. */
  227. if (*flags & (1 << 9))
  228. *flags &= ~(1 << 3);
  229. *flags = (*flags & andmask) | ormask;
  230. /* Transform "right ctrl" to "any ctrl" flag. */
  231. if (*flags & (1 << 8))
  232. *flags |= (1 << 2);
  233. /* Transform "right alt" to "any alt" flag. */
  234. if (*flags & (1 << 9))
  235. *flags |= (1 << 3);
  236. /* Write new LED state */
  237. if (!noled)
  238. {
  239. int value = 0;
  240. int failed;
  241. /* Try 5 times */
  242. for (failed = 0; failed < 5; failed++)
  243. {
  244. value = 0;
  245. /* Send command change LEDs */
  246. grub_outb (0xed, 0x60);
  247. /* Wait */
  248. do
  249. value = grub_inb (0x60);
  250. while ((value != 0xfa) && (value != 0xfe));
  251. if (value == 0xfa)
  252. {
  253. /* Set new LEDs*/
  254. grub_outb ((*flags >> 4) & 7, 0x60);
  255. break;
  256. }
  257. }
  258. }
  259. return GRUB_ERR_NONE;
  260. }
  261. /* Helper for grub_cmd_sendkey. */
  262. static int
  263. find_key_code (char *key)
  264. {
  265. unsigned i;
  266. for (i = 0; i < ARRAY_SIZE(keysym_table); i++)
  267. {
  268. if (keysym_table[i].unshifted_name
  269. && grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
  270. return keysym_table[i].keycode;
  271. else if (keysym_table[i].shifted_name
  272. && grub_strcmp (key, keysym_table[i].shifted_name) == 0)
  273. return keysym_table[i].keycode;
  274. }
  275. return 0;
  276. }
  277. /* Helper for grub_cmd_sendkey. */
  278. static int
  279. find_ascii_code (char *key)
  280. {
  281. unsigned i;
  282. for (i = 0; i < ARRAY_SIZE(keysym_table); i++)
  283. {
  284. if (keysym_table[i].unshifted_name
  285. && grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
  286. return keysym_table[i].unshifted_ascii;
  287. else if (keysym_table[i].shifted_name
  288. && grub_strcmp (key, keysym_table[i].shifted_name) == 0)
  289. return keysym_table[i].shifted_ascii;
  290. }
  291. return 0;
  292. }
  293. static grub_err_t
  294. grub_cmd_sendkey (grub_extcmd_context_t ctxt, int argc, char **args)
  295. {
  296. struct grub_arg_list *state = ctxt->state;
  297. andmask = 0xffffffff;
  298. ormask = 0;
  299. {
  300. int i;
  301. keylen = 0;
  302. for (i = 0; i < argc && keylen < 0x20; i++)
  303. {
  304. int key_code;
  305. key_code = find_key_code (args[i]);
  306. if (key_code)
  307. {
  308. sendkey[keylen++] = find_ascii_code (args[i]);
  309. sendkey[keylen++] = key_code;
  310. }
  311. }
  312. }
  313. {
  314. unsigned i;
  315. for (i = 0; i < ARRAY_SIZE(simple_flag_offsets); i++)
  316. grub_sendkey_set_simple_flag (simple_flag_offsets[i],
  317. grub_sendkey_parse_op(state[i]));
  318. }
  319. /* Set noled. */
  320. noled = (state[ARRAY_SIZE(simple_flag_offsets)].set);
  321. return GRUB_ERR_NONE;
  322. }
  323. static grub_extcmd_t cmd;
  324. static struct grub_preboot *preboot_hook;
  325. GRUB_MOD_INIT (sendkey)
  326. {
  327. cmd = grub_register_extcmd ("sendkey", grub_cmd_sendkey, 0,
  328. N_("[KEYSTROKE1] [KEYSTROKE2] ..."),
  329. /* TRANSLATORS: It can emulate multiple
  330. keypresses. */
  331. N_("Emulate a keystroke sequence"), options);
  332. preboot_hook
  333. = grub_loader_register_preboot_hook (grub_sendkey_preboot,
  334. grub_sendkey_postboot,
  335. GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
  336. }
  337. GRUB_MOD_FINI (sendkey)
  338. {
  339. grub_unregister_extcmd (cmd);
  340. grub_loader_unregister_preboot_hook (preboot_hook);
  341. }