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

/*
 *      mainunixuser.c  --  Transceiver control UNIX stuff (Userspace driver, uses mkiss).
 *
 *      Copyright (C) 2000-2001
 *          Thomas Sailer (t.sailer@alumni.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.
 *
 *  History:
 *   0.1  19.09.2000  Created
 *
 */

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

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

#define _GNU_SOURCE
#define _REENTRANT

/* 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 "trx.h"
#include "simd.h"

#define _USE_BSD
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <termios.h>

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "getopt.h"
#endif

#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif

#ifdef HAVE_LINUX_IF_H
#include <linux/if.h>
#endif
#ifdef HAVE_LINUX_SOCKIOS_H
#include <linux/sockios.h>
#endif
#ifdef HAVE_LINUX_IF_ETHER_H
#include <linux/if_ether.h>
#endif
#if defined(HAVE_ARPA_INET_H) && !defined(WIN32)
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_ARP_H
#include <net/if_arp.h>
#endif
#ifdef HAVE_NETAX25_AX25_H
#include <netax25/ax25.h>
#elif defined(HAVE_LINUX_AX25_H)
#include <linux/ax25.h>
#endif
#ifdef HAVE_NET_ROUTE_H
#include <net/route.h>
#endif

#include <glib.h>
#include <pthread.h>

#include "list.h"

#ifdef HAVE_MKISS
#include <arpa/inet.h>
#endif

#ifdef HAVE_PTY_H
#include <pty.h>
#else
extern int openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct  winsize *winp);
#endif

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

struct orbfdlist {
	struct list_head list;
	GIOPConnection *conn;
	struct pollfd *pfd;
};

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

static int devices_fd = -1;
static LIST_HEAD(devices_list);
static pthread_mutex_t orbit_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t devlist_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t devlist_cond = PTHREAD_COND_INITIALIZER;
static LIST_HEAD(orb_fds);
static int quit = 0;
static int sock_fd = -1;

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

void acquire_orbit_mutex(void)
{
	pthread_mutex_lock(&orbit_mutex);
}

void release_orbit_mutex(void)
{
	pthread_mutex_unlock(&orbit_mutex);
}

void acquire_trxdata_mutex(struct trx_thread_state *state)
{
	pthread_mutex_lock(&state->mutex);
}

void release_trxdata_mutex(struct trx_thread_state *state)
{
	pthread_mutex_unlock(&state->mutex);
}

int check_quit(void)
{
	return quit;
}

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

int reopen_usbdev(struct trx_thread_state *state)
{
	struct timespec tm;
	struct timeval tv;
	int r;

	if (state->usbdev)
		usb_close(state->usbdev);
	state->usbdev = NULL;
	pthread_mutex_lock(&devlist_mutex);
       	state->busnr = state->devnr = -1;
	gettimeofday(&tv, NULL);
	tm.tv_sec = tv.tv_sec + 60;
	tm.tv_nsec = tv.tv_usec * 1000;
	for (;;) {
		if ((r = pthread_cond_timedwait(&devlist_cond, &devlist_mutex, &tm)) && r != EINTR) {
			lprintf(1, "Timeout reopening USB device %s: pthread_cond_wait error %s (%d)\n", state->serial, strerror(r), r);
			pthread_mutex_unlock(&devlist_mutex);
			return -1;
		}
		if (check_quit()) {
			pthread_mutex_unlock(&devlist_mutex);
			return -1;
		}
		if (state->busnr != -1 && state->devnr != -1)
			break;
	}
	pthread_mutex_unlock(&devlist_mutex);
	if (!(state->usbdev = usb_open_bynumber(state->busnr, state->devnr, BAYCOMUSB_VENDORID, -1))) {
		lprintf(1, "Cannot open USB device %03d:%03d\n", state->busnr, state->devnr);
		return -1;
	}
	lprintf(10, "=== THREADCONTINUE: %03d:%03d\n", state->busnr, state->devnr);
	return 0;
}

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

static void *thread_start(void *arg)
{
	struct trx_thread_state *state = arg;
	struct trx_thread_state *state2;
	struct list_head *list;
	int i;

	lprintf(10, ">>> THREADSTART: %03d:%03d\n", state->busnr, state->devnr);
	/* open communications to device */
	pthread_mutex_init(&state->mutex, NULL);
	state->netif_fd = state->netif_fdmaster = -1;
	state->netif_opened = 0;
	/* get serial number from device */
	if (!(state->usbdev = usb_open_bynumber(state->busnr, state->devnr, BAYCOMUSB_VENDORID, -1))) {
		lprintf(1, "Cannot open USB device %03d:%03d\n", state->busnr, state->devnr);
		goto ret;
	}
	i = adapter_init_getserial(state);
	if (i < 0) {
		if (i == -2)
			lprintf(1, "Error loading USB device %03d:%03d\n", state->busnr, state->devnr);
		goto ret;
	}
	lprintf(2, "USB device %03d:%03d serial %s\n", state->busnr, state->devnr, state->serial);
	pthread_mutex_lock(&devlist_mutex);
	for (list = devices_list.next; list != &devices_list; list = list->next) {
		state2 = list_entry(list, struct trx_thread_state, device_list);
		if (state2 != state && !strcmp(state2->serial, state->serial)) {
			state2->busnr = state->busnr;
			state2->devnr = state->devnr;
			state2->mode = state->mode;
			list_del(&state->device_list);
			INIT_LIST_HEAD(&state->device_list);
			pthread_mutex_unlock(&devlist_mutex);
			lprintf(2, "Thread for USB device %03d:%03d serial %s already running\n", 
				state->busnr, state->devnr, state->serial);
			pthread_cond_broadcast(&devlist_cond);
			goto ret;
		}
	}
	pthread_mutex_unlock(&devlist_mutex);
	state->flags = FLG_RELOADMODEM | FLG_MODEMERROR | FLG_T7FERROR;
	/* finally start thread */
	trx_thread(state);
	if (state->usbdev)
		adapter_reset(state->usbdev);

  ret:
	netif_close(state);
	if (state->usbdev)
		usb_close(state->usbdev);
	pthread_mutex_destroy(&state->mutex);
	pthread_mutex_lock(&devlist_mutex);
	list_del(&state->device_list);
	pthread_mutex_unlock(&devlist_mutex);
	lprintf(10, "<<< THREADSTOP: %03d:%03d serial %s\n", 
		state->busnr, state->devnr, state->serial);
	free(state);
	return NULL;
}

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

static void check_new_uninited_modems(void)
{
        char buf[16384];
        char *start, *end, *lineend, *cp;
        int ret;
        unsigned int devnum = 0, busnum = 0, vendorid, productid;
	struct list_head *list;
	struct trx_thread_state *state;
	pthread_attr_t attr;
	pthread_t thr;

	/* now scan devices file */
        if (lseek(devices_fd, 0, SEEK_SET) == (off_t)-1)
                lprintf(0, "lseek: %s (%d)\n", strerror(errno), errno);
	lprintf(10, "read\n");
        ret = read(devices_fd, buf, sizeof(buf)-1);
	lprintf(10, "end read: %d\n", ret);
        if (ret == -1)
                lprintf(0, "read: %s (%d)\n", strerror(errno), errno);
        end = buf + ret;
        *end = 0;
        start = buf;
	while (start < end) {
                lineend = strchr(start, '\n');
                if (!lineend)
                        break;
                *lineend = 0;
                switch (start[0]) {
                case 'T':  /* topology line */
                        if ((cp = strstr(start, "Dev#="))) {
                                devnum = strtoul(cp + 5, NULL, 0);
                        } else
                                devnum = 0;
                        if ((cp = strstr(start, "Bus="))) {
                                busnum = strtoul(cp + 4, NULL, 0);
                        } else
                                busnum = 0;
                        break;

                case 'P':
                        if ((cp = strstr(start, "Vendor="))) {
                                vendorid = strtoul(cp + 7, NULL, 16);
                        } else
                                vendorid = 0xffff;
                        if ((cp = strstr(start, "ProdID="))) {
                                productid = strtoul(cp + 7, NULL, 16);
                        } else
                                productid = 0xffff;
			lprintf(5, "Device %u Bus %u Vendor ID %04x Product ID %04x\n", devnum, busnum, vendorid, productid);
			if (vendorid != BAYCOMUSB_VENDORID ||
                            (productid != BAYCOMUSB_PRODUCTID_EMPTY &&
                             productid != BAYCOMUSB_PRODUCTID_FPGALD &&
                             productid != BAYCOMUSB_PRODUCTID_MODEM &&
                             productid != BAYCOMUSB_PRODUCTID_AUDIO))
				break;
			/* search device; mark and continue if found */
			pthread_mutex_lock(&devlist_mutex);
			for (list = devices_list.next; list != &devices_list; list = list->next) {
				state = list_entry(list, struct trx_thread_state, device_list);
				if (state->devnr != devnum || state->busnr != busnum)
					continue;
				goto devalreadyfound;
			}
			/* not found, make new device and create thread */
			state = malloc(sizeof(struct trx_thread_state));
			if (!state)
				goto err;
			memset(state, 0, sizeof(struct trx_thread_state));
			state->devnr = devnum;
			state->busnr = busnum;
			/* start thread */
			if (pthread_attr_init(&attr))
				goto err;
			if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
				goto err;
			if (pthread_create(&thr, &attr, thread_start, state))
				goto err;
			pthread_attr_destroy(&attr);
			list_add(&state->device_list, &devices_list);
		  devalreadyfound:
			pthread_mutex_unlock(&devlist_mutex);
			break;

		  err:
			if (state)
				free(state);
			pthread_mutex_unlock(&devlist_mutex);
			break;

                default:
                        break;
                }
#if 0
                printf("line: %s\n", start);
#endif
                start = lineend + 1;
        }

}

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

