a5bd9f6
From ec44b444dcdb795834b085825341771296f469ad Mon Sep 17 00:00:00 2001
a5bd9f6
From: Vladimir 'phcoder' Serbinenko <phcoder@gmail.com>
a5bd9f6
Date: Wed, 16 Jan 2013 20:39:54 +0100
a5bd9f6
Subject: [PATCH 110/364] 	New terminal outputs using serial: morse and
a5bd9f6
 spkmodem.
a5bd9f6
a5bd9f6
---
a5bd9f6
 ChangeLog                         |   4 ++
a5bd9f6
 docs/grub.texi                    |  24 +++++--
a5bd9f6
 grub-core/Makefile.core.def       |  12 ++++
a5bd9f6
 grub-core/commands/i386/pc/play.c | 109 ++-----------------------------
a5bd9f6
 grub-core/kern/i386/pit.c         |  35 ++++------
a5bd9f6
 grub-core/term/morse.c            | 131 ++++++++++++++++++++++++++++++++++++++
a5bd9f6
 grub-core/term/spkmodem.c         | 106 ++++++++++++++++++++++++++++++
a5bd9f6
 include/grub/i386/pit.h           |  78 +++++++++++++++++++++++
a5bd9f6
 include/grub/speaker.h            |  47 ++++++++++++++
a5bd9f6
 util/spkmodem-recv.c              |  98 ++++++++++++++++++++++++++++
a5bd9f6
 10 files changed, 512 insertions(+), 132 deletions(-)
a5bd9f6
 create mode 100644 grub-core/term/morse.c
a5bd9f6
 create mode 100644 grub-core/term/spkmodem.c
a5bd9f6
 create mode 100644 include/grub/speaker.h
a5bd9f6
 create mode 100644 util/spkmodem-recv.c
a5bd9f6
a5bd9f6
diff --git a/ChangeLog b/ChangeLog
a5bd9f6
index 96aca66..f1c53e3 100644
a5bd9f6
--- a/ChangeLog
a5bd9f6
+++ b/ChangeLog
a5bd9f6
@@ -1,5 +1,9 @@
a5bd9f6
 2013-01-16  Vladimir Serbinenko  <phcoder@gmail.com>
a5bd9f6
 
a5bd9f6
+	New terminal outputs using serial: morse and spkmodem.
a5bd9f6
+
a5bd9f6
+2013-01-16  Vladimir Serbinenko  <phcoder@gmail.com>
a5bd9f6
+
a5bd9f6
 	Improve bidi handling in entry editor.
a5bd9f6
 
a5bd9f6
 2013-01-16  Vladimir Serbinenko  <phcoder@gmail.com>
a5bd9f6
diff --git a/docs/grub.texi b/docs/grub.texi
a5bd9f6
index e75bae9..efedd27 100644
a5bd9f6
--- a/docs/grub.texi
a5bd9f6
+++ b/docs/grub.texi
a5bd9f6
@@ -1229,9 +1229,9 @@ Select the terminal input device.  You may select multiple devices here,
a5bd9f6
 separated by spaces.
a5bd9f6
 
a5bd9f6
 Valid terminal input names depend on the platform, but may include
