/*****************************************************************************/

/*
 *      fpgatests.c  -- HDLC packet radio modem for EPP using FPGA utility library.
 *
 *      Copyright (C) 1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Please note that the GPL allows you to use the driver, NOT the radio.
 *  In order to use the radio, you need a license from the communications
 *  authority of your country.
 *
 */

/*****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <math.h>
#include <unistd.h>
#include <stdarg.h>

#include "parport.h"
#include "fpga.h"
#include "util.h"

/* ---------------------------------------------------------------------- */

/*
 *The doubly scrambled BERR tests 
 * (once in software, once in the modem)
 * exercise the data exchange between modem and PC
 */

/* note: this is the reverse direction */

#if 0
#define SCRAM_TAP1  0x00001  /* X^17 */
#define SCRAM_TAPN  0x21000  /* X^0+X^5 */

#define DESCRAM_TAPSH1  0
#define DESCRAM_TAPSH2  5
#define DESCRAM_TAPSH3  17
#endif

#define DESCRAM_TAPSH1  0
#define DESCRAM_TAPSH2  7
#define DESCRAM_TAPSH3  10

#define SCRAM_TAP1  1
#define SCRAM_TAPN  ((1<<DESCRAM_TAPSH3)|(1<<(DESCRAM_TAPSH3-DESCRAM_TAPSH2)))

/* ---------------------------------------------------------------------- */

#include "epp_fpga.h"

/* ---------------------------------------------------------------------- */

static unsigned readboundaryword(const unsigned char *b, const unsigned *x, unsigned num)
{
	unsigned mask = 1, ret = 0;

        for (; num > 0; num--, x++, mask <<= 1)
		if (readboundary(b, *x))
			ret |= mask;
	return ret;
}

static void writeboundaryword(unsigned char *b, const unsigned *x, unsigned num, unsigned v)
{
	unsigned sh = 0;

        for (; num > 0; num--, x++, sh++)
		writeboundary(b, *x, (v >> sh) & 1);
}

/* ========= BOUNDARY SCAN ONLY TESTS =================================== */

/*
 * RAM-Test
 */

static const unsigned int ram_addr_o[15] = 
{
	FPGA_PIN_RAMA0_O,
	FPGA_PIN_RAMA1_O,
	FPGA_PIN_RAMA2_O,
	FPGA_PIN_RAMA3_O,
	FPGA_PIN_RAMA4_O,
	FPGA_PIN_RAMA5_O,
	FPGA_PIN_RAMA6_O,
	FPGA_PIN_RAMA7_O,
	FPGA_PIN_RAMA8_O,
	FPGA_PIN_RAMA9_O,
	FPGA_PIN_RAMA10_O,
	FPGA_PIN_RAMA11_O,
	FPGA_PIN_RAMA12_O,
	FPGA_PIN_RAMA13_O,
	FPGA_PIN_RAMA14_O
};

static const unsigned int ram_addr_t[15] = 
{
	FPGA_PIN_RAMA0_T,
	FPGA_PIN_RAMA1_T,
	FPGA_PIN_RAMA2_T,
	FPGA_PIN_RAMA3_T,
	FPGA_PIN_RAMA4_T,
	FPGA_PIN_RAMA5_T,
	FPGA_PIN_RAMA6_T,
	FPGA_PIN_RAMA7_T,
	FPGA_PIN_RAMA8_T,
	FPGA_PIN_RAMA9_T,
	FPGA_PIN_RAMA10_T,
	FPGA_PIN_RAMA11_T,
	FPGA_PIN_RAMA12_T,
	FPGA_PIN_RAMA13_T,
	FPGA_PIN_RAMA14_T
};

static const unsigned int ram_data_t[8] = 
{
	FPGA_PIN_RAMD0_T,
	FPGA_PIN_RAMD1_T,
	FPGA_PIN_RAMD2_T,
	FPGA_PIN_RAMD3_T,
	FPGA_PIN_RAMD4_T,
	FPGA_PIN_RAMD5_T,
	FPGA_PIN_RAMD6_T,
	FPGA_PIN_RAMD7_T
};

static const unsigned int ram_data_o[8] = 
{
	FPGA_PIN_RAMD0_O,
	FPGA_PIN_RAMD1_O,
	FPGA_PIN_RAMD2_O,
	FPGA_PIN_RAMD3_O,
	FPGA_PIN_RAMD4_O,
	FPGA_PIN_RAMD5_O,
	FPGA_PIN_RAMD6_O,
	FPGA_PIN_RAMD7_O
};

static const unsigned int ram_data_i[8] = 
{
	FPGA_PIN_RAMD0_I,
	FPGA_PIN_RAMD1_I,
	FPGA_PIN_RAMD2_I,
	FPGA_PIN_RAMD3_I,
	FPGA_PIN_RAMD4_I,
	FPGA_PIN_RAMD5_I,
	FPGA_PIN_RAMD6_I,
	FPGA_PIN_RAMD7_I
};

/* ---------------------------------------------------------------------- */

static unsigned int ramaccess(unsigned addr, unsigned data, unsigned write)
{
	unsigned char bd1[FPGA_BOUNDSIZE], bd2[FPGA_BOUNDSIZE];

	memcpy(bd1, fpga_safebound, FPGA_BOUNDSIZE);
	writeboundaryword(bd1, ram_addr_t, 15, 0);
	writeboundaryword(bd1, ram_addr_o, 15, addr);
	writeboundary(bd1, FPGA_PIN_RAMCE_T, 0);
	writeboundary(bd1, FPGA_PIN_RAMOE_T, 0);
	writeboundary(bd1, FPGA_PIN_RAMWR_T, 0);
	writeboundary(bd1, FPGA_PIN_RAMCE_O, 0);
	writeboundary(bd1, FPGA_PIN_RAMOE_O, (write) & 1);
	writeboundary(bd1, FPGA_PIN_RAMWR_O, (!write) & 1);
	if (write) {
		writeboundaryword(bd1, ram_data_t, 8, 0);
		writeboundaryword(bd1, ram_data_o, 8, data);
	}
	boundary(FPGA_BOUND, bd1, bd2, 0);
	writeboundary(bd1, FPGA_PIN_RAMCE_O, 1);
	writeboundary(bd1, FPGA_PIN_RAMOE_O, 1);
	writeboundary(bd1, FPGA_PIN_RAMWR_O, 1);	
	boundary(FPGA_BOUND, bd1, bd2, 0);
	if (write)
		return 0;
	return readboundaryword(bd2, ram_data_i, 8);
}

static unsigned char *to_bin(unsigned char *buf, unsigned val, unsigned num)
{
	unsigned int i;

	for (i = 0; i < num; i++, val <<= 1)
		buf[i] = ((val >> (num-1)) & 1) + '0';
	buf[num] = 0;
	return buf;
}

static unsigned ramtestdata(unsigned data)
{
	unsigned val;
	unsigned char s[10];

	ramaccess(0, data, 1);
	val = ramaccess(0, 0, 0);
	tprintf("Testing data pattern 0x%02x read 0x%02x  failure mask %8s\n",
	       data, val, to_bin(s, val ^ data, 8));
	idle_callback(0);
	return val ^ data;
}

static unsigned ramtestaddr(void)
{
	unsigned char ramd[32768];
	unsigned int i, j, k, l, mask = 0;
	unsigned char s[16];

	for (i = 0; i < 32768; i++) {
		if (!(i & 0xff)) {
			tprintf("Writing RAM address 0x%04x...\r", i);
			if (idle_callback(0))
				return mask;
		}
		ramd[i] = random();
		ramaccess(i, ramd[i], 1);
	}
	for (i = 0; i < 32768; i++) {
		if (!(i & 0xff)) {
			tprintf("Reading RAM address 0x%04x...\r", i);
			if (idle_callback(0))
				return mask;
		}
		j = ramaccess(i, 0, 0);
		if (j == ramd[i])
			continue;
		l = 0xffff;
		for (k = 0; k < 32768; k++) {
			if (ramd[k] != j)
				continue;
			if (hweight32(l) > hweight32(k ^ i))
				l = k ^ i;
		}
		mask |= k;
		tprintf("RAM address error at 0x%04x minimum error mask %15s\n", 
		       i, to_bin(s, k, 15));
		if (idle_callback(0))
			return mask;
	}
	return mask;
}

static int ramtest(struct adapter_config *cfg)
{
	unsigned mask;
	unsigned char s[16];
	int i;

	if ((i = adapter_start_no_firmware()))
		return i;
	tprintf("Performing RAM test...\n");
	mask = ramtestdata(0xff);
	mask |= ramtestdata(0x00);
	mask |= ramtestdata(0x55);
	mask |= ramtestdata(0xaa);
	mask |= ramtestdata(0xcc);
	mask |= ramtestdata(0x33);
	if (mask) {
		tprintf("RAM data error mask %s\nRAM data test failed, exiting\n", 
		       to_bin(s, mask, 8));
		reset_modem();
		return -4;
	}
	if ((mask = ramtestaddr())) {
		tprintf("RAM address error mask %s\nRAM address test failed, exiting\n", 
			to_bin(s, mask, 15));
		reset_modem();
		return -5;
	}
	if (!idle_callback(0))
		tprintf("\nRAM test passed\n");
	reset_modem();
	return 0;
}

/* ---------------------------------------------------------------------- */
/*
 * LED test
 */

static int ledtest(struct adapter_config *cfg)
{
	unsigned led = 0;
	unsigned char bd1[FPGA_BOUNDSIZE], bd2[FPGA_BOUNDSIZE];
	int i;

	if ((i = adapter_start_no_firmware()))
		return i;
	memcpy(bd1, fpga_safebound, FPGA_BOUNDSIZE);
	tprintf("LED test, DISCONNECT PTT!!\n");
	for (;;) {
		led = (++led) & 3;
		writeboundary(bd1, FPGA_PIN_LEDSTA_O, led != 0);
		writeboundary(bd1, FPGA_PIN_LEDCON_O, led != 1);
		writeboundary(bd1, FPGA_PIN_LEDDCD_O, led != 2);
		writeboundary(bd1, FPGA_PIN_LEDPTT_O, led == 3);
		writeboundary(bd1, FPGA_PIN_LEDCON_T, 0);
		writeboundary(bd1, FPGA_PIN_LEDSTA_T, 0);
		writeboundary(bd1, FPGA_PIN_LEDDCD_T, 0);
		writeboundary(bd1, FPGA_PIN_LEDPTT_T, 0);
		boundary(FPGA_BOUND, bd1, bd2, 0);
		tprintf("Lighting LED %s\r", ((const char *[4]){ "STA", "CON", "DCD", "PTT"})[led]);
		if (idle_callback(500000)) {
			reset_modem();
			return 0;
		}
	}
}

/* ---------------------------------------------------------------------- */
/*
 * DAC test
 */

static const unsigned int dac_t[7] = 
{
	FPGA_PIN_DAC0_T,
	FPGA_PIN_DAC1_T,
	FPGA_PIN_DAC2_T,
	FPGA_PIN_DAC3_T,
	FPGA_PIN_DAC4_T,
	FPGA_PIN_DAC5_T,
	FPGA_PIN_DAC6_T
};

static const unsigned int dac_o[7] = 
{
	FPGA_PIN_DAC0_O,
	FPGA_PIN_DAC1_O,
	FPGA_PIN_DAC2_O,
	FPGA_PIN_DAC3_O,
	FPGA_PIN_DAC4_O,
	FPGA_PIN_DAC5_O,
	FPGA_PIN_DAC6_O
};


