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

/*
 *      mainwin32.c  --  Transceiver control Win32 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
 *
 *
 *  WARNING:
 *    malloc and free from MSVCRT cause almost immediate crashes.
 *    On the other hand, printf from CRTDLL does not support 64bit
 *    ints while the one from MSVCRT does...
 */

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

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

#define _REENTRANT

#include "trx.h"
#include "simd.h"

#include <windows.h>
#include <winsock.h>
#include <wincon.h>
#include <process.h>
#include <errno.h>
#include <getopt.h>
#include <malloc.h>
#include <ctype.h>

#if defined(ORBITIPC)
#include <glib.h>
#endif

#include "list.h"

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

static HANDLE devlist_mutex;
static HANDLE hglobalshmem;
static struct win32_global_state *globalstate;
static LIST_HEAD(devices_list);
static LIST_HEAD(orb_fds);
static int quit = 0;
static unsigned int fastpoll = 0;

#define POLLINTERVAL    10000  /* check all 10s for new modems */
#define FASTPOLLINTERVAL 1000  /* check all 1s for new modems when anticipated */

#define PAGE_ALIGN(x)     (((x)+((1<<12)-1))&~((1<<12)-1))

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

void acquire_trxdata_mutex(struct trx_thread_state *state)
{
	WaitForSingleObject(state->hmutex, INFINITE);
}

void release_trxdata_mutex(struct trx_thread_state *state)
{
	ReleaseMutex(state->hmutex);
}

int check_quit(void)
{
	return quit;
}

static inline void acquire_devlist_mutex(void)
{
	WaitForSingleObject(devlist_mutex, INFINITE);
}

static inline void release_devlist_mutex(void)
{
	ReleaseMutex(devlist_mutex);
}

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

int reopen_usbdev(struct trx_thread_state *state)
{
	DWORD r;
	HANDLE h[2];

	if (state->usbdev)
		usb_close(state->usbdev);
	state->usbdev = NULL;
	Sleep(500);  /* make sure device disconnected */
	acquire_devlist_mutex();
	if (WaitForSingleObject(state->hevent, 0) == WAIT_OBJECT_0)
		goto outok;
	state->devidx = -1;
	fastpoll = 10;
	release_devlist_mutex();
	/* now wait for device to connect */
	h[0] = devlist_mutex;
	h[1] = state->hevent;
	r = WaitForMultipleObjects(2, h, TRUE, 60000);
	if (r < WAIT_OBJECT_0 || r > (WAIT_OBJECT_0+1)) {
		lprintf(1, "Timeout reopening USB device %s (error %lu)\n", state->serial, r);
		return -1;
	}
  outok:
	release_devlist_mutex();
	if (!(state->usbdev = usb_open(BAYCOMUSB_VENDORID, -1, 0, state->devidx))) {
		lprintf(1, "Cannot open USB device %d (serial %s)\n", state->devidx, state->serial);
		return -1;
	}
	lprintf(10, "=== THREADCONTINUE: %d, %s\n", state->devidx, state->serial);
	return 0;
}

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

void init_shmem_object(struct trx_thread_state *state)
{
	unsigned int j;

	destroy_shmem_object(state);
	for (j = 0; j < MAXUSBMODEMS; j++) {
		if (globalstate->modem[j].hshmem != NULL)
			continue;
		globalstate->modem[j].hshmem = state->hshmem;
		state->globalidx = j;
		break;
	}
}

