Patch from Philipp Fischer, entered into the grub bug tracker at
http://savannah.gnu.org/bugs on Sun 20 Mar 2005 06:23:27 PM UTC as
bug #12388: New Feature: support for hardware i/o based bootup selection

Details:

This little patch adds two new supported commands to grub.
"onportsel" and "portout".
With portout, it is possible to set a certain value to a hardware i/o port.

syntax: portout ADDR VALUE

With onportsel you can change the selected boot entry if the input on a
certain hardware address matches a mask.

Example:

onportsel 2 0x201 xxx0xxxx
 (matches if the first button of a plugged joystick is pressed.
  Joystickport = 0x201)

syntax: onportsel ENTRYNUM PORTADDR MASK
the mask must be 8 chars long and can contain '0's, '1's, or 'x's.
It represents one byte.
x - bits are ignored. All others must be matched to select the entry.

I use this command to switch between windows and linux, using a hardware
switch.
This is especially handy, if you only want to restart but do not want to wait
until the bootmenu pops up, to select the appropriate OS.

Index: grub/stage2/builtins.c
===================================================================
--- grub.orig/stage2/builtins.c	2009-07-20 09:43:43.000000000 +0200
+++ grub/stage2/builtins.c	2009-07-20 09:50:41.000000000 +0200
@@ -62,6 +62,12 @@ static int bootdev;
 int debug = 0;
 /* The default entry.  */
 int default_entry = 0;
+/* OnPortSel variables.  */
+int onportsel_entry = -1;
+int onportsel_addr = 0x379;
+unsigned char onportsel_mask = 0xFF;
+unsigned char onportsel_value = 0xFF;
+
 /* The fallback entry.  */
 int fallback_entryno;
 int fallback_entries[MAX_FALLBACK_ENTRIES];
@@ -105,6 +111,10 @@ void
 init_config (void)
 {
   default_entry = 0;
+  onportsel_entry = -1;
+  onportsel_addr = 0x379;
+  onportsel_mask = 0xFF;
+  onportsel_value = 0xFF;
   password = 0;
   fallback_entryno = -1;
   fallback_entries[0] = -1;
@@ -817,6 +827,101 @@ static struct builtin builtin_default =
 };
 
 
+/* portout */
+static int
+portout_func (char *arg, int flags)
+{
+  int i = 0, addr;
+  unsigned char value;
+  while (1)
+  {
+    switch(i) {
+    case 0:
+      if(!safe_parse_maxint (&arg, &addr)) return 1;
+      break;
+
+    case 1:
+      if(!safe_parse_maxint (&arg, &value)) return 1;
+      break;
+    }
+
+    i++;
+    arg = skip_to (0, arg);
+    if(arg[0]=='\0') break;
+  }
+
+  portout(addr, value);
+
+  return 0;
+}
+
+static struct builtin builtin_portout =
+{
+  "portout",
+  portout_func,
+  BUILTIN_MENU,
+  "portout PORT VALUE",
+  "Write a VALUE to a hardware address (PORT)"
+};
+
+
+/* onportsel */
+static int
+onportsel_func (char *arg, int flags)
+{
+  int i = 0;
+  while (1)
+  {
+    switch(i) {
+    case 0:
+      if(!safe_parse_maxint (&arg, &onportsel_entry)) return 1;
+      break;
+
+    case 1:
+      if(!safe_parse_maxint (&arg, &onportsel_addr)) {onportsel_entry=-1; return 1;}
+      break;
+
+    case 2:
+      onportsel_mask = onportsel_value = 0;
+      int pos;
+      for(pos=0;pos<8;pos++) {
+        switch(arg[pos]) {
+        case '0':
+	  onportsel_mask |= (0x80>>pos);
+	  break;
+	case '1':
+	  onportsel_value |= (0x80>>pos);
+	  onportsel_mask |= (0x80>>pos);
+	  break;
+	case 'x':case 'X':
+	  //actually nothing to do
+	  break;
+	case '\0':
+	  return 1; //err
+	}
+      }
+      break;
+    }
+
+    i++;
+    arg = skip_to (0, arg);
+    if(arg[0]=='\0') break;
+  }
+
+  return 0;
+}
+
+static struct builtin builtin_onportsel =
+{
+  "onportsel",
+  onportsel_func,
+  BUILTIN_MENU,
+  "onportsel NUM PORT MASK",
+  "Set the selection to entry number NUM if the value"
+  " on PORT matches the MASK"
+};
+
+
 #ifdef GRUB_UTIL
 /* device */
 static int
@@ -5266,11 +5371,13 @@ struct builtin *builtin_table[] =
 #endif /* USE_MD5_PASSWORDS */
   &builtin_module,
   &builtin_modulenounzip,