a5bd9f6
-@samp{console} (PC BIOS and EFI consoles), @samp{serial} (serial terminal),
a5bd9f6
-@samp{ofconsole} (Open Firmware console), @samp{at_keyboard} (PC AT
a5bd9f6
-keyboard, mainly useful with Coreboot), or @samp{usb_keyboard} (USB keyboard
a5bd9f6
+@samp{console} (native platform console), @samp{serial} (serial terminal),
a5bd9f6
+@samp{serial_<port>} (serial terminal with explicit port selection),
a5bd9f6
+@samp{at_keyboard} (PC AT keyboard), or @samp{usb_keyboard} (USB keyboard
a5bd9f6
 using the HID Boot Protocol, for cases where the firmware does not handle
a5bd9f6
 this).
a5bd9f6
 
a5bd9f6
@@ -1242,9 +1242,21 @@ Select the terminal output device.  You may select multiple devices here,
a5bd9f6
 separated by spaces.
a5bd9f6
 
a5bd9f6
 Valid terminal output names depend on the platform, but may include
a5bd9f6
-@samp{console} (PC BIOS and EFI consoles), @samp{serial} (serial terminal),
a5bd9f6
-@samp{gfxterm} (graphics-mode output), @samp{ofconsole} (Open Firmware
a5bd9f6
-console), or @samp{vga_text} (VGA text output, mainly useful with Coreboot).
a5bd9f6
+@samp{console} (native platform console), @samp{serial} (serial terminal),
a5bd9f6
+@samp{serial_<port>} (serial terminal with explicit port selection),
a5bd9f6
+@samp{gfxterm} (graphics-mode output), @samp{vga_text} (VGA text output),
a5bd9f6
+@samp{mda_text} (MDA text output), @samp{morse} (Morse-coding using system
a5bd9f6
+beeper) or @samp{spkmodem} (simple data protocol using system speaker).
a5bd9f6
+
a5bd9f6
+@samp{spkmodem} is useful when no serial port is available. Connect the output
a5bd9f6
+of sending system (where GRUB is running) to line-in of receiving system
a5bd9f6
+(usually developper machine).
a5bd9f6
+On receiving system compile @samp{spkmodem-recv} from
a5bd9f6
+@samp{util/spkmodem-recv.c} and run:
a5bd9f6
+
a5bd9f6
+@example
a5bd9f6
+parecord --channels=1 --rate=48000 --format=s16le | ./spkmodem-recv
a5bd9f6
+@end example
a5bd9f6
 
a5bd9f6
 The default is to use the platform's native terminal output.
a5bd9f6
 
a5bd9f6
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
a5bd9f6
index f4dd645..baf80b8 100644
a5bd9f6
--- a/grub-core/Makefile.core.def
a5bd9f6
+++ b/grub-core/Makefile.core.def
a5bd9f6
@@ -779,6 +779,18 @@ module = {
a5bd9f6
 };
a5bd9f6
 
a5bd9f6
 module = {
a5bd9f6
+  name = spkmodem;
a5bd9f6
+  x86 = term/spkmodem.c;
a5bd9f6
+  enable = x86;
a5bd9f6
+};
a5bd9f6
+
a5bd9f6
+module = {
a5bd9f6
+  name = morse;
a5bd9f6
+  x86 = term/morse.c;
a5bd9f6
+  enable = x86;
a5bd9f6
+};
a5bd9f6
+
a5bd9f6
+module = {
a5bd9f6
   name = probe;
a5bd9f6
   common = commands/probe.c;
a5bd9f6
 };
a5bd9f6
diff --git a/grub-core/commands/i386/pc/play.c b/grub-core/commands/i386/pc/play.c
a5bd9f6
index 10a0181..40798c9 100644
a5bd9f6
--- a/grub-core/commands/i386/pc/play.c
a5bd9f6
+++ b/grub-core/commands/i386/pc/play.c
a5bd9f6
@@ -28,80 +28,12 @@
a5bd9f6
 #include <grub/command.h>
a5bd9f6
 #include <grub/i18n.h>
a5bd9f6
 #include <grub/time.h>
a5bd9f6
+#include <grub/speaker.h>
a5bd9f6
 
a5bd9f6
 GRUB_MOD_LICENSE ("GPLv3+");
a5bd9f6
 
a5bd9f6
 #define BASE_TEMPO (60 * 1000)
a5bd9f6
 
a5bd9f6
-/* The speaker port.  */
a5bd9f6
-#define SPEAKER			0x61
a5bd9f6
-
a5bd9f6
-/* If 0, follow state of SPEAKER_DATA bit, otherwise enable output
a5bd9f6
-   from timer 2.  */
a5bd9f6
-#define SPEAKER_TMR2		0x01
a5bd9f6
-
a5bd9f6
-/* If SPEAKER_TMR2 is not set, this provides direct input into the
a5bd9f6
-   speaker.  Otherwise, this enables or disables the output from the
a5bd9f6
-   timer.  */
a5bd9f6
-#define SPEAKER_DATA		0x02
a5bd9f6
-
a5bd9f6
-/* The PIT channel value ports.  You can write to and read from them.
a5bd9f6
-   Do not mess with timer 0 or 1.  */
a5bd9f6
-#define PIT_COUNTER_0		0x40
a5bd9f6
-#define PIT_COUNTER_1		0x41
a5bd9f6
-#define PIT_COUNTER_2		0x42
a5bd9f6
-
a5bd9f6
-/* The frequency of the PIT clock.  */
a5bd9f6
-#define PIT_FREQUENCY		0x1234dd
a5bd9f6
-
a5bd9f6
-/* The PIT control port.  You can only write to it.  Do not mess with
a5bd9f6
-   timer 0 or 1.  */
a5bd9f6
-#define PIT_CTRL		0x43
a5bd9f6
-#define PIT_CTRL_SELECT_MASK	0xc0
a5bd9f6
-#define PIT_CTRL_SELECT_0	0x00
a5bd9f6
-#define PIT_CTRL_SELECT_1	0x40
a5bd9f6
-#define PIT_CTRL_SELECT_2	0x80
a5bd9f6
-
a5bd9f6
-/* Read and load control.  */
a5bd9f6
-#define PIT_CTRL_READLOAD_MASK	0x30
a5bd9f6
-#define PIT_CTRL_COUNTER_LATCH	0x00	/* Hold timer value until read.  */
a5bd9f6
-#define PIT_CTRL_READLOAD_LSB	0x10	/* Read/load the LSB.  */
a5bd9f6
-#define PIT_CTRL_READLOAD_MSB	0x20	/* Read/load the MSB.  */
a5bd9f6
-#define PIT_CTRL_READLOAD_WORD	0x30	/* Read/load the LSB then the MSB.  */
a5bd9f6
-
a5bd9f6
-/* Mode control.  */
a5bd9f6
-#define PIT_CTRL_MODE_MASK	0x0e
a5bd9f6
-
a5bd9f6
-/* Interrupt on terminal count.  Setting the mode sets output to low.
a5bd9f6
-   When counter is set and terminated, output is set to high.  */
a5bd9f6
-#define PIT_CTRL_INTR_ON_TERM	0x00
a5bd9f6
-
a5bd9f6
-/* Programmable one-shot.  When loading counter, output is set to
a5bd9f6
-   high.  When counter terminated, output is set to low.  Can be
a5bd9f6
-   triggered again from that point on by setting the gate pin to
a5bd9f6
-   high.  */
a5bd9f6
-#define PIT_CTRL_PROGR_ONE_SHOT	0x02
a5bd9f6
-
a5bd9f6
-/* Rate generator.  Output is low for one period of the counter, and
a5bd9f6
-   high for the other.  */
a5bd9f6
-#define PIT_CTRL_RATE_GEN	0x04
a5bd9f6
-
a5bd9f6
-/* Square wave generator.  Output is low for one half of the period,
a5bd9f6
-   and high for the other half.  */
a5bd9f6
-#define PIT_CTRL_SQUAREWAVE_GEN	0x06
a5bd9f6
-
a5bd9f6
-/* Software triggered strobe.  Setting the mode sets output to high.
a5bd9f6
-   When counter is set and terminated, output is set to low.  */
a5bd9f6
-#define PIT_CTRL_SOFTSTROBE	0x08
a5bd9f6
-
a5bd9f6
-/* Hardware triggered strobe.  Like software triggered strobe, but
a5bd9f6
-   only starts the counter when the gate pin is set to high.  */
a5bd9f6
-#define PIT_CTRL_HARDSTROBE	0x0a
a5bd9f6
-
a5bd9f6
-/* Count mode.  */
a5bd9f6
-#define PIT_CTRL_COUNT_MASK	0x01
a5bd9f6
-#define PIT_CTRL_COUNT_BINARY	0x00	/* 16-bit binary counter.  */
a5bd9f6
-#define PIT_CTRL_COUNT_BCD	0x01	/* 4-decade BCD counter.  */
a5bd9f6
 
a5bd9f6
 #define T_REST			((grub_uint16_t) 0)
a5bd9f6
 #define T_FINE			((grub_uint16_t) -1)
a5bd9f6
@@ -112,39 +44,6 @@ struct note
a5bd9f6
   grub_uint16_t duration;
a5bd9f6
 };