static void orb_add_connection(GIOPConnection *cnx)
{
	struct orbfdlist *ofd;

	if (!(cnx->user_data = ofd = malloc(sizeof(struct orbfdlist))))
		return;
	ofd->conn = cnx;
	ofd->pfd = NULL;
	list_add(&ofd->list, &orb_fds);
}

static void orb_remove_connection(GIOPConnection *cnx)
{
	struct orbfdlist *ofd = cnx->user_data;

	if (!ofd)
		return;
	ofd->conn = NULL;
}

#define MAXPFD 64

static void mainloop(void)
{
	struct list_head *list;
	struct orbfdlist *ofd;
	struct pollfd *pfd;
	struct pollfd pfds[MAXPFD];
	unsigned int npfd;
	int i;

	while (!quit) {
		npfd = 1;
		pfds[0].fd = devices_fd;
		pfds[0].events = POLLIN;
		acquire_orbit_mutex();
		for (list = orb_fds.next; list != &orb_fds;) {
			ofd = list_entry(list, struct orbfdlist, list);
			list = list->next;
			if (!ofd->conn) {
				list_del(&ofd->list);
				free(ofd);
				continue;
			}
			if (npfd >= MAXPFD) {
				ofd->pfd = NULL;
				continue;
			}
			ofd->pfd = &pfds[npfd++];
			ofd->pfd->fd = GIOP_CONNECTION_GET_FD(ofd->conn);
			ofd->pfd->events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
		}
		release_orbit_mutex();
		i = poll(pfds, npfd, -1);
		if (i == -1 && errno != EINTR) {
			lprintf(0, "poll: %s (%u)\n", strerror(errno), errno);
			exit(1);
		}
		if (pfds[0].revents & POLLIN)
			check_new_uninited_modems();
		acquire_orbit_mutex();
		for (list = orb_fds.next; list != &orb_fds; list = list->next) {
			ofd = list_entry(list, struct orbfdlist, list);
			if (!(pfd = ofd->pfd))
				continue;
#if 0
			printf("fd %d events 0x%x revents 0x%x\n", pfd->fd, pfd->events, pfd->revents);
#endif
			if (pfd->revents & (POLLERR | POLLHUP | POLLNVAL) && ofd->conn) {
				giop_main_handle_connection_exception(ofd->conn);
			}
			if (pfd->revents & (POLLIN | POLLPRI) && ofd->conn) {
				giop_main_handle_connection(ofd->conn);
			}
		}
		release_orbit_mutex();
	}
}

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

static int parsehw(ax25_address *hwaddr, const char *cp)
{
        const char *cp1;
        unsigned int i, j;

        if (!cp || !*cp)
                return 0;
        memset(hwaddr->ax25_call, (' ' << 1), 6);
        cp1 = strchr(cp, '-');
        if (cp1) {
                i = cp1 - cp;
                j = strtoul(cp1 + 1, NULL, 0);
                hwaddr->ax25_call[6] = (j & 15) << 1;
        } else {
                i = strlen(cp);
		hwaddr->ax25_call[6] = 0;
	}
        if (i > 6)
                i = 6;
        for (j = 0; j < i; j++)
                hwaddr->ax25_call[j] = toupper(cp[j]) << 1;
        return 1;
}

static int parsehwpath(struct full_sockaddr_ax25 *fsa, const char *cp)
{
	char tmp[128];
	char *cp2, *ptrptr;

	if (!fsa || !cp || !*cp)
		return 0;
	fsa->fsa_ax25.sax25_family = AF_AX25;
	fsa->fsa_ax25.sax25_ndigis = 0;
	strncpy(tmp, cp, sizeof(tmp));
	cp2 = strtok_r(tmp, " \t\n,", &ptrptr);
	if (!parsehw(&fsa->fsa_ax25.sax25_call, cp2))
		return 0;
	while ((cp2 = strtok_r(NULL, " \t\n,", &ptrptr))) {
		if (fsa->fsa_ax25.sax25_ndigis >= AX25_MAX_DIGIS)
			return 1;
		if (!parsehw(&fsa->fsa_digipeater[fsa->fsa_ax25.sax25_ndigis], cp2))
			return 0;
		fsa->fsa_ax25.sax25_ndigis++;
	}
	return 1;
}

#define PARAM_TXDELAY   1
#define PARAM_PERSIST   2
#define PARAM_SLOTTIME  3
#define PARAM_TXTAIL    4
#define PARAM_FULLDUP   5

