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

/*
 *      adapttests.c  -- HDLC packet radio modem for USB using FPGA/AnchorChips utility library.
 *
 *      Copyright (C) 1998-2000  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

/* AIX requires this to be the first thing in the file.  */
#ifndef __GNUC__
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
#pragma alloca
#  else
#   ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
#   endif
#  endif
# endif
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>

#ifndef HAVE_WINDOWS_H
#include <sys/uio.h>
#endif

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <math.h>
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

#include "baycomusb.h"

#include "sysdeps.h"

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

static void mode_none(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_NONE;
}

static void mode_fsk(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_FSK;
}

static void mode_external(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_EXTERNAL;
}

static void mode_afsk(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_AFSK;
}

static void mode_audio(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_AUDIO;
}

static void mode_bscan(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_BSCAN;
}


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

/*
 *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 "fpga/fsk_fpga.h"

/* ---------------------------------------------------------------------- */
/*
 * 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;
}

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

/*
 * this routine depends on the boundary scan firmware running on the
 * EZUSB controller (i.e. adapter_start_bscan)
 */

static void prbscan(const unsigned char *s, const unsigned char *x)
{
	unsigned i;
	printf("bound %s: ", s);
	for (i = 0; i < FPGA_BOUNDSIZE; i++, x++)
		printf("%02X", *x);
	printf("\n");
}

struct anfwstat {
	unsigned char errcode;
	unsigned char errval;
	unsigned count;
};

static int anchor_fw_stat(struct usbdevice *dev, struct anfwstat *st)
{
 	unsigned char buf[4];
	int r;

	r = usb_control_msg(dev, 0xc0, 0xb1, 0, 0, 4, buf, 5000);
	if (r != 4) {
		lprintf(2, "usb_control_msg(0xb1) error %s\n", strerror(errno));
		return -1;
	}
	lprintf(4, "ezusb: anchorstat: errcode 0x%02x  errval 0x%02x  cfgcntr 0x%04x\n",
		buf[0], buf[1], buf[2] | (buf[3] << 8));
	st->errcode = buf[0];
	st->errval = buf[1];
	st->count = buf[2] | (buf[3] << 8);
	return 0;
}

static int boundary(struct usbdevice *dev, unsigned int blen, const unsigned char *in, unsigned char *out)
{
	struct anfwstat stat;
	int r;

	anchor_fw_stat(dev, &stat);
#if 0
	prbscan("in ", in);
#endif
#if 1
	r = usb_control_msg(dev, 0x40, 0xb8, 0, 0, FPGA_BOUNDSIZE, (void *)in, 5000);
	if (r != FPGA_BOUNDSIZE) {
		lprintf(2, "usb_control_msg(0x40,0xb8) error %s\n", strerror(errno));
		return -1;
	}
#endif
#if 1
	r = usb_control_msg(dev, 0xc0, 0xb9, 0, 0, 0, NULL, 5000);
	if (r) {
		lprintf(2, "usb_control_msg(0xb9) error %s\n", strerror(errno));
		return -1;
	}
#endif
 	for (;;) {
		if (anchor_fw_stat(dev, &stat))
			return -1;
		if (stat.errcode == 0x20)
			break;
		if (stat.errcode != 0x21)
			return -1;
        }
#if 1
	r = usb_control_msg(dev, 0xc0, 0xb8, 0, 0, FPGA_BOUNDSIZE, out, 5000);
	if (r < 0) {
		lprintf(2, "usb_control_msg(0xc0,0xb8) error %s\n", strerror(errno));
		return -1;
	}
	if (r < FPGA_BOUNDSIZE) {
		lprintf(2, "usb_control_msg(0xc0,0xb8) short read %d\n", r);
		return -1;
	}
#endif
#if 0
	prbscan("out", out);
#endif
	return 0;
}

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

extern inline unsigned readboundary(const unsigned char *b, unsigned x)
{
	return (b[x >> 3] >> (x & 7)) & 1;
}

extern inline void writeboundary(unsigned char *b, unsigned x, int v)
{
	b[x >> 3] &= ~(1 << (x & 7));
	b[x >> 3] |= (v & 1) << (x & 7);
}

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

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 =================================== */

/*
 * LED test
 */

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

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

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

static const unsigned int dac_t[7] = 
{
	FPGA_PIN_MDATAOUT0_T,
	FPGA_PIN_MDATAOUT1_T,
	FPGA_PIN_MDATAOUT2_T,
	FPGA_PIN_MDATAOUT3_T,
	FPGA_PIN_MDATAOUT4_T,
	FPGA_PIN_MDATAOUT5_T,
	FPGA_PIN_MDATAOUT6_T
};

static const unsigned int dac_o[7] = 
{
	FPGA_PIN_MDATAOUT0_O,
	FPGA_PIN_MDATAOUT1_O,
	FPGA_PIN_MDATAOUT2_O,
	FPGA_PIN_MDATAOUT3_O,
	FPGA_PIN_MDATAOUT4_O,
	FPGA_PIN_MDATAOUT5_O,
	FPGA_PIN_MDATAOUT6_O
};


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

	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(dev, FPGA_BOUND, bd1, bd2);
		phase = (phase + 1) & 0xff;
		if (phase)
			continue;
		rot = (rot + 1) & 3;
		tprintf("%c\r", "|\\-/"[rot]);
		if (idle_callback(0))
			return 0;
	}
}

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

static int resetadapt(struct usbdevice *dev, struct configentry *cfg)
{
	lprintf(0, "Resetting adapter\n");
	if (adapter_reset(dev)) {
		lprintf(0, "adapter_reset failed\n");
		return -1;
	}
	return 0;
}