a5bd9f6
 
a5bd9f6
-static void
a5bd9f6
-beep_off (void)
a5bd9f6
-{
a5bd9f6
-  unsigned char status;
a5bd9f6
-
a5bd9f6
-  status = grub_inb (SPEAKER);
a5bd9f6
-  grub_outb (status & ~(SPEAKER_TMR2 | SPEAKER_DATA), SPEAKER);
a5bd9f6
-}
a5bd9f6
-
a5bd9f6
-static void
a5bd9f6
-beep_on (grub_uint16_t pitch)
a5bd9f6
-{
a5bd9f6
-  unsigned char status;
a5bd9f6
-  unsigned int counter;
a5bd9f6
-
a5bd9f6
-  if (pitch < 20)
a5bd9f6
-    pitch = 20;
a5bd9f6
-  else if (pitch > 20000)
a5bd9f6
-    pitch = 20000;
a5bd9f6
-
a5bd9f6
-  counter = PIT_FREQUENCY / pitch;
a5bd9f6
-
a5bd9f6
-  /* Program timer 2.  */
a5bd9f6
-  grub_outb (PIT_CTRL_SELECT_2 | PIT_CTRL_READLOAD_WORD
a5bd9f6
-	| PIT_CTRL_SQUAREWAVE_GEN | PIT_CTRL_COUNT_BINARY, PIT_CTRL);
a5bd9f6
-  grub_outb (counter & 0xff, PIT_COUNTER_2);		/* LSB */
a5bd9f6
-  grub_outb ((counter >> 8) & 0xff, PIT_COUNTER_2);	/* MSB */
a5bd9f6
-
a5bd9f6
-  /* Start speaker.  */
a5bd9f6
-  status = grub_inb (SPEAKER);
a5bd9f6
-  grub_outb (status | SPEAKER_TMR2 | SPEAKER_DATA, SPEAKER);
a5bd9f6
-}
a5bd9f6
-
a5bd9f6
 /* Returns whether playing should continue.  */
a5bd9f6
 static int
a5bd9f6
 play (unsigned tempo, struct note *note)
a5bd9f6
@@ -160,11 +59,11 @@ play (unsigned tempo, struct note *note)
a5bd9f6
   switch (note->pitch)
a5bd9f6
     {
a5bd9f6
       case T_REST:
a5bd9f6
-        beep_off ();
a5bd9f6
+        grub_speaker_beep_off ();
a5bd9f6
         break;
a5bd9f6
 
a5bd9f6
       default:
a5bd9f6
-        beep_on (note->pitch);
a5bd9f6
+        grub_speaker_beep_on (note->pitch);
a5bd9f6
         break;
a5bd9f6
     }
a5bd9f6
 
a5bd9f6
@@ -263,7 +162,7 @@ grub_cmd_play (grub_command_t cmd __attribute__ ((unused)),
a5bd9f6
         }
a5bd9f6
     }
a5bd9f6
 
a5bd9f6
-  beep_off ();
a5bd9f6
+  grub_speaker_beep_off ();
a5bd9f6
 
a5bd9f6
   return 0;
a5bd9f6
 }
a5bd9f6
diff --git a/grub-core/kern/i386/pit.c b/grub-core/kern/i386/pit.c
a5bd9f6
index 82a17d3..092481a 100644
a5bd9f6
--- a/grub-core/kern/i386/pit.c
a5bd9f6
+++ b/grub-core/kern/i386/pit.c
a5bd9f6
@@ -20,37 +20,30 @@
a5bd9f6
 #include <grub/i386/io.h>
a5bd9f6
 #include <grub/i386/pit.h>
a5bd9f6
 
a5bd9f6
-#define TIMER2_REG_CONTROL	0x42
a5bd9f6
-#define TIMER_REG_COMMAND	0x43
a5bd9f6
-#define TIMER2_REG_LATCH	0x61
a5bd9f6
-
a5bd9f6
-#define TIMER2_SELECT		0x80
a5bd9f6
-#define TIMER_ENABLE_LSB	0x20
a5bd9f6
-#define TIMER_ENABLE_MSB	0x10
a5bd9f6
-#define TIMER2_LATCH		0x20
a5bd9f6
-#define TIMER2_SPEAKER		0x02
a5bd9f6
-#define TIMER2_GATE		0x01
a5bd9f6
-
a5bd9f6
 void
a5bd9f6
 grub_pit_wait (grub_uint16_t tics)
a5bd9f6
 {
a5bd9f6
   /* Disable timer2 gate and speaker.  */
a5bd9f6
-  grub_outb (grub_inb (TIMER2_REG_LATCH) & ~ (TIMER2_SPEAKER | TIMER2_GATE),
a5bd9f6
-             TIMER2_REG_LATCH);
a5bd9f6
+  grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT)
a5bd9f6
+	     & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2),
a5bd9f6
+             GRUB_PIT_SPEAKER_PORT);
a5bd9f6
 
a5bd9f6
   /* Set tics.  */
a5bd9f6
-  grub_outb (TIMER2_SELECT | TIMER_ENABLE_LSB | TIMER_ENABLE_MSB, TIMER_REG_COMMAND);
a5bd9f6
-  grub_outb (tics & 0xff, TIMER2_REG_CONTROL);
a5bd9f6
-  grub_outb (tics >> 8, TIMER2_REG_CONTROL);
a5bd9f6
+  grub_outb (GRUB_PIT_CTRL_SELECT_2 | GRUB_PIT_CTRL_READLOAD_WORD,
a5bd9f6
+	     GRUB_PIT_CTRL);
a5bd9f6
+  grub_outb (tics & 0xff, GRUB_PIT_COUNTER_2);
a5bd9f6
+  grub_outb (tics >> 8, GRUB_PIT_COUNTER_2);
a5bd9f6
 