void destroy_shmem_object(struct trx_thread_state *state)
{
	if (state->globalidx != -1) {
		globalstate->modem[state->globalidx].hshmem = NULL;
		state->globalidx = -1;
	}
}

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

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: %d\n", state->devidx);
	/* open communications to device */
	state->netif_opened = 0;
	state->hmutex = CreateMutex(NULL, FALSE, NULL);
	if (!state->hmutex)
		goto ret;
	state->hevent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (!state->hevent)
		goto ret;
	state->hflxtxevent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (!state->hflxtxevent)
		goto ret;
	state->hflxrxevent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (!state->hflxrxevent)
		goto ret;
	/* get serial number from device */
	i = adapter_init_getserial(state);
	if (i < 0) {
		if (i == -2)
			lprintf(1, "Error loading USB device %d\n", state->devidx);
		fastpoll = 10;
		goto ret;
	}
	lprintf(2, "USB device %d serial %s\n", state->devidx, state->serial);
	acquire_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->devidx = state->devidx;
			state2->mode = state->mode;
			list_del(&state->device_list);
			INIT_LIST_HEAD(&state->device_list);
			lprintf(2, "Thread for USB device %d serial %s already running\n", state->devidx, state->serial);
			if (state->usbdev)
				usb_close(state->usbdev);
			state->usbdev = NULL;
			SetEvent(state2->hevent);
			release_devlist_mutex();
			goto ret;
		}
	}
	release_devlist_mutex();
	state->flags = FLG_RELOADMODEM | FLG_MODEMERROR | FLG_T7FERROR;
	/* finally start thread */
	trx_thread(state);
	if (state->usbdev)
		adapter_reset(state->usbdev);

  ret:
	state->mode = MODE_DEAD;
	if (state->hflxrxevent) {
		CloseHandle(state->hflxrxevent);
		state->hflxrxevent = NULL;
	}
	if (state->hflxtxevent) {
		CloseHandle(state->hflxtxevent);
		state->hflxtxevent = NULL;
	}
	if (state->hevent) {
		CloseHandle(state->hevent);
		state->hevent = NULL;
	}
	if (state->hmutex) {
		CloseHandle(state->hmutex);
		state->hmutex = NULL;
	}
	if (state->hshmem) {
		CloseHandle(state->hshmem);
		state->hshmem = NULL;
	}
	if (state->usbdev)
		usb_close(state->usbdev);
	acquire_devlist_mutex();
	destroy_shmem_object(state);
	list_del(&state->device_list);
	fastpoll = 10;
	release_devlist_mutex();
	lprintf(10, "<<< THREADSTOP: %s\n", state->serial);
	UnmapViewOfFile(state);
	return;
}

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

static void check_new_uninited_modems(void)
{
	struct list_head *list;
	struct trx_thread_state *state;
	unsigned int devmask = 0;
	unsigned int i;
	struct usbdevice *usbdev;
	HANDLE hshmem;

	for (list = devices_list.next; list != &devices_list; list = list->next) {
		state = list_entry(list, struct trx_thread_state, device_list);
		if (state->devidx == -1)
			continue;
		devmask |= (1 << state->devidx);
	}
	for (i = 0; i < MAXUSBMODEMS; i++) {
		if (devmask & (1 << i))
			continue;
		if (!(usbdev = usb_open(BAYCOMUSB_VENDORID, -1, 0, i)))
			continue;
		hshmem = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
					   PAGE_ALIGN(sizeof(struct trx_thread_state)), NULL);
		if (!hshmem) {
			usb_close(usbdev);
			lprintf(0, "Cannot create file mapping (error %ld)\n", GetLastError());
			continue;
		}
		state = MapViewOfFile(hshmem, FILE_MAP_WRITE, 0, 0, 0);
		if (!state) {
			usb_close(usbdev);
			CloseHandle(hshmem);
			lprintf(0, "Cannot map file mapping (error %ld)\n", GetLastError());
			continue;
		}
		memset(state, 0, sizeof(struct trx_thread_state));
		state->hshmem = hshmem;
		state->devidx = i;
		state->usbdev = usbdev;
		state->globalidx = -1;
		_beginthread(thread_start, 0, state);
		list_add(&state->device_list, &devices_list);
	}
}

/* --------------------------------------------------------------------- */
#if defined(ORBITIPC)

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

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

#ifdef USECRITICALSECT
static CRITICAL_SECTION orbit_mutex;
#else
static HANDLE orbit_mutex;
#endif

void acquire_orbit_mutex(void)
{
#ifdef USECRITICALSECT
	EnterCriticalSection(&orbit_mutex);
#else
	WaitForSingleObject(orbit_mutex, INFINITE);
#endif
}

void release_orbit_mutex(void)
{
#ifdef USECRITICALSECT
	LeaveCriticalSection(&orbit_mutex);
#else
	ReleaseMutex(orbit_mutex);
#endif
}

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

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

	if (!(cnx->user_data = ofd = malloc(sizeof(struct orbfdlist))))
		return;
	ofd->conn = cnx;
	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;
}