static int set_intf(struct trx_thread_state *state)
{
	struct sockaddr_ax25 sax25;
        struct sockaddr_in sin;
        struct ifreq ifr;
	struct arpreq arpreq;
	struct rtentry rtentry;
	struct full_sockaddr_ax25 gwpath;
        struct ax25_routes_struct ax25_route;
        struct ax25_route_opt_struct ax25_opt;
	int fd;

        if (state->mode != MODE_FSK && state->mode != MODE_EXTERNAL && state->mode != MODE_AFSK)
                return 0;
	strncpy(ifr.ifr_name, state->ifname, sizeof(ifr.ifr_name));
	if (ioctl(sock_fd, SIOCGIFINDEX, &ifr) == -1)
		lprintf(1, "ioctl: SIOCGIFINDEX failed: %s (%u)\n", strerror(errno), errno);
	ifr.ifr_mtu = 256;
	if (ioctl(sock_fd, SIOCSIFMTU, &ifr) == -1)
		lprintf(1, "ioctl: SIOCSIFMTU failed: %s (%u)\n", strerror(errno), errno);
	if (parsehw(&sax25.sax25_call, state->cfg.intf.hwaddr)) {
                sax25.sax25_family = AF_AX25;
                sax25.sax25_ndigis = 0;
                memcpy(&ifr.ifr_hwaddr, &sax25, sizeof(ifr.ifr_hwaddr));
                if (ioctl(sock_fd, SIOCSIFHWADDR, &ifr) == -1)
                        lprintf(0, "ioctl: SIOCSIFHWADDR failed: %s (%u)\n", strerror(errno), errno);
	}
	sin.sin_addr.s_addr = (state->cfg.intf.ipaddr[3] << 24) | (state->cfg.intf.ipaddr[2] << 16) |
		(state->cfg.intf.ipaddr[1] << 8) | state->cfg.intf.ipaddr[0];
	if (sin.sin_addr.s_addr != INADDR_ANY && sin.sin_addr.s_addr != INADDR_NONE) {
		sin.sin_family = AF_INET;
		memcpy(&ifr.ifr_addr, &sin, sizeof(ifr.ifr_addr));
		if (ioctl(sock_fd, SIOCSIFADDR, &ifr) == -1)
			lprintf(1, "ioctl: SIOCSIFADDR failed: %s (%u)\n", strerror(errno), errno);
	}
	sin.sin_addr.s_addr = (state->cfg.intf.netmask[3] << 24) | (state->cfg.intf.netmask[2] << 16) |
		(state->cfg.intf.netmask[1] << 8) | state->cfg.intf.netmask[0];
	if (sin.sin_addr.s_addr != INADDR_ANY && sin.sin_addr.s_addr != INADDR_NONE) {
		sin.sin_family = AF_INET;
		memcpy(&ifr.ifr_addr, &sin, sizeof(ifr.ifr_addr));
		if (ioctl(sock_fd, SIOCSIFNETMASK, &ifr) == -1)
			lprintf(1, "ioctl: SIOCSIFNETMASK failed: %s (%u)\n", strerror(errno), errno);
	}
	sin.sin_addr.s_addr = (state->cfg.intf.broadcast[3] << 24) | (state->cfg.intf.broadcast[2] << 16) |
		(state->cfg.intf.broadcast[1] << 8) | state->cfg.intf.broadcast[0];
	if (sin.sin_addr.s_addr != INADDR_ANY && sin.sin_addr.s_addr != INADDR_NONE) {
		sin.sin_family = AF_INET;
		memcpy(&ifr.ifr_addr, &sin, sizeof(ifr.ifr_addr));
		if (ioctl(sock_fd, SIOCSIFBRDADDR, &ifr) == -1)
			lprintf(1, "ioctl: SIOCSIFBRDADDR failed: %s (%u)\n", strerror(errno), errno);
	}
        if (state->mode == MODE_FSK || state->mode == MODE_EXTERNAL || state->mode == MODE_AFSK) {
		if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1)
			lprintf(0, "ioctl: SIOCGIFFLAGS failed: %s (%u)\n", strerror(errno), errno);
		else {
			ifr.ifr_flags &= ~IFF_NOARP;
			ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
			if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1)
				lprintf(0, "ioctl: SIOCSIFFLAGS failed: %s (%u)\n", strerror(errno), errno);
		}
	}
	/* set gateway ARP etc */
	if (!state->cfg.intf.gwipaddr[0] && !state->cfg.intf.gwipaddr[1] &&
	    !state->cfg.intf.gwipaddr[2] && !state->cfg.intf.gwipaddr[3])
		return 0;
	if (!parsehwpath(&gwpath, state->cfg.intf.gwhwaddr))
		return 0;
	/* get interface address */
	if (ioctl(sock_fd, SIOCGIFHWADDR, &ifr) == -1) {
		lprintf(0, "ioctl: SIOCSIFHWADDR failed: %s (%u)\n", strerror(errno), errno);
		return 0;
	}
	memcpy(&sax25, &ifr.ifr_hwaddr, sizeof(sax25));
	if (sax25.sax25_family != AF_AX25) {
		lprintf(0, "ioctl: SIOCSIFHWADDR invalid address family: %u\n", sax25.sax25_family);
		return 0;
	}
	sin.sin_addr.s_addr = (state->cfg.intf.gwipaddr[3] << 24) | (state->cfg.intf.gwipaddr[2] << 16) |
		(state->cfg.intf.gwipaddr[1] << 8) | state->cfg.intf.gwipaddr[0];
	if (sin.sin_addr.s_addr == INADDR_ANY || sin.sin_addr.s_addr == INADDR_NONE)
		return 0;
	sin.sin_family = AF_INET;
	arpreq.arp_flags = ATF_COM | ATF_PERM;
	((struct sockaddr_in *)&arpreq.arp_pa)->sin_family = AF_INET;
	((struct sockaddr_in *)&arpreq.arp_pa)->sin_port = 0;
	((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr = sin.sin_addr;
	memcpy(&arpreq.arp_ha, &gwpath, sizeof(arpreq.arp_ha));
	((struct sockaddr_ax25 *)&arpreq.arp_ha)->sax25_ndigis = 0;
	strncpy(arpreq.arp_dev, state->ifname, sizeof(arpreq.arp_dev));
	if (ioctl(sock_fd, SIOCSARP, &arpreq))
		lprintf(0, "ioctl: SIOCSARP failed: %s (%u)\n", strerror(errno), errno);
	if ((fd = socket(AF_AX25, SOCK_SEQPACKET, 0)) < 0) {
		lprintf(0, "socket: AF_AX25,SOCK_SEQPACKET failed: %s (%u)\n", strerror(errno), errno);
		return 0;
	}
	ax25_route.port_addr = sax25.sax25_call;
	ax25_route.dest_addr = gwpath.fsa_ax25.sax25_call;
	ax25_route.digi_count = gwpath.fsa_ax25.sax25_ndigis;
	memcpy(&ax25_route.digi_addr, &gwpath.fsa_digipeater, sizeof(ax25_route.digi_addr));
	if (ioctl(fd, SIOCADDRT, &ax25_route) != 0)
		lprintf(0, "ioctl: SIOCADDRT (ax25) failed: %s (%u)\n", strerror(errno), errno);
	ax25_opt.port_addr = sax25.sax25_call;
	ax25_opt.dest_addr = gwpath.fsa_ax25.sax25_call;
	ax25_opt.cmd = AX25_SET_RT_IPMODE;
	ax25_opt.arg = state->cfg.intf.gwipmode;
	if (ioctl(fd, SIOCAX25OPTRT, &ax25_opt) != 0)
		lprintf(0, "ioctl: SIOCAX25OPTRT failed: %s (%u)\n", strerror(errno), errno);
	close(fd);
	memset(&rtentry, 0, sizeof(rtentry));
	rtentry.rt_flags = RTF_UP | RTF_GATEWAY;
	((struct sockaddr_in *)&rtentry.rt_dst)->sin_family = AF_INET;
	((struct sockaddr_in *)&rtentry.rt_dst)->sin_port = 0;
	((struct sockaddr_in *)&rtentry.rt_dst)->sin_addr = sin.sin_addr;
	((struct sockaddr_in *)&rtentry.rt_gateway)->sin_family = AF_INET;
	((struct sockaddr_in *)&rtentry.rt_gateway)->sin_port = 0;
	((struct sockaddr_in *)&rtentry.rt_gateway)->sin_addr = sin.sin_addr;
	sin.sin_addr.s_addr = (state->cfg.intf.gwnetmask[3] << 24) | (state->cfg.intf.gwnetmask[2] << 16) |
		(state->cfg.intf.gwnetmask[1] << 8) | state->cfg.intf.gwnetmask[0];
	if (sin.sin_addr.s_addr != INADDR_ANY && sin.sin_addr.s_addr != INADDR_NONE) {
		((struct sockaddr_in *)&rtentry.rt_genmask)->sin_family = AF_INET;
		((struct sockaddr_in *)&rtentry.rt_genmask)->sin_port = 0;
		((struct sockaddr_in *)&rtentry.rt_genmask)->sin_addr = sin.sin_addr;
		((struct sockaddr_in *)&rtentry.rt_dst)->sin_addr.s_addr &= sin.sin_addr.s_addr;
	} else
		rtentry.rt_flags |= RTF_HOST;
	rtentry.rt_dev = state->ifname;
	if (ioctl(sock_fd, SIOCADDRT, &rtentry) != 0)
		lprintf(0, "ioctl: SIOCADDRT (ipv4) failed: %s (%u)\n", strerror(errno), errno);
	return 0;
}

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

#define SCRIPTNAME "/etc/ax25/baycomusb.ifchange"

static void runscript(struct trx_thread_state *state, const char *cmd)
{
	pid_t pid;
	char home[128];
	char ifname[64];
	char hwaddr[64];
	char ipaddr[64];
	char netmask[64];
	char broadcast[64];
	char gwhwaddr[192];
	char gwipaddr[64];
	char gwnetmask[64];
	char gwipmode[64];
	char *cp;
	char *const argv[] = { SCRIPTNAME, cmd, NULL };
	char *const envp[] = { "TERM=linux", home, ifname, hwaddr, ipaddr, netmask, broadcast,
			       gwhwaddr, gwipaddr, gwnetmask, gwipmode, NULL };
	
	return;

	lprintf(4, "Running Script: %s\n", cmd);
	pid = fork();
	if (pid) {
		if (pid != -1)
			return;
		lprintf(1, "fork failed: %s (%u)\n", strerror(errno), errno);
		return;
	}
	cp = getenv("HOME");
	snprintf(home, sizeof(home), "HOME=%s", cp ? cp : "/");
	snprintf(ifname, sizeof(ifname), "IFNAME=%s", state->ifname);
	snprintf(hwaddr, sizeof(hwaddr), "HWADDR=%s", state->cfg.intf.hwaddr);
	snprintf(ipaddr, sizeof(ipaddr), "IPADDR=%u.%u.%u.%u", state->cfg.intf.ipaddr[0], state->cfg.intf.ipaddr[1],
		 state->cfg.intf.ipaddr[2], state->cfg.intf.ipaddr[3]);
	snprintf(netmask, sizeof(netmask), "NETMASK=%u.%u.%u.%u", state->cfg.intf.netmask[0], state->cfg.intf.netmask[1],
		 state->cfg.intf.netmask[2], state->cfg.intf.netmask[3]);
	snprintf(broadcast, sizeof(broadcast), "BROADCAST=%u.%u.%u.%u", state->cfg.intf.broadcast[0], state->cfg.intf.broadcast[1],
		 state->cfg.intf.broadcast[2], state->cfg.intf.broadcast[3]);
	snprintf(gwhwaddr, sizeof(gwhwaddr), "GWHWADDR=%s", state->cfg.intf.gwhwaddr);
	snprintf(gwipaddr, sizeof(gwipaddr), "GWIPADDR=%u.%u.%u.%u", state->cfg.intf.gwipaddr[0], state->cfg.intf.gwipaddr[1],
		 state->cfg.intf.gwipaddr[2], state->cfg.intf.gwipaddr[3]);
	snprintf(gwnetmask, sizeof(gwnetmask), "GWNETMASK=%u.%u.%u.%u", state->cfg.intf.gwnetmask[0], state->cfg.intf.gwnetmask[1],
			 state->cfg.intf.gwnetmask[2], state->cfg.intf.gwnetmask[3]);
	snprintf(gwipmode, sizeof(gwipmode), "GWIPMODE=%c", state->cfg.intf.gwipmode);
	execve(SCRIPTNAME, argv, envp);
	lprintf(4, "execve("SCRIPTNAME") failed: %s (%u)\n", strerror(errno), errno);
}

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

#define KISS_FEND   ((unsigned char)0300)
#define KISS_FESC   ((unsigned char)0333)
#define KISS_TFEND  ((unsigned char)0334)
#define KISS_TFESC  ((unsigned char)0335)

#define KISS_CMD_DATA       0
#define KISS_CMD_TXDELAY    1
#define KISS_CMD_PPERSIST   2
#define KISS_CMD_SLOTTIME   3
#define KISS_CMD_TXTAIL     4
#define KISS_CMD_FULLDUP    5
#define KISS_CMD_HARDWARE   6
#define KISS_CMD_FECLEVEL   8
#define KISS_CMD_RETURN     255

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

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

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

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

static void kiss_encodepkt(struct trx_thread_state *state, unsigned char *data, unsigned dlen)
{
	unsigned char *kbuf = alloca(dlen * 2 + 3);
	unsigned char *bp = kbuf;
	int i, len;

#if 0
	if (!check_crc_ccitt(data, dlen+2)) {
		u_int16_t x1 = calc_crc_ccitt(data, dlen);
		u_int16_t x2 = data[dlen] | (data[dlen+1] << 8);

		printf("CRC ERROR: 0x%04x  0x%04x\n", x1, x2);
		abort();
	}
#endif
#if 0
	{
		char buf[512];
		snprintpkt(buf, sizeof(buf), data, dlen);
		printf("Rx(%u): %s\n", dlen, buf);
	}
#endif
	if (state->netif_fd == -1) {
		state->stat.kiss_outerr++;
		lprintf(1, "baycomusb: KISS outerr %u\n", state->stat.kiss_outerr);
		return;
	}
	*bp++ = KISS_FEND;
	*bp++ = KISS_CMD_DATA;
	for (; dlen > 0; dlen--, data++) {
		if (*data == KISS_FEND) {
			*bp++ = KISS_FESC;
			*bp++ = KISS_TFEND;
		} else if (*data == KISS_FESC) {
			*bp++ = KISS_FESC;
			*bp++ = KISS_TFESC;
		} else 
			*bp++ = *data;
	}
	*bp++ = KISS_FEND;
	len = bp - kbuf;
	i = write(state->netif_fd, kbuf, len);
	if (i < 0) {
		if (errno == EAGAIN || errno == EIO) {
			state->stat.kiss_outerr++;
			lprintf(1, "baycomusb: KISS outerr %u\n", state->stat.kiss_outerr);
		} else {
			lerr(0, "baycomusb: write");
			abort();
		}
	} else if (i < len)
		lprintf(1, "baycomusb: write error: %d < %d\n", i, len);
	else
		state->stat.kiss_out++;
}

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

static void kiss_process_pkt(struct trx_thread_state *state, u_int8_t *pkt, unsigned int len, unsigned int txurb)
{
	unsigned int txd;

        if (len < 2)
                return;
	state->stat.kiss_in++;
        switch (pkt[0]) {
	case KISS_CMD_DATA:
		if (len <= MAXFLEN-1) {
			txd = state->cfg.chacc.txdelay * state->txdmul / 10;
			state->du.p.txurb[txurb].urb.buffer_length = len+1;
			memcpy(state->du.p.txurb[txurb].b.buf+2, pkt+1, len-1);
			state->du.p.txurb[txurb].b.buf[0] = txd;
			state->du.p.txurb[txurb].b.buf[1] = txd >> 8;
			state->du.p.txurb[txurb].urbstate = URBSTATE_FILLED;
		}
#if 0
		{
			char buf[512];
			snprintpkt(buf, sizeof(buf), pkt+1, len-1);
			printf("Tx(%u): %s\n", len-1, buf);
#if 0
			printf("CRC: 0x%04x\n", calc_crc_ccitt(pkt+1, len-1));
#endif
		}
#endif
		return;

        case KISS_CMD_TXDELAY:
		state->cfg.chacc.txdelay = pkt[1]*10;
                lprintf(10, "baycomusb: txdelay = %ums\n", pkt[1]*10);
                return;
                        
        case KISS_CMD_TXTAIL:
                lprintf(10, "baycomusb: txtail = %ums\n", pkt[1]*10);
                return;

        case KISS_CMD_PPERSIST:
		state->cfg.chacc.ppersistence = pkt[1];
		lprintf(10, "baycomusb: ppersist = %u/256\n", pkt[1]);
                return;

        case KISS_CMD_SLOTTIME:
		state->cfg.chacc.slottime = pkt[1]*10;
                lprintf(10, "baycomusb: slottime = %ums\n", pkt[1]*10);
                return;

        case KISS_CMD_FULLDUP:
		state->cfg.chacc.fullduplex = !!pkt[1];
                lprintf(10, "baycomusb: %sduplex\n", pkt[1] ? "full" : "half");
                return;

        default:
                lprintf(5, "unknown kiss packet: 0x%02x 0x%02x\n", pkt[0], pkt[1]);
                return;
        }
}

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

/* we decode inplace */
static void kiss_decode(struct trx_thread_state *state, u_int8_t *b, int len, unsigned int txurb)
{
        int nlen = 0;
        u_int8_t *p1 = b, *p2, *iframe;

	iframe = p2 = alloca(len);
        while (len > 0) {
                if (*p1 != KISS_FESC) {
                        *p2++ = *p1++;
                        nlen++;
                        len--;
                } else {
			if (len < 2)
				goto err; /* invalid escape */
			if (p1[1] == KISS_TFEND)
				*p2++ = KISS_FEND;
			else if (p1[1] == KISS_TFESC)
				*p2++ = KISS_FESC;
			else
				goto err; /* invalid escape */
			nlen++;
			p1 += 2;
			len -= 2;
		}
        }
	if (len > 0)
		goto err;
        kiss_process_pkt(state, iframe, nlen, txurb);
        return;
 err:
	lprintf(1, "KISS input error\n");
	state->stat.kiss_inerr++;
	lprintf(1, "baycomusb: KISS inerr %u\n", state->stat.kiss_inerr);
}

static void kiss_input(struct trx_thread_state *state)
{
	char *cp1, *cp2, *endp;
	unsigned int txurb = 0;
	int i;
	
	if (state->netif_fd == -1)
		return;
	if (state->kiss.ibufptr < sizeof(state->kiss.ibuf)) {
		i = read(state->netif_fd, state->kiss.ibuf + state->kiss.ibufptr, sizeof(state->kiss.ibuf) - state->kiss.ibufptr);
		if (i < 0) {
			if (errno == EIO) {
				state->kiss.ioerr++;
				i = 0;
				lprintf(1, "baycomusb: KISS ioerr %u\n", state->kiss.ioerr);
			} else if (errno == EAGAIN || errno == EINTR) {
				i = 0;
			} else {
				lerr(0, "baycomusb: read");
				abort();
				return;
			}
		}
		state->kiss.ibufptr += i;
	}
	endp = state->kiss.ibuf + state->kiss.ibufptr;
        cp1 = memchr(state->kiss.ibuf, KISS_FEND, state->kiss.ibufptr);
        while (cp1) {
		cp2 = NULL;
		/* find free TxURB */
		if (txurb >= NUMTXPACKETS || state->du.p.txurb[txurb].urbstate != URBSTATE_UNUSED) {
			for (txurb = 0; txurb < NUMTXPACKETS; txurb++) {
				if (state->du.p.txurb[txurb].urbstate == URBSTATE_UNUSED)
					break;
			}
		}
		/* if TxURB found */
		if (txurb < NUMTXPACKETS)
			cp2 = memchr(cp1+1, KISS_FEND, endp - cp1 - 1);
                if (!cp2) {
                        state->kiss.ibufptr = endp-cp1;
                        memmove(state->kiss.ibuf, cp1, state->kiss.ibufptr);
                        return;
                }
                kiss_decode(state, cp1+1, cp2-cp1-1, txurb);
                cp1 = cp2;
        }
}

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

static unsigned short random_num(void)
{
	static unsigned short random_seed;

        random_seed = 28629 * random_seed + 157;
        return random_seed;
}

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

void netif_close(struct trx_thread_state *state)
{
	unsigned int i;

	if (state->netif_opened) {
		/* kill pending URBs */
		acquire_trxdata_mutex(state);
		if (state->iurb.urbstate == URBSTATE_INPROGRESS) {
			if (usb_discardurb(state->usbdev, &state->iurb.urb))
				lprintf(1, "error %s (%d) killing interrupt urb\n", strerror(errno), errno);
		}
		if (state->curb.urbstate == URBSTATE_INPROGRESS) {
			if (usb_discardurb(state->usbdev, &state->curb.urb))
				lprintf(1, "error %s (%d) killing control urb\n", strerror(errno), errno);
		}
		if (state->mode == MODE_FSK || state->mode == MODE_EXTERNAL || state->mode == MODE_AFSK) {
			for (i = 0; i < NUMRXPACKETS; i++) {
				if (state->du.p.rxurb[i].urbstate != URBSTATE_INPROGRESS)
					continue;
				if (usb_discardurb(state->usbdev, &state->du.p.rxurb[i].urb))
					lprintf(1, "error %s (%d) killing rx packet urb %u\n", strerror(errno), errno, i);
			}
			for (i = 0; i < NUMTXPACKETS; i++) {
				if (state->du.p.txurb[i].urbstate != URBSTATE_INPROGRESS)
					continue;
				if (usb_discardurb(state->usbdev, &state->du.p.txurb[i].urb))
					lprintf(1, "error %s (%d) killing tx packet urb %u\n", strerror(errno), errno, i);
			}
		} else if (state->mode == MODE_AUDIO) {
			for (i = 0; i < NUMAUDIOURB; i++) {
				if (state->du.a.inurb[i].urbstate != URBSTATE_INPROGRESS)
					continue;
				if (usb_discardurb(state->usbdev, &state->du.a.inurb[i].urb))
					lprintf(1, "error %s (%d) killing audio input urb %u\n", strerror(errno), errno, i);
			}
			for (i = 0; i < NUMAUDIOURB; i++) {
				if (state->du.a.outurb[i].urbstate != URBSTATE_INPROGRESS)
					continue;
				if (usb_discardurb(state->usbdev, &state->du.a.outurb[i].urb))
					lprintf(1, "error %s (%d) killing audio output urb %u\n", strerror(errno), errno, i);
			}
			audio_close(state);
#if 0
			if (usb_releaseinterface(state->usbdev, 3))
				lprintf(2, "netif_close: cannot release interface 3\n");
			if (usb_releaseinterface(state->usbdev, 2))
				lprintf(2, "netif_close: cannot release interface 2\n");
			if (usb_releaseinterface(state->usbdev, 1))
				lprintf(2, "netif_close: cannot release interface 1\n");
#endif
		}
		release_trxdata_mutex(state);
		if (usb_releaseinterface(state->usbdev, 0))
			lprintf(2, "netif_close: cannot release interface 0\n");
	}
	/* close network interface stuff */
	if (state->netif_fd != -1)
		close(state->netif_fd);
	if (state->netif_fdmaster != -1)
		close(state->netif_fdmaster);
	state->netif_fd = state->netif_fdmaster = -1;
	if (state->netif_opened)
		runscript(state, "down");
	state->netif_opened = 0;
	lprintf(30, "netif_close\n");
}

int netif_open(struct trx_thread_state *state)
{
        struct termios tm;
	char ttyname[32];
	int master, slave, fd, disc = N_AX25, encap = 4;
        struct ifreq ifr;
	unsigned char br[6];
	unsigned int u, k;
	int r;

	netif_close(state);
	/* initialize URBs */
	state->iurb.urbstate = 0;
	state->iurb.nexttime = gettimems();
	state->iurb.urb.type = USBDEVFS_URB_TYPE_BULK;
	state->iurb.urb.endpoint = 0x81;
	state->iurb.urb.flags = 0;
        state->iurb.urb.buffer = state->iurb.buf;
	state->iurb.urb.buffer_length = sizeof(state->iurb.buf);
	state->iurb.urb.signr = 0;
        state->iurb.urb.usercontext = (void *)0x20000;
	state->curb.urbstate = 0;
	state->curb.urb.type = USBDEVFS_URB_TYPE_CONTROL;
	state->curb.urb.endpoint = 0;
	state->curb.urb.flags = 0;
        state->curb.urb.buffer = &state->curb.buf;
	state->curb.urb.buffer_length = 8;
	state->curb.urb.signr = 0;
        state->curb.urb.usercontext = (void *)0x30000;
	if (state->mode == MODE_FSK || state->mode == MODE_EXTERNAL || state->mode == MODE_AFSK) {
		state->du.p.rxurbptr = 0;
		for (u = 0; u < NUMRXPACKETS; u++) {
			state->du.p.rxurb[u].urbstate = 0;
			state->du.p.rxurb[u].urb.type = USBDEVFS_URB_TYPE_BULK;
			state->du.p.rxurb[u].urb.endpoint = 0x82;
			state->du.p.rxurb[u].urb.flags = (NUMRXPACKETS > 1) ? USBDEVFS_URB_QUEUE_BULK : 0;
			state->du.p.rxurb[u].urb.buffer = state->du.p.rxurb[u].b.buf;
			state->du.p.rxurb[u].urb.buffer_length = sizeof(state->du.p.rxurb[u].b.buf);
			state->du.p.rxurb[u].urb.signr = 0;
			state->du.p.rxurb[u].urb.usercontext = (void *)(0x00000 | u);
		}
		for (u = 0; u < NUMTXPACKETS; u++) {
			state->du.p.txurb[u].urbstate = 0;
			state->du.p.txurb[u].urb.type = USBDEVFS_URB_TYPE_BULK;
			state->du.p.txurb[u].urb.endpoint = 0x02;
			state->du.p.txurb[u].urb.flags = ((NUMTXPACKETS > 1) ? USBDEVFS_URB_QUEUE_BULK : 0) | USBDEVFS_URB_ZERO_PACKET;
			state->du.p.txurb[u].urb.buffer = state->du.p.txurb[u].b.buf;
			state->du.p.txurb[u].urb.buffer_length = sizeof(state->du.p.txurb[u].b.buf);
			state->du.p.txurb[u].urb.signr = 0;
			state->du.p.txurb[u].urb.usercontext = (void *)(0x10000 | u);
		}
	} else if (state->mode == MODE_AUDIO) {
		for (u = 0; u < NUMAUDIOURB; u++) {
			state->du.a.inurb[u].urbstate = 0;
			state->du.a.inurb[u].urb.type = USBDEVFS_URB_TYPE_ISO;
			state->du.a.inurb[u].urb.endpoint = 0x88;
			state->du.a.inurb[u].urb.flags = USBDEVFS_URB_ISO_ASAP;
			state->du.a.inurb[u].urb.buffer = state->du.a.inurb[u].buf;
			state->du.a.inurb[u].urb.buffer_length = sizeof(state->du.a.inurb[u].buf);
			state->du.a.inurb[u].urb.number_of_packets = NUMFRAMESPERURB;
			state->du.a.inurb[u].urb.signr = 0;
			state->du.a.inurb[u].urb.usercontext = (void *)(0x00000 | u);
			for (k = 0; k < NUMFRAMESPERURB; k++)
				state->du.a.inurb[u].urb.iso_frame_desc[k].length = AUDIOSAMPLINGRATE/1000;
		}
		for (u = 0; u < NUMAUDIOURB; u++) {
			state->du.a.outurb[u].urbstate = 0;
			state->du.a.outurb[u].urb.type = USBDEVFS_URB_TYPE_ISO;
			state->du.a.outurb[u].urb.endpoint = 0x08;
			state->du.a.outurb[u].urb.flags = USBDEVFS_URB_ISO_ASAP;
			state->du.a.outurb[u].urb.buffer = state->du.a.outurb[u].buf;
			state->du.a.outurb[u].urb.buffer_length = sizeof(state->du.a.outurb[u].buf);
			state->du.a.outurb[u].urb.number_of_packets = NUMFRAMESPERURB;
			state->du.a.outurb[u].urb.signr = 0;
			state->du.a.outurb[u].urb.usercontext = (void *)(0x10000 | u);
			for (k = 0; k < NUMFRAMESPERURB; k++)
				state->du.a.outurb[u].urb.iso_frame_desc[k].length = AUDIOSAMPLINGRATE/1000;
			/* clear tx data */
			memset(&state->du.a.outurb[u].buf, 0x80, sizeof(state->du.a.outurb[u].buf));
		}
	}
	/* claim device */
	if (usb_setconfiguration(state->usbdev, 1)) {
		lprintf(2, "netif_open: cannot set configuration 1\n");
		return -1;
	}
	/* claim interface 0 */
	if (usb_claiminterface(state->usbdev, 0)) {
		lprintf(2, "netif_open: cannot claim interface 0\n");
		return -1;
	}
	/* set interface 0 altsetting 1, to enable the bulk ep's */
	if (usb_setinterface(state->usbdev, 0, 1)) {
		lprintf(2, "netif_open: cannot set interface 0 altsetting 1\n");
		goto errsetif0;
	}
	state->bitraterx = state->bitratetx = state->txdmul = 0;
	if (state->mode == MODE_FSK || state->mode == MODE_EXTERNAL || state->mode == MODE_AFSK) {
		r = usb_control_msg(state->usbdev, 0xc0, 0xd1, 0, 0, 6, br, 100);
		if (r != 6) {
			lprintf(1, "Error while reading adapter bitrate\n");
			goto errsetif0;
		}
		state->bitratetx = br[0] | (br[1] << 8) | (br[2] << 16);
		state->bitraterx = br[3] | (br[4] << 8) | (br[5] << 16);
		state->txdmul = (state->bitratetx + 799) / 800;
		lprintf(5, "Adapter %s bitrate %u/%u\n",
			state->serial, state->bitraterx, state->bitratetx);
		if (openpty(&master, &slave, ttyname, NULL, NULL)) {
			lerr(0, "openpty");
			goto errsetif0;
		}
		/* set mode to raw */
		memset(&tm, 0, sizeof(tm));
		tm.c_cflag = CS8 | CREAD | CLOCAL;
		if (tcsetattr(master, TCSANOW, &tm)) {
			lerr(0, "master: tcsetattr");
			goto errms;
		}
		memset(&tm, 0, sizeof(tm));
		tm.c_cflag = CS8 | CREAD | CLOCAL;
		if (tcsetattr(slave, TCSANOW, &tm)) {
			lerr(0, "slave: tcsetattr");
			goto errms;
		}
		/* set the line discipline */
		if (ioctl(master, TIOCSETD, &disc) == -1) {
			lerr(0, "ioctl: TIOCSETD");
			goto errms;
		}
		if (ioctl(master, SIOCSIFENCAP, &encap) == -1) {
			lerr(0, "ioctl: SIOCSIFENCAP");
			goto errms;
		}
		/* try to set the interface name */
		strncpy(state->ifname, state->cfg.intf.ifname, sizeof(state->ifname));
		if (!state->ifname[0])
			strncpy(state->ifname, "bcu0", sizeof(state->ifname));
		if (ioctl(master, SIOCGIFNAME, ifr.ifr_name) == -1) {
			lerr(0, "ioctl: SIOCGIFNAME");
			goto errms;
		}
#ifdef HAVE_IFRNEWNAME
		if (strcmp(state->ifname, ifr.ifr_name)) {
			strncpy(ifr.ifr_newname, state->ifname, sizeof(ifr.ifr_newname));
			if (ioctl(master, SIOCSIFNAME, &ifr) == -1) {
				lerr(1, "ioctl: SIOCSIFNAME");
				lprintf(1, "baycomusb: cannot set ifname to %s, using %s (old kernel version?)\n",
					state->ifname, ifr.ifr_name);
			}
		}
#endif
		if (ioctl(master, SIOCGIFNAME, ifr.ifr_name) == -1) {
			lerr(0, "ioctl: SIOCGIFNAME");
			goto errms;
		}
		strncpy(state->ifname, ifr.ifr_name, sizeof(state->ifname));
		/* start the interface */
		if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
			lerr(0, "socket");
			goto errms;
		}
		ifr.ifr_mtu = 256;
		if (ioctl(fd, SIOCSIFMTU, &ifr) == -1) {
			lerr(0, "ioctl: SIOCSIFMTU");
			goto errmsfd;
		}
		if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
			lerr(0, "ioctl: SIOCGIFFLAGS");
			goto errmsfd;
		}
		ifr.ifr_flags &= ~IFF_NOARP;
		ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
		if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
			lerr(0, "ioctl: SIOCSIFFLAGS");
			goto errmsfd;
		}
		close(fd);
		/* prepare for using it */
		state->netif_fd = slave;
		state->netif_fdmaster = master;
		state->kiss.ioerr = 0;
		fcntl(state->netif_fd, F_SETFL, fcntl(state->netif_fd, F_GETFL, 0) | O_NONBLOCK);
		state->kiss.ibufptr = 0;
		state->dcd = 0;
		if (set_intf(state))
			goto errms;
		runscript(state, "up");
		state->slottime = gettimems();
        } else if (state->mode == MODE_AUDIO) {
#if 0
		/* claim interface 1 */
		if (usb_claiminterface(state->usbdev, 1)) {
			lprintf(2, "netif_open: cannot claim interface 1\n");
			goto errsetif0;
		}
		/* set interface 1 altsetting 0 */
		if (usb_setinterface(state->usbdev, 1, 0)) {
			lprintf(2, "netif_open: cannot set interface 1 altsetting 0\n");
			goto errsetif1;
		}
		/* claim interface 2 */
		if (usb_claiminterface(state->usbdev, 2)) {
			lprintf(2, "netif_open: cannot claim interface 2\n");
			goto errsetif1;
		}
		/* set interface 2 altsetting 1, to enable the ISO ep's */
		if (usb_setinterface(state->usbdev, 2, 1)) {
			lprintf(2, "netif_open: cannot set interface 2 altsetting 1\n");
			goto errsetif2;
		}
		/* claim interface 3 */
		if (usb_claiminterface(state->usbdev, 3)) {
			lprintf(2, "netif_open: cannot claim interface 3\n");
			goto errsetif2;
		}
		/* set interface 3 altsetting 1, to enable the ISO ep's */
		if (usb_setinterface(state->usbdev, 3, 1)) {
			lprintf(2, "netif_open: cannot set interface 3 altsetting 1\n");
			goto errsetif3;
		}
#endif
		audio_open(state);
	}
	lprintf(30, "netif_open\n");
	state->netif_opened = 1;
	return 0;
	