static int dactest(struct adapter_config *cfg)
{
	unsigned phase = 0, data, rot = 0;
	unsigned char bd1[FPGA_BOUNDSIZE], bd2[FPGA_BOUNDSIZE];
	int i;

	if ((i = adapter_start_no_firmware()))
		return i;
	memcpy(bd1, fpga_safebound, FPGA_BOUNDSIZE);
	tprintf("DAC test\n");
	for (;;) {
		data = 64 + 63 * sin(M_PI / 128 * phase);
		writeboundaryword(bd1, dac_t, 7, 0);
		writeboundaryword(bd1, dac_o, 7, data);
		boundary(FPGA_BOUND, bd1, bd2, 0);
		phase = (phase + 1) & 0xff;
		if (phase)
			continue;
		rot = (rot + 1) & 3;
		tprintf("%c\r", "|\\-/"[rot]);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
}

/* ---------------------------------------------------------------------- */
/*
 * Parport Signal Tests
 */

static const unsigned int ppinsig[] = {
	FPGA_PIN_EPPNWRITE_I,    /* strobe */
	FPGA_PIN_EPPNDATASTB_I,  /* nAutoFD */
	FPGA_PIN_EPPNRESET_I,    /* nInit */
	FPGA_PIN_EPPNADDRSTB_I,  /* SlctIn */
	FPGA_PIN_EPPDATA3_I,
	FPGA_PIN_EPPDATA4_I,
	FPGA_PIN_EPPDATA5_I,
	FPGA_PIN_EPPDATA6_I,
	FPGA_PIN_EPPDATA7_I
};

static const unsigned int ppoutsig[] = {
	FPGA_PIN_EPPNERR_O,
	FPGA_PIN_SPPPE_O
};

static const unsigned int ppoutsigt[] = {
	FPGA_PIN_EPPNERR_T,
	FPGA_PIN_SPPPE_T
};

static const char *ppsigstr[] = {
	"nError (15)", "PE (12)", 
	"nWrite/Strobe (1)", "nDataStb/nAutoFD (14)", "nReset/nInit (16)", "nAddrStb/nSlctIn (17)", 
	"D3 (5)", "D4 (6)", "D5 (7)", "D6 (8)", "D7 (9)"
};

static unsigned ppsigcheckone(unsigned val)
{
	unsigned char bd1[FPGA_BOUNDSIZE], bd2[FPGA_BOUNDSIZE];
	unsigned v;

	memcpy(bd1, fpga_safebound, FPGA_BOUNDSIZE);
	writeboundaryword(bd1, ppoutsigt, 2, 0);
	writeboundaryword(bd1, ppoutsig, 2, val);
	parport_write_control(((val >> 2) & 0xf) ^ (LPTCTRL_WRITE|LPTCTRL_ADDRSTB|LPTCTRL_DATASTB));
	boundary(FPGA_BOUND, bd1, bd2, val >> 3);
	v = parport_read_status();
	v = ((v >> 3) & 1) | ((v >> 4) & 2);
	boundary(FPGA_BOUND, bd1, bd2, val >> 3);
        parport_write_control(LPTCTRL_PROGRAM);
	return v | ((readboundaryword(bd2, ppinsig, 9) << 2) & 0x7fc);
}

static int ppsigtest(struct adapter_config *cfg)
{
	unsigned val, v2, err[11];
	int i, ret = 0;

	if ((i = adapter_start_no_firmware()))
		return i;
	for (i = 0; i < 11; i++)
		err[i] = 0;
	tprintf("Checking parallel port signals...\n");
	for (val = 0; val < 0x800; val++) {
		/* make sure the FPGA is not reset */
		if (!(val & ((LPTCTRL_ADDRSTB|LPTCTRL_PROGRAM)<<2)))
			continue;
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
		v2 = ppsigcheckone(val);
#if 0
		tprintf("check: 0x%03x  ret: 0x%03x  diff: 0x%03x\n", val, v2, v2 ^ val);
#endif
		v2 ^= val;
		if (val & 0x400)
			v2 &= 0x7fc;
		if (!v2)
			continue;
		ret = -1;
		for (i = 0; i < 11; i++)
			err[i] += ((v2 >> i) & 1);
	}
	tprintf("Parport Signal Errors:\n");
	for (i = 0; i < 11; i++)
		tprintf("%-22s  %5d\n", ppsigstr[i], err[i]);
	reset_modem();
	return ret;
}

/* ========= EPP CHECK FIRMWARE TESTS =================================== */

static int chkcountertest(struct adapter_config *cfg)
{
	struct timeval time1, time2;
	unsigned int cnt0 = 0, cnt1 = 0, cnt2 = 0, cnt3 = 0, 
		prevcnt0 = 0, prevcnt1 = 0, prevcnt2 = 0, prevcnt3 = 0,
		freq0 = 0, freq1 = 0, freq2 = 0, freq3 = 0, usec;
	int i;
	unsigned char eio[3];
	
	if ((i = adapter_start_eppchk()))
		return i;
	/* reset counters */
	eio[0] = 0;
	if (parport_epp_write_addr(eio, 1) != 1)
		goto errret;
	tprintf("Frequency counter\n"
	       "Counter value                Frequency (Hz)\n"
	       "fclk__ clk___ RxC___ TxC___  fclk____ clk_____ RxC_____ TxC_____\n");
	if (gettime(&time1)) {
		reset_modem();
		return -6;
	}
	for (;;) {
		/* read counters */
		eio[0] = 4;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_data(eio, 3) != 3)
			goto errret;
		cnt0 = eio[0] | (((unsigned int)eio[1]) << 8) | (((unsigned int)eio[2]) << 16);
		eio[0] = 5;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_data(eio, 3) != 3)
			goto errret;
		cnt1 = eio[0] | (((unsigned int)eio[1]) << 8) | (((unsigned int)eio[2]) << 16);
		eio[0] = 6;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_data(eio, 3) != 3)
			goto errret;
		cnt2 = eio[0] | (((unsigned int)eio[1]) << 8) | (((unsigned int)eio[2]) << 16);
		eio[0] = 7;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_data(eio, 3) != 3)
			goto errret;
		cnt3 = eio[0] | (((unsigned int)eio[1]) << 8) | (((unsigned int)eio[2]) << 16);
		if (gettime(&time2)) {
			reset_modem();
			return -6;
		}
		usec = ((time2.tv_sec - time1.tv_sec) % 60 * 1000000)
			+ time2.tv_usec - time1.tv_usec;
		if (usec >= 1000000) {
			time1 = time2;
			freq0 = ((cnt0 - prevcnt0) & 0xffffff) * 1000000.0 / usec;
			prevcnt0 = cnt0;
			freq1 = ((cnt1 - prevcnt1) & 0xffffff) * 8000000.0 / usec;
			prevcnt1 = cnt1;
			freq2 = ((cnt2 - prevcnt2) & 0xffffff) * 1000000.0 / usec;
			prevcnt2 = cnt2;
			freq3 = ((cnt3 - prevcnt3) & 0xffffff) * 1000000.0 / usec;
			prevcnt3 = cnt3;
		}
		tprintf("%06x %06x %06x %06x  %8d %8d %8d %8d\r", 
		       cnt0, cnt1, cnt2, cnt3, freq0, freq1, freq2, freq3);
		if (idle_callback(200000)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * LED test
 */

static int chkledtest(struct adapter_config *cfg)
{
	unsigned led = 0;
	int i;
	unsigned char eio;
	
	if ((i = adapter_start_eppchk()))
		return i;
	tprintf("LED test, DISCONNECT PTT!!\n");
	eio = 1;
	if (parport_epp_write_addr(&eio, 1) != 1)
		goto errret;
	for (;;) {
		led = (++led) & 3;
		eio = 1 << led;
		if (parport_epp_write_data(&eio, 1) != 1)
			goto errret;
		tprintf("Lighting LED %s\r", ((const char *[4]){ "DCD", "CON", "STA", "PTT"})[led]);
		if (idle_callback(500000)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * DAC test
 */

static int chkdactest(struct adapter_config *cfg)
{
	unsigned phase = 0, rot = 0;
	unsigned char eio;
	int i;
	
	if ((i = adapter_start_eppchk()))
		return i;
	tprintf("DAC test\n");
	eio = 2;
	if (parport_epp_write_addr(&eio, 1) != 1)
		goto errret;
	for (;;) {
		eio = 128 + 127 * sin(M_PI / 128 * phase);
		if (parport_epp_write_data(&eio, 1) != 1)
			goto errret;
		phase = (phase + 1) & 0xff;
		if (phase)
			continue;
		rot = (rot + 1) & 3;
		tprintf("%c\r", "|\\-/"[rot]);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * Register test
 */

static int chkregtest(struct adapter_config *cfg)
{
	unsigned data, read, cnt = 0, err = 0;
	unsigned char eio[5];
	int i;
	
	if ((i = adapter_start_eppchk()))
		return i;
	tprintf("Checking LED register\n");
	eio[0] = 1;
	if (parport_epp_write_addr(eio, 1) != 1)
		goto errret;
	for (data = 0; data < 0x100; data += 0x40) {
		eio[0] = data;
		if (parport_epp_write_data(eio, 1) != 1)
			goto errret;
		read = parport_read_status();
		tprintf("LED register: 0x%02x  LPT port status: 0x%02x  %sOK\n", data, read, 
		       (((data ^ (read << 4)) & 0x80) | ((data ^ (read << 1)) & 0x40)) ? "NOT " : "");
	}
	tprintf("EPP register read/write test\n");
	for (;;) {
		eio[0] = data;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(eio, 5) != 5)
			goto errret;
		if ((data ^ eio[0]) & 0xff)
			err++;
		cnt++;
		data++;
		tprintf("Count: %6d  Errors: %6d\r", cnt, err);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * RAM-Test
 */

static int chkramaccess(unsigned addr, unsigned data, unsigned write)
{
	unsigned char rd;
	unsigned char eio[3];

	eio[0] = 8;
	if (parport_epp_write_addr(eio, 1) != 1)
		return -1;
	eio[0] = addr & 0xff;
	if (parport_epp_write_data(eio, 1) != 1)
		return -1;
	eio[0] = 9;
	if (parport_epp_write_addr(eio, 1) != 1)
		return -1;
	eio[0] = addr >> 8;
	if (parport_epp_write_data(eio, 1) != 1)
		return -1;
	if (write) {
		eio[0] = 11;
		if (parport_epp_write_addr(eio, 1) != 1)
			return -1;
		eio[0] = data;
		if (parport_epp_write_data(eio, 1) != 1)
			return -1;
		eio[0] = 8;
		eio[1] = 10;
		eio[2] = 8;
		if (parport_epp_write_addr(eio, 3) != 3)
			return -1;
		return 0;
	}
	eio[0] = 11;
	if (parport_epp_write_addr(eio, 1) != 1)
		return -1;
	if (parport_epp_read_data(eio, 1) != 1)
		return -1;
	rd = eio[0];
	eio[0] = 8;
	if (parport_epp_write_addr(eio, 1) != 1)
		return -1;
	return rd;
}

static int chkramtestdata(unsigned data)
{
	int val;
	unsigned char s[10];

	if (chkramaccess(0, data, 1))
		return -1;
	val = chkramaccess(0, 0, 0);
	if (val == -1)
		return -1;
	tprintf("Testing data pattern 0x%02x read 0x%02x  failure mask %8s\n",
	       data, val, to_bin(s, val ^ data, 8));
	idle_callback(0);
	return val ^ data;
}

static int chkramtestaddr(void)
{
	unsigned char ramd[32768];
	unsigned int i, j, k, l, mask = 0;
	unsigned char s[16];

	for (i = 0; i < 32768; i++) {
		if (!(i & 0xff)) {
			tprintf("Writing RAM address 0x%04x...\r", i);
			if (idle_callback(0))
				return mask;
		}
		ramd[i] = random();
		if (chkramaccess(i, ramd[i], 1))
			return -1;
	}
	for (i = 0; i < 32768; i++) {
		if (!(i & 0xff)) {
			tprintf("Reading RAM address 0x%04x...\r", i);
			if (idle_callback(0))
				return mask;
		}
		j = chkramaccess(i, 0, 0);
		if (j == -1)
			return -1;
		if (j == ramd[i])
			continue;
		l = 0xffff;
		for (k = 0; k < 32768; k++) {
			if (ramd[k] != j)
				continue;
			if (hweight32(l) > hweight32(k ^ i))
				l = k ^ i;
		}
		mask |= k;
		tprintf("RAM address error at 0x%04x minimum error mask %15s\n", 
		       i, to_bin(s, k, 15));
		if (idle_callback(0))
			return mask;
	}
	return mask;
}

static int chkramtest(struct adapter_config *cfg)
{
	int mask, val;
	unsigned char s[16];
	int i;
	
	if ((i = adapter_start_eppchk()))
		return i;
	tprintf("Performing RAM test...\n");
	if ((mask = chkramtestdata(0xff)) == -1)
		goto errret;
	if ((val = chkramtestdata(0x00)) == -1)
		goto errret;
	mask |= val;
	if ((val = chkramtestdata(0x55)) == -1)
		goto errret;
	mask |= val;
	if ((val = chkramtestdata(0xaa)) == -1)
		goto errret;
	mask |= val;
	if ((val = chkramtestdata(0xcc)) == -1)
		goto errret;
	mask |= val;
	if ((val = chkramtestdata(0x33)) == -1)
		goto errret;
	mask |= val;
	if (mask) {
		tprintf("RAM data error mask %s\nRAM data test failed, exiting\n", 
		       to_bin(s, mask, 8));
		reset_modem();
		return -4;
	}
	if ((mask = chkramtestaddr()) == -1)
		goto errret;
	if (mask) {
		tprintf("RAM address error mask %s\nRAM address test failed, exiting\n", 
			to_bin(s, mask, 15));
		reset_modem();
		return -5;
	}
	if (!idle_callback(0))
		tprintf("\nRAM test passed\n");
	reset_modem();
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * Sigma Delta A/D converter
 */

static int chkadctest(struct adapter_config *cfg)
{
	unsigned char eio;
	int i;

	if ((i = adapter_start_eppchk()))
		return i;
	tprintf("Performing SigmaDelta A/D converter test...\n");
	tprintf("ADC signal is now routed to DAC...\n");
	i = 19666660 / 9600;
	eio = 0x1e;
	if (parport_epp_write_addr(&eio, 1) != 1)
		goto errret;
	eio = i;
	if (parport_epp_write_data(&eio, 1) != 1)
		goto errret;
	eio = 0x1f;
	if (parport_epp_write_addr(&eio, 1) != 1)
		goto errret;
	eio = i >> 8;
	if (parport_epp_write_data(&eio, 1) != 1)
		goto errret;
	eio = 0x10;
	if (parport_epp_write_addr(&eio, 1) != 1)
		goto errret;
	for (;;) {
		if (idle_callback(100000))
			break;
	}
	reset_modem();
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ========= EPP FIRMWARE TESTS ========================================= */

/*
 * EPP LED test
 */

static int eppledtest(struct adapter_config *cfg)
{
	unsigned led = 0;
	unsigned char eio;
	int i;
	
	if ((i = adapter_start_epp(cfg)))
		return i;
	tprintf("EPP port LED test\n");
	for (;;) {
		led = !led;
		eio = led ? EPP_LED_STA : EPP_LED_CON;
		if (parport_epp_write_addr(&eio, 1) != 1)
			goto errret;
		tprintf("Lighting LED %s\r", ((const char *[2]){ "CON", "STA" })[led]);
		if (idle_callback(500000)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

static int eppledtest2(struct adapter_config *cfg)
{
	unsigned led = 0;
	unsigned char eio;
	int i;
	
	if ((i = adapter_start_epp(cfg)))
		return i;
	tprintf("EPP port LED test (fast)\n");
	for (;;) {
		led = !led;
		eio = led ? EPP_LED_STA : EPP_LED_CON;
		if (parport_epp_write_addr(&eio, 1) != 1)
			goto errret;
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * EPP modem test
 */

static int eppmodemtest(struct adapter_config *cfg, unsigned int flags)
{
	unsigned int enc_state = 0x100;
	unsigned int dec_state = 0;
	unsigned int mask1, mask2;
	unsigned char stat;
        int cnt, cntx, i, j, icnt, ocnt, icntx, ocntx;
	unsigned int berr = 0;
#if 0
	unsigned int pcnt = 0;
#endif
	unsigned char eio[2048];
	
	if ((i = adapter_start_epp(cfg)))
		return i;
	/* reset modem */
	eio[0] = 0;
	eio[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
	if (parport_epp_write_addr(eio, 2) != 2)
		goto errret;
	for (;;) {
		if (parport_epp_read_addr(&stat, 1) != 1)
			goto errret;
		/* determine TX fifo size from status bits */
		switch (stat & (EPP_NTAEF|EPP_NTHF)) {
		case EPP_NTHF:
			ocntx = 2048 - 256;
			break;

		case EPP_NTAEF:
			ocntx = 2048 - 1793;
			break;

		case 0:
			ocntx = 0;
			break;

		default:
			ocntx = 2048 - 1025;
			break;
		}
		/* determine RX fifo size from status bits */
		switch (stat & (EPP_NRAEF|EPP_NRHF)) {
		case EPP_NRHF:
			icntx = 0;
			break;
                
		case EPP_NRAEF:
			icntx = 1025;
			break;
                
		case 0:
			icntx = 1793;
			break;
                
		default:
			icntx = 256;
			break;
		}
		ocnt = ocntx;
		icnt = icntx;
		/* try to determine the FIFO count if in extended mode */
		if (cfg->extstat) {
			eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1;
			if (parport_epp_write_addr(eio, 1) != 1)
				goto errret;
getpid();
			if (parport_epp_read_addr(eio, 2) != 2)
				goto errret;
getpid();
			icnt = eio[0] | (((unsigned int)eio[1]) << 8);
			eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2;
			if (parport_epp_write_addr(eio, 1) != 1)
				goto errret;
getpid();
			if (parport_epp_read_addr(eio, 2) != 2)
				goto errret;
getpid();
			ocnt = eio[0] | (((unsigned int)eio[1]) << 8);
			eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
			if (parport_epp_write_addr(eio, 1) != 1)
				goto errret;
			ocnt = 16384 - (ocnt & 0x7fff);
			icnt &= 0x7fff;
#if 0
			{
				unsigned ocnt2, icnt2;
				eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1;
				if (parport_epp_write_addr(eio, 1) != 1)
					goto errret;
				if (parport_epp_read_addr(eio, 2) != 2)
					goto errret;
				icnt2 = eio[0] | (((unsigned int)eio[1]) << 8);
				eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2;
				if (parport_epp_write_addr(eio, 1) != 1)
					goto errret;
				if (parport_epp_read_addr(eio, 2) != 2)
					goto errret;
				ocnt2 = eio[0] | (((unsigned int)eio[1]) << 8);
				eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
				if (parport_epp_write_addr(eio, 1) != 1)
					goto errret;
				ocnt2 = 16384 - (ocnt2 & 0x7fff);
				icnt2 &= 0x7fff;

				if (icnt != icnt2 || ocnt != ocnt2) {
					printf("\nCOUNT ERROR!! icnt 0x%x icnt2 0x%x ocnt 0x%x ocnt2 0x%x\n",
					       icnt, icnt2, ocnt, ocnt2);
				}
			}
#endif

		}
		/* try to tx */
		for (cntx = ocnt; cntx > 0; cntx -= cnt) {
			cnt = cntx;
			if (cnt > sizeof(eio))
				cnt = sizeof(eio);
			if (flags & 2) {
				for (j = 0; j < cnt; j++) {
					enc_state >>= 8;
					for (i = 0, mask1 = SCRAM_TAP1, mask2 = SCRAM_TAPN; 
					     i < 8; i++, mask1 <<= 1, mask2 <<= 1)
						if (enc_state & mask1)
							enc_state ^= mask2;
					eio[j] = enc_state;
				}
			} else
				memset(eio, 0, cnt);
			if (parport_epp_write_data(eio, cnt) != cnt)
				goto errret;
		}
		if (icnt <= 0) {
			if (idle_callback(10000)) {
				reset_modem();
				return 0;
			}
		} else {
			for (cntx = icnt; cntx > 0; cntx -= cnt) {
				cnt = cntx;
				if (cnt > sizeof(eio))
					cnt = sizeof(eio);
				if (parport_epp_read_data(eio, cnt) != cnt)
					goto errret;
				if (flags & 2) {
					for (j = 0; j < cnt; j++) {
						dec_state = (dec_state >> 8) | (((unsigned int)eio[j]) << DESCRAM_TAPSH3);
						eio[j] = ((dec_state >> DESCRAM_TAPSH1) ^ 
							  (dec_state >> DESCRAM_TAPSH2) ^
							  (dec_state >> DESCRAM_TAPSH3)) & 0xff;
					}
				}
				for (j = 0; j < cnt; j++) {
					if (eio[j])
						berr += hweight8(eio[j]);
					if (flags & 1)
						for (i = 0; i < 8; i++)
							fputc('0' + ((eio[j] >> i) & 1), stdout);
				}
			}
			if (flags & 1)
				fputc('\n', stdout);
		}
#if 0
		pcnt = (++pcnt) & 0x3f;
		if (pcnt)
			continue;
#endif
		/* DCD has inverse logic */
		tprintf("BERR: %8d  DCD:%c  stat: %02x   icnt %5d icntx %5d ocnt %5d ocntx %5d\r", 
		       berr, (stat & EPP_DCDBIT) ? '-' : 'D', stat, icnt, icntx, ocnt, ocntx);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

static int eppmodemtest1(struct adapter_config *cfg)
{
	return eppmodemtest(cfg, 0);
}

static int eppmodemtest2(struct adapter_config *cfg)
{
	return eppmodemtest(cfg, 1);
}

static int eppmodemtest3(struct adapter_config *cfg)
{
	return eppmodemtest(cfg, 2);
}

static int eppmodemtest4(struct adapter_config *cfg)
{
	return eppmodemtest(cfg, 3);
}

/* ========= ECP FIRMWARE TESTS ========================================= */

/*
 * ECP LED test
 */

static int ecpledtest(struct adapter_config *cfg)
{
	unsigned led = 0;
	unsigned char eio;
	int i;
	
	if ((i = adapter_start_ecp(cfg)))
		return i;
	tprintf("ECP port LED test\n");
	for (;;) {
		led = !led;
		eio = 0xb0 | (led + 1);
		if (parport_ecp_write_addr(&eio, 1) != 1)
			goto errret;
		tprintf("Lighting LED %s\r", ((const char *[2]){ "CON", "STA" })[led]);
		if (idle_callback(500000)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "ECP timeout\n");
	return -1;
}

static int ecpledtest2(struct adapter_config *cfg)
{
	unsigned led = 0;
	unsigned char eio;
	int i;
	
	if ((i = adapter_start_ecp(cfg)))
		return i;
	tprintf("ECP port LED test (fast)\n");
	for (;;) {
		led = !led;
		eio = 0xb0 | (led + 1);
		if (parport_ecp_write_addr(&eio, 1) != 1)
			goto errret;
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "ECP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * ECP modem test
 */

static int ecpmodemtest(struct adapter_config *cfg, unsigned int flags)
{
	unsigned int enc_state = 0x100;
	unsigned int dec_state = 0;
	unsigned int mask1, mask2;
	unsigned char stat[6];
	unsigned char data[4096];
        int cnt, cntx, i, j, icnt, ocnt;
	unsigned int berr = 0;
#if 0
	unsigned int pcnt = 0;
#endif

	if ((i = adapter_start_ecp(cfg)))
		return i;
	data[0] = 0xaf;  /* reset register: all reset */
	data[1] = 0xa0;  /* reset register: terminate reset */
	data[2] = 0xb0;
	if (parport_ecp_write_addr(data, 3) != 3)
		goto errret;
	for (;;) {
		data[0] = 0x81;
		if (parport_ecp_write_addr(data, 1) != 1)
			goto errret;
		if (parport_ecp_read_data(stat, 6) != 6)
			goto errret;
		data[0] = 0x80;
		if (parport_ecp_write_addr(data, 1) != 1)
			goto errret;
		icnt = ((unsigned int)stat[2] << 8) | stat[1];
		ocnt = ((unsigned int)stat[4] << 8) | stat[3];
		/* read */
		if (icnt > 0) {
			for (cntx = icnt; cntx > 0; cntx -= cnt) {
				cnt = cntx;
				if (cnt > sizeof(data))
					cnt = sizeof(data);
				if (cnt > 0xfff)
					cnt = 0xfff;
				data[0] = 0xc0 | (cnt & 0xf);
				data[1] = 0xd0 | ((cnt >> 4) & 0xf);
				data[2] = 0xe0 | ((cnt >> 8) & 0xf);
				if (parport_ecp_write_addr(data, 3) != 3)
					goto errret;
				if (parport_ecp_read_data(data, cnt) != cnt)
					goto errret;
				/* if doubly scrambled do it in place */
				if (flags & 2) {
					for (i = 0; i < cnt; i++) {
						dec_state = (dec_state >> 8) | (((unsigned)data[i]) << DESCRAM_TAPSH3);
						data[i] = (dec_state >> DESCRAM_TAPSH1) ^ 
							(dec_state >> DESCRAM_TAPSH2) ^
							(dec_state >> DESCRAM_TAPSH3);
					}
				}
				for (i = 0; i < cnt; i++) {
					if (data[i])
						berr += hweight8(data[i]);
					if (flags & 1)
						for (i = 0; i < 8; i++)
							fputc('0' + ((data[i] >> i) & 1), stdout);
				}
			}
			if (flags & 1)
				fputc('\n', stdout);
		} else {
			if (idle_callback(10000)) {
				reset_modem();
				return 0;
			}
		}
		/* write */
		for (cntx = 16384 - ocnt; cntx > 0; cntx -= cnt) {
			cnt = cntx;
			if (cnt > sizeof(data))
				cnt = sizeof(data);
			if (flags & 2) {
				for (i = 0; i < cnt; i++) {
					enc_state >>= 8;
					for (j = 0, mask1 = SCRAM_TAP1, mask2 = SCRAM_TAPN;
					     j < 8; j++, mask1 <<= 1, mask2 <<= 1)
						if (enc_state & mask1)
							enc_state ^= mask2;
					data[i] = enc_state;
				}
			} else
				memset(data, 0, cnt);
			if (parport_ecp_write_data(data, cnt) != cnt)
				goto errret;
		}
#if 0
		pcnt = (++pcnt) & 0x3f;
		if (pcnt)
			continue;
#endif
		/* DCD has inverse logic */
		tprintf("BERR: %8d  DCD:%c  stat: %02x   icnt %5d ocnt %5d\r", 
		       berr, (stat[0] & EPP_DCDBIT) ? '-' : 'D', stat[0], icnt, ocnt);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "ECP timeout\n");
	return -1;
}

static int ecpmodemtest1(struct adapter_config *cfg)
{
	return ecpmodemtest(cfg, 0);
}

static int ecpmodemtest2(struct adapter_config *cfg)
{
	return ecpmodemtest(cfg, 1);
}

static int ecpmodemtest3(struct adapter_config *cfg)
{
	return ecpmodemtest(cfg, 2);
}

static int ecpmodemtest4(struct adapter_config *cfg)
{
	return ecpmodemtest(cfg, 3);
}

/* ========= EPPAFSK FIRMWARE TESTS ===================================== */

/*
 * EPP AFSK LED test
 */

static int eppafskledtest(struct adapter_config *cfg)
{
	unsigned led = 0;
	unsigned char eio;
	int i;
	
	cfg->bitrate = 1200;
	if ((i = adapter_start_eppafsk(cfg)))
		return i;
	tprintf("EPPAFSK port LED test\n");
	for (;;) {
		led = !led;
		eio = led ? EPP_LED_STA : EPP_LED_CON;
		if (parport_epp_write_addr(&eio, 1) != 1)
			goto errret;
		tprintf("Lighting LED %s\r", ((const char *[2]){ "CON", "STA" })[led]);
		if (idle_callback(500000)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

static int eppafskledtest2(struct adapter_config *cfg)
{
	unsigned led = 0;
	unsigned char eio;
	int i;
	
	cfg->bitrate = 1200;
	if ((i = adapter_start_eppafsk(cfg)))
		return i;
	tprintf("EPPAFSK port LED test (fast)\n");
	for (;;) {
		led = !led;
		eio = led ? EPP_LED_STA : EPP_LED_CON;
		if (parport_epp_write_addr(&eio, 1) != 1)
			goto errret;
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * EPP AFSK modem test
 */

static int eppafskmodemtest(struct adapter_config *cfg, unsigned int flags)
{
	unsigned int enc_state = 0x100;
	unsigned int dec_state = 0;
	unsigned int mask1, mask2;
	unsigned char stat, txbyte;
        int i, j, icnt, ocnt;
	unsigned int berr = 0;
#if 0
	unsigned int pcnt = 0;
#endif
	unsigned char eio[32];

	cfg->bitrate = 1200;
	if ((i = adapter_start_eppafsk(cfg)))
		return i;
	txbyte = -((flags >> 1) & 1);
	/* reset modem */
	eio[0] = 0x00;
	eio[1] = 0x18;
	if (parport_epp_write_addr(eio, 2) != 2)
		goto errret;
	for (;;) {
		if (parport_epp_read_addr(&stat, 1) != 1)
			goto errret;
		/* determine the FIFO count */
		eio[0] = 0x19;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(eio, 1) != 1)
			goto errret;
		ocnt = 0x10 - (eio[0] & 0x1f);
		eio[0] = 0x1a;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(eio, 1) != 1)
			goto errret;
		icnt = eio[0] & 0x1f;
		eio[0] = 0x18;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		/* try to tx */
		if (ocnt > 0) {
			if (flags & 2) {
				for (j = 0; j < ocnt; j++) {
					enc_state >>= 8;
					for (i = 0, mask1 = SCRAM_TAP1, mask2 = SCRAM_TAPN; 
					     i < 8; i++, mask1 <<= 1, mask2 <<= 1)
						if (enc_state & mask1)
							enc_state ^= mask2;
					eio[j] = enc_state;
				}
			} else
				memset(eio, 0, ocnt);
			if (parport_epp_write_data(eio, ocnt) != ocnt)
				goto errret;
		}
		if (icnt <= 0) {
			if (idle_callback(1000)) {
				reset_modem();
				return 0;
			}
		}
		else {
			if (parport_epp_read_data(eio, icnt) != icnt)
				goto errret;
			if (flags & 2) {
				for (j = 0; j < icnt; j++) {
					dec_state = (dec_state >> 8) | (((unsigned int)eio[j]) << DESCRAM_TAPSH3);
					eio[j] = ((dec_state >> DESCRAM_TAPSH1) ^ 
						  (dec_state >> DESCRAM_TAPSH2) ^
						  (dec_state >> DESCRAM_TAPSH3)) & 0xff;
				}
			}
			for (j = 0; j < icnt; j++) {
				if (eio[j])
					berr += hweight8(eio[j]);
				if (flags & 1)
					for (i = 0; i < 8; i++)
						fputc('0' + ((eio[j] >> i) & 1), stdout);
			}
			if (flags & 1)
				fputc('\n', stdout);
		}
#if 0
		pcnt = (++pcnt) & 0x3f;
		if (pcnt)
			continue;
#endif
		/* DCD has inverse logic */
		tprintf("BERR: %8d  DCD:%c PTT:%c  stat: %02x   icnt %5d ocnt %5d\r", 
		       berr, (stat & 0x80) ? '-' : 'D', (stat & 0x40) ? '-' : 'P', stat, icnt, ocnt);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

static int eppafskmodemtest1(struct adapter_config *cfg)
{
	return eppafskmodemtest(cfg, 0);
}

static int eppafskmodemtest2(struct adapter_config *cfg)
{
	return eppafskmodemtest(cfg, 1);
}

static int eppafskmodemtest3(struct adapter_config *cfg)
{
	return eppafskmodemtest(cfg, 2);
}

static int eppafskmodemtest4(struct adapter_config *cfg)
{
	return eppafskmodemtest(cfg, 3);
}

static int eppafskmodemtest5(struct adapter_config *cfg)
{
	return eppafskmodemtest(cfg, 4);
}

static int eppafskmodemtest6(struct adapter_config *cfg)
{
	return eppafskmodemtest(cfg, 6);
}

/* ========= SPPAFSK FIRMWARE TESTS ===================================== */

/*
 * SPP AFSK LED test
 */

static int sppafskledtest(struct adapter_config *cfg)
{
	unsigned led = 0;
	int i;
	
	cfg->bitrate = 1200;
	if ((i = adapter_start_sppafsk(cfg)))
		return i;
	tprintf("SPPAFSK port LED test\n");
	for (;;) {
		led = !led;
		sppafsk_wrcmd(led ? EPP_LED_STA : EPP_LED_CON);
		tprintf("Lighting LED %s\r", ((const char *[2]){ "CON", "STA" })[led]);
		if (idle_callback(500000)) {
			reset_modem();
			return 0;
		}
	}
	return 0;
}

static int sppafskledtest2(struct adapter_config *cfg)
{
	unsigned led = 0;
	int i;
	
	cfg->bitrate = 1200;
	if ((i = adapter_start_eppafsk(cfg)))
		return i;
	tprintf("SPPAFSK port LED test (fast)\n");
	for (;;) {
		led = !led;
		sppafsk_wrcmd(led ? EPP_LED_STA : EPP_LED_CON);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;
}

/* ---------------------------------------------------------------------- */
/*
 * SPP AFSK modem test
 */

static int sppafskmodemtest(struct adapter_config *cfg, unsigned int flags)
{
	unsigned int enc_state = 0x100;
	unsigned int dec_state = 0;
	unsigned int mask1, mask2;
	unsigned char stat, txbyte;
        int i, j, icnt, ocnt;
	unsigned int berr = 0;
#if 0
	unsigned int pcnt = 0;
#endif
	unsigned char currx;

	cfg->bitrate = 1200;
	if ((i = adapter_start_sppafsk(cfg)))
		return i;
	txbyte = -((flags >> 1) & 1);
	/* reset modem */
	sppafsk_wrcmd(0x00);
	sppafsk_wrcmd(0x18);
	for (;;) {
		stat = sppafsk_rdcmd();
		/* determine the FIFO count */
		sppafsk_wrcmd(0x19);
		ocnt = sppafsk_rdcmd();
		sppafsk_wrcmd(0x1a);
		icnt = sppafsk_rdcmd();
		sppafsk_wrcmd(0x18);
		if (icnt > 0x10 || ocnt > 0x10)
			goto errret;
		ocnt = 0x10 - ocnt;
		/* try to tx */
		if (ocnt > 0) {
			if (flags & 2) {
				for (j = 0; j < ocnt; j++) {
					enc_state >>= 8;
					for (i = 0, mask1 = SCRAM_TAP1, mask2 = SCRAM_TAPN; 
					     i < 8; i++, mask1 <<= 1, mask2 <<= 1)
						if (enc_state & mask1)
							enc_state ^= mask2;
					sppafsk_wrdata(enc_state);
				}
			} else {
				for (j = 0; j < ocnt; j++) 
					sppafsk_wrdata(0);
			}
		}
		if (icnt <= 0) {
			if (idle_callback(1000)) {
				reset_modem();
				return 0;
			}
		}
		else {
			for (j = 0; j < icnt; j++) {
				currx = sppafsk_rddata();
				if (flags & 2) {
					dec_state = (dec_state >> 8) | (((unsigned int)currx) << DESCRAM_TAPSH3);
					currx = ((dec_state >> DESCRAM_TAPSH1) ^ 
						 (dec_state >> DESCRAM_TAPSH2) ^
						 (dec_state >> DESCRAM_TAPSH3)) & 0xff;
				}
				if (currx)
					berr += hweight8(currx);
				if (flags & 1)
					for (i = 0; i < 8; i++)
						fputc('0' + ((currx >> i) & 1), stdout);
			}
			if (flags & 1)
				fputc('\n', stdout);
		}
#if 0
		pcnt = (++pcnt) & 0x3f;
		if (pcnt)
			continue;
#endif
		/* DCD has inverse logic */
		tprintf("BERR: %8d  DCD:%c PTT:%c  stat: %02x   icnt %5d ocnt %5d\r", 
		       berr, (stat & 0x80) ? '-' : 'D', (stat & 0x40) ? '-' : 'P', stat, icnt, ocnt);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "SPP bogus values read\n");
	return -1;
}

static int sppafskmodemtest1(struct adapter_config *cfg)
{
	return sppafskmodemtest(cfg, 0);
}

static int sppafskmodemtest2(struct adapter_config *cfg)
{
	return sppafskmodemtest(cfg, 1);
}

static int sppafskmodemtest3(struct adapter_config *cfg)
{
	return sppafskmodemtest(cfg, 2);
}

static int sppafskmodemtest4(struct adapter_config *cfg)
{
	return sppafskmodemtest(cfg, 3);
}

static int sppafskmodemtest5(struct adapter_config *cfg)
{
	return sppafskmodemtest(cfg, 4);
}

static int sppafskmodemtest6(struct adapter_config *cfg)
{
	return sppafskmodemtest(cfg, 6);
}

/* ---------------------------------------------------------------------- */
/*
 * HDLC receive tests
 */

static void tprintpkt(const unsigned char *pkt, unsigned len)
{
        unsigned char v1=1,cmd=0;
        unsigned char i,j;

        if (!pkt || len < 8)
                return;
        if (pkt[1] & 1) {
                /*
                 * FlexNet Header Compression
                 */
                v1 = 0;
                cmd = (pkt[1] & 2) != 0;
                tprintf("fm ? to ");
                i = (pkt[2] >> 2) & 0x3f;
                if (i)
                        tprintf("%c",i+0x20);
                i = ((pkt[2] << 4) | ((pkt[3] >> 4) & 0xf)) & 0x3f;
                if (i)
                        tprintf("%c", i+0x20);
                i = ((pkt[3] << 2) | ((pkt[4] >> 6) & 3)) & 0x3f;
                if (i)
                        tprintf("%c", i+0x20);
                i = pkt[4] & 0x3f;
                if (i)
                        tprintf("%c", i+0x20);
                i = (pkt[5] >> 2) & 0x3f;
                if (i)
                        tprintf("%c", i+0x20);
                i = ((pkt[5] << 4) | ((pkt[6] >> 4) & 0xf)) & 0x3f;
                if (i)
                        tprintf("%c", i+0x20);
                tprintf("-%u QSO Nr %u", pkt[6] & 0xf, (pkt[0] << 6) | (pkt[1] >> 2));
                pkt += 7;
                len -= 7;
        } else {
                /*
                 * normal header
                 */
                if (len < 15)
                        return;
                if ((pkt[6] & 0x80) != (pkt[13] & 0x80)) {
                        v1 = 0;
                        cmd = (pkt[6] & 0x80);
                }
                tprintf("fm ");
                for(i = 7; i < 13; i++) 
                        if ((pkt[i] &0xfe) != 0x40)
                                tprintf("%c", pkt[i] >> 1);
                tprintf("-%u to ", (pkt[13] >> 1) & 0xf);
                for(i = 0; i < 6; i++) 
                        if ((pkt[i] &0xfe) != 0x40)
                                tprintf("%c", pkt[i] >> 1);
                tprintf("-%u", (pkt[6] >> 1) & 0xf);
                pkt += 14;
                len -= 14;
                if ((!(pkt[-1] & 1)) && (len >= 7))
                        tprintf(" via ");
                while ((!(pkt[-1] & 1)) && (len >= 7)) {
                        for(i = 0; i < 6; i++) 
                                if ((pkt[i] &0xfe) != 0x40)
                                        tprintf("%c", pkt[i] >> 1);
                        tprintf("-%u", (pkt[6] >> 1) & 0xf);
                        pkt += 7;
                        len -= 7;
                        if ((!(pkt[-1] & 1)) && (len >= 7))
                                tprintf(",");
                }
        }
        if(!len) 
                return;
        i = *pkt++;
        len--;
        j = v1 ? ((i & 0x10) ? '!' : ' ') : 
                ((i & 0x10) ? (cmd ? '+' : '-') : (cmd ? '^' : 'v'));
        if (!(i & 1)) {
                /*
                 * Info frame
                 */
                tprintf(" I%u%u%c", (i >> 5) & 7, (i >> 1) & 7, j);
        } else if (i & 2) {
                /*
                 * U frame
                 */
                switch (i & (~0x10)) {
                case 0x03:
                        tprintf(" UI%c", j);
                        break;
                case 0x2f:
                        tprintf(" SABM%c", j);
                        break;
                case 0x43:
                        tprintf(" DISC%c", j);
                        break;
                case 0x0f:
                        tprintf(" DM%c", j);
                        break;
                case 0x63:
                        tprintf(" UA%c", j);
                        break;
                case 0x87:
                        tprintf(" FRMR%c", j);
                        break;
                default:
                        tprintf(" unknown U (0x%x)%c", i & (~0x10), j);
                        break;
                }
        } else {
                /*
                 * supervisory
                 */
                switch (i & 0xf) {
                case 0x1:
                        tprintf(" RR%u%c", (i >> 5) & 7, j);
                        break;
                case 0x5:
                        tprintf(" RNR%u%c", (i >> 5) & 7, j);
                        break;
                case 0x9:
                        tprintf(" REJ%u%c", (i >> 5) & 7, j);
                        break;
                default:
                        tprintf(" unknown S (0x%x)%u%c", i & 0xf, (i >> 5) & 7, j);
                        break;
                }
        }
        if (!len) {
                tprintf("\n");
                return;
        }
        tprintf(" pid=%02X\n", *pkt++);
        len--;
        j = 0;
        while (len) {
                i = *pkt++;
		if ((i >= 32) && (i < 128))
			tprintf("%c", i);
		else if (i == 13) {
                        if (j)
                                tprintf("\n");
                        j = 0;
                } else
                        tprintf(".");
                if (i >= 32) 
                        j = 1;
                len--;
        }
        if (j)
                tprintf("\n");
}

/* ---------------------------------------------------------------------- */
/*
 * the CRC routines are stolen from WAMPES
 * by Dieter Deyke
 */

static const u_int16_t crc_ccitt_table[0x100] = {
        0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
        0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
        0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
        0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
        0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
        0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
        0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
        0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
        0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
        0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
        0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
        0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
        0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
        0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
        0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
        0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
        0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
        0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
        0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
        0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
        0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
        0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
        0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
        0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
        0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
        0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
        0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
        0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
        0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
        0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};

/* --------------------------------------------------------------------- */

extern inline u_int16_t calc_crc_ccitt(const u_int8_t *buffer, int len)
{
        u_int16_t crc = 0xffff;

        for (;len>0;len--)
                crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
        crc ^= 0xffff;
	return crc;
}

extern inline void append_crc_ccitt(u_int8_t *buffer, int len)
{
        u_int16_t crc = calc_crc_ccitt(buffer, len);
        *buffer++ = crc;
        *buffer++ = crc >> 8;
}

extern inline int check_crc_ccitt(const u_int8_t *buffer, int len)
{
        u_int16_t crc = calc_crc_ccitt(buffer, len);
        return (crc & 0xffff) == 0x0f47;
}

/* ---------------------------------------------------------------------- */

#define MAXFLEN           512
#define RXBUFFER_SIZE     ((MAXFLEN*6/5)+8)

struct hdlc_receiver {
        unsigned int bitbuf, bitstream, numbits, state;
        unsigned char *bufptr;
        int bufcnt;
        unsigned char buf[RXBUFFER_SIZE];
};

extern inline void hdlc_initrx(struct hdlc_receiver *h)
{       
        memset(h, 0, sizeof(struct hdlc_receiver));
        h->bufptr = h->buf;
}

static void do_rxpacket(struct hdlc_receiver *h)
{
	if (h->bufcnt < 4) 
		return;
	if (!check_crc_ccitt(h->buf, h->bufcnt)) 
		return;
	tprintf("                                                              \r");
	tprintpkt(h->buf, h->bufcnt-2);
}

#define DECODEITERA(j)                                                        \
({                                                                            \
        if (!(notbitstream & (0x0fc << j)))              /* flag or abort */  \
                goto flgabrt##j;                                              \
        if ((bitstream & (0x1f8 << j)) == (0xf8 << j))   /* stuffed bit */    \
                goto stuff##j;                                                \
  enditer##j:                                                                 \
})

#define DECODEITERB(j)                                                                 \
({                                                                                     \
  flgabrt##j:                                                                          \
        if (!(notbitstream & (0x1fc << j))) {              /* abort received */        \
                state = 0;                                                             \
                goto enditer##j;                                                       \
        }                                                                              \
        if ((bitstream & (0x1fe << j)) != (0x0fc << j))   /* flag received */          \
                goto enditer##j;                                                       \
        if (state)                                                                     \
                do_rxpacket(h);                                                        \
        h->bufcnt = 0;                                                                 \
        h->bufptr = h->buf;                                                            \
        state = 1;                                                                     \
        numbits = 7-j;                                                                 \
        goto enditer##j;                                                               \
  stuff##j:                                                                            \
        numbits--;                                                                     \
        bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1);        \
        goto enditer##j;                                                               \
})
        
/* ---------------------------------------------------------------------- */

static void hdlc_receive(struct hdlc_receiver *h, u_int8_t *bits, int cnt)
{
        unsigned int bitbuf, notbitstream, bitstream, numbits, state;
        u_int8_t ch;

        numbits = h->numbits;
        state = h->state;
        bitstream = h->bitstream;
        bitbuf = h->bitbuf;
        for (; cnt > 0; cnt--) {
                ch = *bits++;
                bitstream >>= 8;
                bitstream |= ch << 8;
                bitbuf >>= 8;
                bitbuf |= ch << 8;
                numbits += 8;
                notbitstream = ~bitstream;
                DECODEITERA(0);
                DECODEITERA(1);
                DECODEITERA(2);
                DECODEITERA(3);
                DECODEITERA(4);
                DECODEITERA(5);
                DECODEITERA(6);
                DECODEITERA(7);
                goto enddec;
                DECODEITERB(0);
                DECODEITERB(1);
                DECODEITERB(2);
                DECODEITERB(3);
                DECODEITERB(4);
                DECODEITERB(5);
                DECODEITERB(6);
                DECODEITERB(7);
          enddec:
                while (state && numbits >= 8) {
                        if (h->bufcnt >= RXBUFFER_SIZE) {
                                state = 0;
                        } else {
                                *(h->bufptr)++ = bitbuf >> (16-numbits);
                                h->bufcnt++;
                                numbits -= 8;
                        }
                }
        }
        h->numbits = numbits;
        h->state = state;
        h->bitstream = bitstream;
        h->bitbuf = bitbuf;
}

/* ---------------------------------------------------------------------- */

static int epprx(struct adapter_config *cfg)
{
	u_int8_t stat;
        int cnt, cntx, i, icnt, ocnt, icntx, ocntx;
#if 0
	unsigned int pcnt = 0;
#endif
	struct hdlc_receiver hdlcrx;
	unsigned char eio[1024];

	hdlc_initrx(&hdlcrx);
	if ((i = adapter_start_epp(cfg)))
		return i;
	/* reset modem */
	eio[0] = 0;
	eio[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
	if (parport_epp_write_addr(eio, 2) != 2)
		goto errret;
	for (;;) {
		if (parport_epp_read_addr(&stat, 1) != 1)
			goto errret;
		/* determine TX fifo size from status bits */
		switch (stat & (EPP_NTAEF|EPP_NTHF)) {
		case EPP_NTHF:
			ocntx = 2048 - 256;
			break;

		case EPP_NTAEF:
			ocntx = 2048 - 1793;
			break;

		case 0:
			ocntx = 0;
			break;

		default:
			ocntx = 2048 - 1025;
			break;
		}
		/* determine RX fifo size from status bits */
		switch (stat & (EPP_NRAEF|EPP_NRHF)) {
		case EPP_NRHF:
			icntx = 0;
			break;
                
		case EPP_NRAEF:
			icntx = 1025;
			break;
                
		case 0:
			icntx = 1793;
			break;
                
		default:
			icntx = 256;
			break;
		}
		ocnt = ocntx;
		icnt = icntx;
		/* try to determine the FIFO count if in extended mode */
		if (cfg->extstat) {
			eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1;
			if (parport_epp_write_addr(eio, 1) != 1)
				goto errret;
			if (parport_epp_read_addr(eio, 2) != 2)
				goto errret;
			icnt = eio[0] | (((unsigned int)eio[1]) << 8);
			eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2;
			if (parport_epp_write_addr(eio, 1) != 1)
				goto errret;
			if (parport_epp_read_addr(eio, 2) != 2)
				goto errret;
			ocnt = eio[0] | (((unsigned int)eio[1]) << 8);
			eio[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE;
			if (parport_epp_write_addr(eio, 1) != 1)
				goto errret;
			ocnt = 16384 - (ocnt & 0x7fff);
			icnt &= 0x7fff;
		}
		/* try to tx */
#if 0
		if (ocnt > 0) {
		}
#endif
		if (icnt > 0) {
			for (cntx = icnt; cntx > 0; cntx -= cnt) {
				cnt = cntx;
				if (cnt > sizeof(eio))
					cnt = sizeof(eio);
				if (parport_epp_read_data(eio, cnt) != cnt)
					goto errret;
				hdlc_receive(&hdlcrx, eio, cnt);
			}
		} else {
			if (idle_callback(10000)) {
				reset_modem();
				return 0;
			}
		}
#if 0
		pcnt = (++pcnt) & 0x3f;
		if (pcnt)
			continue;
#endif
		/* DCD has inverse logic */
		tprintf("DCD:%c  stat: %02x   icnt %5d icntx %5d ocnt %5d ocntx %5d\r", 
		       (stat & EPP_DCDBIT) ? '-' : 'D', stat, icnt, icntx, ocnt, ocntx);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */

static int ecprx(struct adapter_config *cfg)
{
	u_int8_t stat[6];
        int cnt, cntx, i, icnt, ocnt;
#if 0
	unsigned int pcnt = 0;
#endif
	unsigned char data[4096];
	struct hdlc_receiver hdlcrx;

	hdlc_initrx(&hdlcrx);
	if ((i = adapter_start_ecp(cfg)))
		return i;
	data[0] = 0xaf;  /* reset register: all reset */
	data[1] = 0xa0;  /* reset register: terminate reset */
	data[2] = 0xb0;
	if (parport_ecp_write_addr(data, 3) != 3)
		goto errret;
	for (;;) {
		data[0] = 0x81;
		if (parport_ecp_write_addr(data, 1) != 1)
			goto errret;
		if (parport_ecp_read_data(stat, 6) != 6)
			goto errret;
		data[0] = 0x80;
		if (parport_ecp_write_addr(data, 1) != 1)
			goto errret;
		icnt = ((unsigned)stat[2] << 8) | stat[1];
		ocnt = ((unsigned)stat[4] << 8) | stat[3];
		/* read */
		if (icnt > 0) {
			for (cntx = icnt; cntx > 0; cntx -= cnt) {
				cnt = cntx;
				if (cnt > sizeof(data))
					cnt = sizeof(data);
				if (cnt > 0xfff)
					cnt = 0xfff;
				data[0] = 0xc0 | (cnt & 0xf);
				data[1] = 0xd0 | ((cnt >> 4) & 0xf);
				data[2] = 0xe0 | ((cnt >> 8) & 0xf);
				if (parport_ecp_write_addr(data, 3) != 3)
					goto errret;
				if (parport_ecp_read_data(data, cnt) != cnt)
					goto errret;
				hdlc_receive(&hdlcrx, data, cnt);
			}
		} else {
			if (idle_callback(10000)) {
				reset_modem();
				return 0;
			}
		}
		/* write */
#if 0
		if (ocnt < 8192) { /* fixme */
			cnt = 8192 - ocnt;
			if (cnt > sizeof(data))
				cnt = sizeof(data);
		}
#endif
#if 0
		pcnt = (++pcnt) & 0x3f;
		if (pcnt)
			continue;
#endif
		/* DCD has inverse logic */
		tprintf("DCD:%c  stat: %02x   icnt %5d ocnt %5d\r", 
			(stat[0] & EPP_DCDBIT) ? '-' : 'D', stat[0], icnt, ocnt);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "ECP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */

static int eppafskrx(struct adapter_config *cfg)
{
	u_int8_t stat;
        int i, icnt, ocnt;
#if 0
	int j;
	unsigned int pcnt = 0;
#endif
	unsigned char eio[32];
	struct hdlc_receiver hdlcrx;

	hdlc_initrx(&hdlcrx);
	cfg->bitrate = 1200;
	if ((i = adapter_start_eppafsk(cfg)))
		return i;
	/* reset modem */
	eio[0] = 0x00;
	eio[1] = 0x18;
	if (parport_epp_write_addr(eio, 2) != 2)
		goto errret;
	for (;;) {
		if (parport_epp_read_addr(&stat, 1) != 1)
			goto errret;
		/* determine the FIFO count */
		eio[0] = 0x19;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(eio, 1) != 1)
			goto errret;
		ocnt = 0x10 - (eio[0] & 0x1f);
		eio[0] = 0x1a;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(eio, 1) != 1)
			goto errret;
		icnt = eio[0] & 0x1f;
		eio[0] = 0x18;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		/* try to tx */
#if 0
		if (ocnt > 0) {
		}
#endif
		if (icnt > 0) {
			if (parport_epp_read_data(eio, icnt) != icnt)
				goto errret;
#if 0
			for (i = 0; i < icnt; i++)
				for (j = 0; j < 8; j++)
					putc('0' + ((eio[i] >> j) & 1), stdout);
			fflush(stdout);
#endif
			hdlc_receive(&hdlcrx, eio, icnt);
		} else {
			if (idle_callback(10000)) {
				reset_modem();
				return 0;
			}
		}
#if 0
		pcnt = (++pcnt) & 0x3f;
		if (pcnt)
			continue;
#endif
		/* DCD has inverse logic */
#if 1
		tprintf("DCD:%c  stat: %02x   icnt %5d ocnt %5d\r", 
		       (stat & EPP_DCDBIT) ? '-' : 'D', stat, icnt, ocnt);
#endif
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */

static int sppafskrx(struct adapter_config *cfg)
{
	u_int8_t stat;
        int i, icnt, ocnt;
#if 0
	unsigned int pcnt = 0;
#endif
	unsigned char eio[32];
	struct hdlc_receiver hdlcrx;

	hdlc_initrx(&hdlcrx);
	cfg->bitrate = 1200;
	if ((i = adapter_start_sppafsk(cfg)))
		return i;
	/* reset modem */
	sppafsk_wrcmd(0x00);
	sppafsk_wrcmd(0x18);
	for (;;) {
		stat = sppafsk_rdcmd();
		/* determine the FIFO count */
		sppafsk_wrcmd(0x19);
		ocnt = sppafsk_rdcmd();
		sppafsk_wrcmd(0x1a);
		icnt = sppafsk_rdcmd();
		sppafsk_wrcmd(0x18);
		if (icnt > 0x10 || ocnt > 0x10)
			goto errret;
		ocnt = 0x10 - ocnt;
		/* try to tx */
#if 0
		if (ocnt > 0) {
		}
#endif
		if (icnt > 0) {
			for (i = 0; i < icnt; i++)
				eio[i] = sppafsk_rddata();
			hdlc_receive(&hdlcrx, eio, icnt);
		} else {
			if (idle_callback(10000)) {
				reset_modem();
				return 0;
			}
		}
#if 0
		pcnt = (++pcnt) & 0x3f;
		if (pcnt)
			continue;
#endif
		/* DCD has inverse logic */
		tprintf("DCD:%c  stat: %02x   icnt %5d ocnt %5d\r", 
		       (stat & EPP_DCDBIT) ? '-' : 'D', stat, icnt, ocnt);
		if (idle_callback(0)) {
			reset_modem();
			return 0;
		}
	}
	return 0;

errret:
	lprintf(0, "SPP bogus values read\n");
	return -1;
}

/* ---------------------------------------------------------------------- */
/*
 * Sample IO Firmware Tests
 */

static int sine1k(struct adapter_config *cfg)
{
#if 0
	static const char scdisp[] = "                                                               *\n";
#endif
	signed char sine[128];
	unsigned char eio[1024];
	unsigned char ctrl = 0;
	unsigned phase = 0, ledcnt = 0;
	int i, icnt, ocnt, cnt, cntx;

	cfg->bitrate = 8000;
	if ((i = adapter_start_eppsamp(cfg)))
		return i;
	for (i = 0; i < sizeof(sine); i++)
		sine[i] = 127 * sin((2.0 * M_PI / sizeof(sine)) * i);
	tprintf("Sine 1k Test\n");
	eio[0] = 7;
	eio[1] = 0;
	if (parport_epp_write_addr(eio, 2) != 2)
		goto errret;
	for (;;) {
		/* determine the FIFO count */
		eio[0] = ctrl | 1;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(eio, 2) != 2)
			goto errret;
		icnt = eio[0] | (((unsigned int)eio[1]) << 8);
		eio[0] = ctrl | 2;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(eio, 2) != 2)
			goto errret;
		ocnt = eio[0] | (((unsigned int)eio[1]) << 8);
		eio[0] = ctrl;
		if (parport_epp_write_addr(eio, 1) != 1)
			goto errret;
		ocnt = 16384 - (ocnt & 0x7fff);
		icnt &= 0x7fff;
		/* try to tx */
		if (ocnt > 0) {
			for (cntx = ocnt; cntx > 0; cntx -= cnt) {
				cnt = cntx;
				if (cnt > sizeof(eio))
					cnt = sizeof(eio);
				for (i = 0; i < cnt; i++) {
					eio[i] = sine[(phase >> 9) & 0x7f];
					phase += 0x1789;  /* pretty arbitrary */
				}
				if (parport_epp_write_data(eio, cnt) != cnt)
					goto errret;
			}
		}
		if (icnt > 0) {
			for (cntx = icnt; cntx > 0; cntx -= cnt) {
				cnt = cntx;
				if (cnt > sizeof(eio))
					cnt = sizeof(eio);
				if (parport_epp_read_data(eio, cnt) != cnt)
					goto errret;
				for (i = 0; i < cnt; i++) {
#if 0
					tprintf(scdisp + (((eio[i] + 128) & 0xff) / 4));
#elif 1
					if (i < 64)
						tprintf("%4d\n", ((signed char)eio[i]));
					else if (i == 64)
						tprintf("--\n");
#endif
					if ((++ledcnt) >= 4000) {
						ledcnt = 0;
						ctrl += 0x40;
					}
				}
			}
		} else {
			if (idle_callback(10000)) {
				reset_modem();
				return 0;
			}
		}
	}

errret:
	lprintf(0, "EPP timeout\n");
	return -1;
}

/* ---------------------------------------------------------------------- */

#ifdef HAVE_LINUX_SOUNDCARD_H

#include <linux/soundcard.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#define SNDLATENCY 4000   /* approx 1/2 seconds */
#define PHASEFRAC  12
#define PHASEMASK  ((1<<PHASEFRAC)-1)
#define SINEOVERLAY

static int saudio(struct adapter_config *cfg)
{
	unsigned char bufu[8192];   /* must be significantly bigger than SNDLATENCY! */
	signed char bufs[8192];     /* must be significantly bigger than SNDLATENCY! */
	audio_buf_info aboinfo, abiinfo;
	int fddsp, apar;
	unsigned srate;
	unsigned char ctrl = 0;
	unsigned phinceppdsp, phincdspepp, pheppdsp = 0, phdspepp = 0;
	unsigned ledcnt = 0;
	int i, icnt, ocnt, cnt, ocnts, omax;
#ifdef SINEOVERLAY
	signed char sine[128];
	unsigned phase = 0;
#endif

#ifdef SINEOVERLAY
	for (i = 0; i < sizeof(sine); i++)
		sine[i] = 63 * sin((2.0 * M_PI / sizeof(sine)) * i);
#endif
	cfg->bitrate = 8000;
	if ((i = adapter_start_eppsamp(cfg)))
		return i;
	if ((fddsp = open("/dev/dsp", O_RDWR | O_NONBLOCK)) == -1) {
		lprintf(0, "cannot open /dev/dsp (%s)\n", strerror(errno));
		return -1;
	}
	fcntl(fddsp, F_SETFL, fcntl(fddsp, F_GETFL, 0) | O_NONBLOCK);
	if (ioctl(fddsp, SNDCTL_DSP_NONBLOCK, 0) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_NONBLOCK failed (%s)\n", strerror(errno));
		goto errsnd;
	}
	if (ioctl(fddsp, SNDCTL_DSP_GETCAPS, &apar) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_GETCAPS failed (%s)\n", strerror(errno));
		goto errsnd;
	}
	if (!(apar & DSP_CAP_DUPLEX)) {
		lprintf(0, "full duplex soundcard required\n");
		goto errsnd;
	}
	if (!(apar & DSP_CAP_TRIGGER)) {
		lprintf(0, "soundcard does not support trigger\n");
		goto errsnd;
	}
	if (ioctl(fddsp, SNDCTL_DSP_SETDUPLEX, 0) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_SETDUPLEX failed (%s)\n", strerror(errno));
		goto errsnd;
	}
	apar = AFMT_U8;
	if (ioctl(fddsp, SNDCTL_DSP_SETFMT, &apar) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_SETFMT failed (%s), U8 not supported??\n", strerror(errno));
		goto errsnd;
	}
	apar = 0;
	if (ioctl(fddsp, SNDCTL_DSP_STEREO, &apar) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_STEREO failed (%s), mono not supported??\n", strerror(errno));
		goto errsnd;
	}
	srate = cfg->bitrate;
	if (ioctl(fddsp, SNDCTL_DSP_SPEED, &srate) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_SPEED failed (%s), samplingrate %u not supported??\n", 
			strerror(errno), cfg->bitrate);
		goto errsnd;
	}
	lprintf(1, "epp adapter sampling rate %u, soundcard sampling rate %u\n", cfg->bitrate, srate);
	if (abs(cfg->bitrate - srate) > cfg->bitrate / 2) {
		lprintf(0, "sampling rates (%u,%u) too different\n", cfg->bitrate, srate);
		goto errsnd;
	}
	phinceppdsp = ((1 << PHASEFRAC) * cfg->bitrate + srate / 2) / srate;
	phincdspepp = ((1 << PHASEFRAC) * srate + cfg->bitrate / 2) / cfg->bitrate;
	lprintf(1, "epp->dsp phase inc: 0x%05x  dsp->epp phase inc: 0x%05x\n", phinceppdsp, phincdspepp);
	if (ioctl(fddsp, SNDCTL_DSP_GETOSPACE, &aboinfo) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_GETOSPACE failed (%s)\n", strerror(errno));
		goto errsnd;
	}
	if (ioctl(fddsp, SNDCTL_DSP_GETISPACE, &abiinfo) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_GETISPACE failed (%s)\n", strerror(errno));
		goto errsnd;
	}
	if (aboinfo.fragstotal * aboinfo.fragsize < 2*SNDLATENCY ||
	    abiinfo.fragstotal * abiinfo.fragsize < 2*SNDLATENCY) {
		lprintf(0, "soundcard buffers too small (%u,%u)\n", 
			aboinfo.fragstotal * aboinfo.fragsize, 
			abiinfo.fragstotal * abiinfo.fragsize);
		goto errsnd;
	}
	tprintf("Audio IO to Linux Soundcard\n");
	/* reset the EPP adapter */
	bufu[0] = 7;
	bufu[1] = 0;
	if (parport_epp_write_addr(bufu, 2) != 2)
		goto errret;
	/* prefill EPP adapter to nominal queue size */
	memset(bufs, 0, SNDLATENCY);
	if (parport_epp_write_data(bufs, SNDLATENCY) != SNDLATENCY)
		goto errret;
	/* prefill to nominal queue size and stard soundcard */
	memset(bufu, 0x80, SNDLATENCY);
	if ((i = write(fddsp, bufu, SNDLATENCY)) != SNDLATENCY) {
		lprintf(0, "write(%d) failed %i (%s)\n", SNDLATENCY, i, strerror(errno));
		goto errsnd;
	}
	apar = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
	if (ioctl(fddsp, SNDCTL_DSP_SETTRIGGER, &apar) == -1) {
		lprintf(0, "ioctl SNDCTL_DSP_SETTRIGGER failed (%s)\n", strerror(errno));
		goto errsnd;
	}
	/* start the whole thing */
	for (;;) {
		/* first do the epp->dsp direction */
		/* get FIFO count */
		bufu[0] = ctrl | 1;
		if (parport_epp_write_addr(bufu, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(bufu, 2) != 2)
			goto errret;
		icnt = bufu[0] | (((unsigned int)bufu[1]) << 8);
		bufu[0] = ctrl;
		if (parport_epp_write_addr(bufu, 1) != 1)
			goto errret;
		icnt &= 0x7fff;
		if (icnt > 0) {
			if (icnt > sizeof(bufs)/2)
				icnt = sizeof(bufs)/2;
			if (parport_epp_read_data(bufs, icnt) != icnt)
				goto errret;
			pheppdsp &= PHASEMASK;
			cnt = 0;
			while (pheppdsp < (icnt << PHASEFRAC)) {
				bufu[cnt] = bufs[pheppdsp >> PHASEFRAC] + 128;
				pheppdsp += phinceppdsp;
				cnt++;
			}
			if ((i = write(fddsp, bufu, cnt)) != cnt) {
				lprintf(0, "write(%d) failed %i (%s)\n", cnt, i, strerror(errno));
				goto errsnd;
			}
			if (ioctl(fddsp, SNDCTL_DSP_GETODELAY, &apar) == -1) {
				lprintf(0, "ioctl SNDCTL_DSP_GETODELAY failed (%s)\n", strerror(errno));
				goto errsnd;
			}
			/* adjust speed */
			lprintf(4, "icnt %d cnt %d odel %d\n", icnt, cnt, apar);
			if (apar > SNDLATENCY)
				phinceppdsp++;
			else if (apar < SNDLATENCY)
				phinceppdsp--;
			if (phinceppdsp < (0x9 << (PHASEFRAC-4)) || phinceppdsp > (0x1f << (PHASEFRAC-4))) {
				lprintf(0, "phinceppdsp (0x%05x) out of range\n", phinceppdsp);
				goto errsnd;
			}
			/* play games with the LEDS */
			ledcnt += icnt;
			if (ledcnt >= 4000) {
				ledcnt %= 4000;
				ctrl += 0x40;
			}
		}
		/* next do the dsp->epp direction */
		/* get FIFO count */
		bufu[0] = ctrl | 2;
		if (parport_epp_write_addr(bufu, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(bufu, 2) != 2)
			goto errret;
		ocnt = bufu[0] | (((unsigned int)bufu[1]) << 8);
		bufu[0] = ctrl;
		if (parport_epp_write_addr(bufu, 1) != 1)
			goto errret;
		ocnt &= 0x7fff;
		omax = 16384 - ocnt;
		/* read sound */
		ocnts = read(fddsp, bufu, sizeof(bufu)/2);
		if (ocnts == -1 && errno == EAGAIN)
			ocnts = 0;
		if (ocnts < 0) {
			lprintf(0, "read(%d) failed %i (%s)\n", sizeof(bufu)/2, ocnts, strerror(errno));
			goto errsnd;
		}
		if (ocnts > 0) {
			phdspepp &= PHASEMASK;
			cnt = 0;
			while (phdspepp < (ocnts << PHASEFRAC)) {
				bufs[cnt] = bufu[phdspepp >> PHASEFRAC] - 128;
				phdspepp += phincdspepp;
				cnt++;
			}
			if (cnt > omax) {
				lprintf(0, "epp adapter output overrun (%d, %d)\n", cnt, omax);
				goto errsnd;
			}
#ifdef SINEOVERLAY
			for (i = 0; i < cnt; i++) {
				bufs[i] += sine[(phase >> 9) & 0x7f];
				phase += 0x1789;  /* pretty arbitrary */
			}
#endif
			if (parport_epp_write_data(bufs, cnt) != cnt)
				goto errret;
			/* reget the FIFO count */
			bufu[0] = ctrl | 2;
			if (parport_epp_write_addr(bufu, 1) != 1)
				goto errret;
			if (parport_epp_read_addr(bufu, 2) != 2)
				goto errret;
			ocnt = bufu[0] | (((unsigned int)bufu[1]) << 8);
			bufu[0] = ctrl;
			if (parport_epp_write_addr(bufu, 1) != 1)
				goto errret;
			ocnt &= 0x7fff;
			/* adjust speed */
			lprintf(4, "ocnts %d cnt %d ocnt %d\n", ocnts, cnt, ocnt);
			if (ocnt > SNDLATENCY)
				phincdspepp++;
			else if (ocnt < SNDLATENCY)
				phincdspepp--;
			if (phincdspepp < (0x9 << (PHASEFRAC-4)) || phincdspepp > (0x1f << (PHASEFRAC-4))) {
				lprintf(0, "phincdspepp (0x%05x) out of range\n", phincdspepp);
				goto errsnd;
			}
		}
		/* wait a bit */
		if (idle_callback(50000)) {
			reset_modem();
			return 0;
		}
		lprintf(1, "phase increments: 0x%05x 0x%05x\n", phinceppdsp, phincdspepp);
	}

errret:
	lprintf(0, "EPP timeout\n");
errsnd:
	close(fddsp);
	return -1;
}

#endif /* HAVE_LINUX_SOUNDCARD_H */

/* ---------------------------------------------------------------------- */

#ifdef HAVE_DIRECTX

#include <directx.h>

#define SNDLATENCY 4000   /* approx 1/2 seconds */
#define PHASEFRAC  12
#define PHASEMASK  ((1<<PHASEFRAC)-1)
#define SINEOVERLAY

static BOOL CALLBACK DSEnumProc(LPGUID guid, LPCSTR lpszDesc, LPCSTR lpszDrvName, LPVOID lpcontext)
{
        lprintf(1, "has %sGUID, desc %s drvname %s\n", guid ? "" : "no ", lpszDesc, lpszDrvName);
        return TRUE;
}

static LRESULT CALLBACK MyWndProc(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)
{
	switch (umsg) {
		/* Add cases such as WM_CREATE, WM_COMMAND, WM_PAINT if you don't 
		    want to pass these messages along for default processing. */
	case WM_CLOSE:
		DestroyWindow (hwnd);
		return 0;

	case WM_DESTROY:
		PostQuitMessage (0);
		return 0;
	}
	return DefWindowProc (hwnd, umsg, wParam, lParam);
}

static int saudio(struct adapter_config *cfg)
{
	LPDIRECTSOUND dsplay = NULL;
	LPDIRECTSOUNDCAPTURE dsrec = NULL;
	LPDIRECTSOUNDBUFFER playbuf = NULL;
	LPDIRECTSOUNDCAPTUREBUFFER recbuf = NULL;
	WNDCLASS wndclass;
	HRESULT res;
        WAVEFORMATEX waveformat;
        DSBUFFERDESC bdesc;
        DSCBUFFERDESC cbdesc;
	HANDLE hinst;
	HWND hwnd;
	DWORD playbufsz, recbufsz, playptr = 0, recptr = 0;
	DWORD lockbytes, lockbytes2, delay;
	unsigned char *uptr;
	unsigned char *uptr2;
	unsigned char *uptr3;
	int retval = -1;
	unsigned char bufu[8];
	signed char bufs[8192];     /* must be significantly bigger than SNDLATENCY! */
	unsigned char ctrl = 0;
	unsigned phinceppdsp, phincdspepp, pheppdsp = 0, phdspepp = 0;
	unsigned ledcnt = 0;
	int i, icnt, ocnt, cnt, ocnts, omax;
#ifdef SINEOVERLAY
	signed char sine[128];
	unsigned phase = 0;
#endif

#ifdef SINEOVERLAY
	for (i = 0; i < sizeof(sine); i++)
		sine[i] = 63 * sin((2.0 * M_PI / sizeof(sine)) * i);
#endif
        lprintf(1, "DirectSound drivers\n");
        DirectSoundEnumerateA(DSEnumProc, NULL);
        lprintf(1, "DirectSoundCapture drivers\n");
        DirectSoundCaptureEnumerateA(DSEnumProc, NULL);
	hinst = GetModuleHandleA(0);
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = (WNDPROC)MyWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hIcon = NULL;
	wndclass.hInstance = hinst;
	wndclass.hCursor = NULL;
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = "MyClass";
	if (!RegisterClass(&wndclass)) {
		lprintf(0, "RegisterClass error, %d\n", GetLastError());
		return -1;
	}
	hwnd = CreateWindowEx(WS_EX_APPWINDOW, "MyClass", "eppfpga", WS_DISABLED | WS_POPUP,
			      0, 0, 0, 0, NULL, NULL, hinst, NULL);
	if (!hwnd) {
		lprintf(0, "CreateWindowEx error, %d\n", GetLastError());
		goto errcreatewin;
	}
        if (FAILED(res = DirectSoundCreate(NULL, &dsplay, NULL))) {
                lprintf(0, "DirectSoundCreate error %lu\n", res);
                goto errdscreate;
        }
        if (FAILED(res = IDirectSound_SetCooperativeLevel(dsplay, hwnd, DSSCL_NORMAL))) {
                lprintf(0, "SetCooperativeLevel error %lu\n", res);
                goto errdscreate1;
        }
        if (FAILED(res = DirectSoundCaptureCreate(NULL, &dsrec, NULL))) {
                lprintf(0, "DirectSoundCaptureCreate error %lu\n", res);
                goto errdscreate1;
        }
        memset(&waveformat, 0, sizeof(waveformat));
        waveformat.wFormatTag = WAVE_FORMAT_PCM;
        waveformat.wBitsPerSample = 8;
        waveformat.nChannels = 1;
        waveformat.nSamplesPerSec = 8000;
        waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
        waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
        memset(&cbdesc, 0, sizeof(cbdesc));
        cbdesc.dwSize = sizeof(cbdesc);
        cbdesc.dwFlags = /* DSCBCAPS_WAVEMAPPED */ 0;
        recbufsz = cbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec;
        cbdesc.lpwfxFormat = &waveformat;
        if (FAILED(res = IDirectSoundCapture_CreateCaptureBuffer(dsrec, &cbdesc, &recbuf, NULL))) {
                lprintf(0, "CreateSoundCaptureBuffer error %lu\n", res);
                goto errdscreate2;
        }
        memset(&waveformat, 0, sizeof(waveformat));
        waveformat.wFormatTag = WAVE_FORMAT_PCM;
        waveformat.wBitsPerSample = 8;
        waveformat.nChannels = 1;
        waveformat.nSamplesPerSec = 8000;
        waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8;
        waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign;
        memset(&bdesc, 0, sizeof(bdesc));
        bdesc.dwSize = sizeof(bdesc);
        bdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN;
        playbufsz = bdesc.dwBufferBytes = waveformat.nAvgBytesPerSec;
        bdesc.lpwfxFormat = &waveformat;
        if (FAILED(res = IDirectSound_CreateSoundBuffer(dsplay, &bdesc, &playbuf, NULL))) {
                lprintf(0, "CreateSoundBuffer error %lu\n", res);
                goto errdscreate3;
        }
	cfg->bitrate = 8000;
	if ((i = adapter_start_eppsamp(cfg))) {
		retval = i;
		goto errdscreate4;
	}
	phinceppdsp = ((1 << PHASEFRAC) * cfg->bitrate + 8000 / 2) / 8000;
	phincdspepp = ((1 << PHASEFRAC) * 8000 + cfg->bitrate / 2) / cfg->bitrate;
	lprintf(1, "epp->dsp phase inc: 0x%05x  dsp->epp phase inc: 0x%05x\n", phinceppdsp, phincdspepp);
	tprintf("Audio IO to DirectX\n");
	/* reset the EPP adapter */
	bufu[0] = 7;
	bufu[1] = 0;
	if (parport_epp_write_addr(bufu, 2) != 2)
		goto errret;
	/* prefill EPP adapter to nominal queue size */
	memset(bufs, 0, SNDLATENCY);
	if (parport_epp_write_data(bufs, SNDLATENCY) != SNDLATENCY)
		goto errret;
	/* prefill to nominal queue size and stard soundcard */
	if (FAILED(res = IDirectSoundBuffer_Lock(playbuf, 0, SNDLATENCY, (LPVOID)&uptr, &lockbytes, NULL, NULL, 0))) {
                lprintf(0, "IDirectSoundBuffer_Lock error %lu\n", res);
                goto errresmodem;
        }
	memset(uptr, 0x80, SNDLATENCY);
	if (FAILED(res = IDirectSoundBuffer_Unlock(playbuf, uptr, lockbytes, NULL, 0))) {
                lprintf(0, "IDirectSoundBuffer_Unlock error %lu\n", res);
                goto errresmodem;
        }
	playptr = SNDLATENCY;
	if (FAILED(res = IDirectSoundBuffer_Play(playbuf, 0, 0, DSBPLAY_LOOPING))) {
                lprintf(0, "IDirectSoundBuffer_Play error %lu\n", res);
                goto errresmodem;
        }
	if (FAILED(res = IDirectSoundCaptureBuffer_Start(recbuf, DSCBSTART_LOOPING))) {
                lprintf(0, "IDirectSoundCaptureBuffer_Start error %lu\n", res);
                goto errresmodem;
        }
	/* start the whole thing */
	for (;;) {
		/* first do the epp->dsp direction */
		/* get FIFO count */
		bufu[0] = ctrl | 1;
		if (parport_epp_write_addr(bufu, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(bufu, 2) != 2)
			goto errret;
		icnt = bufu[0] | (((unsigned int)bufu[1]) << 8);
		bufu[0] = ctrl;
		if (parport_epp_write_addr(bufu, 1) != 1)
			goto errret;
		icnt &= 0x7fff;
		if (icnt > 0) {
			if (icnt > sizeof(bufs)/2)
				icnt = sizeof(bufs)/2;
			if (parport_epp_read_data(bufs, icnt) != icnt)
				goto errret;
			pheppdsp &= PHASEMASK;
			/* write to direct sound and convert */
			if (FAILED(res = IDirectSoundBuffer_Lock(playbuf, playptr, SNDLATENCY, (LPVOID)&uptr, &lockbytes,
								 (LPVOID)&uptr2, &lockbytes2, 0))) {
				lprintf(0, "IDirectSoundBuffer_Lock error %lu\n", res);
				goto errresmodem;
			}
			uptr3 = uptr;
			while (pheppdsp < (icnt << PHASEFRAC)) {
				*uptr3++ = bufs[pheppdsp >> PHASEFRAC] + 128;
				pheppdsp += phinceppdsp;
				playptr++;
				if (playptr >= playbufsz) {
					playptr = 0;
					uptr3 = uptr2;
				}
			}
			if (FAILED(res = IDirectSoundBuffer_Unlock(playbuf, uptr, lockbytes, uptr2, lockbytes2))) {
				lprintf(0, "IDirectSoundBuffer_Unlock error %lu\n", res);
				goto errresmodem;
			}
			/* get output delay */
			if (FAILED(res = IDirectSoundBuffer_GetCurrentPosition(playbuf, &delay, NULL))) {
				lprintf(0, "IDirectSoundBuffer_GetCurrentPosition error %lu\n", res);
				goto errresmodem;
			}
			delay = (playbufsz + playptr - delay) % playbufsz;
			/* adjust speed */
			lprintf(4, "icnt %d cnt %d odel %u\n", icnt, cnt, delay);
			if (delay > SNDLATENCY)
				phinceppdsp++;
			else if (delay < SNDLATENCY)
				phinceppdsp--;
			if (phinceppdsp < (0x9 << (PHASEFRAC-4)) || phinceppdsp > (0x1f << (PHASEFRAC-4))) {
				lprintf(0, "phinceppdsp (0x%05x) out of range\n", phinceppdsp);
				goto errresmodem;
			}
			/* play games with the LEDS */
			ledcnt += icnt;
			if (ledcnt >= 4000) {
				ledcnt %= 4000;
				ctrl += 0x40;
			}
		}
		/* next do the dsp->epp direction */
		/* get FIFO count */
		bufu[0] = ctrl | 2;
		if (parport_epp_write_addr(bufu, 1) != 1)
			goto errret;
		if (parport_epp_read_addr(bufu, 2) != 2)
			goto errret;
		ocnt = bufu[0] | (((unsigned int)bufu[1]) << 8);
		bufu[0] = ctrl;
		if (parport_epp_write_addr(bufu, 1) != 1)
			goto errret;
		ocnt &= 0x7fff;
		omax = 16384 - ocnt;
		/* read sound */
		if (FAILED(res = IDirectSoundCaptureBuffer_GetCurrentPosition(recbuf, &delay, NULL))) {
			lprintf(0, "IDirectSoundCaptureBuffer_GetCurrentPosition error %lu\n", res);
			goto errresmodem;
		}
		ocnts = (recbufsz + delay - recptr) % recbufsz;
		if (ocnts > 0) {
			if (FAILED(res = IDirectSoundCaptureBuffer_Lock(recbuf, recptr, SNDLATENCY, (LPVOID)&uptr, &lockbytes,
									(LPVOID)&uptr2, &lockbytes2, 0))) {
				lprintf(0, "IDirectSoundCaptureBuffer_Lock error %lu\n", res);
				goto errresmodem;
			}
			uptr3 = uptr;
			phdspepp &= PHASEMASK;
			cnt = 0;
			while (phdspepp < (ocnts << PHASEFRAC)) {
				if (phdspepp >= ((recbufsz - recptr) << PHASEFRAC)) {
					uptr3 = uptr2;
					phdspepp -= (recbufsz - recptr) << PHASEFRAC;
					recptr = 0;
				}
				bufs[cnt] = uptr3[phdspepp >> PHASEFRAC] - 128;
				phdspepp += phincdspepp;
				cnt++;
			}
			recptr = (recptr + (phdspepp >> PHASEFRAC)) % recbufsz;
			if (FAILED(res = IDirectSoundCaptureBuffer_Unlock(recbuf, uptr, lockbytes, uptr2, lockbytes2))) {
				lprintf(0, "IDirectSoundCaptureBuffer_Unlock error %lu\n", res);
				goto errresmodem;
			}
			if (cnt > omax) {
				lprintf(0, "epp adapter output overrun (%d, %d)\n", cnt, omax);
				goto errresmodem;
			}
#ifdef SINEOVERLAY
			for (i = 0; i < cnt; i++) {
				bufs[i] += sine[(phase >> 9) & 0x7f];
				phase += 0x1789;  /* pretty arbitrary */
			}
#endif
			if (parport_epp_write_data(bufs, cnt) != cnt)
				goto errret;
			/* reget the FIFO count */
			bufu[0] = ctrl | 2;
			if (parport_epp_write_addr(bufu, 1) != 1)
				goto errret;
			if (parport_epp_read_addr(bufu, 2) != 2)
				goto errret;
			ocnt = bufu[0] | (((unsigned int)bufu[1]) << 8);
			bufu[0] = ctrl;
			if (parport_epp_write_addr(bufu, 1) != 1)
				goto errret;
			ocnt &= 0x7fff;
			/* adjust speed */
			lprintf(4, "ocnts %d cnt %d ocnt %d\n", ocnts, cnt, ocnt);
			if (ocnt > SNDLATENCY)
				phincdspepp++;
			else if (ocnt < SNDLATENCY)
				phincdspepp--;
			if (phincdspepp < (0x9 << (PHASEFRAC-4)) || phincdspepp > (0x1f << (PHASEFRAC-4))) {
				lprintf(0, "phincdspepp (0x%05x) out of range\n", phincdspepp);
				goto errresmodem;
			}
		}
		/* wait a bit */
		if (idle_callback(50000)) {
			reset_modem();
			retval = 0;
			goto errresmodem;
		}
		lprintf(1, "phase increments: 0x%05x 0x%05x\n", phinceppdsp, phincdspepp);
	}

errret:
	lprintf(0, "EPP timeout\n");
errresmodem:
	IDirectSoundBuffer_Stop(playbuf);
	IDirectSoundCaptureBuffer_Stop(recbuf);
	reset_modem();
errdscreate4:
	IDirectSoundBuffer_Release(playbuf);
errdscreate3:
	IDirectSoundCaptureBuffer_Release(recbuf);
errdscreate2:
	IDirectSoundCapture_Release(dsrec);
errdscreate1:
	IDirectSound_Release(dsplay);
errdscreate:
	DestroyWindow(hwnd);
errcreatewin:
	UnregisterClass("MyClass", hinst);
	return -1;
}

#endif /* HAVE_DIRECTX */

/* ---------------------------------------------------------------------- */

const struct eppfpgatests eppfpgatests[] = {
	{ "ram", "tests the on board SRAM", ramtest },
	{ "bled", "tests the on board LED's using boundary scan", ledtest },
	{ "dac", "tests the on board DAC", dactest },
	{ "ppsig", "tests the parallel port signals using boundary scan", ppsigtest },
	{ "led", "tests the CON and STA leds using EPP address write", eppledtest },
	{ "ledfast", "tests the CON and STA leds using fast EPP address write", eppledtest2 },
	{ "berr", "tests the complete adapter", eppmodemtest1 },
	{ "berrv", "tests the complete adapter (verbose)", eppmodemtest2 },
	{ "berrd", "tests the complete adapter (exercises data transfer)", eppmodemtest3 },
	{ "berrdv", "tests the complete adapter (exercises data transfer, verbose)", eppmodemtest4 },
	{ "ccnt", "frequency counter using the check firmware", chkcountertest },
	{ "cdac", "fast DAC test using the check firmware", chkdactest },
	{ "creg", "EPP register read/write test using the check firmware", chkregtest },
	{ "cram", "fast RAM test using the check firmware", chkramtest },
	{ "cled", "LED test using the check firmware", chkledtest },
	{ "cadc", "tests the sigma delta A/D converter", chkadctest },
	{ "ecpled", "tests the CON and STA leds using ECP write", ecpledtest },
	{ "ecpledfast", "tests the CON and STA leds using fast ECP write", ecpledtest2 },
	{ "ecpberr", "tests the complete adapter in ECP mode", ecpmodemtest1 },
	{ "ecpberrv", "tests the complete adapter in ECP mode (verbose)", ecpmodemtest2 },
	{ "ecpberrd", "tests the complete adapter in ECP mode (exercises data transfer)", ecpmodemtest3 },
	{ "ecpberrdv", "tests the complete adapter in ECP mode (exercises data transfer, verbose)", ecpmodemtest4 },
	{ "eppafskled", "tests the CON and STA leds using EPP address write", eppafskledtest },
	{ "eppafskledfast", "tests the CON and STA leds using fast EPP address write", eppafskledtest2 },
	{ "eppafskberr", "tests the complete adapter", eppafskmodemtest1 },
	{ "eppafskberrv", "tests the complete adapter (verbose)", eppafskmodemtest2 },
	{ "eppafskberrd", "tests the complete adapter (exercises data transfer)", eppafskmodemtest3 },
	{ "eppafskberrdv", "tests the complete adapter (exercises data transfer, verbose)", eppafskmodemtest4 },
	{ "eppafsktx0", "transmit AFSK 0 constantly", eppafskmodemtest5 },
	{ "eppafsktx1", "transmit AFSK 1 constantly", eppafskmodemtest6 },
	{ "sppafskled", "tests the CON and STA leds using EPP address write", sppafskledtest },
	{ "sppafskledfast", "tests the CON and STA leds using fast EPP address write", sppafskledtest2 },
	{ "sppafskberr", "tests the complete adapter", sppafskmodemtest1 },
	{ "sppafskberrv", "tests the complete adapter (verbose)", sppafskmodemtest2 },
	{ "sppafskberrd", "tests the complete adapter (exercises data transfer)", sppafskmodemtest3 },
	{ "sppafskberrdv", "tests the complete adapter (exercises data transfer, verbose)", sppafskmodemtest4 },
	{ "sppafsktx0", "transmit AFSK 0 constantly", sppafskmodemtest5 },
	{ "sppafsktx1", "transmit AFSK 1 constantly", sppafskmodemtest6 },
	{ "epprx", "decodes received packets using EPP mode", epprx },
	{ "ecprx", "decodes received packets using ECP mode", ecprx },
	{ "eppafskrx", "decodes received packets using EPP AFSK mode", eppafskrx },
	{ "sppafskrx", "decodes received packets using SPP AFSK mode", sppafskrx },
	{ "sine1k", "generate approx 1k sine using the sample IO firmware", sine1k },
#if defined(HAVE_LINUX_SOUNDCARD_H) || defined(HAVE_DIRECTX)
	{ "saudio", "generate approx 1k sine using the sample IO firmware", saudio },
#endif /* HAVE_LINUX_SOUNDCARD_H */
	{ NULL, NULL, NULL }
};

/* ---------------------------------------------------------------------- */