static void mainloop(void)
{
	struct list_head *list;
	struct orbfdlist *ofd;
	fd_set rmask, emask;
	int i, fd, maxfd;
	struct timeval tv;
	DWORD nextpolltime;
	long tdiff;

	nextpolltime = GetTickCount();
	while (!quit) {
		FD_ZERO(&rmask);
		FD_ZERO(&emask);
		maxfd = 0;
		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;
                        }
			fd = GIOP_CONNECTION_GET_FD(ofd->conn);
			if (fd >= maxfd)
				maxfd = fd + 1;
			FD_SET(fd, &rmask);
			FD_SET(fd, &emask);
		}
		tdiff = nextpolltime - GetTickCount();
		if (tdiff < 0)
			tdiff = 0;
		tv.tv_sec = tdiff / 1000;
		tv.tv_usec = (tdiff - tv.tv_sec * 1000) * 1000;
		i = select(maxfd, &rmask, NULL, &emask, &tv);
		if (i == -1 || errno == EINTR) {
			lprintf(0, "poll: %s (%u)\n", strerror(errno), errno);
			exit(1);
		}
		for (list = orb_fds.next; list != &orb_fds; list = list->next) {
			ofd = list_entry(list, struct orbfdlist, list);
			fd = GIOP_CONNECTION_GET_FD(ofd->conn);
                        if (FD_ISSET(fd, &emask) && ofd->conn)
				giop_main_handle_connection_exception(ofd->conn);
			if (FD_ISSET(fd, &rmask) && ofd->conn)
				giop_main_handle_connection(ofd->conn);
		}
		tdiff = GetTickCount() - nextpolltime;
		if (tdiff >= 0) {
			acquire_devlist_mutex();
			check_new_uninited_modems();
			if (fastpoll > 0) {
				nextpolltime = GetTickCount() + FASTPOLLINTERVAL;
				fastpoll--;
			} else {
				nextpolltime = GetTickCount() + POLLINTERVAL;
			}
			release_devlist_mutex();
		}
	}
}

/* --------------------------------------------------------------------- */
#else

static void mainloop(void)
{
	DWORD nextpolltime;
	long tdiff;

	nextpolltime = GetTickCount();
	while (!quit) {
		tdiff = nextpolltime - GetTickCount();
		if (tdiff < 0)
			tdiff = 0;
		Sleep(tdiff);
		tdiff = GetTickCount() - nextpolltime;
		if (tdiff >= 0) {
			acquire_devlist_mutex();
			check_new_uninited_modems();
			if (fastpoll > 0) {
				nextpolltime = GetTickCount() + FASTPOLLINTERVAL;
				fastpoll--;
			} else {
				nextpolltime = GetTickCount() + POLLINTERVAL;
			}
			release_devlist_mutex();
		}
	}
}

#endif
/* --------------------------------------------------------------------- */

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);
		/* close network interface stuff */
		if (usb_releaseinterface(state->usbdev, 0))
			lprintf(2, "netif_close: cannot release interface 0\n");
	}
	lprintf(30, "netif_close\n");
	state->netif_opened = 0;
}

int netif_open(struct trx_thread_state *state)
{
	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 = USBDEVFS_URB_QUEUE_BULK;
			state->du.p.rxurb[u].urb.buffer = &state->du.p.rxurb[u].b.frame.frame[0];
			state->du.p.rxurb[u].urb.buffer_length = sizeof(state->du.p.rxurb[u].b.frame.frame)+2;
			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 = USBDEVFS_URB_QUEUE_BULK | 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);
			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);
        } 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);