#if 0
  errsetif3:
	usb_releaseinterface(state->usbdev, 3);
  errsetif2:
	usb_releaseinterface(state->usbdev, 2);
  errsetif1:
	usb_releaseinterface(state->usbdev, 1);
	goto errsetif0;
#endif
  errmsfd:
	close(fd);
  errms:
	close(master);
	close(slave);
  errsetif0:
	usb_releaseinterface(state->usbdev, 0);
	state->netif_fd = state->netif_fdmaster = -1;
	lprintf(30, "netif_open error\n");
	return -1;
}

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

static int process(struct trx_thread_state *state, unsigned int timeout)
{
	unsigned int curtime = gettimems(), rettime = curtime + timeout;
	struct usbdevfs_urb *urb;
	signed int tdiff, tdiff1;
	unsigned int i, j, maytx, nrtxfree;
	struct pollfd pfd[2];
	int r;

	if (!state->netif_opened) {
		if (timeout)
			poll(NULL, 0, timeout);
		return 0;
	}
	if (!timeout)
		rettime++;
	acquire_trxdata_mutex(state);
	for (;;) {
		nrtxfree = 0;
		if (state->iurb.urbstate != URBSTATE_INPROGRESS) {
			tdiff = curtime - state->iurb.nexttime;
			if (tdiff >= 0) {
				r = usb_submiturb(state->usbdev, &state->iurb.urb);
				if (r < 0) {
					lprintf(1, "error submitting interrupt urb\n");
					goto submiterr;
				}
				state->iurb.urbstate = URBSTATE_INPROGRESS;
				state->iurb.nexttime += INTERRUPT_INTERVAL;
				tdiff = curtime - state->iurb.nexttime;
				if (tdiff > 0)
					state->iurb.nexttime = curtime;
			}
		} else {
			/* interrupt urb timeout processing */
			tdiff = curtime - state->iurb.nexttime;
			if (tdiff >= 1000) {
				lprintf(1, "interrupt urb timeout\n");
				if (usb_discardurb(state->usbdev, &state->iurb.urb))
					lprintf(1, "error %s (%d) killing interrupt urb\n", strerror(errno), errno);
				state->iurb.urbstate = URBSTATE_UNUSED;
			}
		}
		if (state->curb.urbstate != URBSTATE_INPROGRESS) {
			if (state->uarttxempty >= UARTTXDELAY && state->tfifo.rd != state->tfifo.wr) {
				state->curb.buf.creq.requesttype = 0x40;
				state->curb.buf.creq.request = 0xd3;
				state->curb.buf.creq.value[0] = state->tfifo.buf[state->tfifo.rd];
				state->curb.buf.creq.value[1] = 0;
				state->curb.buf.creq.index[0] = 0;
				state->curb.buf.creq.index[1] = 0;
				state->curb.buf.creq.length[0] = 0;
				state->curb.buf.creq.length[1] = 0;
				state->curb.urb.buffer_length = 8;
				r = usb_submiturb(state->usbdev, &state->curb.urb);
				if (r < 0) {
					lprintf(1, "error submitting control urb (txuart)\n");
					goto submiterr;
				}
				state->curb.urbstate = URBSTATE_INPROGRESS;
				state->curb.starttime = gettimems();
				state->tfifo.rd = (state->tfifo.rd + 1) % sizeof(state->tfifo.buf);
				state->uarttxempty = 0;
			} else if (state->flags & FLG_CLRPTT) {
				state->curb.buf.creq.requesttype = 0xc0;
				state->curb.buf.creq.request = 0xd0;
				state->curb.buf.creq.value[0] = 0;
				state->curb.buf.creq.value[1] = 0;
				state->curb.buf.creq.index[0] = 1;
				state->curb.buf.creq.index[1] = 0;
				state->curb.buf.creq.length[0] = 3;
				state->curb.buf.creq.length[1] = 0;				
				state->curb.urb.buffer_length = 11;
				r = usb_submiturb(state->usbdev, &state->curb.urb);
				if (r < 0) {
					lprintf(1, "error submitting control urb (ptt)\n");
					goto submiterr;
				}
				state->curb.urbstate = URBSTATE_INPROGRESS;
				state->curb.starttime = gettimems();
				state->flags &= ~FLG_CLRPTT;
			} else if (state->flags & FLG_SETPTT && !(state->flags & FLG_INHIBITSETPTT)) {
				state->curb.buf.creq.requesttype = 0xc0;
				state->curb.buf.creq.request = 0xd0;
				state->curb.buf.creq.value[0] = (state->flags & FLG_MANUALPTT) ? 1 : 0;
				state->curb.buf.creq.value[1] = 0;
				state->curb.buf.creq.index[0] = 1;
				state->curb.buf.creq.index[1] = 0;
				state->curb.buf.creq.length[0] = 3;
				state->curb.buf.creq.length[1] = 0;
				state->curb.urb.buffer_length = 11;
				r = usb_submiturb(state->usbdev, &state->curb.urb);
				if (r < 0) {
					lprintf(1, "error submitting control urb (ptt)\n");
					goto submiterr;
				}
				state->curb.urbstate = URBSTATE_INPROGRESS;
				state->curb.starttime = gettimems();
				state->flags &= ~FLG_SETPTT;
			} else if (state->flags & FLG_SETMDISCDIR) {
				state->curb.buf.creq.requesttype = 0xc0;
				state->curb.buf.creq.request = 0xd4;
				state->curb.buf.creq.value[0] = state->cfg.mdisc.direction;
				state->curb.buf.creq.value[1] = 0;
				state->curb.buf.creq.index[0] = 2;
				state->curb.buf.creq.index[1] = 0;
				state->curb.buf.creq.length[0] = 3;
				state->curb.buf.creq.length[1] = 0;
				state->curb.urb.buffer_length = 11;
				/* enable RxC, TxC or TxD outputs if special output mode is selected */
				if (state->mode == MODE_FSK) {
					if (state->cfg.mdisc.rxc)
						state->curb.buf.creq.value[0] &= ~(1 << 0);
					if (state->cfg.mdisc.txc)
						state->curb.buf.creq.value[0] &= ~(1 << 1);
					if (state->cfg.mdisc.txd)
						state->curb.buf.creq.value[0] &= ~(1 << 3);
				}
				r = usb_submiturb(state->usbdev, &state->curb.urb);
				if (r < 0) {
					lprintf(1, "error submitting control urb (mdiscdir)\n");
					goto submiterr;
				}
				state->curb.urbstate = URBSTATE_INPROGRESS;
				state->curb.starttime = gettimems();
				state->flags &= ~FLG_SETMDISCDIR;
			} else if (state->flags & FLG_SETMDISCOUT) {
				state->curb.buf.creq.requesttype = 0xc0;
				state->curb.buf.creq.request = 0xd4;
				state->curb.buf.creq.value[0] = state->cfg.mdisc.output;
				state->curb.buf.creq.value[1] = 0;
				state->curb.buf.creq.index[0] = 1;
				state->curb.buf.creq.index[1] = 0;
				state->curb.buf.creq.length[0] = 3;
				state->curb.buf.creq.length[1] = 0;
				state->curb.urb.buffer_length = 11;
				r = usb_submiturb(state->usbdev, &state->curb.urb);
				if (r < 0) {
					lprintf(1, "error submitting control urb (mdiscout)\n");
					goto submiterr;
				}
				state->curb.urbstate = URBSTATE_INPROGRESS;
				state->curb.starttime = gettimems();
				state->flags &= ~FLG_SETMDISCOUT;
			} else if (state->flags & FLG_SETT7FPORT) {
				state->curb.buf.creq.requesttype = 0xc0;
				state->curb.buf.creq.request = 0xd5;
				state->curb.buf.creq.value[0] = state->t7fport;
				state->curb.buf.creq.value[1] = 0;
				state->curb.buf.creq.index[0] = 1;
				state->curb.buf.creq.index[1] = 0;
				state->curb.buf.creq.length[0] = 2;
				state->curb.buf.creq.length[1] = 0;
				state->curb.urb.buffer_length = 10;
				r = usb_submiturb(state->usbdev, &state->curb.urb);
				if (r < 0) {
					lprintf(1, "error submitting control urb (t7fport)\n");
					goto submiterr;
				}
				state->curb.urbstate = URBSTATE_INPROGRESS;
				state->curb.starttime = gettimems();
				state->flags &= ~FLG_SETT7FPORT;
			} else if (state->flags & FLG_SETLEDS) {
				state->curb.buf.creq.requesttype = 0x40;
				state->curb.buf.creq.request = 0xd2;
				state->curb.buf.creq.value[0] = state->leds & 3;
				state->curb.buf.creq.value[1] = 0;
				state->curb.buf.creq.index[0] = 0;
				state->curb.buf.creq.index[1] = 0;
				state->curb.buf.creq.length[0] = 0;
				state->curb.buf.creq.length[1] = 0;
				state->curb.urb.buffer_length = 8;
				r = usb_submiturb(state->usbdev, &state->curb.urb);
				if (r < 0) {
					lprintf(1, "error submitting control urb (setleds)\n");
					goto submiterr;
				}
				state->curb.urbstate = URBSTATE_INPROGRESS;
				state->curb.starttime = gettimems();
				state->flags &= ~FLG_SETLEDS;
			}
		} else {
			/* control URB timeout processing */
			tdiff = curtime - state->curb.starttime;
			if (tdiff >= 2000) {
				lprintf(1, "control urb timeout\n");
				if (usb_discardurb(state->usbdev, &state->curb.urb))
					lprintf(1, "error %s (%d) killing interrupt urb\n", strerror(errno), errno);
				state->curb.urbstate = URBSTATE_UNUSED;
			}
		}
                if (state->mode == MODE_FSK || state->mode == MODE_EXTERNAL || state->mode == MODE_AFSK) {
			kiss_input(state);
			for (;;) {
				i = state->du.p.rxurbptr;
				if (state->du.p.rxurb[i].urbstate == URBSTATE_INPROGRESS)
					break;
				if (state->du.p.rxurb[i].urbstate == URBSTATE_DONE)
					kiss_encodepkt(state, state->du.p.rxurb[i].b.buf, state->du.p.rxurb[i].urb.actual_length-2);
				state->du.p.rxurb[i].urbstate = URBSTATE_UNUSED;
				r = usb_submiturb(state->usbdev, &state->du.p.rxurb[i].urb);
				if (r < 0) {
					lprintf(1, "error submitting rx packet urb %u\n", i);
					goto submiterr;
				}
				state->du.p.rxurb[i].urbstate = URBSTATE_INPROGRESS;
				state->du.p.rxurbptr = (i + 1) % NUMRXPACKETS;
			}
			for (i = 0; i < NUMTXPACKETS; i++)
				if (state->du.p.txurb[i].urbstate == URBSTATE_UNUSED)
					nrtxfree++;
			/* channel access */
			maytx = state->ptt || state->cfg.chacc.fullduplex;
			tdiff = state->slottime - curtime;
			if (tdiff < 0 || tdiff > 10000) {
				state->slottime = curtime + state->cfg.chacc.slottime;
				if (!state->dcd && (random_num() & 0xff) <= state->cfg.chacc.ppersistence)
					maytx = 1;
			}
			if (maytx) {
				for (i = 0; i < NUMTXPACKETS; i++) {
					if (state->du.p.txurb[i].urbstate != URBSTATE_FILLED)
						continue;
					r = usb_submiturb(state->usbdev, &state->du.p.txurb[i].urb);
					if (r < 0) {
						lprintf(1, "error submitting tx packet urb %u\n", i);
						goto submiterr;
					}
					state->du.p.txurb[i].urbstate = URBSTATE_INPROGRESS;
				}
			}
		} else if (state->mode == MODE_AUDIO) {
			for (i = 0; i < NUMAUDIOURB; i++) {
				if (state->du.a.inurb[i].urbstate != URBSTATE_UNUSED)
					continue;
				r = usb_submiturb(state->usbdev, &state->du.a.inurb[i].urb);
				if (r < 0) {
					lprintf(1, "error submitting audio input urb %u\n", i);
					goto submiterr;
				}
				state->du.a.inurb[i].urbstate = URBSTATE_INPROGRESS;
			}
			for (i = 0; i < NUMAUDIOURB; i++) {
				if (state->du.a.outurb[i].urbstate != URBSTATE_UNUSED)
					continue;
				r = usb_submiturb(state->usbdev, &state->du.a.outurb[i].urb);
				if (r < 0) {
					lprintf(1, "error submitting audio output urb %u\n", i);
					goto submiterr;
				}
				state->du.a.outurb[i].urbstate = URBSTATE_INPROGRESS;
			}
		}
		tdiff = rettime - curtime;
		if (tdiff < 0) {
			release_trxdata_mutex(state);
			return 0;
		}
		tdiff1 = state->iurb.nexttime - curtime;
		if (tdiff1 < 0)
			tdiff1 = 0;
		if (tdiff1 < tdiff)
			tdiff = tdiff1;
		release_trxdata_mutex(state);
#if 1
		pfd[0].fd = usb_getfd(state->usbdev);
		pfd[0].events = POLLOUT;
		pfd[1].fd = state->netif_fd;
		pfd[1].events = POLLIN;
		r = poll(pfd, (pfd[1].fd == -1 || !nrtxfree) ? 1 : 2, tdiff);
		if (r == -1)
			lerr(1, "poll");
		urb = usb_reapurb(state->usbdev, 0);
#else
		urb = usb_reapurb(state->usbdev, tdiff);
#endif
		acquire_trxdata_mutex(state);
		curtime = gettimems();
		if (!urb)
			continue;
		switch (((unsigned int)urb->usercontext) & ~0xffff) {
		case 0x00000:
			i = ((unsigned int)urb->usercontext) & 0xffff;
#if 0
			printf("Rx: %u                \n", i);
#endif
			if (state->mode == MODE_FSK || state->mode == MODE_EXTERNAL || state->mode == MODE_AFSK) {
				if (i > NUMRXPACKETS || urb != &state->du.p.rxurb[i].urb)
					abort();
				if (!state->du.p.rxurb[i].urb.status && state->du.p.rxurb[i].urb.actual_length > 2) {
					state->du.p.rxurb[i].urbstate = URBSTATE_DONE;
				} else {
					lprintf(1, "receive transfer failed, status %u length %u\n",
						state->du.p.rxurb[i].urb.status, state->du.p.rxurb[i].urb.actual_length);
					state->du.p.rxurb[i].urbstate = URBSTATE_UNUSED;
				}
			} else if (state->mode == MODE_AUDIO) {
				if (i > NUMAUDIOURB || urb != &state->du.a.inurb[i].urb)
					abort();
				if (state->du.a.inurb[i].urb.status)
					lprintf(1, "audio input transfer failed, status %u\n",
						state->du.a.inurb[i].urb.status);
				for (j = 0; j < NUMFRAMESPERURB; j++) {
					if (!state->du.a.inurb[i].urb.iso_frame_desc[j].status)
						continue;
					lprintf(1, " audio input transfer frame %u status %u\n", 
						j, state->du.a.inurb[i].urb.iso_frame_desc[j].status);
					memset(&state->du.a.inurb[i].buf[j*(AUDIOSAMPLINGRATE/1000)], 
					       0x80, (AUDIOSAMPLINGRATE/1000));
				}
				audio_input(state, state->du.a.inurb[i].buf, NUMFRAMESPERURB*(AUDIOSAMPLINGRATE/1000));
				state->du.a.inurb[i].urbstate = URBSTATE_UNUSED;
			}
			break;

		case 0x10000:
			i = ((unsigned int)urb->usercontext) & 0xffff;
#if 0
			printf("Tx: %u                \n", i);
#endif
			if (state->mode == MODE_FSK || state->mode == MODE_EXTERNAL || state->mode == MODE_AFSK) {
				if (i > NUMTXPACKETS || urb != &state->du.p.txurb[i].urb)
					abort();
				if (state->du.p.txurb[i].urb.status)
					lprintf(1, "transmit transfer failed, status %u length %u\n",
						state->du.p.txurb[i].urb.status, state->du.p.txurb[i].urb.actual_length);
				state->du.p.txurb[i].urbstate = URBSTATE_UNUSED;
			} else if (state->mode == MODE_AUDIO) {
				if (i > NUMAUDIOURB || urb != &state->du.a.outurb[i].urb)
					abort();
				if (state->du.a.outurb[i].urb.status)
					lprintf(1, "audio output transfer failed, status %u\n",
						state->du.a.outurb[i].urb.status);
				for (j = 0; j < NUMFRAMESPERURB; j++) {
					if (!state->du.a.outurb[i].urb.iso_frame_desc[j].status)
						continue;
					lprintf(1, " audio output transfer frame %u status %u\n", 
						j, state->du.a.outurb[i].urb.iso_frame_desc[j].status);
				}
				audio_output(state, state->du.a.outurb[i].buf, NUMFRAMESPERURB*(AUDIOSAMPLINGRATE/1000));
				state->du.a.outurb[i].urbstate = URBSTATE_UNUSED;
			}
			break;

		case 0x20000:
			if (urb != &state->iurb.urb)
				abort();
			if (!state->iurb.urb.status && state->iurb.urb.actual_length >= 5) {
#if 0
				printf("stat: %02x %02x %02x %02x %02x\r", state->iurb.buf[0],
				       state->iurb.buf[1], state->iurb.buf[2], state->iurb.buf[3], state->iurb.buf[4]);
				fflush(stdout);
#endif
				state->ptt = !!(state->iurb.buf[0] & 4);
				if (state->mode != MODE_AUDIO)
					state->dcd = !!(state->iurb.buf[0] & 8);
				state->rssi = state->iurb.buf[3];
				if (state->iurb.buf[0] & 32)
					state->uarttxempty++;
				else
					state->uarttxempty = 0;
				/* add UART RxChars */
				if (state->iurb.urb.actual_length > 5)
					addrxfifo(state, &state->iurb.buf[5], state->iurb.urb.actual_length-5);
			} else
				lprintf(1, "interrupt transfer failed, status %u length %u\n",
					state->iurb.urb.status, state->iurb.urb.actual_length);
#if 0
			printf("ptt %d dcd %d rssi %u tempt %u slottime %u curtime %u\n", 
			       state->ptt, state->dcd, state->rssi, state->uarttxempty, state->slottime, curtime);
#endif
			state->iurb.urbstate = URBSTATE_UNUSED;
			break;

		case 0x30000:
			if (urb != &state->curb.urb)
				abort();
			if (state->iurb.urb.status)
				lprintf(1, "control transfer failed, status %u length %u\n",
					state->curb.urb.status, state->curb.urb.actual_length);
			state->curb.urbstate = URBSTATE_UNUSED;
			break;

		default:
			abort();
		}
	}
  submiterr:
	release_trxdata_mutex(state);
	lprintf(1, "Error submitting urb: %d/%s (%u)\n", r, strerror(errno), errno);
	return -1;
}

