summaryrefslogtreecommitdiffstats
path: root/serial_over_speaker.c
diff options
context:
space:
mode:
Diffstat (limited to 'serial_over_speaker.c')
-rw-r--r--serial_over_speaker.c572
1 files changed, 572 insertions, 0 deletions
diff --git a/serial_over_speaker.c b/serial_over_speaker.c
new file mode 100644
index 0000000..9995123
--- /dev/null
+++ b/serial_over_speaker.c
@@ -0,0 +1,572 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/io.h>
+
+#define BASE_CLK 1193180
+#define F 2200
+
+#define DIVISOR (BASE_CLK /F)
+
+#define PIT_MCR_CH2 (2UL << 6)
+#define PIT_MCR_AM_LOHI (3UL << 4)
+#define PIT_MCR_MODE0 (0UL << 1)
+#define PIT_MCR_MODE3 (3UL << 1)
+
+#define PIT_DATA_CH2 0x42
+#define PIT_MCR 0x43
+
+#define PORTB 0x61
+#define PORTB_SP_GATE (1 << 1)
+#define PORTB_SP_CH2_GATE (1 << 0)
+#define PORTB_SP_STATUS (1 << 5)
+
+static inline uint64_t
+rdtsc (void)
+{
+ uint32_t low, high;
+ __asm__ __volatile__ ("rdtsc":"=a" (low), "=d" (high));
+ return ((uint64_t) high << 32) | low;
+}
+
+static inline void
+sti (void)
+{
+ __asm__ __volatile__ ("sti");
+}
+
+static inline void
+cli (void)
+{
+ __asm__ __volatile__ ("cli");
+}
+
+static inline void
+out_8 (uint16_t port, uint8_t value)
+{
+ asm volatile ("outb %b0,%w1"::"a" (value), "Nd" (port));
+}
+
+
+static inline uint8_t
+in_8 (uint16_t port)
+{
+ uint8_t ret;
+ asm volatile ("inb %w1,%b0":"=a" (ret):"Nd" (port));
+ return ret;
+}
+
+
+static uint64_t t_f, t_b;
+static int p = 0, f = 0;
+static uint64_t tsc_baud, tsc_2f;
+static unsigned baud;
+
+static const char *varicode_fsk[] = {
+ "1010101011", /* 0 - <NUL> */
+ "1011011011", /* 1 - <SOH> */
+ "1011101101", /* 2 - <STX> */
+ "1101110111", /* 3 - <ETX> */
+ "1011101011", /* 4 - <EOT> */
+ "1101011111", /* 5 - <ENQ> */
+ "1011101111", /* 6 - <ACK> */
+ "1011111101", /* 7 - <BEL> */
+ "1011111111", /* 8 - <BS> */
+ "11101111", /* 9 - <TAB> */
+ "11101", /* 10 - <LF> */
+ "1101101111", /* 11 - <VT> */
+ "1011011101", /* 12 - <FF> */
+ "11111", /* 13 - <CR> */
+ "1101110101", /* 14 - <SO> */
+ "1110101011", /* 15 - <SI> */
+ "1011110111", /* 16 - <DLE> */
+ "1011110101", /* 17 - <DC1> */
+ "1110101101", /* 18 - <DC2> */
+ "1110101111", /* 19 - <DC3> */
+ "1101011011", /* 20 - <DC4> */
+ "1101101011", /* 21 - <NAK> */
+ "1101101101", /* 22 - <SYN> */
+ "1101010111", /* 23 - <ETB> */
+ "1101111011", /* 24 - <CAN> */
+ "1101111101", /* 25 - <EM> */
+ "1110110111", /* 26 - <SUB> */
+ "1101010101", /* 27 - <ESC> */
+ "1101011101", /* 28 - <FS> */
+ "1110111011", /* 29 - <GS> */
+ "1011111011", /* 30 - <RS> */
+ "1101111111", /* 31 - <US> */
+ "1", /* 32 - <SPC> */
+ "111111111", /* 33 - ! */
+ "101011111", /* 34 - '"' */
+ "111110101", /* 35 - # */
+ "111011011", /* 36 - $ */
+ "1011010101", /* 37 - % */
+ "1010111011", /* 38 - & */
+ "101111111", /* 39 - ' */
+ "11111011", /* 40 - ( */
+ "11110111", /* 41 - ) */
+ "101101111", /* 42 - * */
+ "111011111", /* 43 - + */
+ "1110101", /* 44 - , */
+ "110101", /* 45 - - */
+ "1010111", /* 46 - . */
+ "110101111", /* 47 - / */
+ "10110111", /* 48 - 0 */
+ "10111101", /* 49 - 1 */
+ "11101101", /* 50 - 2 */
+ "11111111", /* 51 - 3 */
+ "101110111", /* 52 - 4 */
+ "101011011", /* 53 - 5 */
+ "101101011", /* 54 - 6 */
+ "110101101", /* 55 - 7 */
+ "110101011", /* 56 - 8 */
+ "110110111", /* 57 - 9 */
+ "11110101", /* 58 - : */
+ "110111101", /* 59 - ; */
+ "111101101", /* 60 - < */
+ "1010101", /* 61 - = */
+ "111010111", /* 62 - > */
+ "1010101111", /* 63 - ? */
+ "1010111101", /* 64 - @ */
+ "1111101", /* 65 - A */
+ "11101011", /* 66 - B */
+ "10101101", /* 67 - C */
+ "10110101", /* 68 - D */
+ "1110111", /* 69 - E */
+ "11011011", /* 70 - F */
+ "11111101", /* 71 - G */
+ "101010101", /* 72 - H */
+ "1111111", /* 73 - I */
+ "111111101", /* 74 - J */
+ "101111101", /* 75 - K */
+ "11010111", /* 76 - L */
+ "10111011", /* 77 - M */
+ "11011101", /* 78 - N */
+ "10101011", /* 79 - O */
+ "11010101", /* 80 - P */
+ "111011101", /* 81 - Q */
+ "10101111", /* 82 - R */
+ "1101111", /* 83 - S */
+ "1101101", /* 84 - T */
+ "101010111", /* 85 - U */
+ "110110101", /* 86 - V */
+ "101011101", /* 87 - W */
+ "101110101", /* 88 - X */
+ "101111011", /* 89 - Y */
+ "1010101101", /* 90 - Z */
+ "111110111", /* 91 - [ */
+ "111101111", /* 92 - \ */
+ "111111011", /* 93 - ] */
+ "1010111111", /* 94 - ^ */
+ "101101101", /* 95 - _ */
+ "1011011111", /* 96 - ` */
+ "1011", /* 97 - a */
+ "1011111", /* 98 - b */
+ "101111", /* 99 - c */
+ "101101", /* 100 - d */
+ "11", /* 101 - e */
+ "111101", /* 102 - f */
+ "1011011", /* 103 - g */
+ "101011", /* 104 - h */
+ "1101", /* 105 - i */
+ "111101011", /* 106 - j */
+ "10111111", /* 107 - k */
+ "11011", /* 108 - l */
+ "111011", /* 109 - m */
+ "1111", /* 110 - n */
+ "111", /* 111 - o */
+ "111111", /* 112 - p */
+ "110111111", /* 113 - q */
+ "10101", /* 114 - r */
+ "10111", /* 115 - s */
+ "101", /* 116 - t */
+ "110111", /* 117 - u */
+ "1111011", /* 118 - v */
+ "1101011", /* 119 - w */
+ "11011111", /* 120 - x */
+ "1011101", /* 121 - y */
+ "111010101", /* 122 - z */
+ "1010110111", /* 123 - { */
+ "110111011", /* 124 - | */
+ "1010110101", /* 125 - } */
+ "1011010111", /* 126 - ~ */
+ "1110110101", /* 127 - <DEL> */
+ "1110111101", /* 128 - */
+ "1110111111", /* 129 - */
+ "1111010101", /* 130 - */
+ "1111010111", /* 131 - */
+ "1111011011", /* 132 - */
+ "1111011101", /* 133 - */
+ "1111011111", /* 134 - */
+ "1111101011", /* 135 - */
+ "1111101101", /* 136 - */
+ "1111101111", /* 137 - */
+ "1111110101", /* 138 - */
+ "1111110111", /* 139 - */
+ "1111111011", /* 140 - */
+ "1111111101", /* 141 - */
+ "1111111111", /* 142 - */
+ "10101010101", /* 143 - */
+ "10101010111", /* 144 - */
+ "10101011011", /* 145 - */
+ "10101011101", /* 146 - */
+ "10101011111", /* 147 - */
+ "10101101011", /* 148 - */
+ "10101101101", /* 149 - */
+ "10101101111", /* 150 - */
+ "10101110101", /* 151 - */
+ "10101110111", /* 152 - */
+ "10101111011", /* 153 - */
+ "10101111101", /* 154 - */
+ "10101111111", /* 155 - */
+ "10110101011", /* 156 - */
+ "10110101101", /* 157 - */
+ "10110101111", /* 158 - */
+ "10110110101", /* 159 - */
+ "10110110111", /* 160 - */
+ "10110111011", /* 161 - */
+ "10110111101", /* 162 - */
+ "10110111111", /* 163 - */
+ "10111010101", /* 164 - */
+ "10111010111", /* 165 - */
+ "10111011011", /* 166 - */
+ "10111011101", /* 167 - */
+ "10111011111", /* 168 - */
+ "10111101011", /* 169 - */
+ "10111101101", /* 170 - */
+ "10111101111", /* 171 - */
+ "10111110101", /* 172 - */
+ "10111110111", /* 173 - */
+ "10111111011", /* 174 - */
+ "10111111101", /* 175 - */
+ "10111111111", /* 176 - */
+ "11010101011", /* 177 - */
+ "11010101101", /* 178 - */
+ "11010101111", /* 179 - */
+ "11010110101", /* 180 - */
+ "11010110111", /* 181 - */
+ "11010111011", /* 182 - */
+ "11010111101", /* 183 - */
+ "11010111111", /* 184 - */
+ "11011010101", /* 185 - */
+ "11011010111", /* 186 - */
+ "11011011011", /* 187 - */
+ "11011011101", /* 188 - */
+ "11011011111", /* 189 - */
+ "11011101011", /* 190 - */
+ "11011101101", /* 191 - */
+ "11011101111", /* 192 - */
+ "11011110101", /* 193 - */
+ "11011110111", /* 194 - */
+ "11011111011", /* 195 - */
+ "11011111101", /* 196 - */
+ "11011111111", /* 197 - */
+ "11101010101", /* 198 - */
+ "11101010111", /* 199 - */
+ "11101011011", /* 200 - */
+ "11101011101", /* 201 - */
+ "11101011111", /* 202 - */
+ "11101101011", /* 203 - */
+ "11101101101", /* 204 - */
+ "11101101111", /* 205 - */
+ "11101110101", /* 206 - */
+ "11101110111", /* 207 - */
+ "11101111011", /* 208 - */
+ "11101111101", /* 209 - */
+ "11101111111", /* 210 - */
+ "11110101011", /* 211 - */
+ "11110101101", /* 212 - */
+ "11110101111", /* 213 - */
+ "11110110101", /* 214 - */
+ "11110110111", /* 215 - */
+ "11110111011", /* 216 - */
+ "11110111101", /* 217 - */
+ "11110111111", /* 218 - */
+ "11111010101", /* 219 - */
+ "11111010111", /* 220 - */
+ "11111011011", /* 221 - */
+ "11111011101", /* 222 - */
+ "11111011111", /* 223 - */
+ "11111101011", /* 224 - */
+ "11111101101", /* 225 - */
+ "11111101111", /* 226 - */
+ "11111110101", /* 227 - */
+ "11111110111", /* 228 - */
+ "11111111011", /* 229 - */
+ "11111111101", /* 230 - */
+ "11111111111", /* 231 - */
+ "101010101011", /* 232 - */
+ "101010101101", /* 233 - */
+ "101010101111", /* 234 - */
+ "101010110101", /* 235 - */
+ "101010110111", /* 236 - */
+ "101010111011", /* 237 - */
+ "101010111101", /* 238 - */
+ "101010111111", /* 239 - */
+ "101011010101", /* 240 - */
+ "101011010111", /* 241 - */
+ "101011011011", /* 242 - */
+ "101011011101", /* 243 - */
+ "101011011111", /* 244 - */
+ "101011101011", /* 245 - */
+ "101011101101", /* 246 - */
+ "101011101111", /* 247 - */
+ "101011110101", /* 248 - */
+ "101011110111", /* 249 - */
+ "101011111011", /* 250 - */
+ "101011111101", /* 251 - */
+ "101011111111", /* 252 - */
+ "101101010101", /* 253 - */
+ "101101010111", /* 254 - */
+ "101101011011" /* 255 - */
+};
+
+static void
+program_pit_ch2 (uint32_t divisor, uint8_t mode)
+{
+
+ out_8 (PIT_MCR, PIT_MCR_CH2 | PIT_MCR_AM_LOHI | mode);
+
+ out_8 (PIT_DATA_CH2, divisor & 0xff);
+ out_8 (PIT_DATA_CH2, divisor >> 8);
+}
+
+
+static void
+set_mask (int mask, uint8_t what)
+{
+ uint8_t v;
+
+ v = in_8 (PORTB);
+
+ if (mask)
+ v &= ~(what);
+ else
+ v |= what;
+
+ out_8 (PORTB, v);
+}
+
+static void
+count_cycles (unsigned num)
+{
+
+ while (num--)
+ {
+ while (!(in_8 (PORTB) & PORTB_SP_STATUS));
+ while (in_8 (PORTB) & PORTB_SP_STATUS);
+ }
+
+}
+
+
+
+static void
+give_to_pit (void)
+{
+ uint64_t t;
+
+ set_mask (1, PORTB_SP_CH2_GATE);
+ program_pit_ch2 (DIVISOR, PIT_MCR_MODE3);
+
+
+ for (;;)
+ {
+ t = rdtsc ();
+
+ if (t > t_f)
+ {
+ t_f += tsc_2f;
+ f = !f;
+
+ if (f ^ p)
+ {
+ set_mask (1, PORTB_SP_GATE);
+ set_mask (0, PORTB_SP_CH2_GATE);
+ set_mask (0, PORTB_SP_GATE);
+ return;
+ }
+ else
+ {
+ set_mask (0, PORTB_SP_GATE);
+ }
+ }
+ }
+
+}
+
+
+void
+take_from_pit (void)
+{
+ uint64_t t;
+
+ t = rdtsc ();
+
+ while (t_b < t)
+ t_b += tsc_baud;
+
+ while (!(in_8 (PORTB) & PORTB_SP_STATUS)); /*Low */
+ while ((in_8 (PORTB) & PORTB_SP_STATUS)); /*High */
+ t_f = rdtsc ();
+ set_mask (1, PORTB_SP_GATE | PORTB_SP_CH2_GATE);
+
+ t_f += tsc_2f;
+
+ while (t_b < t_f)
+ t_b += tsc_baud;
+}
+
+
+static void
+start (void)
+{
+/* This gets OUT2 from the 8254 high*/
+ set_mask (1, PORTB_SP_CH2_GATE);
+ t_b = t_f = rdtsc ();
+}
+
+
+
+
+static void
+send_bit_raw (int b)
+{
+ uint64_t t;
+
+ if (!b)
+ p = !p;
+
+ t_b += tsc_baud;
+
+ while ((t = rdtsc ()) < t_b)
+ {
+ if (t > t_f)
+ {
+ t_f += tsc_2f;
+ f = !f;
+ set_mask (f ^ p, PORTB_SP_GATE);
+ }
+ }
+
+}
+
+static void
+send_bit (int b)
+{
+ send_bit_raw (b);
+}
+
+
+static void
+stop (void)
+{
+ set_mask (1, PORTB_SP_GATE | PORTB_SP_CH2_GATE);
+}
+
+
+static void
+send_sync (int bits, int bit)
+{
+
+ if (bit == 2)
+ {
+ bits >>= 1;
+
+ while (bits--)
+ {
+ send_bit (1);
+ send_bit (0);
+ }
+ }
+ else
+ {
+ while (bits--)
+ send_bit (bit);
+ }
+}
+
+static void
+send_char (uint8_t c)
+{
+ const char *v = varicode_fsk[c];
+
+ if (!v)
+ return;
+
+ while (*v)
+ send_bit (*(v++) - '0');
+
+ send_bit (0);
+ send_bit (0);
+}
+
+
+void
+modem_xmit (void *_buf, size_t buf_len)
+{
+ uint8_t *buf = _buf;
+ take_from_pit ();
+
+ send_sync (baud / 5, 0);
+ send_char (0);
+
+ while (buf_len--)
+ send_char (*(buf++));
+
+ give_to_pit ();
+}
+
+
+
+void
+modem_on (unsigned _baud)
+{
+ uint64_t tsc, tsc_start;
+
+ baud = _baud;
+
+ program_pit_ch2 (DIVISOR, PIT_MCR_MODE3);
+ set_mask (0, PORTB_SP_GATE | PORTB_SP_CH2_GATE);
+
+ count_cycles (10);
+ tsc_start = rdtsc ();
+ count_cycles (F);
+ tsc = rdtsc ();
+
+ tsc -= tsc_start;
+
+ tsc_baud = (tsc / baud);
+ tsc_2f = (tsc / (2 * F));
+
+
+ start ();
+ send_sync (baud * 2, 0);
+
+ give_to_pit ();
+}
+
+void
+modem_off (void)
+{
+ take_from_pit ();
+ send_sync (150, 1);
+ stop ();
+}
+
+int
+main (int argc, char *argv[])
+{
+ char msg[] = "The quick brown fox jumps over the lazy dog\r\n";
+
+ iopl (3);
+ cli ();
+
+ modem_on (125);
+ modem_xmit (msg, sizeof (msg));
+ modem_off ();
+
+ sti ();
+
+ return 0;
+}