#endif
  errsetif0:
	usb_releaseinterface(state->usbdev, 0);
	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;
	int r;
	DWORD wres;
	HANDLE whandle[2];

	if (!state->netif_opened) {
		if (timeout)
			Sleep(timeout);
		return 0;
	}
	if (!timeout)
		rettime++;
	//lprintf(20, ">>> process: %d\n", timeout);
	acquire_trxdata_mutex(state);
	for (;;) {
		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) {
			for (;;) {
				i = state->du.p.rxurbptr;
				if (state->du.p.rxurb[i].urbstate == URBSTATE_INPROGRESS ||
				    state->du.p.rxurb[i].urbstate == URBSTATE_DONE ||
				    state->du.p.rxurb[i].urbstate == URBSTATE_CORBASEND)
					break;
				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_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;
#if 0
				{
					char buf[512];
					snprintpkt(buf, sizeof(buf), state->du.p.txurb[i].urb.buffer+2, 
						   state->du.p.txurb[i].urb.buffer_length-2);
					printf("Tx(%u): %s\n", state->du.p.txurb[i].urb.buffer_length, buf);
				}
#endif
			}
#if 0
			{
				unsigned int rxu = 0, txu = 0;
				for (i = 0; i < NUMRXPACKETS; i++)
					if (state->du.p.rxurb[i].urbstate == URBSTATE_INPROGRESS)
						rxu++;
				for (i = 0; i < NUMTXPACKETS; i++)
					if (state->du.p.txurb[i].urbstate == URBSTATE_INPROGRESS)
						txu++;
				if (rxu != NUMRXPACKETS || txu)
					lprintf(10, "queued URBs: rx %u tx %u\n", rxu, txu);
			}
#endif
		} 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);
			//lprintf(20, "<<< process\n");
			return 0;
		}
		tdiff1 = state->iurb.nexttime - curtime;
		if (tdiff1 < 0)
			tdiff1 = 0;
		if (tdiff1 < tdiff)
			tdiff = tdiff1;
		release_trxdata_mutex(state);
#if 1
		//lprintf(20, ">>>WaitForMultipleObjects %d\n", tdiff);
		whandle[0] = state->hflxtxevent;
		whandle[1] = usb_getfd(state->usbdev);
		wres = WaitForMultipleObjects(2, whandle, FALSE, tdiff);
		urb = usb_reapurb(state->usbdev, 0);
		//lprintf(20, "<<<WaitForMultipleObjects %p\n", urb);
#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 (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) {
					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 0
					{
						char buf[512];
						snprintpkt(buf, sizeof(buf), state->du.p.rxurb[i].urb.buffer,
							   state->du.p.rxurb[i].urb.actual_length-2);
						printf("Rx(%d): %s\n", state->du.p.rxurb[i].urb.actual_length, buf);
					}
#endif
					state->du.p.rxurb[i].urbstate = URBSTATE_DONE;
					SetEvent(state->hflxrxevent);
				}
			} 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 (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) {
				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);
				goto interr;
			}
#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;

  interr:
	release_trxdata_mutex(state);
	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, reload;
	DWORD endtime;

        if (!ctrl)
                return -1;
	endtime = GetTickCount() + timeout;
        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 ((signed)(GetTickCount()-endtime) > 0)
                        break;
		if (process(ctrl, 25))
                        return -1;
        }
	if (buf)
		*buf = 0;
        return cpy;
}

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

static BOOL WINAPI signal_handler(DWORD type)
{
	switch (type) {
	case CTRL_C_EVENT:
	case CTRL_BREAK_EVENT:
		lprintf(1, "CTRL-C/BREAK pressed\n");
		quit = 1;
		return TRUE;

	case CTRL_CLOSE_EVENT:
		lprintf(1, "Console closed\n");
		return FALSE;

	case CTRL_LOGOFF_EVENT:
		lprintf(1, "Current user logging out\n");
		return FALSE;

	case CTRL_SHUTDOWN_EVENT:
		lprintf(1, "System shutdown\n");
		quit = 1;
		return TRUE;

	default:
		lprintf(1, "Unknown ConsoleCtrl type %lu\n", type);
		return FALSE;
	}
	return FALSE;
}

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