/* ---------------------------------------------------------------------- */
/*
 * LED test with FSK firmware
 */

static int fsk_write_xmem(struct usbdevice *dev, unsigned int addr, unsigned int data)
{
	int i;

	i = usb_control_msg(dev, 0x40, 0xd8, data, addr, 0, NULL, 5000);
	if (i < 0) {
		lprintf(0, "usb_control_msg(0x40,0xd8) failed\n");
		return -1;
	}
	return 0;
}

static int fsk_read_xmem(struct usbdevice *dev, unsigned int addr)
{
	int i;
	unsigned char ch;

	i = usb_control_msg(dev, 0xc0, 0xd8, 0, addr, 1, &ch, 5000);
	if (i < 0) {
		lprintf(0, "usb_control_msg(0xc0,0xd8) failed\n");
		return -1;
	}
	return ch;
}

static int fskledtest(struct usbdevice *dev, struct configentry *cfg)
{
	unsigned int ctrlreg = 0x40;
	int i;

	tprintf("FSK Firmware/LED test\n");
	for (;;) {
		ctrlreg ^= 0x04;
		if (fsk_write_xmem(dev, 0xc008, ctrlreg))
			return -1;
		if ((i = fsk_read_xmem(dev, 0xc008)) < 0)
			return -1;
		tprintf("FPGA Control register written: 0x%02x read: 0x%02x\n", ctrlreg, i);
		if (idle_callback(500000))
			return 0;
	}
}

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

static void sendpkt(struct usbdevice *dev, const void *pkt, unsigned int pktlen)
{
	int i;
	unsigned char *p;

	if (!pktlen)
		return;
	p = alloca(pktlen + 2);
	memcpy(p+2, pkt, pktlen);
	p[0] = 100;
	p[1] = 0;
	i = usb_bulk_msg(dev, 0x02, pktlen+2, p, 5000);
	if (i < 0) {
		lprintf(0, "USB bulk send packet failed\n");
		/*return -1;*/
	}
}

static unsigned recvpkt(struct usbdevice *dev, void *pkt, unsigned int maxlen)
{
	int i;

	i = usb_bulk_msg(dev, 0x82, maxlen, pkt, 5000);
	if (i < 0) {
		lprintf(0, "USB bulk receive packet failed\n");
		return 0;
	}
	lprintf(1, "Received Packet length: %d\n", i);
	return i;
}