int polldevice(struct trx_thread_state *state)
{
	return process(state, 25);
}

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

int getuartline(struct trx_thread_state *ctrl, unsigned int *ptr, unsigned char *buf, size_t bufsz, unsigned int timeout)
{
        unsigned int cpy = 0, tdiff, reload;
        struct timeval tv1, tv2;

        if (!ctrl)
                return -1;
        if (gettimeofday(&tv1, NULL))
                return -1;
	timeout *= 1000;
        while (cpy < bufsz) {
		acquire_trxdata_mutex(ctrl);
		if (buf) {
			while (cpy < bufsz && getrxfifo(ctrl, ptr, buf, 1) == 1) {
				if (*buf == '\r' || *buf == '\n') {
					cpy++;
					*buf++ = '\n';
					*buf = 0;
					release_trxdata_mutex(ctrl);
					return cpy;
				}
				cpy++;
				buf++;
			}
		}
		reload = ctrl->flags & FLG_RELOADMODEM;
		release_trxdata_mutex(ctrl);
		if (reload)
			break;
                if (gettimeofday(&tv2, NULL))
                        break;
                tdiff = 1000000 * (tv2.tv_sec - tv1.tv_sec) + tv2.tv_usec - tv1.tv_usec;
                if (tdiff > timeout)
			break;
		if (process(ctrl, 25))
                        return -1;
        }
	if (buf)
		*buf = 0;
        return cpy;
}

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