static int do_init(int argc, char *argv[])
{
        static const struct option long_options[] = {
                { "drv", 1, 0, 'D' },
                { "config", 1, 0, 'C' },
                { "verbose", 1, 0, 'v' },
                { "help", 0, 0, 'h' },
		{ "nommx", 0, 0, 0x400 },
		{ "mmx", 0, 0, 0x401 },
                { 0, 0, 0, 0 }
        };
        int c, err = 0;
	unsigned int simd = 1;
        const char *cfgfile = NULL;

	hglobalshmem = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
					 PAGE_ALIGN(sizeof(struct win32_global_state)),
					 "baycomusb");
	if (!hglobalshmem) {
		lprintf(0, "Cannot create shared memory segment (error %lu)\n", GetLastError());
		return -1;
	}
	if (GetLastError() == ERROR_ALREADY_EXISTS) {
		CloseHandle(hglobalshmem);
		lprintf(0, "baycomusbserv already running\n");
		return -1;
	}
	globalstate = MapViewOfFile(hglobalshmem, FILE_MAP_ALL_ACCESS, 0, 0, 0);
	if (!globalstate) {
		lprintf(0, "Cannot map shared memory segment (error %lu)\n", GetLastError());
		return -1;
	}
	memset(globalstate, 0, sizeof(struct win32_global_state));
	strncpy(globalstate->signature, BAYUSBSIGNATURE, sizeof(globalstate->signature));
	globalstate->processid = GetCurrentProcessId();

        devlist_mutex = CreateMutex(NULL, FALSE, NULL);
	if (!devlist_mutex) {
		lprintf(0, "devlist_mutex creation error %lu\n", GetLastError());
		return -1;
	}

#if defined(ORBITIPC)
#ifdef USECRITICALSECT
	InitializeCriticalSection(&orbit_mutex);
#else
	orbit_mutex = CreateMutex(NULL, FALSE, NULL);
	if (!orbit_mutex) {
		lprintf(0, "orbit_mutex creation error %lu\n", GetLastError());
		return -1;
	}
#endif
	/* init custom IO handlers */
	IIOPAddConnectionHandler = orb_add_connection;
        IIOPRemoveConnectionHandler = orb_remove_connection;
#endif
	if (server_init1(&argc, argv))
		return -1;

	while ((c = getopt_long(argc, argv, "D:C:sv:S", long_options, NULL)) != EOF) {
                switch (c) {
                case 'D':
                        usb_setmountpoint(optarg);
                        break;

                case 'C':
                        cfgfile = optarg;
                        break;

                case 's':
                        syslogmsg = 1;
                        break;

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

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

                default:
                        err++;
                        break;
                }
        }
        if (err) {
                lprintf(1, "usage: %s [-v <vl>] [-s] [-D <drvpath>] [-C <configfile>]\n", argv[0]);
		return -1;
        }
        if (config_load(cfgfile))
		return -1;
	initsimd(simd);
        return 0;
}

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

int server_entry_parse_argv(int argc, char *argv[])
{
        if (do_init(argc, argv))
                return 1;
	return 0;
}

int server_entry_run(void)
{
	if (!SetConsoleCtrlHandler(signal_handler, TRUE))
		lprintf(1, "Cannot add ConsoleCtrlHandler\n");
	if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS))
		lprintf(1, "Cannot set scheduling priority, error 0x%lx\n", GetLastError());
	audio_globalinit();
	if (server_init2())
		return 1;
	mainloop();
        config_save();
	return 0;
}

#define MAXARGV  64