a5bd9f6
   /* Enable timer2 gate, keep speaker disabled.  */
a5bd9f6
-  grub_outb ((grub_inb (TIMER2_REG_LATCH) & ~ TIMER2_SPEAKER) | TIMER2_GATE,
a5bd9f6
-             TIMER2_REG_LATCH);
a5bd9f6
+  grub_outb ((grub_inb (GRUB_PIT_SPEAKER_PORT) & ~ GRUB_PIT_SPK_DATA)
a5bd9f6
+	     | GRUB_PIT_SPK_TMR2,
a5bd9f6
+             GRUB_PIT_SPEAKER_PORT);
a5bd9f6
 
a5bd9f6
   /* Wait.  */
a5bd9f6
-  while ((grub_inb (TIMER2_REG_LATCH) & TIMER2_LATCH) == 0x00);
a5bd9f6
+  while ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00);
a5bd9f6
 
a5bd9f6
   /* Disable timer2 gate and speaker.  */
a5bd9f6
-  grub_outb (grub_inb (TIMER2_REG_LATCH) & ~ (TIMER2_SPEAKER | TIMER2_GATE),
a5bd9f6
-             TIMER2_REG_LATCH);
a5bd9f6
+  grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT)
a5bd9f6
+	     & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2),
a5bd9f6
+             GRUB_PIT_SPEAKER_PORT);
a5bd9f6
 }