static int getstat(struct usbdevice *dev, unsigned char *irq)
{
	int i;

	memset(irq, 0, 23);
#if 1
	i = usb_control_msg(dev, 0xc0, 0xc0, 0, 0, 23, irq, 5000);
	if (i < 0) {
		lprintf(0, "USB control failed\n");
		return -1;
	}
	lprintf(2, "USB control: return: %d\n", i);
#else
	i = usb_bulk_msg(dev, 0x81, 20, irq, 5000);
	if (i < 0) {
		lprintf(0, "USB bulk ep 1 in failed\n");
		return -1;
	}
	lprintf(2, "USB bulk: return: %d\n", i);
#endif
	lprintf(2, "USBstat: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		irq[0], irq[1], irq[2], irq[3], irq[4], irq[5]);
	lprintf(2, "  tx: st: 0x%02x rd: 0x%02x wr: 0x%02x 0x%02x cnt: 0x%02x flg: 0x%04x\n",
		irq[6], irq[7], irq[8], irq[9], irq[10], irq[11] | (irq[12] << 8));
	lprintf(2, "  rx: st: 0x%02x rd: 0x%02x wr: 0x%02x 0x%02x cnt: 0x%02x\n",
		irq[13], irq[14], irq[15], irq[16], irq[17]);
	lprintf(2, "  rxcnt: 0x%02x txcnt: 0x%02x ctrl: 0x%02x stat: 0x%02x\n",
		irq[18], irq[19], irq[20], irq[21]);
	lprintf(2, "  out2cs: 0x%02x\n", irq[22]);
	return 0;
}

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

static int modemstat(struct usbdevice *dev, struct configentry *cfg)
{
	unsigned char irq[23];
	int i;

	i = usb_control_msg(dev, 0xc0, 0xd1, 0, 0, 2, irq, 5000);
	if (i < 0) {
		lprintf(0, "USB control failed\n");
		return -1;
	}
	lprintf(0, "Bitrate: %02x %02x\n", irq[0], irq[1]);

	i = usb_control_msg(dev, 0xc0, 0xc0, 0, 0, 23, irq, 5000);
	if (i < 0) {
		lprintf(0, "USB control failed\n");
		return -1;
	}
	lprintf(2, "USB control: return: %d\n", i);
	lprintf(0, "USBstat: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		irq[0], irq[1], irq[2], irq[3], irq[4], irq[5]);
	lprintf(0, "  tx: st: 0x%02x rd: 0x%02x wr: 0x%02x 0x%02x cnt: 0x%02x flg: 0x%04x\n",
		irq[6], irq[7], irq[8], irq[9], irq[10], irq[11] | (irq[12] << 8));
	lprintf(0, "  rx: st: 0x%02x rd: 0x%02x wr: 0x%02x 0x%02x cnt: 0x%02x\n",
		irq[13], irq[14], irq[15], irq[16], irq[17]);
	lprintf(0, "  rxcnt: 0x%02x txcnt: 0x%02x ctrl: 0x%02x stat: 0x%02x\n",
		irq[18], irq[19], irq[20], irq[21]);
	lprintf(0, "  out2cs: 0x%02x\n", irq[22]);
	return 0;
}

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

static int fsktest(struct usbdevice *dev, struct configentry *cfg)
{
	unsigned char irq[20];
	int i;

	if (usb_claiminterface(dev, 0)) {
		lprintf(0, "Cannot claim interface 0\n");
		return -1;
	}
	usb_bulk_msg(dev, 0x81, sizeof(irq), irq, 5000);
	for (;;) {
		i = usb_bulk_msg(dev, 0x81, sizeof(irq), irq, 5000);
		if (i < 0) {
			lprintf(0, "USB interrupt failed\n");
			return -1;
		}
		if (i < 6) 
			lprintf(0, "USB Interrupt data too short %d\n", i);
		else {
			printf("USBstat: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", 
			       irq[0], irq[1], irq[2], irq[3], irq[4], irq[5]);
		}
		if (idle_callback(10))
			return 0;
	}
	return 0;
}

static int fsktest2(struct usbdevice *dev, struct configentry *cfg)
{
	unsigned char irq[23];
	int i;

	if (usb_claiminterface(dev, 0)) {
		lprintf(0, "Cannot claim interface 0\n");
		return -1;
	}
	for (;;) {
		i = usb_control_msg(dev, 0xc0, 0xc0, 0, 0, sizeof(irq), irq, 5000);
		if (i < 0) {
			lprintf(0, "USB control failed\n");
			return -1;
		}
		printf("USBstat: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		       irq[0], irq[1], irq[2], irq[3], irq[4], irq[5]);
		if (idle_callback(10))
			return 0;
	}
	return 0;
}

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

static int uarttest(struct usbdevice *dev, struct configentry *cfg)
{
	static const char text[] = "This is a test text.";
	unsigned char irq[20];
	unsigned idx = 0;
	int i, j;

	for (;;) {
		i = usb_bulk_msg(dev, 0x81, sizeof(irq), irq, 500);
		if (i < 0) {
			lprintf(0, "USB bulk in failed\n");
			/*return -1;*/
		}
		lprintf(2, "USBstat: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		       irq[0], irq[1], irq[2], irq[3], irq[4], irq[5]);
		for (j = 5; j < i; j++)
			printf("Receive: %c\n", irq[j]);
		if (irq[0] & 0x20) {
			if (!text[idx] && idx > sizeof(text))
				idx = 0;
			printf("Send: %c\n", text[idx]);
			i = usb_control_msg(dev, 0x40, 0xd3, text[idx++], 0, 0, NULL, 5000);
			if (i < 0) {
				lprintf(0, "USB control failed\n");
				return -1;
			}
		}
		if (idle_callback(10))
			return 0;
	}
	return 0;
}

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

static int transmittest(struct usbdevice *dev, struct configentry *cfg)
{
#if 0
	static const char text[] = "Test";
#elif 0
	static const char text[] = "TestpktTestpktTestpktTestpktTestpktTestpktTestpktTestpktTestpktTestpkt.";
#elif 0
	static const char text[] = "        dpl0    = 0x82\n"
		"        dph0    = 0x83\n"
		"        dpl1    = 0x84\n"
		"        dph1    = 0x85\n"
		"        dps     = 0x86\n"
		"        ckcon   = 0x8E\n"
		"        spc_fnc = 0x8F\n"
		"        exif    = 0x91\n"
		"        mpage   = 0x92\n"
		"        scon0   = 0x98\n"
		"        sbuf0   = 0x99\n"
		"        scon1   = 0xC0\n"
		"        sbuf1   = 0xC1\n"
		"        eicon   = 0xD8\n"
		"        eie     = 0xE8\n"
		"        eip     = 0xF8\n";
#elif 0
	static const char text[] = "0123456789012345678901234567890123456789012345678901234567890";
#elif 1
	static const char text[] = "01234567890123456789012345678901234567890123456789012345678901";
#elif 0
	static const char text[] = "012345678901234567890123456789012345678901234567890123456789012";
#elif 0
	static const char text[] = "0123456789012345678901234567890123456789012345678901234567890123";
#else
	static const char text[] = "01234567890123456789012345678901234567890123456789012345678901234";
#endif
	unsigned char rxpkt[MAXFLEN+2+1];
	unsigned char irq[23];
	unsigned plen;
	int i;

	if (usb_claiminterface(dev, 0)) {
		lprintf(0, "Cannot claim interface 0\n");
		return -1;
	}
#if 0
	{
		unsigned char x[64];

		getstat(dev, irq);
		x[0] = 0xaa;
		x[1] = 0x55;
		x[2] = 0x12;
		x[3] = 0x34;
		x[4] = 0x56;
		i = usb_bulk_msg(dev, 0x02, 5, x, 5000);
		if (i < 0) {
			lprintf(0, "USB bulk out packet failed\n");
			/*return -1;*/
		} else
			lprintf(0, "Bulk out: result %d\n", i);
		getstat(dev, irq);
		getstat(dev, irq);
		i = usb_bulk_msg(dev, 0x82, sizeof(x), x, 5000);
		if (i < 0) {
			lprintf(0, "USB bulk in packet failed\n");
			/*return -1;*/
		} else
			lprintf(0, "Bulk in: length %d; data %02x %02x %02x %02x\n",
				i, x[0], x[1], x[2], x[3]);
		getstat(dev, irq);
		i = usb_bulk_msg(dev, 0x82, sizeof(x), x, 5000);
		if (i < 0) {
			lprintf(0, "USB bulk in packet failed\n");
			/*return -1;*/
		} else
			lprintf(0, "Bulk in: length %d; data %02x %02x %02x %02x %02x %02x %02x %02x\n",
				i, x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]);
	}
#endif
	getstat(dev, irq);
	sendpkt(dev, text, strlen(text));
	getstat(dev, irq);
	sendpkt(dev, text, strlen(text));
	getstat(dev, irq);

//return 0;
	for (;;) {
		if (getstat(dev, irq))
			return -1;
		if (irq[14] != irq[15]) {
			plen = recvpkt(dev, rxpkt, sizeof(rxpkt));
			lprintf(0, "RX: %u  text: %02x %02x %02x %02x %02x %02x\n",
				plen, rxpkt[0], rxpkt[1], rxpkt[2], rxpkt[3], rxpkt[4], rxpkt[5]);
			rxpkt[plen] = 0;
			lprintf(0, "RX Packet:");
			for (i = 0; i < plen; i++)
				lprintf(0, " %02x", rxpkt[i]);
			lprintf(0, "\n");
		} /*else*/ if (irq[7] == irq[8])
			sendpkt(dev, text, strlen(text));
		if (idle_callback(10))
			return 0;
	}
	return 0;
}

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

static int fskrx(struct usbdevice *dev, struct configentry *cfg)
{
	unsigned char rxpkt[MAXFLEN+2];
	unsigned char irq[23];
	unsigned plen;

	if (usb_claiminterface(dev, 0)) {
		lprintf(0, "Cannot claim interface 0\n");
		return -1;
	}
	for (;;) {
		if (getstat(dev, irq)) 
			goto errret;
		if (irq[14] != irq[15]) {
			plen = recvpkt(dev, rxpkt, sizeof(rxpkt));
			if (plen > 4)
				tprintpkt(rxpkt, plen-2);
		}
		if (idle_callback(10000))
			return 0;
		/* DCD has inverse logic */
		tprintf("DCD:%c\r", (irq[0] & 0x08) ? 'D' : '-');
		if (idle_callback(0))
			return 0;
	}
	return 0;

 errret:
	lprintf(0, "USB Control Transfer failed\n");
	return -1;
}

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

static int transmittestafsk(struct usbdevice *dev, struct configentry *cfg)
{
#if 0
	static const char text[] = "Test";
#elif 0
	static const char text[] = "TestpktTestpktTestpktTestpktTestpktTestpktTestpktTestpktTestpktTestpkt.";
#elif 0
	static const char text[] = "        dpl0    = 0x82\n"
		"        dph0    = 0x83\n"
		"        dpl1    = 0x84\n"
		"        dph1    = 0x85\n"
		"        dps     = 0x86\n"
		"        ckcon   = 0x8E\n"
		"        spc_fnc = 0x8F\n"
		"        exif    = 0x91\n"
		"        mpage   = 0x92\n"
		"        scon0   = 0x98\n"
		"        sbuf0   = 0x99\n"
		"        scon1   = 0xC0\n"
		"        sbuf1   = 0xC1\n"
		"        eicon   = 0xD8\n"
		"        eie     = 0xE8\n"
		"        eip     = 0xF8\n";
#elif 0
	static const char text[] = "0123456789012345678901234567890123456789012345678901234567890";
#elif 1
	static const char text[] = "01234567890123456789012345678901234567890123456789012345678901";
#elif 0
	static const char text[] = "012345678901234567890123456789012345678901234567890123456789012";
#elif 0
	static const char text[] = "0123456789012345678901234567890123456789012345678901234567890123";
#else
	static const char text[] = "01234567890123456789012345678901234567890123456789012345678901234";
#endif
	unsigned char rxpkt[MAXFLEN+2+1];
	unsigned char irq[23];
	unsigned plen;
	int i;

	if (usb_claiminterface(dev, 0)) {
		lprintf(0, "Cannot claim interface 0\n");
		return -1;
	}
	i = calc_crc_ccitt(text, strlen(text));
	printf("CRC: 0x%04x\n", i);

#if 0
	{
		unsigned char x[64];

		getstat(dev, irq);
		i = usb_bulk_msg(dev, 2, 1, sizeof(x), x);
		if (i < 0) {
			lprintf(0, "USB bulk receive packet failed\n");
			/*return -1;*/
		}
	}
#endif
	getstat(dev, irq);
	sendpkt(dev, text, strlen(text));
	getstat(dev, irq);
	sendpkt(dev, text, strlen(text));
	getstat(dev, irq);
	for (;;) {
		if (getstat(dev, irq))
			return -1;
		if (irq[14] != irq[15]) {
			plen = recvpkt(dev, rxpkt, sizeof(rxpkt));
			lprintf(0, "RX: %u  text: %02x %02x %02x %02x %02x %02x\n",
				plen, rxpkt[0], rxpkt[1], rxpkt[2], rxpkt[3], rxpkt[4], rxpkt[5]);
			rxpkt[plen] = 0;
			lprintf(0, "RX Packet:");
			for (i = 0; i < plen; i++)
				lprintf(0, " %02x", rxpkt[i]);
			lprintf(0, "\n");
		} /*else*/ if (irq[7] == irq[8])
			sendpkt(dev, text, strlen(text));
		if (idle_callback(10))
			return 0;
	}
	return 0;
}

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

static int afskrx(struct usbdevice *dev, struct configentry *cfg)
{
	unsigned char rxpkt[MAXFLEN+2];
	unsigned char irq[23];
	unsigned plen;

	if (usb_claiminterface(dev, 0)) {
		lprintf(0, "Cannot claim interface 0\n");
		return -1;
	}
	for (;;) {
		if (getstat(dev, irq)) 
			goto errret;
		if (irq[14] != irq[15]) {
			plen = recvpkt(dev, rxpkt, sizeof(rxpkt));
			if (plen > 4)
				tprintpkt(rxpkt, plen-2);
		}
		if (idle_callback(10000))
			return 0;
		/* DCD has inverse logic */
		tprintf("DCD:%c\r", (irq[0] & 0x08) ? 'D' : '-');
		if (idle_callback(0))
			return 0;
	}
	return 0;

 errret:
	lprintf(0, "USB Control Transfer failed\n");
	return -1;
}

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

static void cfg_clkfrequency(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_AUDIO;
	cfg->adapter.samplerate = 2000;
}

static int clkfrequency(struct usbdevice *dev, struct configentry *cfg)
{
	unsigned val[8], freq[5];
	unsigned char data[7];
	struct timeval tv1, tv2;
	unsigned timediff;
	int i;

	if (usb_setinterface(dev, 0, 0))
		goto usberr;
	/* measure clk24 and clkmodem  */
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x00, 1, 7, data, 5000) < 7)
		goto usberr;
	lprintf(5, "data: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x40, 1, 0, NULL, 5000) < 0)
		goto usberr;
	gettime(&tv1);
	if (idle_callback(400000)) 
		return 0;
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x10, 1, 7, data, 5000) < 7)
		goto usberr;
	gettime(&tv2);
	lprintf(5, "data: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
	val[0] = data[1] | (data[2] << 8) | (data[3] << 16);
	val[1] = data[4] | (data[5] << 8) | (data[6] << 16);
	timediff = (1000000 + tv2.tv_usec - tv1.tv_usec) % 1000000;
	lprintf(1, "Measurement1: cntr0: 0x%06x  cntr1: 0x%06x  timediff: %6uus\n",
		val[0], val[1], timediff);
	/* measure clk8m */
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x04, 1, 0, NULL, 5000) < 0)
		goto usberr;
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x54, 1, 0, NULL, 5000) < 0)
		goto usberr;
	if (idle_callback(400000))
		return 0;
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x14, 1, 7, data, 5000) < 7)
		goto usberr;
	lprintf(5, "data: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
	val[2] = data[1] | (data[2] << 8) | (data[3] << 16);
	val[3] = data[4] | (data[5] << 8) | (data[6] << 16);
	lprintf(1, "Measurement2: cntr0: 0x%06x  cntr1: 0x%06x\n", val[2], val[3]);
	/* measure rxc */
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x08, 1, 0, NULL, 5000) < 0)
		goto usberr;
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x68, 1, 0, NULL, 5000) < 0)
		goto usberr;
	if (idle_callback(400000))
		return 0;
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x18, 1, 7, data, 5000) < 7)
		goto usberr;
	lprintf(5, "data: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
	val[4] = data[1] | (data[2] << 8) | (data[3] << 16);
	val[5] = data[4] | (data[5] << 8) | (data[6] << 16);
	lprintf(1, "Measurement3: cntr0: 0x%06x  cntr1: 0x%06x\n", val[4], val[5]);
	/* measure txc */
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x0c, 1, 0, NULL, 5000) < 0)
		goto usberr;
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x7c, 1, 0, NULL, 5000) < 0)
		goto usberr;
	if (idle_callback(400000))
		return 0;
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x1c, 1, 7, data, 5000) < 7)
		goto usberr;
	lprintf(5, "data: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
		data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
	val[6] = data[1] | (data[2] << 8) | (data[3] << 16);
	val[7] = data[4] | (data[5] << 8) | (data[6] << 16);
	lprintf(1, "Measurement4: cntr0: 0x%06x  cntr1: 0x%06x\n", val[6], val[7]);
	/* print measurements */
	if (!timediff)
		timediff = 1;
	freq[4] = (val[0] * 1000000ULL + timediff / 2) / timediff;
	for (i = 0; i < 4; i++) {
		if (!val[2*i])
			val[2*i] = 1;
		freq[i] = (24000000ULL * val[2*i+1] + val[2*i]/2) / val[2*i];
	}
	lprintf(0, "Clock frequencies\n"
		"  clk24:     %'8u Hz  (approximate!)\n"
		"  clkmodem:  %'8u Hz\n"
		"  clk8m:     %'8u Hz\n"
		"  mrxc:      %'8u Hz\n"
		"  mtxc:      %'8u Hz\n", 
		freq[4], freq[0]*8, freq[1], freq[2], freq[3]);
	/* reset LED's */
	if (usb_control_msg(dev, 0xc0, 0xe0, 0x00, 1, 0, NULL, 5000) < 0)
		goto usberr;
	return 0;

 usberr:
	lprintf(0, "USB Control Transfer failed\n");
	return -1;
}

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

