Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 31 Aug 2007 09:42:51 +0000
From:      Poul-Henning Kamp <phk@phk.freebsd.dk>
To:        arch@freebsd.org
Subject:   Proof of concept: soundcard as console device
Message-ID:  <8412.1188553371@critter.freebsd.dk>

next in thread | raw e-mail | index | archive | help

Yesterday while I was futzing with my new laptop and ACPI suspend/resume,
I once again found myself cursing the nutjob who took away our serial
ports.

Then my mind wandered around a bit and suddenly it struck me: all
modern hardware have built in AC97 sound hardware, and it offers a
channel with a pretty decent bandwidth.

This morning I ran a simple experiment, from userland, but the
result show clearly that the idea is workable.

The transmission format I used: send a negative transient to start
a character and after N samples, send a positive transient, where
N is the ASCII value of the character + a small constant.

I have attached my proof-of-concept code, which uses the two stereo
channels as a differential wire (not sure if this is a actually
good idea).

In my first attempt, I was able to transmit around 320 characters
per second with something that looks like 1% error rate.

What's left to do:

	* optimize transmission schema.

		This can be done with two computers, a jack-jack cable
		and a bit of C-coding.

		The difference in sample rate between the two machines
		causes off-by-one errors.  I don't know of a peak detector
		which interpolates can resolve this, of if we need use
		oversampling in the receiver.

		The two pulses can have two different polarities each,
		so we can transmit two bits just that way, this could speed
		up transmission by a factor of four, if we can find a way
		to synchronize properly.

		The two stereo channels can be used independently, offering
		a doubling in speed.

	* write a kernel console driver which talks to the sound hardware
	        without using interrupts.

		I don't know what the hardware interface to a sound card
		looks like, but I suspect this might be fairly easy.

Even if the connection is one-way, kernel-hackers like me will worship
any person who completes this task.

Have at it...

Poul-Henning

-----------------------------------------------------------------------
/* proof of concept transmission code */
#include <stdio.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/soundcard.h>

#define OFF 5

static short int buf[2*128 + 2 * OFF];

int
main(int argc __unused, char **argv __unused)
{
	int fd_dsp;
	int i, j, k, c;

	fd_dsp = open("/dev/dsp0.1", O_RDWR);
	if (fd_dsp < 0)
		err(1, "open /dev/dsp");

	i = ioctl(fd_dsp, SNDCTL_DSP_RESET, &j);
	assert(i == 0);

	j = 2;
	i = ioctl(fd_dsp, SNDCTL_DSP_CHANNELS, &j);
	assert(i == 0);

	j = 44100;
	i = ioctl(fd_dsp, SNDCTL_DSP_SPEED, &j);
	assert(i == 0);

	j = 16;
	i = ioctl(fd_dsp, SNDCTL_DSP_SETFMT, &j);
	assert(i == 0);


	while (1) {
		c = getchar();
		if (c == EOF)
			break;
		buf[OFF] =  32000;
		buf[OFF + 1] = -32000;

		buf[OFF + 2 * c] = -32000;
		buf[OFF + 2 * c + 1] =  32000;

		i = write(fd_dsp, buf, sizeof buf);
		assert(i == sizeof buf);

		buf[OFF + 2 * c] = 0;
		buf[OFF + 1 + 2 * c] = 0;
	}

	exit (0);
}
-----------------------------------------------------------------------
/* proof of concept reception code */
#include <assert.h>
#include <stdio.h>

static int
sample(FILE *f, const char *p)
{
        short l, r;
        int i, s;

        i = fread(&l, sizeof l, 1, stdin);
        assert(i == 1);
        i = fread(&r, sizeof l, 1, stdin);
        assert(i == 1);
        s = l;
        s -= r;
        if (0 && p != NULL)
                printf("%6d %s\n", s, p);
        return (s);
}
 
static void
find_neg_peak(FILE *f)
{
        int s, sl;

        while (1) {
                s = sample(stdin, "v");
                if (s < -10000)
                        break;
        }
        sl = s;
        while (1) {
                s = sample(stdin, "N");
                if (s > sl)
                        return;
                sl = s;
        }
}

static int
find_pos_peak(FILE *f)
{
        int s, sl, k;
 
        k = 0;
        while (1) {
                k++;
                s = sample(stdin, "^");
                if (s > 10000)
                        break;
        }
        sl = s;
        while (1) {
                k++;
                s = sample(stdin, "P");
                if (s < sl)
                        return (k);
                sl = s;
        }
}
 
 
int
main(int argc __unused, char **argv)
{
        short l, r;
        int i, k, p, s, sl;
 
        k = 0;
        p = 0;
        while (1) {
                find_neg_peak(stdin);
                k = find_pos_peak(stdin);
                if (k == 10)
                        printf("\\n\n");
                else if (k >= ' ' && k <= '~')
                        printf("%c", k);
                else
                        printf("\\x%02x", k);
        }
        exit (0);
}
-----------------------------------------------------------------------




-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
phk@FreeBSD.ORG         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe
Never attribute to malice what can adequately be explained by incompetence.



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?8412.1188553371>