static RETSIGTYPE signal_quit(int signum)
{
        quit = 1;
}

static RETSIGTYPE signal_child(int signum, siginfo_t *info, void *dummy)
{
	int status;
	struct rusage rusage;

	if (info->si_code == CLD_STOPPED || info->si_code == CLD_CONTINUED)
		return;
	wait4(-1, &status, WNOHANG, &rusage);
}

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

int main (int argc, char *argv[])
{
        static const struct option long_options[] = {
                { "drv", 1, 0, 'D' },
                { "config", 1, 0, 'C' },
		{ "daemonize", 0, 0, 'd' },
                { "verbose", 0, 0, 'v' },
                { "help", 0, 0, 'h' },
		{ "nommx", 0, 0, 0x400 },
		{ "mmx", 0, 0, 0x401 },
                { 0, 0, 0, 0 }
        };
        int c, err = 0;
	unsigned int daemonize = 0, simd = 1;
	char devname[256] = "/proc/bus/usb/devices";
	const char *cfgfile = NULL;
        int pfds[2];
        pid_t pid;
        unsigned char uch;
	struct sigaction sigact;

        printf("baycomusbserv v" VERSION " (user) (c) 1999-2001 by Thomas Sailer, HB9JNX/AE4WA\n");
	/* init custom IO handlers */
	IIOPAddConnectionHandler = orb_add_connection;
        IIOPRemoveConnectionHandler = orb_remove_connection;
	if (server_init1(&argc, argv))
		return -1;
	while ((c = getopt_long(argc, argv, "D:C:sv:d", long_options, NULL)) != EOF) {
                switch (c) {
                case 'D':
                        usb_setmountpoint(optarg);
			snprintf(devname, sizeof(devname), "%s/devices", optarg);
                        break;

                case 'C':
                        cfgfile = optarg;
                        break;

#if defined(HAVE_SYSLOG) && defined(HAVE_VSYSLOG)
                case 's':
                        if (syslogmsg)
                                break;
                        openlog("baycomusb", LOG_PID, LOG_USER);
                        syslogmsg = 1;
                        break;
#endif

		case 'd':
			daemonize = 1;
			break;

                case 'v':
                        verboselevel = strtoul(optarg, NULL, 0);
                        break;

		case 0x400:
		case 0x401:
			simd = c & 1;
			break;

                default:
                        err++;
                        break;
                }
        }
        if (err) {
                lprintf(0, "usage: %s [-v <vl>] [-D <drvpath>] [-C <configfile>]\n", argv[0]);
		exit(1);
        }
        if (daemonize) {
                if (pipe(pfds)) {
                        lprintf(0, "pipe error %s (%u)", strerror(errno), errno);
			exit(1);
		}
                switch (pid = fork()) {
                case -1:
                        lprintf(0, "fork error %s (%u)", strerror(errno), errno);
			exit(1);
			
                case 0: /* child process */
                        close(pfds[0]);
                        setsid(); /* become a process group leader and drop controlling terminal */
                        fclose(stdin); /* no more standard in */
                        break;
                        
                default: /* parent process */
                        close(pfds[1]);
                        err = read(pfds[0], &uch, sizeof(uch));
                        if (err != sizeof(uch)) {
                                lprintf(0, "baycomusbserv init failed\n");
				_exit(1);
			}
			/* need to run _exit, otherwise ORBit inadvertedly kills some needed stuff */
                        _exit(0);
                }
        }
        if (config_load(cfgfile))
		exit(1);
	if ((devices_fd = open(devname, 0)) == -1) {
		lprintf(0, "Cannot open %s, usbdevfs not mounted?\n", devname);
		exit(1);
	}
	sigact.sa_handler = signal_quit;
	sigemptyset(&sigact.sa_mask);
	sigact.sa_flags = SA_RESTART;
#ifdef SIGHUP
        sigaction(SIGHUP, &sigact, NULL);
#endif
        sigaction(SIGINT, &sigact, NULL);
#ifdef SIGQUIT
        sigaction(SIGQUIT, &sigact, NULL);
#endif
        sigaction(SIGTERM, &sigact, NULL);
	sigact.sa_sigaction = signal_child;
	sigemptyset(&sigact.sa_mask);
	sigact.sa_flags = SA_RESTART | SA_NOCLDSTOP | SA_SIGINFO;
        sigaction(SIGCHLD, &sigact, NULL);
        if (daemonize) {
                uch = 0;
                if (write(pfds[1], &uch, sizeof(uch)) != sizeof(uch)) {
                        lprintf(0, "write error %s (%u)", strerror(errno), errno);
			exit(1);
		}
                close(pfds[1]);
        }
	if ((sock_fd = socket(/*PF_PACKET*/AF_INET, /*SOCK_RAW*/SOCK_PACKET, htons(ETH_P_AX25))) < 0) {
		lprintf(0, "Cannot create socket: %s (%u) \n", strerror(errno), errno);
		exit(1);;
	}
	initsimd(simd);
	audio_globalinit();
	if (server_init2())
		return 1;
	mainloop();
	config_save();
	return 0;
}