#ifndef HAVE_WINDOWS_H

#define ISOFRAMES 128

static void cfg_isotest(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_AUDIO;
	cfg->adapter.samplerate = 2000;
}

static int isotest(struct usbdevice *dev, struct configentry *cfg)
{
	struct {
		struct usbdevfs_urb urb;
		struct usbdevfs_iso_packet_desc frames[ISOFRAMES];
	} urb[2];
	struct usbdevfs_urb *purb;
	unsigned char data[ISOFRAMES*16];
	unsigned char *datap;
	unsigned char irq[23];
	int i, j;

 	if (getstat(dev, irq)) 
		goto errret;
        memset(&urb[0], 0, sizeof(urb[0]));
	urb[0].urb.type = USBDEVFS_URB_TYPE_ISO;
	urb[0].urb.endpoint = 0x88;
	urb[0].urb.flags = USBDEVFS_URB_ISO_ASAP;
	urb[0].urb.buffer = data;
	urb[0].urb.buffer_length = 2*ISOFRAMES;
	urb[0].urb.number_of_packets = ISOFRAMES;
	urb[0].urb.signr = 0;
	urb[0].urb.usercontext = NULL;
	for (j = 0; j < ISOFRAMES; j++)
		urb[0].urb.iso_frame_desc[j].length = 2;
	urb[1] = urb[0];
	urb[1].urb.buffer = data+2*ISOFRAMES;
	for (i = 0; i < 2; i++) {
		if (usb_submiturb(dev, &urb[i].urb)) {
			lprintf(0, "usb_submiturb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
	}
	for (;;) {
                if (!(purb = usb_reapurb(dev, 0))) {
			lprintf(0, "usb_reapurb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
		if (purb != &urb[0].urb && purb != &urb[1].urb) {
			lprintf(1, "Argh, usb_reapurb returned invalid pointer %p (urb0 %p urb1 %p)\n", purb, &urb[0].urb, &urb[1].urb);
			continue;
		}
		datap = (unsigned char *)purb->buffer;

		lprintf(0, "async completed:\n"
			"status %d length %u error_count %d stat[0].length %u stat[0].status %u\n",
			purb->status, purb->actual_length, purb->error_count,
			purb->iso_frame_desc[0].actual_length, purb->iso_frame_desc[0].status);

		for (i = 0; i < ISOFRAMES; i++)
			lprintf(0, "  frame %02d  len %u stat %u data %02x %02x\n", i, 
				purb->iso_frame_desc[i].actual_length, purb->iso_frame_desc[i].status,
				datap[2*i], datap[2*i+1]);

		if (usb_submiturb(dev, purb)) {
			lprintf(0, "usb_submiturb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
#if 0
		if (getstat(dev, irq)) 
			goto errret;
		if (idle_callback(10000))
			return 0;
#endif		
		/* DCD has inverse logic */
		tprintf("DCD:%c\r", (irq[0] & 0x08) ? 'D' : '-');
		if (idle_callback(0))
			return 0;
	}
	return 0;

 errret:
	lprintf(0, "USB Control Transfer failed\n");
	return -1;
}

#undef ISOFRAMES

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

#define ISOFRAMES 128
#define ISOFRAMESIZE 16

static void cfg_isotest2(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_AUDIO;
        cfg->adapter.samplerate = ISOFRAMESIZE*1000;
}

static int isotest2(struct usbdevice *dev, struct configentry *cfg)
{
	struct {
		struct usbdevfs_urb urb;
		struct usbdevfs_iso_packet_desc frames[ISOFRAMES];
	} urb[4];
	struct usbdevfs_urb *purb;
	unsigned char data[4*ISOFRAMES*ISOFRAMESIZE];
	unsigned char *datap;
	unsigned char irq[23];
	int i, j;

	if (getstat(dev, irq))
		goto errret;
	/* initialize the output data */
	for (i = j = 0; i < 2*ISOFRAMES*ISOFRAMESIZE; i++) {
		data[2*ISOFRAMES*ISOFRAMESIZE+i] = 127 * cos((M_PI / 32768) * j);
		j += 65536U * 1000U / cfg->adapter.samplerate;
		j &= 0xffff;
	}
	/* fill ISO URBs */
        memset(&urb[0], 0, sizeof(urb[0]));
	urb[0].urb.type = USBDEVFS_URB_TYPE_ISO;
	urb[0].urb.endpoint = 0x88;
	urb[0].urb.flags = USBDEVFS_URB_ISO_ASAP;
	urb[0].urb.buffer = data;
	urb[0].urb.buffer_length = ISOFRAMESIZE*ISOFRAMES;
	urb[0].urb.number_of_packets = ISOFRAMES;
	urb[0].urb.signr = 0;
	urb[0].urb.usercontext = NULL;
	for (j = 0; j < ISOFRAMES; j++)
		urb[0].urb.iso_frame_desc[j].length = ISOFRAMESIZE;
	urb[1] = urb[0];
	urb[2] = urb[0];
	urb[2].urb.endpoint = 0x08;
	urb[3] = urb[2];
	urb[1].urb.buffer = data+ISOFRAMES*ISOFRAMESIZE;
	urb[2].urb.buffer = data+2*ISOFRAMES*ISOFRAMESIZE;
	urb[3].urb.buffer = data+3*ISOFRAMES*ISOFRAMESIZE;
	for (i = 0; i < 4; i++) {
		if (usb_submiturb(dev, &urb[i].urb)) {
			lprintf(0, "usb_submiturb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
	}
	for (;;) {
                if (!(purb = usb_reapurb(dev, 0))) {
			lprintf(0, "usb_reapurb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
		if (purb != &urb[0].urb && purb != &urb[1].urb && purb != &urb[2].urb && purb != &urb[3].urb) {
			lprintf(1, "Argh, USB_PROC_REAPURB returned invalid pointer %p (urb0 %p urb1 %p urb2 %p urb3 %p)\n",
				purb, &urb[0].urb, &urb[1].urb, &urb[2].urb, &urb[3].urb);
			continue;
		}
		datap = (unsigned char *)purb->buffer;

		/* check for output isobuf */
		if (!(purb->endpoint & 0x80)) {
                        if (usb_submiturb(dev, purb)) {
				lprintf(0, "usb_submiturb failed, %s (errno %d)\n", strerror(errno), errno);
				goto errret;
			}
			continue;
		}

		/* check for errors */
		for (i = 0; i < ISOFRAMES; i++) {
			if (purb->iso_frame_desc[i].status != 0 ||
			    purb->iso_frame_desc[i].actual_length != ISOFRAMESIZE) {
				lprintf(1, "USB iso transfer: warning: subframe %d status %d length %d\n",
					i, purb->iso_frame_desc[i].status, purb->iso_frame_desc[i].actual_length);
				memset(datap+i*ISOFRAMESIZE, 0, ISOFRAMESIZE);
			}
		}
		/* restart iso */
		if (usb_submiturb(dev, purb)) {
			lprintf(0, "usb_submiturb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
		/* print frame contents */
#if 0
		for (i = 0; i < ISOFRAMES; i++) {
			if (purb->iso_frame_desc[i].status == 0 &&
			    purb->iso_frame_desc[i].actual_length == ISOFRAMESIZE) {
				lprintf(2, "USB Frame %3d:", i);
				for (j = 0; j < ISOFRAMESIZE; j++)
					lprintf(2, " %02x", datap[i*ISOFRAMESIZE+j]);
				lprintf(2, "\n");
			}
		}
#endif
		/* fetch PLL variables */
		if (usb_control_msg(dev, 0xc0, 0xe1, 0, 0, 7, irq, 5000) < 7)
			goto errret;
		tprintf("PLL: sofcnt: %3d framesz: %2u div: %5u divrel: %3u pllcorr: %2u txcnt: %2u\n",
			irq[0], irq[1], irq[2] | (irq[3] << 8), irq[4], irq[5], irq[6]);
		/* check samples written to tx */
		if (usb_control_msg(dev, 0xc0, 0xe2, 0, 0, ISOFRAMESIZE, irq, 5000) < ISOFRAMESIZE)
			goto errret;
		lprintf(0, "Tx sample frame:");
		for(i = 0; i < ISOFRAMESIZE; i++)
			lprintf(0, " %02x", irq[i]);
		lprintf(0, "\n");
		/* check for CTRL-C */
		if (idle_callback(0))
			return 0;
	}
	return 0;

errret:
	lprintf(0, "USB Control Transfer failed\n");
	return -1;
}

#undef ISOFRAMES
#undef ISOFRAMESIZE

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

#ifdef HAVE_LINUX_SOUNDCARD_H

#include <sys/ioctl.h>
#include <linux/soundcard.h>

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

#define ISOFRAMES    128
#define ISOFRAMESIZE 16

static void cfg_saudio(struct configentry *cfg)
{
	cfg->adapter.mode = MODE_AUDIO;
        cfg->adapter.samplerate = ISOFRAMESIZE*1000;
}

static int saudio(struct usbdevice *dev, struct configentry *cfg)
{
	/* USB variables */
	struct {
		struct usbdevfs_urb urb;
		struct usbdevfs_iso_packet_desc frames[ISOFRAMES];
	} urb[2];
	struct usbdevfs_urb *purb;
	unsigned char data[2*ISOFRAMES*ISOFRAMESIZE];
	unsigned char *datap;
	/* soundcard variables */
        unsigned char buf[SNDLATENCY];   /* must be at least SNDLATENCY! */
        audio_buf_info aboinfo, abiinfo;
        unsigned srate;
        unsigned char ctrl = 0;
        unsigned phincusbdsp, phincdspusb, phusbdsp = 0, phdspusb = 0;
        unsigned ledcnt = 0;
        int i, j, fddsp, apar;
#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

	/* initialize soundcard */
        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->adapter.samplerate;
        if (ioctl(fddsp, SNDCTL_DSP_SPEED, &srate) == -1) {
                lprintf(0, "ioctl SNDCTL_DSP_SPEED failed (%s), samplingrate %u not supported??\n", 
                        strerror(errno), cfg->adapter.samplerate);
                goto errsnd;
        }
        lprintf(1, "usb adapter sampling rate %u, soundcard sampling rate %u\n", cfg->adapter.samplerate, srate);
        if (abs(cfg->adapter.samplerate - srate) > cfg->adapter.samplerate / 2) {
                lprintf(0, "sampling rates (%u,%u) too different\n", cfg->adapter.samplerate, srate);
                goto errsnd;
        }
        phincusbdsp = ((1 << PHASEFRAC) * cfg->adapter.samplerate + srate / 2) / srate;
        phincdspusb = ((1 << PHASEFRAC) * srate + cfg->adapter.samplerate / 2) / cfg->adapter.samplerate;
        lprintf(1, "usb->dsp phase inc: 0x%05x  dsp->usb phase inc: 0x%05x\n", phincusbdsp, phincdspusb);
        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;
        }
	/* prepare and start USB transfer */
        memset(&urb[0], 0, sizeof(urb[0]));
	urb[0].urb.type = USBDEVFS_URB_TYPE_ISO;
	urb[0].urb.endpoint = 0x88;
	urb[0].urb.flags = USBDEVFS_URB_ISO_ASAP;
	urb[0].urb.buffer = data;
	urb[0].urb.buffer_length = ISOFRAMES*ISOFRAMESIZE;
	urb[0].urb.number_of_packets = ISOFRAMES;
	urb[0].urb.signr = 0;
	urb[0].urb.usercontext = NULL;
	for (j = 0; j < ISOFRAMES; j++)
		urb[0].urb.iso_frame_desc[j].length = ISOFRAMESIZE;
	urb[1] = urb[0];
	urb[1].urb.buffer = data+ISOFRAMES*ISOFRAMESIZE;
	for (i = 0; i < 2; i++) {
		if (usb_submiturb(dev, &urb[i].urb)) {
			lprintf(0, "usb_submiturb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
	}
	tprintf("Audio IO to Linux Soundcard\n");
        /* prefill to nominal queue size and stard soundcard */
        memset(buf, 0x80, SNDLATENCY);
        if ((i = write(fddsp, buf, 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 (;;) {
                if (!(purb = usb_reapurb(dev, 0))) {
			lprintf(0, "usb_reapurb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
		if (purb != &urb[0].urb && purb != &urb[1].urb) {
			lprintf(1, "Argh, USB_PROC_REAPURB returned invalid pointer %p (urb0 %p urb1 %p)\n", purb, &urb[0].urb, &urb[1].urb);
			continue;
		}
		datap = (unsigned char *)purb->buffer;
		for (i = 0; i < ISOFRAMES; i++) {
			if (purb->iso_frame_desc[i].status != 0 ||
			    purb->iso_frame_desc[i].actual_length != ISOFRAMESIZE) {
				lprintf(1, "USB iso transfer: warning: subframe %d status %d length %d\n",
					i, purb->iso_frame_desc[i].status, purb->iso_frame_desc[i].actual_length);
				memset(datap+i*ISOFRAMESIZE, 0, ISOFRAMESIZE);
			}
		}
		if ((i = write(fddsp, datap, ISOFRAMES*ISOFRAMESIZE)) != ISOFRAMES*ISOFRAMESIZE) {
			lprintf(0, "write(%d) failed %i (%s)\n", ISOFRAMES*ISOFRAMESIZE, i, strerror(errno));
			goto errsnd;
		}
		if (usb_submiturb(dev, purb)) {
			lprintf(0, "usb_submiturb failed, %s (errno %d)\n", strerror(errno), errno);
			goto errret;
		}
		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, "odel %d\n", apar);
		if (apar > SNDLATENCY)
			phincusbdsp++;
		else if (apar < SNDLATENCY)
			phincusbdsp--;
		if (phincusbdsp < (0x9 << (PHASEFRAC-4)) || phincusbdsp > (0x1f << (PHASEFRAC-4))) {
			lprintf(0, "phincusbdsp (0x%05x) out of range\n", phincusbdsp);
			goto errsnd;
		}
		/* play games with the LEDS */
		ledcnt += ISOFRAMES*ISOFRAMESIZE;
		if (ledcnt >= 4000) {
			ledcnt %= 4000;
			ctrl += 0x40;
		}

#if 0
                /* next do the dsp->usb direction */
                /* get FIFO count */
                bufu[0] = ctrl | 2;
                if (parport_usb_write_addr(bufu, 1) != 1)
                        goto errret;
                if (parport_usb_read_addr(bufu, 2) != 2)
                        goto errret;
                ocnt = bufu[0] | (((unsigned int)bufu[1]) << 8);
                bufu[0] = ctrl;
                if (parport_usb_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) {
                        phdspusb &= PHASEMASK;
                        cnt = 0;
                        while (phdspusb < (ocnts << PHASEFRAC)) {
                                bufs[cnt] = bufu[phdspusb >> PHASEFRAC] - 128;
                                phdspusb += phincdspusb;
                                cnt++;
                        }
                        if (cnt > omax) {
                                lprintf(0, "usb 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_usb_write_data(bufs, cnt) != cnt)
                                goto errret;
                        /* reget the FIFO count */
                        bufu[0] = ctrl | 2;
                        if (parport_usb_write_addr(bufu, 1) != 1)
                                goto errret;
                        if (parport_usb_read_addr(bufu, 2) != 2)
                                goto errret;
                        ocnt = bufu[0] | (((unsigned int)bufu[1]) << 8);
                        bufu[0] = ctrl;
                        if (parport_usb_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)
                                phincdspusb++;
                        else if (ocnt < SNDLATENCY)
                                phincdspusb--;
                        if (phincdspusb < (0x9 << (PHASEFRAC-4)) || phincdspusb > (0x1f << (PHASEFRAC-4))) {
                                lprintf(0, "phincdspusb (0x%05x) out of range\n", phincdspusb);
                                goto errsnd;
                        }
                }
#endif
                /* we block when waiting for a new USB event */
                if (idle_callback(0))
                        return 0;
               lprintf(1, "phase increments: 0x%05x 0x%05x\n", phincusbdsp, phincdspusb);
        }

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

#endif /* HAVE_LINUX_SOUNDCARD_H */

#endif /* HAVE_WINDOWS_H */

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

const struct usbtests usbtests[] = {
	{ "bled", "tests the on board LED's using boundary scan", mode_bscan, ledtest },
	{ "dac", "tests the on board DAC", mode_bscan, dactest },
	{ "reset", "Reset the adapter (Disconnect & Reconnect)", mode_none, resetadapt },

	{ "fskmodemstat", "Retrieve FSK Modem Status", mode_fsk, modemstat },
	{ "fskmodemstat", "Retrieve AFSK Modem Status", mode_afsk, modemstat },

	{ "fskledtest", "FSK Firmware/LED test", mode_fsk, fskledtest },

	{ "fsktest", "fsktest", mode_fsk, fsktest },
	{ "fsktest2", "fsktest2", mode_fsk, fsktest2 },

	{ "uarttest", "uarttest", mode_fsk, uarttest },
	{ "txtest", "transmittest", mode_fsk, transmittest },
	{ "fskrx", "decodes received FSK packets", mode_fsk, fskrx },
	{ "txtestafsk", "transmittest", mode_afsk, transmittestafsk },
	{ "afskrx", "decodes received FSK packets", mode_afsk, afskrx },
	{ "clk", "measures various clock frequencies", cfg_clkfrequency, clkfrequency },
#ifndef HAVE_WINDOWS_H
	{ "isotest", "test isochronous transfers", cfg_isotest, isotest },
	{ "isotest2", "test isochronous transfers and sample lock to SOF rate PLL", cfg_isotest2, isotest2 },
#ifdef HAVE_LINUX_SOUNDCARD_H
        { "saudio", "generate approx 1k sine using the sample IO firmware", cfg_saudio, saudio },
#endif
#endif
	{ NULL, NULL, NULL }
};

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