+  &builtin_onportsel,
   &builtin_pager,
   &builtin_partnew,
   &builtin_parttype,
   &builtin_password,
   &builtin_pause,
+  &builtin_portout,
   &builtin_print,
 #ifdef GRUB_UTIL
   &builtin_quit,
Index: grub/stage2/char_io.c
===================================================================
--- grub.orig/stage2/char_io.c	2009-07-20 09:43:43.000000000 +0200
+++ grub/stage2/char_io.c	2009-07-20 09:44:25.000000000 +0200
@@ -267,9 +267,12 @@ void
 init_page (void)
 {
   cls ();
+  char *apnd = "";
+  if(onportsel_entry >= 0)
+    if(mbi.lpt_val) apnd = "  OnPort(==)"; else apnd = "  OnPort(!=)";
 
-  grub_printf ("\n    GNU GRUB  version %s  (%dK lower / %dK upper memory)\n\n",
-	  version_string, mbi.mem_lower, mbi.mem_upper);
+  grub_printf ("\n    GNU GRUB  version %s  (%dK lower / %dK upper memory)%s\n\n",
+	  version_string, mbi.mem_lower, mbi.mem_upper, apnd);
 }
 
 /* The number of the history entries.  */
Index: grub/stage2/mb_info.h
===================================================================
--- grub.orig/stage2/mb_info.h	2003-07-09 13:45:53.000000000 +0200
+++ grub/stage2/mb_info.h	2009-07-20 09:51:04.000000000 +0200
@@ -170,6 +170,9 @@ struct multiboot_info
   unsigned short vbe_interface_seg;
   unsigned short vbe_interface_off;
   unsigned short vbe_interface_len;
+
+  /* LPT */
+  unsigned char lpt_val;
 };
 
 /*
Index: grub/stage2/shared.h
===================================================================
--- grub.orig/stage2/shared.h	2009-07-20 09:43:43.000000000 +0200
+++ grub/stage2/shared.h	2009-07-20 09:44:25.000000000 +0200
@@ -597,6 +597,10 @@ extern void assign_device_name (int driv
 extern int fallback_entries[MAX_FALLBACK_ENTRIES];
 extern int fallback_entryno;
 extern int default_entry;
+extern int onportsel_entry;
+extern int onportsel_addr;
+extern unsigned char onportsel_mask;
+extern unsigned char onportsel_value;
 extern int current_entryno;
 
 /* The constants for password types.  */
@@ -819,6 +823,18 @@ int biosdisk (int subfunc, int drive, st
 	      unsigned int sector, int nsec, int segment);
 void stop_floppy (void);
 
+/* Port I/O */
+static inline unsigned char portin(unsigned int port) {
+ unsigned char value;
+ __asm__ __volatile__("inb %w1,%b0" : "=a"(value) : "d"(port));
+ return value;
+}
+
+static inline unsigned char portout(unsigned int port, unsigned char value) {
+ __asm__ __volatile__("outb %%al,%%dx"::"a" (value),"d" (port));
+ return value;
+}
+
 /* Command-line interface functions. */
 #ifndef STAGE1_5
 
Index: grub/stage2/stage2.c
===================================================================
--- grub.orig/stage2/stage2.c	2009-07-20 09:43:43.000000000 +0200
+++ grub/stage2/stage2.c	2009-07-20 09:48:54.000000000 +0200
@@ -246,6 +246,16 @@ run_menu (char *menu_entries, char *conf
    */
 
 restart:
+  //check onportsel:
+  if(onportsel_entry >= 0)
+    {
+      unsigned char portval = portin(onportsel_addr);
+      if( (portval & onportsel_mask) == (onportsel_value & onportsel_mask) )
+        { entryno = onportsel_entry; mbi.lpt_val = 1; }
+      else
+        mbi.lpt_val = 0;
+    }
+
   /* Dumb terminal always use all entries for display 
      invariant for TERM_DUMB: first_entry == 0  */
   if (! (current_term->flags & TERM_DUMB))
@@ -723,6 +733,13 @@ restart:
 		  enter_cmdline (heap, 0);
 		  goto restart;
 		}
+	      if (c == 'x')
+		{
+		  cls();
+		  grub_printf("onportsel_addr == %d\nonportsel_mask == %d\nonportsel_value == %d\nonportsel_entry == %d\nPort[0x%x] == %d\n",onportsel_addr,onportsel_mask,onportsel_value,onportsel_entry,onportsel_addr, portin(onportsel_addr));
+		  getkey();
+		  goto restart;
+		}
 #ifdef GRUB_UTIL
 	      if (c == 'q')
 		{

