Pull to refresh

Звуковая карта в качестве консольного устройства

Reading time 3 min
Views 1.1K
Poul-Henning Kamp в списке рассылки freebsd-arch продемонстрировал пример кода, позволяющего осуществлять консольный ввод-вывод через звуковую карту. Оригинал его письма здесь, а я предлагаю свой, по возможности максимально близкий к оригиналу, перевод. Некоторые слова я перевести не смог, поэтому оставляю в оригинале и в скобках даю свои предположения.

Вчера, когда я страдал фигней, занимаясь со своим новым лэптопом и функциями приостановки/возобновления ACPI, я снова поймал себя на том, что проклинаю того чокнутого, который убрал (с лэптопов — прим. перев.) наши последовательные порты.

Я подумал немного и меня внезапно озарило: все современные устройства имеют встроенную поддержку звукового оборудования AC97, предоставляющего довольно хорошую пропускную способность.

Этим утром я начал простой эксперимент. Результат ясно дает понять, что идея рабочая, хотя и работает from userland (может быть, в пространстве пользователя? определения Google дают именно такой ответ — прим. перев.).

Я использовал следующий формат для передачи: посылка кратковременного отрицательного перепада для начала символа, а затем N дискрет, затем положительный перепад. N — ASCII-значение символа + маленькая константа.

Я прилагаю код, подтверждающий концепцию(proof-of-concept в оригинале — прим. перев.), который использует два стереоканала на дифференциальной паре (не уверен, что это хорошая идея).

В первой попытке, я был способен передавать около 320 символов в секунду с 1% ошибок.

Что осталось сделать:

    * оптимизировать схему передачи.

        Это может быть сделано с помощью двух компьютеров, кабеля jack-в-jack и немного кода на C.

        Различие в частоте дискрет на у двух компьютеров приводит к ошибке занижения или завышения на единицу. Я не знаю, может ли детектор пиков с интерполяцией решить эту проблему, либо мы будем вынуждены использовать дискретизацию с запасом по частоте (oversampling — прим. перев.) в приемнике.

        Два импульса могут иметь различные полярности, т.е. мы можем передавать два бита, что может повысить скорость передачи на множитель четырех (factor of four скорее всего означает именно это — прим. перев.), если найдем способ должным образом выполнить синхронизацию.

        Два стереоканала могут быть использованы независимо, вдвое увеличивая скорость.

    * написать дравер консоли уровня ядра, работающий со звуковым оборудованием без использования прерываний.

        Я не знаю, как выглядит интерфейс к звуковой карте, но я подозреваю, что довольно просто.

Даже если соединение будет однонаправленное, хакеры ядра, вроде меня, будут почитать любого, который закончит эту работу.

Налетай!

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);
}

Tags:
Hubs:
+8
Comments 12
Comments Comments 12

Articles