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

/*
 *      mainunix.c  --  Transceiver control UNIX stuff.
 *
 *      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 _REENTRANT

#include "trx.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>

#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"

#include "baycom_usb.h"

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

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

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

#if 0
static int findinterface(struct trx_thread_state *state)
{
	struct ifreq ifr;
  	int fd;
	union {
		struct baycomusb_device bdev;
		char buf[1024];
	} bd;
	FILE *f;
	char *cp;

	if (!(f = fopen("/proc/net/dev", "r"))) {
		lprintf(1, "cannot open /proc/net/dev\n");
		return -1;
	}
	if ((fd = socket(PF_AX25, SOCK_SEQPACKET, 0)) < 0) {
		lprintf(0, "Cannot create socket: %s (%u) \n", strerror(errno), errno);
	}
	for (;;) {
		if (!fgets(bd.buf, sizeof(bd.buf), f)) {
			lprintf(1, "Cannot find network interface for %03d:%03d\n", state->busnr, state->devnr);
			fclose(f);
			close(fd);
			return -1;
		}
		if (!(cp = strchr(bd.buf, ':')))
			continue;
		*cp = 0;
		cp = bd.buf;
		while (*cp == ' ')
			cp++;
		strncpy(ifr.ifr_name, cp, sizeof(ifr.ifr_name));
		if (ioctl(fd, SIOCGIFHWADDR, &ifr))
			continue;
		if (ifr.ifr_addr.sa_family != AF_AX25)
			continue;
		ifr.ifr_data = (void *)&bd.bdev;
		bd.bdev.cmd = BAYCOMUSB_GETDEVICE;
		if (ioctl(fd, SIOCDEVPRIVATE, &ifr))
			continue;

		printf("Baycomusb netdev: %s usbdev: %03d:%03d\n", ifr.ifr_name, bd.bdev.busnr, bd.bdev.devnr);

		if (bd.bdev.busnr != state->busnr || bd.bdev.devnr != state->devnr)
			continue;
		break;
	}
	fclose(f);
	close(fd);
	if (strcmp(state->serial, bd.bdev.serial))
		lprintf(1, "USB device %03d:%03d: serial numbers %s / %s do not match!\n",
			state->busnr, state->devnr, state->serial, bd.bdev.serial);
	state->bitrate = bd.bdev.bitrate;
	strncpy(state->ifname, ifr.ifr_name, sizeof(state->ifname));
	return 0;
}
#endif

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

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

	trx_thread(state);

  ret:
	pthread_mutex_destroy(&state->mutex);
	if (state->usbdev)
		usb_close(state->usbdev);
	netif_close(state);
	pthread_mutex_lock(&devlist_mutex);
	list_del(&state->device_list);
	pthread_mutex_unlock(&devlist_mutex);
	lprintf(10, "<<< THREADSTOP: %s\n", 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));
#ifdef HAVE_IFRNEWNAME
	if (state->cfg.intf.ifname[0] && strcmp(state->cfg.intf.ifname, state->ifname)) {
		strncpy(ifr.ifr_newname, state->cfg.intf.ifname, sizeof(ifr.ifr_newname));
		if (ioctl(sock_fd, SIOCSIFNAME, &ifr) == -1) {
			lprintf(1, "Cannot rename interface (SIOCSIFNAME: %s (%u))\n", strerror(errno), errno);
			netif_close(state);
			if (netif_open(state))
				return -1;
		} else {
			strncpy(state->ifname, ifr.ifr_newname, sizeof(state->ifname));
			strncpy(ifr.ifr_name, state->ifname, sizeof(ifr.ifr_name));
		}
	}
#endif
	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;
}

static void set_chacc(struct trx_thread_state *state)
{
	struct sockaddr sa;
	unsigned char buf[2];

        if (state->mode != MODE_FSK && state->mode != MODE_EXTERNAL && state->mode != MODE_AFSK)
                return;
	strncpy(sa.sa_data, state->ifname, sizeof(sa.sa_data));
	sa.sa_family = AF_PACKET;
	buf[0] = PARAM_TXDELAY;
	buf[1] = state->cfg.chacc.txdelay / 10;
	if (sendto(sock_fd, buf, 2, 0, &sa, sizeof(struct sockaddr)) == -1)
		lprintf(1, "sendto failed: %s (%u)\n", strerror(errno), errno);
	buf[0] = PARAM_PERSIST;
	buf[1] = state->cfg.chacc.ppersistence;
	if (sendto(sock_fd, buf, 2, 0, &sa, sizeof(sa)) == -1)
		lprintf(1, "sendto failed: %s (%u)\n", strerror(errno), errno);
	buf[0] = PARAM_SLOTTIME;
	buf[1] = state->cfg.chacc.slottime / 10;
	if (sendto(sock_fd, buf, 2, 0, &sa, sizeof(sa)) == -1)
		lprintf(1, "sendto failed: %s (%u)\n", strerror(errno), errno);
	buf[0] = PARAM_TXTAIL;
	buf[1] = state->cfg.chacc.txtail / 10;
	if (sendto(sock_fd, buf, 2, 0, &sa, sizeof(sa)) == -1)
		lprintf(1, "sendto failed: %s (%u)\n", strerror(errno), errno);
	buf[0] = PARAM_FULLDUP;
	buf[1] = state->cfg.chacc.fullduplex;
	if (sendto(sock_fd, buf, 2, 0, &sa, sizeof(sa)) == -1)
		lprintf(1, "sendto failed: %s (%u)\n", strerror(errno), errno);
}

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

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

void netif_close(struct trx_thread_state *state)
{
	if (state->netif_fd != -1)
		close(state->netif_fd);
	state->netif_fd = -1;
	runscript(state, "down");
}

int netif_open(struct trx_thread_state *state)
{
	struct baycomusb_startnetdev stn;
	unsigned char br[6];
	int r;
	
	if (state->netif_fd != -1)
		close(state->netif_fd);
	state->netif_fd = -1;
	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");
			return -1;
		}
		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);
	} else
		state->bitraterx = state->bitratetx = state->txdmul = 0;
	if ((state->netif_fd = open(NETIFMUXDEV, O_RDWR)) == -1) {
		lprintf(1, "Cannot open " NETIFMUXDEV "\n");
		return -1;
	}
	stn.busnr = state->busnr;
	stn.devnr = state->devnr;
	strncpy(stn.ifname, state->cfg.intf.ifname, sizeof(stn.ifname));
	if (!stn.ifname[0])
		strncpy(stn.ifname, "bcu0", sizeof(stn.ifname));
	if (ioctl(state->netif_fd, BAYCOMUSB_STARTNETDEV, &stn)) {
		lprintf(1, "ioctl: BAYCOMUSB_STARTNETDEV: error %s (%d)\n", strerror(errno), errno);
		close(state->netif_fd);
		state->netif_fd = -1;
		return -1;
	}
	strncpy(state->ifname, stn.ifname, sizeof(state->ifname));
	if (set_intf(state))
		return -1;
	runscript(state, "up");	
	return 0;
}

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

#if 0

static int bcusbioctl(struct trx_thread_state *ctrl, int cmd, void *data)
{
        struct ifreq ifr;

        if (!ctrl)
                return -1;
        strncpy(ifr.ifr_name, ctrl->ifname, sizeof(ifr.ifr_name));
        ifr.ifr_data = data;
        *((int *)data) = cmd;
        return ioctl(sock_fd, SIOCDEVPRIVATE, &ifr);
}

#else

static int bcusbioctl(struct trx_thread_state *ctrl, int cmd, void *data)
{
        if (!ctrl)
                return -1;
        return ioctl(ctrl->netif_fd, cmd, data);
}

#endif

static int periodic(struct trx_thread_state *state)
{
        struct baycomusb_status b;
        struct baycomusb_receiveuart ruart;
        struct baycomusb_transmituart tuart;
	struct baycomusb_forceptt bforceptt;
        struct baycomusb_modemdisc bmdisc;
        struct baycomusb_t7fcontrol bt7f;
        struct baycomusb_setleds bleds;
	
	if (bcusbioctl(state, BAYCOMUSB_GETSTATUS, &b))
                return -1;
	state->ptt = b.ptt;
	state->dcd = b.dcd;
	state->rssi = b.rssi;
	if (b.uartchars) {
		if (!bcusbioctl(state, BAYCOMUSB_RECEIVEUART, &ruart))
			addrxfifo(state, ruart.buffer, ruart.len);
	}
	if (state->tfifo.rd != state->tfifo.wr) {
		tuart.txchar = state->tfifo.buf[state->tfifo.rd];
		if (!bcusbioctl(state, BAYCOMUSB_TRANSMITUART, &tuart)) {
			state->tfifo.rd = (state->tfifo.rd + 1) % sizeof(state->tfifo.buf);
		}
	}
	if (state->flags & FLG_CLRPTT) {
		state->flags &= ~FLG_CLRPTT;
		bforceptt.ptt = 0;
		goto setptt;
	}
	if (state->flags & FLG_SETPTT && !(state->flags & FLG_INHIBITSETPTT)) {
		state->flags &= ~FLG_SETPTT;
		bforceptt.ptt = (state->flags & FLG_MANUALPTT) ? 1 : 0;
	  setptt:
		if (bcusbioctl(state, BAYCOMUSB_FORCEPTT, &bforceptt))
			lprintf(1, "USB device %03d:%03d: failed to set ptt\n", state->busnr, state->devnr);
	}
	if (state->flags & FLG_RECONFINTF) {
		state->flags &= ~FLG_RECONFINTF;
		if (set_intf(state))
			return -1;
	}
	if (state->flags & FLG_RECONFCHACC) {
		state->flags &= ~FLG_RECONFCHACC;
		set_chacc(state);
	}
	if (state->flags & (FLG_SETMDISCDIR | FLG_SETMDISCOUT)) {
		state->flags &= ~(FLG_SETMDISCDIR | FLG_SETMDISCOUT);
		bmdisc.setdirection = state->cfg.mdisc.direction;
		bmdisc.setoutput = state->cfg.mdisc.output;
		/* enable RxC, TxC or TxD outputs if special output mode is selected */
		if (state->mode == MODE_FSK) {
			if (state->cfg.mdisc.rxc)
				bmdisc.setdirection &= ~(1 << 0);
			if (state->cfg.mdisc.txc)
				bmdisc.setdirection &= ~(1 << 1);
			if (state->cfg.mdisc.txd)
				bmdisc.setdirection &= ~(1 << 3);
		}
		if (bcusbioctl(state, BAYCOMUSB_MODEMDISC, &bmdisc))
			lprintf(1, "USB device %03d:%03d: failed to set modem disconnect port\n", state->busnr, state->devnr);
	}
	if (state->flags & FLG_SETT7FPORT) {
		state->flags &= ~FLG_SETT7FPORT;
		bt7f.setoutput = state->t7fport;
		if (bcusbioctl(state, BAYCOMUSB_T7FCONTROL, &bt7f))
			lprintf(1, "USB device %03d:%03d: failed to set T7F control port\n", state->busnr, state->devnr);
	}
	if (state->flags & FLG_SETLEDS) {
		state->flags &= ~FLG_SETLEDS;
		bleds.leds = state->leds;
		if (bcusbioctl(state, BAYCOMUSB_SETLEDS, &bleds))
			lprintf(1, "USB device %03d:%03d: failed to set LEDS\n", state->busnr, state->devnr);
	}
	return 0;
}

int polldevice(struct trx_thread_state *state)
{
	int r;

	usleep(25000);
	acquire_trxdata_mutex(state);
	r = periodic(state);
	release_trxdata_mutex(state);
	return r;
}

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

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++;
			}
		}
		if (periodic(ctrl)) {
			release_trxdata_mutex(ctrl);
                        return -1;
		}
		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;
                usleep(25000);
        }
	if (buf)
		*buf = 0;
        return cpy;
}

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

struct trxapi_baycomusb_adapter_audio_devs *audio_get_device_list(void)
{
	return NULL;
}

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

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

static void 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' },
                { 0, 0, 0, 0 }
        };
        int c, err = 0;
	unsigned int daemonize = 0;
	char devname[256] = "/proc/bus/usb/devices";
	const char *cfgfile = NULL;
	CORBA_Environment ev;
        int pfds[2];
        pid_t pid;
        unsigned char uch;
	struct sigaction sigact;

        printf("baycomusbserv v" VERSION " (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;

                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);;
	}
	if (server_init2())
		return 1;
	mainloop();
	config_save();
	return 0;
}