a5bd9f6
diff --git a/grub-core/term/morse.c b/grub-core/term/morse.c
a5bd9f6
new file mode 100644
a5bd9f6
index 0000000..3d6c650
a5bd9f6
--- /dev/null
a5bd9f6
+++ b/grub-core/term/morse.c
a5bd9f6
@@ -0,0 +1,131 @@
a5bd9f6
+/*
a5bd9f6
+ *  GRUB  --  GRand Unified Bootloader
a5bd9f6
+ *  Copyright (C) 2011,2012,2013  Free Software Foundation, Inc.
a5bd9f6
+ *
a5bd9f6
+ *  GRUB is free software: you can redistribute it and/or modify
a5bd9f6
+ *  it under the terms of the GNU General Public License as published by
a5bd9f6
+ *  the Free Software Foundation, either version 3 of the License, or
a5bd9f6
+ *  (at your option) any later version.
a5bd9f6
+ *
a5bd9f6
+ *  GRUB is distributed in the hope that it will be useful,
a5bd9f6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
a5bd9f6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a5bd9f6
+ *  GNU General Public License for more details.
a5bd9f6
+ *
a5bd9f6
+ *  You should have received a copy of the GNU General Public License
a5bd9f6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
a5bd9f6
+ */
a5bd9f6
+
a5bd9f6
+#include <grub/term.h>
a5bd9f6
+#include <grub/misc.h>
a5bd9f6
+#include <grub/types.h>
a5bd9f6
+#include <grub/err.h>
a5bd9f6
+#include <grub/dl.h>
a5bd9f6
+#include <grub/time.h>
a5bd9f6
+#include <grub/speaker.h>
a5bd9f6
+
a5bd9f6
+GRUB_MOD_LICENSE ("GPLv3+");
a5bd9f6
+
a5bd9f6
+#define BASE_TIME 250
a5bd9f6
+
a5bd9f6
+static const char codes[0x80][6] =
a5bd9f6
+  {
a5bd9f6
+    ['0'] = { 3, 3, 3, 3, 3, 0 },
a5bd9f6
+    ['1'] = { 1, 3, 3, 3, 3, 0 },
a5bd9f6
+    ['2'] = { 1, 1, 3, 3, 3, 0 },
a5bd9f6
+    ['3'] = { 1, 1, 1, 3, 3, 0 },
a5bd9f6
+    ['4'] = { 1, 1, 1, 1, 3, 0 },
a5bd9f6
+    ['5'] = { 1, 1, 1, 1, 1, 0 },
a5bd9f6
+    ['6'] = { 3, 1, 1, 1, 1, 0 },
a5bd9f6
+    ['7'] = { 3, 3, 1, 1, 1, 0 },
a5bd9f6
+    ['8'] = { 3, 3, 3, 1, 1, 0 },
a5bd9f6
+    ['9'] = { 3, 3, 3, 3, 1, 0 },
a5bd9f6
+    ['a'] = { 1, 3, 0 },
a5bd9f6
+    ['b'] = { 3, 1, 1, 1, 0 },
a5bd9f6
+    ['c'] = { 3, 1, 3, 1, 0 },
a5bd9f6
+    ['d'] = { 3, 1, 1, 0 },
a5bd9f6
+    ['e'] = { 1, 0 },
a5bd9f6
+    ['f'] = { 1, 1, 3, 1, 0 },
a5bd9f6
+    ['g'] = { 3, 3, 1, 0 },
a5bd9f6
+    ['h'] = { 1, 1, 1, 1, 0 },
a5bd9f6
+    ['i'] = { 1, 1, 0 },
a5bd9f6
+    ['j'] = { 1, 3, 3, 3, 0 },
a5bd9f6
+    ['k'] = { 3, 1, 3, 0 },
a5bd9f6
+    ['l'] = { 1, 3, 1, 1, 0 },
a5bd9f6
+    ['m'] = { 3, 3, 0 },
a5bd9f6
+    ['n'] = { 3, 1, 0 },
a5bd9f6
+    ['o'] = { 3, 3, 3, 0 },
a5bd9f6
+    ['p'] = { 1, 3, 3, 1, 0 },
a5bd9f6
+    ['q'] = { 3, 3, 1, 3, 0 },
a5bd9f6
+    ['r'] = { 1, 3, 1, 0 },
a5bd9f6
+    ['s'] = { 1, 1, 1, 0 },
a5bd9f6
+    ['t'] = { 3, 0 },
a5bd9f6
+    ['u'] = { 1, 1, 3, 0 },
a5bd9f6
+    ['v'] = { 1, 1, 1, 3, 0 },
a5bd9f6
+    ['w'] = { 1, 3, 3, 0 },
a5bd9f6
+    ['x'] = { 3, 1, 1, 3, 0 },
a5bd9f6
+    ['y'] = { 3, 1, 3, 3, 0 },
a5bd9f6
+    ['z'] = { 3, 3, 1, 1, 0 }
a5bd9f6
+  };
a5bd9f6
+
a5bd9f6
+static void
a5bd9f6
+grub_audio_tone (int length)
a5bd9f6
+{
a5bd9f6
+  grub_speaker_beep_on (1000);
a5bd9f6
+  grub_millisleep (length);
a5bd9f6
+  grub_speaker_beep_off ();
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+static void
a5bd9f6
+grub_audio_putchar (struct grub_term_output *term __attribute__ ((unused)),
a5bd9f6
+		    const struct grub_unicode_glyph *c_in)
a5bd9f6
+{
a5bd9f6
+  grub_uint8_t c;
a5bd9f6
+  int i;
a5bd9f6
+
a5bd9f6
+  /* For now, do not try to use a surrogate pair.  */
a5bd9f6
+  if (c_in->base > 0x7f)
a5bd9f6
+    c = '?';
a5bd9f6
+  else
a5bd9f6
+    c = grub_tolower (c_in->base);
a5bd9f6
+  for (i = 0; codes[c][i]; i++)
a5bd9f6
+    {
a5bd9f6
+      grub_audio_tone (codes[c][i] * BASE_TIME);
a5bd9f6
+      grub_millisleep (BASE_TIME);
a5bd9f6
+    }
a5bd9f6
+  grub_millisleep (2 * BASE_TIME);
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+
a5bd9f6
+static int
a5bd9f6
+dummy (void)
a5bd9f6
+{
a5bd9f6
+  return 0;
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+static struct grub_term_output grub_audio_term_output =
a5bd9f6
+  {
a5bd9f6
+   .name = "morse",
a5bd9f6
+   .init = (void *) dummy,
a5bd9f6
+   .fini = (void *) dummy,
a5bd9f6
+   .putchar = grub_audio_putchar,
a5bd9f6
+   .getwh = (void *) dummy,
a5bd9f6
+   .getxy = (void *) dummy,
a5bd9f6
+   .gotoxy = (void *) dummy,
a5bd9f6
+   .cls = (void *) dummy,
a5bd9f6
+   .setcolorstate = (void *) dummy,
a5bd9f6
+   .setcursor = (void *) dummy,
a5bd9f6
+   .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR,
a5bd9f6
+   .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR,
a5bd9f6
+   .flags = GRUB_TERM_CODE_TYPE_ASCII | GRUB_TERM_DUMB
a5bd9f6
+  };
a5bd9f6
+
a5bd9f6
+GRUB_MOD_INIT (morse)
a5bd9f6
+{
a5bd9f6
+  grub_term_register_output ("audio", &grub_audio_term_output);
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+GRUB_MOD_FINI (morse)
a5bd9f6
+{
a5bd9f6
+  grub_term_unregister_output (&grub_audio_term_output);
a5bd9f6
+}
a5bd9f6
diff --git a/grub-core/term/spkmodem.c b/grub-core/term/spkmodem.c
a5bd9f6
new file mode 100644
a5bd9f6
index 0000000..31dab65
a5bd9f6
--- /dev/null
a5bd9f6
+++ b/grub-core/term/spkmodem.c
a5bd9f6
@@ -0,0 +1,106 @@
a5bd9f6
+/*  console.c -- Open Firmware console for GRUB.  */
a5bd9f6
+/*
a5bd9f6
+ *  GRUB  --  GRand Unified Bootloader
a5bd9f6
+ *  Copyright (C) 2003,2004,2005,2007,2008,2009  Free Software Foundation, Inc.
a5bd9f6
+ *
a5bd9f6
+ *  GRUB is free software: you can redistribute it and/or modify
a5bd9f6
+ *  it under the terms of the GNU General Public License as published by
a5bd9f6
+ *  the Free Software Foundation, either version 3 of the License, or
a5bd9f6
+ *  (at your option) any later version.
a5bd9f6
+ *
a5bd9f6
+ *  GRUB is distributed in the hope that it will be useful,
a5bd9f6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
a5bd9f6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a5bd9f6
+ *  GNU General Public License for more details.
a5bd9f6
+ *
a5bd9f6
+ *  You should have received a copy of the GNU General Public License
a5bd9f6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
a5bd9f6
+ */
a5bd9f6
+
a5bd9f6
+#include <grub/term.h>
a5bd9f6
+#include <grub/types.h>
a5bd9f6
+#include <grub/misc.h>
a5bd9f6
+#include <grub/mm.h>
a5bd9f6
+#include <grub/time.h>
a5bd9f6
+#include <grub/terminfo.h>
a5bd9f6
+#include <grub/dl.h>
a5bd9f6
+#include <grub/speaker.h>
a5bd9f6
+
a5bd9f6
+GRUB_MOD_LICENSE ("GPLv3+");
a5bd9f6
+
a5bd9f6
+extern struct grub_terminfo_output_state grub_spkmodem_terminfo_output;
a5bd9f6
+
a5bd9f6
+static void
a5bd9f6
+put (struct grub_term_output *term __attribute__ ((unused)), const int c)
a5bd9f6
+{
a5bd9f6
+  int i;
a5bd9f6
+
a5bd9f6
+  for (i = 7; i >= 0; i--)
a5bd9f6
+    {
a5bd9f6
+      if ((c >> i) & 1)
a5bd9f6
+	grub_speaker_beep_on (2000);
a5bd9f6
+      else
a5bd9f6
+	grub_speaker_beep_on (4000);
a5bd9f6
+      grub_millisleep (10);
a5bd9f6
+      grub_speaker_beep_on (1000);
a5bd9f6
+      grub_millisleep (10);
a5bd9f6
+    }
a5bd9f6
+  grub_speaker_beep_on (50);
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+static grub_err_t
a5bd9f6
+grub_spkmodem_init_output (struct grub_term_output *term)
a5bd9f6
+{
a5bd9f6
+  grub_speaker_beep_on (50);
a5bd9f6
+  grub_millisleep (50);
a5bd9f6
+
a5bd9f6
+  grub_terminfo_output_init (term);
a5bd9f6
+
a5bd9f6
+  return 0;
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+static grub_err_t
a5bd9f6
+grub_spkmodem_fini_output (struct grub_term_output *term __attribute__ ((unused)))
a5bd9f6
+{
a5bd9f6
+  grub_speaker_beep_off ();
a5bd9f6
+  return 0;
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+
a5bd9f6
+
a5bd9f6
+
a5bd9f6
+struct grub_terminfo_output_state grub_spkmodem_terminfo_output =
a5bd9f6
+  {
a5bd9f6
+    .put = put,
a5bd9f6
+    .width = 80,
a5bd9f6
+    .height = 24
a5bd9f6
+  };
a5bd9f6
+
a5bd9f6
+static struct grub_term_output grub_spkmodem_term_output =
a5bd9f6
+  {
a5bd9f6
+    .name = "spkmodem",
a5bd9f6
+    .init = grub_spkmodem_init_output,
a5bd9f6
+    .fini = grub_spkmodem_fini_output,
a5bd9f6
+    .putchar = grub_terminfo_putchar,
a5bd9f6
+    .getxy = grub_terminfo_getxy,
a5bd9f6
+    .getwh = grub_terminfo_getwh,
a5bd9f6
+    .gotoxy = grub_terminfo_gotoxy,
a5bd9f6
+    .cls = grub_terminfo_cls,
a5bd9f6
+    .setcolorstate = grub_terminfo_setcolorstate,
a5bd9f6
+    .setcursor = grub_terminfo_setcursor,
a5bd9f6
+    .flags = GRUB_TERM_CODE_TYPE_ASCII,
a5bd9f6
+    .data = &grub_spkmodem_terminfo_output,
a5bd9f6
+    .normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR,
a5bd9f6
+    .highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR,
a5bd9f6
+  };
a5bd9f6
+
a5bd9f6
+GRUB_MOD_INIT (spkmodem)
a5bd9f6
+{
a5bd9f6
+  grub_term_register_output ("spkmodem", &grub_spkmodem_term_output);
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+
a5bd9f6
+GRUB_MOD_FINI (spkmodem)
a5bd9f6
+{
a5bd9f6
+  grub_term_unregister_output (&grub_spkmodem_term_output);
a5bd9f6
+}
a5bd9f6
diff --git a/include/grub/i386/pit.h b/include/grub/i386/pit.h
a5bd9f6
index ff9b9a6..e1c92cd 100644
a5bd9f6
--- a/include/grub/i386/pit.h
a5bd9f6
+++ b/include/grub/i386/pit.h
a5bd9f6
@@ -22,6 +22,84 @@
a5bd9f6
 #include <grub/types.h>
a5bd9f6
 #include <grub/err.h>
a5bd9f6
 
a5bd9f6
+enum
a5bd9f6
+  {
a5bd9f6
+    /* The PIT channel value ports.  You can write to and read from them.
a5bd9f6
+       Do not mess with timer 0 or 1.  */
a5bd9f6
+    GRUB_PIT_COUNTER_0 = 0x40,
a5bd9f6
+    GRUB_PIT_COUNTER_1 = 0x41,
a5bd9f6
+    GRUB_PIT_COUNTER_2 = 0x42,
a5bd9f6
+    /* The PIT control port.  You can only write to it.  Do not mess with
a5bd9f6
+       timer 0 or 1.  */
a5bd9f6
+    GRUB_PIT_CTRL = 0x43,
a5bd9f6
+    /* The speaker port.  */
a5bd9f6
+    GRUB_PIT_SPEAKER_PORT = 0x61,
a5bd9f6
+  };
a5bd9f6
+
a5bd9f6
+
a5bd9f6
+/* The speaker port.  */
a5bd9f6
+enum
a5bd9f6
+  {
a5bd9f6
+    /* If 0, follow state of SPEAKER_DATA bit, otherwise enable output
a5bd9f6
+       from timer 2.  */
a5bd9f6
+    GRUB_PIT_SPK_TMR2 = 0x01,
a5bd9f6
+    /* If SPEAKER_TMR2 is not set, this provides direct input into the
a5bd9f6
+       speaker.  Otherwise, this enables or disables the output from the
a5bd9f6
+       timer.  */
a5bd9f6
+    GRUB_PIT_SPK_DATA = 0x02,
a5bd9f6
+
a5bd9f6
+    GRUB_PIT_SPK_TMR2_LATCH = 0x20
a5bd9f6
+  };
a5bd9f6
+
a5bd9f6
+/* The PIT control port.  You can only write to it.  Do not mess with
a5bd9f6
+   timer 0 or 1.  */
a5bd9f6
+enum
a5bd9f6
+  {
a5bd9f6
+    GRUB_PIT_CTRL_SELECT_MASK = 0xc0,
a5bd9f6
+    GRUB_PIT_CTRL_SELECT_0 = 0x00,
a5bd9f6
+    GRUB_PIT_CTRL_SELECT_1 = 0x40,
a5bd9f6
+    GRUB_PIT_CTRL_SELECT_2 = 0x80,
a5bd9f6
+
a5bd9f6
+    /* Read and load control.  */
a5bd9f6
+    GRUB_PIT_CTRL_READLOAD_MASK= 0x30,
a5bd9f6
+    GRUB_PIT_CTRL_COUNTER_LATCH = 0x00,	/* Hold timer value until read.  */
a5bd9f6
+    GRUB_PIT_CTRL_READLOAD_LSB = 0x10,	/* Read/load the LSB.  */
a5bd9f6
+    GRUB_PIT_CTRL_READLOAD_MSB = 0x20,	/* Read/load the MSB.  */
a5bd9f6
+    GRUB_PIT_CTRL_READLOAD_WORD = 0x30,	/* Read/load the LSB then the MSB.  */
a5bd9f6
+
a5bd9f6
+    /* Mode control.  */
a5bd9f6
+    GRUB_PIT_CTRL_MODE_MASK = 0x0e,
a5bd9f6
+    /* Interrupt on terminal count.  Setting the mode sets output to low.
a5bd9f6
+       When counter is set and terminated, output is set to high.  */
a5bd9f6
+    GRUB_PIT_CTRL_INTR_ON_TERM = 0x00,
a5bd9f6
+    /* Programmable one-shot.  When loading counter, output is set to
a5bd9f6
+       high.  When counter terminated, output is set to low.  Can be
a5bd9f6
+       triggered again from that point on by setting the gate pin to
a5bd9f6
+       high.  */
a5bd9f6
+    GRUB_PIT_CTRL_PROGR_ONE_SHOT = 0x02,
a5bd9f6
+
a5bd9f6
+    /* Rate generator.  Output is low for one period of the counter, and
a5bd9f6
+       high for the other.  */
a5bd9f6
+    GRUB_PIT_CTRL_RATE_GEN = 0x04,
a5bd9f6
+
a5bd9f6
+    /* Square wave generator.  Output is low for one half of the period,
a5bd9f6
+       and high for the other half.  */
a5bd9f6
+    GRUB_PIT_CTRL_SQUAREWAVE_GEN = 0x06,
a5bd9f6
+    /* Software triggered strobe.  Setting the mode sets output to high.
a5bd9f6
+       When counter is set and terminated, output is set to low.  */
a5bd9f6
+    GRUB_PIT_CTRL_SOFTSTROBE = 0x08,
a5bd9f6
+
a5bd9f6
+    /* Hardware triggered strobe.  Like software triggered strobe, but
a5bd9f6
+       only starts the counter when the gate pin is set to high.  */
a5bd9f6
+    GRUB_PIT_CTRL_HARDSTROBE = 0x0a,
a5bd9f6
+
a5bd9f6
+
a5bd9f6
+    /* Count mode.  */
a5bd9f6
+    GRUB_PIT_CTRL_COUNT_MASK = 0x01,
a5bd9f6
+    GRUB_PIT_CTRL_COUNT_BINARY = 0x00,	/* 16-bit binary counter.  */
a5bd9f6
+    GRUB_PIT_CTRL_COUNT_BCD = 0x01	/* 4-decade BCD counter.  */
a5bd9f6
+  };
a5bd9f6
+
a5bd9f6
 void EXPORT_FUNC(grub_pit_wait) (grub_uint16_t tics);
a5bd9f6
 
a5bd9f6
 #endif /* ! KERNEL_CPU_PIT_HEADER */
a5bd9f6
diff --git a/include/grub/speaker.h b/include/grub/speaker.h
a5bd9f6
new file mode 100644
a5bd9f6
index 0000000..a076fcf
a5bd9f6
--- /dev/null
a5bd9f6
+++ b/include/grub/speaker.h
a5bd9f6
@@ -0,0 +1,47 @@
a5bd9f6
+#ifndef GRUB_SPEAKER_HEADER
a5bd9f6
+#define GRUB_SPEAKER_HEADER 1
a5bd9f6
+
a5bd9f6
+#include <grub/cpu/io.h>
a5bd9f6
+#include <grub/i386/pit.h>
a5bd9f6
+
a5bd9f6
+/* The frequency of the PIT clock.  */
a5bd9f6
+#define GRUB_SPEAKER_PIT_FREQUENCY		0x1234dd
a5bd9f6
+
a5bd9f6
+static inline void
a5bd9f6
+grub_speaker_beep_off (void)
a5bd9f6
+{
a5bd9f6
+  unsigned char status;
a5bd9f6
+
a5bd9f6
+  status = grub_inb (GRUB_PIT_SPEAKER_PORT);
a5bd9f6
+  grub_outb (status & ~(GRUB_PIT_SPK_TMR2 | GRUB_PIT_SPK_DATA),
a5bd9f6
+	     GRUB_PIT_SPEAKER_PORT);
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+static inline void
a5bd9f6
+grub_speaker_beep_on (grub_uint16_t pitch)
a5bd9f6
+{
a5bd9f6
+  unsigned char status;
a5bd9f6
+  unsigned int counter;
a5bd9f6
+
a5bd9f6
+  if (pitch < 20)
a5bd9f6
+    pitch = 20;
a5bd9f6
+  else if (pitch > 20000)
a5bd9f6
+    pitch = 20000;
a5bd9f6
+
a5bd9f6
+  counter = GRUB_SPEAKER_PIT_FREQUENCY / pitch;
a5bd9f6
+
a5bd9f6
+  /* Program timer 2.  */
a5bd9f6
+  grub_outb (GRUB_PIT_CTRL_SELECT_2
a5bd9f6
+	     | GRUB_PIT_CTRL_READLOAD_WORD
a5bd9f6
+	     | GRUB_PIT_CTRL_SQUAREWAVE_GEN
a5bd9f6
+	     | GRUB_PIT_CTRL_COUNT_BINARY, GRUB_PIT_CTRL);
a5bd9f6
+  grub_outb (counter & 0xff, GRUB_PIT_COUNTER_2);		/* LSB */
a5bd9f6
+  grub_outb ((counter >> 8) & 0xff, GRUB_PIT_COUNTER_2);	/* MSB */
a5bd9f6
+
a5bd9f6
+  /* Start speaker.  */
a5bd9f6
+  status = grub_inb (GRUB_PIT_SPEAKER_PORT);
a5bd9f6
+  grub_outb (status | GRUB_PIT_SPK_TMR2 | GRUB_PIT_SPK_DATA,
a5bd9f6
+	     GRUB_PIT_SPEAKER_PORT);
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+#endif
a5bd9f6
diff --git a/util/spkmodem-recv.c b/util/spkmodem-recv.c
a5bd9f6
new file mode 100644
a5bd9f6
index 0000000..cbec3af
a5bd9f6
--- /dev/null
a5bd9f6
+++ b/util/spkmodem-recv.c
a5bd9f6
@@ -0,0 +1,98 @@
a5bd9f6
+#include <stdio.h>
a5bd9f6
+#include <stdlib.h>
a5bd9f6
+#include <string.h>
a5bd9f6
+
a5bd9f6
+/* Compilation:  gcc -o spkmodem-recv spkmodem-recv  */
a5bd9f6
+/* Usage: parecord --channels=1 --rate=48000 --format=s16le | ./spkmodem-recv */
a5bd9f6
+
a5bd9f6
+#define RATE 48000
a5bd9f6
+#define SAMPLES_PER_TRAME 480
a5bd9f6
+#define AMPLITUDE_THRESHOLD 100000
a5bd9f6
+#define FREQ_SEP_MIN 15
a5bd9f6
+#define FREQ_SEP_NOM 20
a5bd9f6
+#define FREQ_SEP_MAX 25
a5bd9f6
+
a5bd9f6
+#define FREQ_DATA_MIN 10
a5bd9f6
+#define FREQ_DATA_THRESHOLD 60
a5bd9f6
+#define FREQ_DATA_MAX 120
a5bd9f6
+#define AMPLITUDE_SAMPLES 2 * SAMPLES_PER_TRAME
a5bd9f6
+
a5bd9f6
+#define THRESHOLD 1000
a5bd9f6
+
a5bd9f6
+#define DEBUG 0
a5bd9f6
+
a5bd9f6
+static signed short trame[2 * SAMPLES_PER_TRAME];
a5bd9f6
+static signed short pulse[2 * SAMPLES_PER_TRAME];
a5bd9f6
+static int ringpos = 0;
a5bd9f6
+static int pos, f1, f2;
a5bd9f6
+static int amplitude = 0;
a5bd9f6
+static int lp = 0;
a5bd9f6
+
a5bd9f6
+static void
a5bd9f6
+read_sample (void)
a5bd9f6
+{
a5bd9f6
+  amplitude -= abs (trame[ringpos]);
a5bd9f6
+  f1 -= pulse[ringpos];
a5bd9f6
+  f1 += pulse[(ringpos + SAMPLES_PER_TRAME) % (2 * SAMPLES_PER_TRAME)];
a5bd9f6
+  f2 -= pulse[(ringpos + SAMPLES_PER_TRAME) % (2 * SAMPLES_PER_TRAME)];
a5bd9f6
+  fread (trame + ringpos, 1, sizeof (trame[0]), stdin);
a5bd9f6
+  amplitude += abs (trame[ringpos]);
a5bd9f6
+
a5bd9f6
+  if (pos ? (trame[ringpos] < -THRESHOLD)
a5bd9f6
+      : (trame[ringpos] > +THRESHOLD))
a5bd9f6
+    {
a5bd9f6
+      pulse[ringpos] = 1;
a5bd9f6
+      pos = !pos;
a5bd9f6
+      f2++;
a5bd9f6
+    }
a5bd9f6
+  else
a5bd9f6
+    pulse[ringpos] = 0;
a5bd9f6
+  ringpos++;
a5bd9f6
+  ringpos %= 2 * SAMPLES_PER_TRAME;
a5bd9f6
+  lp++;
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+int
a5bd9f6
+main ()
a5bd9f6
+{
a5bd9f6
+  int bitn = 7;
a5bd9f6
+  char c = 0;
a5bd9f6
+  int i;
a5bd9f6
+  while (!feof (stdin))
a5bd9f6
+    {
a5bd9f6
+      if (lp > 20000)
a5bd9f6
+	{
a5bd9f6
+	  fflush (stdout);
a5bd9f6
+	  bitn = 7;
a5bd9f6
+	  c = 0;
a5bd9f6
+	  lp = 0;
a5bd9f6
+	}
a5bd9f6
+      if (f2 > 12 && f2 < 25
a5bd9f6
+	  && f1 > 5 && f1 < 120)
a5bd9f6
+	{
a5bd9f6
+#if DEBUG
a5bd9f6
+	  printf ("%d %d %d @%d\n", f1, f2, FREQ_DATA_THRESHOLD,
a5bd9f6
+		  ftell (stdin) - sizeof (trame));
a5bd9f6
+#endif
a5bd9f6
+	  if (f1 < 60)
a5bd9f6
+	    c |= (1 << bitn);
a5bd9f6
+	  bitn--;
a5bd9f6
+	  if (bitn < 0)
a5bd9f6
+	    {
a5bd9f6
+#if DEBUG
a5bd9f6
+	      printf ("<%c, %x>", c, c);
a5bd9f6
+#else
a5bd9f6
+	      printf ("%c", c);
a5bd9f6
+#endif
a5bd9f6
+	      bitn = 7;
a5bd9f6
+	      c = 0;
a5bd9f6
+	    }
a5bd9f6
+	  lp = 0;
a5bd9f6
+	  for (i = 0; i < SAMPLES_PER_TRAME; i++)
a5bd9f6
+	    read_sample ();
a5bd9f6
+	  continue;
a5bd9f6
+	}
a5bd9f6
+      read_sample ();
a5bd9f6
+    }
a5bd9f6
+  return 0;
a5bd9f6
+}
a5bd9f6
-- 
a5bd9f6
1.8.1.4
a5bd9f6