Logo Search packages:      
Sourcecode: baycomusb version File versions  Download package

mainunixuser.c

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

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

Generated by  Doxygen 1.6.0   Back to index