void CALLBACK server_entry_cmdline(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
{
	unsigned int argc = 1;
	char *argv[MAXARGV];
	char *cp = lpszCmdLine;

	verboselevel = ~0;
	syslogmsg = 1;
	argv[0] = "bayusb.dll";
#if 0
	lprintf(0, "server_entry_cmdline: \"%s\"\n", lpszCmdLine);
#endif
	while (*cp) {
		while (*cp && isspace(*cp))
			cp++;
		if (!*cp)
			break;
		if (argc >= MAXARGV)
			break;
		argv[argc++] = cp;
		while (*cp && !isspace(*cp))
			cp++;
		if (!*cp)
			break;
		*cp++ = 0;
	}
	lprintf(0, "server_entry_cmdline argc %u\n", argc);
#if 0
	{
		unsigned int i;
		for (i = 0; i < argc; i++)
			lprintf(0, "server_entry_cmdline argv[%u] \"%s\"\n", i, argv[i]);
	}
#endif
#if 1
	if (server_entry_parse_argv(argc, argv))
		return;
	server_entry_run();
#endif
}

void server_entry_quit(void)
{
	quit = 1;
}

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

static SERVICE_STATUS          MyServiceStatus; 
static SERVICE_STATUS_HANDLE   MyServiceStatusHandle; 

static void BaycomUsbServiceCtrlHandler(DWORD Opcode) 
{ 
        DWORD status; 
 
        switch(Opcode) { 
        case SERVICE_CONTROL_STOP: 
                quit = 1;
                MyServiceStatus.dwWin32ExitCode = 0; 
                MyServiceStatus.dwCurrentState  = SERVICE_STOP_PENDING; 
                MyServiceStatus.dwCheckPoint    = 0; 
                MyServiceStatus.dwWaitHint      = 0;
                if (!SetServiceStatus(MyServiceStatusHandle, &MyServiceStatus)) { 
                        status = GetLastError(); 
                        lprintf(1, "SetServiceStatus error %ld\n",status); 
                }
                lprintf(1, "Stopping baycomusbserv\n");
                return; 
 
        case SERVICE_CONTROL_INTERROGATE: 
                /* Fall through to send current status. */
                break;
 
        default:
                lprintf(1, "BaycomUsbServiceCtrlHandler: Unrecognized opcode %ld\n", Opcode); 
        }
        /* Send current status. */
        if (!SetServiceStatus(MyServiceStatusHandle, &MyServiceStatus)) { 
                status = GetLastError();
                lprintf(1, "SetServiceStatus error %ld\n",status); 
        }
}

static void BaycomUsbServiceStart(DWORD argc, LPTSTR *argv)
{ 
        DWORD status;
 
        MyServiceStatus.dwServiceType        = SERVICE_WIN32; 
        MyServiceStatus.dwCurrentState       = SERVICE_START_PENDING; 
        MyServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP; 
        MyServiceStatus.dwWin32ExitCode      = 0; 
        MyServiceStatus.dwServiceSpecificExitCode = 0; 
        MyServiceStatus.dwCheckPoint         = 0; 
        MyServiceStatus.dwWaitHint           = 0; 
 
        MyServiceStatusHandle = RegisterServiceCtrlHandler(SERVICENAME, BaycomUsbServiceCtrlHandler);
        if (MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
                lprintf(1, "RegisterServiceCtrlHandler failed %ld\n", GetLastError());
                return;
        }
        if (do_init(argc, argv)) {
                MyServiceStatus.dwCurrentState       = SERVICE_STOPPED; 
                MyServiceStatus.dwCheckPoint         = 0; 
                MyServiceStatus.dwWaitHint           = 0; 
                MyServiceStatus.dwWin32ExitCode      = ERROR_ACCESS_DENIED; 
                MyServiceStatus.dwServiceSpecificExitCode = ERROR_INVALID_DATA;
                SetServiceStatus(MyServiceStatusHandle, &MyServiceStatus); 
                return; 
        }
        /* Initialization complete - report running status. */
        MyServiceStatus.dwCurrentState       = SERVICE_RUNNING; 
        MyServiceStatus.dwCheckPoint         = 0; 
        MyServiceStatus.dwWaitHint           = 0;
        if (!SetServiceStatus(MyServiceStatusHandle, &MyServiceStatus)) { 
                status = GetLastError(); 
                lprintf(1, "SetServiceStatus error %ld\n",status); 
        } 
	mainloop();
        config_save();
        MyServiceStatus.dwWin32ExitCode = 0; 
        MyServiceStatus.dwCurrentState  = SERVICE_STOPPED; 
        MyServiceStatus.dwCheckPoint    = 0; 
        MyServiceStatus.dwWaitHint      = 0;
        if (!SetServiceStatus(MyServiceStatusHandle, &MyServiceStatus)) { 
                status = GetLastError(); 
                lprintf(1, "SetServiceStatus error %ld\n",status); 
        } 
        lprintf(1, "baycomusbserv stopped\n");
}

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

int main(int argc, char *argv[])
{
        SERVICE_TABLE_ENTRY DispatchTable[] = { 
                { SERVICENAME, BaycomUsbServiceStart },
                { NULL, NULL } 
        };

        printf("baycomusbserv v" VERSION " (c) 1999-2001 by Thomas Sailer, HB9JNX/AE4WA\n");
        if (argc >= 2 && !strcmp(argv[1], "-S")) {
                verboselevel = 10;
                syslogmsg = 1;
                if (!StartServiceCtrlDispatcher(DispatchTable)) {
                        lprintf(0, "Error starting service: %lu\n", GetLastError());
                        exit(1);
                }
                lprintf(0, "Starting Service\n");
                return 0;
        }
        if (server_entry_parse_argv(argc, argv))
                exit(1);
	return server_entry_run();
}
