]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Implement sendkey support.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 19 Aug 2010 19:03:14 +0000 (21:03 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 19 Aug 2010 19:03:14 +0000 (21:03 +0200)
* commands/i386/pc/sendkey.c: New file.
* conf/i386-pc.rmk (pkglib_MODULES): Add sendkey.mod.
(sendkey_mod_SOURCES): New variable.
(sendkey_mod_CFLAGS): Likewise.
(sendkey_mod_LDFLAGS): Likewise.

ChangeLog
commands/i386/pc/sendkey.c [new file with mode: 0644]
conf/i386-pc.rmk

index bf1a0cfbf20c52017d2d75595580e16eb46a3c2a..a20e76892151c26010bee15fefeada8211788150 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2010-08-19  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Implement sendkey support.
+
+       * commands/i386/pc/sendkey.c: New file.
+       * conf/i386-pc.rmk (pkglib_MODULES): Add sendkey.mod.
+       (sendkey_mod_SOURCES): New variable.
+       (sendkey_mod_CFLAGS): Likewise.
+       (sendkey_mod_LDFLAGS): Likewise.
+
 2010-08-18  Colin Watson  <cjwatson@ubuntu.com>
 
        * configure.ac: Move AM_INIT_AUTOMAKE after AC_CANONICAL_TARGET to
diff --git a/commands/i386/pc/sendkey.c b/commands/i386/pc/sendkey.c
new file mode 100644 (file)
index 0000000..aa7ffdb
--- /dev/null
@@ -0,0 +1,381 @@
+/* sendkey.c - fake keystroke. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009  Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/cpu/io.h>
+#include <grub/loader.h>
+
+static char sendkey[0x20];
+/* Length of sendkey.  */
+static int keylen = 0;
+static int noled = 0;
+static const struct grub_arg_option options[] =
+  {
+    {"num", 'n', 0, "set numlock mode", "[keep|on|off]", ARG_TYPE_STRING},
+    {"caps", 'c', 0, "set capslock mode", "[keep|on|off]", ARG_TYPE_STRING},
+    {"scroll", 's', 0, "set scrolllock mode", "[keep|on|off]", ARG_TYPE_STRING},
+    {"insert", 0, 0, "set insert mode", "[keep|on|off]", ARG_TYPE_STRING},
+    {"wait", 0, 0, "set wait mode", "[keep|on|off]", ARG_TYPE_STRING},
+    {"left-shift", 0, 0, "press left shift", "[keep|on|off]", ARG_TYPE_STRING},
+    {"right-shift", 0, 0, "press right shift", "[keep|on|off]", ARG_TYPE_STRING},
+    {"sysreq", 0, 0, "press sysreq", "[keep|on|off]", ARG_TYPE_STRING},
+    {"numkey", 0, 0, "press NumLock key", "[keep|on|off]", ARG_TYPE_STRING},
+    {"capskey", 0, 0, "press CapsLock key", "[keep|on|off]", ARG_TYPE_STRING},
+    {"scrollkey", 0, 0, "press ScrollLock key", "[keep|on|off]", ARG_TYPE_STRING},
+    {"inserkey", 0, 0, "press Insert key", "[keep|on|off]", ARG_TYPE_STRING},
+    {"left-alt", 0, 0, "press left alt", "[keep|on|off]", ARG_TYPE_STRING},
+    {"right-alt", 0, 0, "press rightt alt", "[keep|on|off]", ARG_TYPE_STRING},
+    {"left-ctrl", 0, 0, "press left ctrl", "[keep|on|off]", ARG_TYPE_STRING},
+    {"right-ctrl", 0, 0, "press rightt ctrl", "[keep|on|off]", ARG_TYPE_STRING},
+    {"no-led", 0, 0, "don't update LED state", 0, 0},
+    {0, 0, 0, 0, 0, 0}
+  };
+static int simple_flag_offsets[] 
+= {5, 6, 4, 7, 11, 1, 0, 10, 13, 14, 12, 15, 9, 3, 8, 2};
+
+static grub_uint32_t andmask = 0xffffffff, ormask = 0;
+
+struct 
+keysym
+{
+  char *unshifted_name;                        /* the name in unshifted state */
+  char *shifted_name;                  /* the name in shifted state */
+  unsigned char unshifted_ascii;       /* the ascii code in unshifted state */
+  unsigned char shifted_ascii;         /* the ascii code in shifted state */
+  unsigned char keycode;               /* keyboard scancode */
+};
+
+/* The table for key symbols. If the "shifted" member of an entry is
+   NULL, the entry does not have shifted state. Copied from GRUB Legacy setkey fuction  */
+static struct keysym keysym_table[] =
+{
+  {"escape",           0,              0x1b,   0,      0x01},
+  {"1",                        "exclam",       '1',    '!',    0x02},
+  {"2",                        "at",           '2',    '@',    0x03},
+  {"3",                        "numbersign",   '3',    '#',    0x04},
+  {"4",                        "dollar",       '4',    '$',    0x05},
+  {"5",                        "percent",      '5',    '%',    0x06},
+  {"6",                        "caret",        '6',    '^',    0x07},
+  {"7",                        "ampersand",    '7',    '&',    0x08},
+  {"8",                        "asterisk",     '8',    '*',    0x09},
+  {"9",                        "parenleft",    '9',    '(',    0x0a},
+  {"0",                        "parenright",   '0',    ')',    0x0b},
+  {"minus",            "underscore",   '-',    '_',    0x0c},
+  {"equal",            "plus",         '=',    '+',    0x0d},
+  {"backspace",                0,              '\b',   0,      0x0e},
+  {"tab",              0,              '\t',   0,      0x0f},
+  {"q",                        "Q",            'q',    'Q',    0x10},
+  {"w",                        "W",            'w',    'W',    0x11},
+  {"e",                        "E",            'e',    'E',    0x12},
+  {"r",                        "R",            'r',    'R',    0x13},
+  {"t",                        "T",            't',    'T',    0x14},
+  {"y",                        "Y",            'y',    'Y',    0x15},
+  {"u",                        "U",            'u',    'U',    0x16},
+  {"i",                        "I",            'i',    'I',    0x17},
+  {"o",                        "O",            'o',    'O',    0x18},
+  {"p",                        "P",            'p',    'P',    0x19},
+  {"bracketleft",      "braceleft",    '[',    '{',    0x1a},
+  {"bracketright",     "braceright",   ']',    '}',    0x1b},
+  {"enter",            0,              '\r',   0,      0x1c},
+  {"control",          0,              0,      0,      0x1d},
+  {"a",                        "A",            'a',    'A',    0x1e},
+  {"s",                        "S",            's',    'S',    0x1f},
+  {"d",                        "D",            'd',    'D',    0x20},
+  {"f",                        "F",            'f',    'F',    0x21},
+  {"g",                        "G",            'g',    'G',    0x22},
+  {"h",                        "H",            'h',    'H',    0x23},
+  {"j",                        "J",            'j',    'J',    0x24},
+  {"k",                        "K",            'k',    'K',    0x25},
+  {"l",                        "L",            'l',    'L',    0x26},
+  {"semicolon",                "colon",        ';',    ':',    0x27},
+  {"quote",            "doublequote",  '\'',   '"',    0x28},
+  {"backquote",                "tilde",        '`',    '~',    0x29},
+  {"shift",            0,              0,      0,      0x2a},
+  {"backslash",                "bar",          '\\',   '|',    0x2b},
+  {"z",                        "Z",            'z',    'Z',    0x2c},
+  {"x",                        "X",            'x',    'X',    0x2d},
+  {"c",                        "C",            'c',    'C',    0x2e},
+  {"v",                        "V",            'v',    'V',    0x2f},
+  {"b",                        "B",            'b',    'B',    0x30},
+  {"n",                        "N",            'n',    'N',    0x31},
+  {"m",                        "M",            'm',    'M',    0x32},
+  {"comma",            "less",         ',',    '<',    0x33},
+  {"period",           "greater",      '.',    '>',    0x34},
+  {"slash",            "question",     '/',    '?',    0x35},
+  {"rshift",           0,              0,      0,      0x36},
+  {"numasterisk",              0,              '*',    0,      0x37},
+  {"alt",              0,              0,      0,      0x38},
+  {"space",            0,              ' ',    0,      0x39},
+  {"capslock",         0,              0,      0,      0x3a},
+  {"F1",               0,              0,      0,      0x3b},
+  {"F2",               0,              0,      0,      0x3c},
+  {"F3",               0,              0,      0,      0x3d},
+  {"F4",               0,              0,      0,      0x3e},
+  {"F5",               0,              0,      0,      0x3f},
+  {"F6",               0,              0,      0,      0x40},
+  {"F7",               0,              0,      0,      0x41},
+  {"F8",               0,              0,      0,      0x42},
+  {"F9",               0,              0,      0,      0x43},
+  {"F10",              0,              0,      0,      0x44},
+  {"num7",             "numhome",              '7',    0,      0x47},
+  {"num8",             "numup",                '8',    0,      0x48},
+  {"num9",             "numpgup",              '9',    0,      0x49},
+  {"numminus",         0,              '-',    0,      0x4a},
+  {"num4",             "numleft",              '4',    0,      0x4b},
+  {"num5",             "num5numlock",          '5',    0,      0x4c},
+  {"num6",             "numright",             '6',    0,      0x4d},
+  {"numplus",          0,              '-',    0,      0x4e},
+  {"num1",             "numend",               '1',    0,      0x4f},
+  {"num2",             "numdown",              '2',    0,      0x50},
+  {"num3",             "numpgdown",            '3',    0,      0x51},
+  {"num0",             "numinsert",            '0',    0,      0x52},
+  {"numperiod",        "numdelete", 0, 0x7f,           0x53},
+  {"F11",              0,              0,      0,      0x57},
+  {"F12",              0,              0,      0,      0x58},
+  {"numenter",         0,              '\r',   0,      0xe0},
+  {"numslash",         0,              '/',    0,      0xe0},
+  {"delete",           0,              0x7f,   0,      0xe0},
+  {"insert",           0,              0xe0,   0,      0x52},
+  {"home",             0,              0xe0,   0,      0x47},
+  {"end",              0,              0xe0,   0,      0x4f},
+  {"pgdown",           0,              0xe0,   0,      0x51},
+  {"pgup",             0,              0xe0,   0,      0x49},
+  {"down",             0,              0xe0,   0,      0x50},
+  {"up",               0,              0xe0,   0,      0x48},
+  {"left",             0,              0xe0,   0,      0x4b},
+  {"right",            0,              0xe0,   0,      0x4d}
+};
+
+/* Set a simple flag in flags variable  
+   OUTOFFSET - offset of flag in FLAGS,
+   OP - action id
+*/
+static void
+grub_sendkey_set_simple_flag (int outoffset, int op)
+{      
+  if (op == 2)
+    {
+      andmask |= (1 << outoffset);
+      ormask &= ~(1 << outoffset);
+    }
+  else
+    {
+      andmask &= (~(1 << outoffset));
+      if (op == 1)
+       ormask |= (1 << outoffset);
+      else
+       ormask &= ~(1 << outoffset);
+    }
+}
+
+static int
+grub_sendkey_parse_op (struct grub_arg_list state)
+{
+  if (! state.set)
+    return 2;
+
+  if (grub_strcmp (state.arg, "off") == 0 || grub_strcmp (state.arg, "0") == 0 
+      || grub_strcmp (state.arg, "unpress") == 0)
+    return 0;
+
+  if (grub_strcmp (state.arg, "on")  == 0 || grub_strcmp (state.arg, "1")  == 0
+      || grub_strcmp (state.arg, "press") == 0)
+    return 1;
+
+  return 2;
+}
+
+static grub_uint32_t oldflags;
+
+static grub_err_t
+grub_sendkey_postboot (void)
+{
+  /* For convention: pointer to flags.  */
+  grub_uint32_t *flags = (grub_uint32_t *) 0x417;
+
+  *flags = oldflags;
+
+  *((char *) 0x41a) = 0x1e;
+  *((char *) 0x41c) = 0x1e;
+
+  return GRUB_ERR_NONE;
+}
+
+/* Set keyboard buffer to our sendkey  */
+static grub_err_t
+grub_sendkey_preboot (int noret __attribute__ ((unused)))
+{
+  /* For convention: pointer to flags.  */
+  grub_uint32_t *flags = (grub_uint32_t *) 0x417;
+
+  oldflags = *flags;
+  
+  /* Set the sendkey.  */
+  *((char *) 0x41a) = 0x1e;
+  *((char *) 0x41c) = keylen + 0x1e;
+  grub_memcpy ((char *) 0x41e, sendkey, 0x20);
+
+  /* Transform "any ctrl" to "right ctrl" flag.  */
+  if (*flags & (1 << 8))
+    *flags &= ~(1 << 2);
+
+  /* Transform "any alt" to "right alt" flag.  */
+  if (*flags & (1 << 9))
+    *flags &= ~(1 << 3);
+  
+  *flags = (*flags & andmask) | ormask;
+
+  /* Transform "right ctrl" to "any ctrl" flag.  */
+  if (*flags & (1 << 8))
+    *flags |= (1 << 2);
+
+  /* Transform "right alt" to "any alt" flag.  */
+  if (*flags & (1 << 9))
+    *flags |= (1 << 3);
+
+  /* Write new LED state  */
+  if (!noled)
+    {
+      int value = 0;
+      int failed;
+      /* Try 5 times  */
+      for (failed = 0; failed < 5; failed++)
+       {
+         value = 0;
+         /* Send command change LEDs  */
+         grub_outb (0xed, 0x60);
+
+         /* Wait */
+         do
+           value = grub_inb (0x60);
+         while ((value != 0xfa) && (value != 0xfe));
+
+         if (value == 0xfa)
+           {
+             /* Set new LEDs*/
+             grub_outb ((*flags >> 4) & 7, 0x60);
+             break;
+           }
+       }
+    }
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_sendkey (grub_extcmd_t cmd, int argc, char **args)
+{
+  struct grub_arg_list *state = cmd->state;
+
+  auto int find_key_code (char *key); 
+  auto int find_ascii_code (char *key);
+
+  int find_key_code (char *key)
+    {
+      unsigned i;
+
+      for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
+       {
+         if (keysym_table[i].unshifted_name 
+             && grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
+           return keysym_table[i].keycode;
+         else if (keysym_table[i].shifted_name 
+                  && grub_strcmp (key, keysym_table[i].shifted_name) == 0)
+           return keysym_table[i].keycode;
+       }
+
+      return 0;
+    }
+
+  int find_ascii_code (char *key)
+    {
+      unsigned i;
+
+      for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
+       {
+         if (keysym_table[i].unshifted_name 
+             && grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
+           return keysym_table[i].unshifted_ascii;
+         else if (keysym_table[i].shifted_name 
+                  && grub_strcmp (key, keysym_table[i].shifted_name) == 0)
+           return keysym_table[i].shifted_ascii;
+       }
+
+      return 0;
+    }
+
+  {
+    int i;
+
+    keylen = 0;
+
+    for (i = 0; i < argc && keylen < 0x20; i++)
+      {
+       int key_code;
+       
+       key_code = find_key_code (args[i]);
+       if (key_code)
+         {
+           sendkey[keylen++] = find_ascii_code (args[i]);
+           sendkey[keylen++] = key_code;
+         }
+      }
+  }
+
+  {
+    unsigned i;
+    for (i = 0; i < sizeof (simple_flag_offsets) 
+          / sizeof (simple_flag_offsets[0]); i++)
+      grub_sendkey_set_simple_flag (simple_flag_offsets[i], 
+                                   grub_sendkey_parse_op(state[i]));
+  }
+
+  /* Set noled. */
+  noled = (state[sizeof (simple_flag_offsets) 
+                / sizeof (simple_flag_offsets[0])].set);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_extcmd_t cmd;
+static void *preboot_hook;
+
+GRUB_MOD_INIT (sendkey)
+{
+  cmd = grub_register_extcmd ("sendkey", grub_cmd_sendkey, 
+                             GRUB_COMMAND_FLAG_BOTH,
+                             "sendkey [KEYSTROKE1] [KEYSTROKE2] ...",
+                             "Emulate a keystroke", options);
+
+  preboot_hook 
+    = grub_loader_register_preboot_hook (grub_sendkey_preboot, 
+                                        grub_sendkey_postboot,
+                                        GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
+}
+
+GRUB_MOD_FINI (sendkey)
+{
+  grub_unregister_extcmd (cmd);
+  grub_loader_unregister_preboot_hook (preboot_hook);
+}
index c157aaf01e9b2f3d12ee1bdaa1a33937c690f517..5e9ec882f481d2608d3b7d6afffa436c262733d0 100644 (file)
@@ -235,6 +235,12 @@ datetime_mod_SOURCES = lib/cmos_datetime.c
 datetime_mod_CFLAGS = $(COMMON_CFLAGS)
 datetime_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For sendkey.mod
+pkglib_MODULES += sendkey.mod
+sendkey_mod_SOURCES = commands/i386/pc/sendkey.c
+sendkey_mod_CFLAGS = $(COMMON_CFLAGS)
+sendkey_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 # For ata_pthru.mod.
 ata_pthru_mod_SOURCES = disk/ata_pthru.c
 ata_pthru_mod_CFLAGS = $(COMMON_CFLAGS)