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

/*
 *	bayusb.c  --  FlexNet driver for Baycom USB modem.
 *
 *	Copyright (C) 1999-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.
 *
 *
 *  History:
 *   0.1  23.06.1999  Created
 *   0.2  07.01.2000  Expanded to usbdevfs capabilities
 *
 */

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

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

#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdlib.h>

//#include "flexdrv.h"
#include "resource.h"

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

#include "trx.h"
#include "win32client.h"

#define VERBOSE

#ifdef VERBOSE
#define verbose(x) x
#else
#define verbose(x)
#endif

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

#define DRIVER_NAME	"BaycomUSB"
#define DRIVER_VERSION  "0.2"

#define APIFLG_COUNTMASK       0x0ffff
#define APIFLG_TERMINATE       0x10000
#define APIFLG_CLOSE           0x20000

static struct state {
	struct trxapi *api;
	unsigned int apiflags;
	CRITICAL_SECTION apilock;

	HANDLE hdevlife;

       	char serial[16];   /* device serial number, or NULL string for any device */
	char devname[32];

	u16 mode;

	UINT calibtimer;

	L1FRAME txframe;
	L1_STATISTICS stat;
} state = { 0, };

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

static struct trx_thread_state *api_lock()
{
	struct trx_thread_state *servant = NULL;
	const struct trxapi_description *desc;
	struct trxapi *api;
	unsigned int i;

	EnterCriticalSection(&state.apilock);
	if (state.apiflags & (APIFLG_TERMINATE|APIFLG_CLOSE))
		goto out;
	if (!state.api) {
		for (i = 0;; i++) {
			if (!(api = win32shmem_open_byindex(i)))
				break;
			desc = win32shmem_get_description(api);
			if (desc && (!state.serial[0] || !strcmp(desc->instid, state.serial)))
				break;
			win32shmem_close(api);
		}
		if (api) {
			snprintf(state.devname, sizeof(state.devname), "%s (%s)", desc->model, desc->instid);
			servant = api->state;
			win32shmem_lock(api);
			if (servant->mode == MODE_DEAD || servant->flexnetlock) {
				win32shmem_unlock(api);
				win32shmem_close(api);
			} else {
				servant->flexnetlock = 1;
				win32shmem_unlock(api);
				SetEvent(state.hdevlife);
				state.api = api;
			}
		}
	}
	if (!state.api)
		goto out;
	servant = state.api->state;
	if (servant->mode == MODE_DEAD) {
		servant = NULL;
		state.apiflags |= APIFLG_CLOSE;
		SetEvent(state.api->hflxrxevent);
		if (!(state.apiflags & APIFLG_COUNTMASK)) {
			win32shmem_lock(state.api);
			state.api->state->flexnetlock = 0;
			win32shmem_unlock(state.api);
			win32shmem_close(state.api);
			state.api = NULL;
			state.apiflags &= ~APIFLG_CLOSE;
			SetEvent(state.hdevlife);
		}
		goto out;
	}
	state.apiflags = (state.apiflags & ~APIFLG_COUNTMASK) | ((state.apiflags + 1) & APIFLG_COUNTMASK);
  out:
	LeaveCriticalSection(&state.apilock);
	return servant;
}

static void api_unlock(void)
{
	EnterCriticalSection(&state.apilock);
	state.apiflags = (state.apiflags & ~APIFLG_COUNTMASK) | ((state.apiflags - 1) & APIFLG_COUNTMASK);
	if ((state.apiflags & APIFLG_CLOSE) && !(state.apiflags & APIFLG_COUNTMASK)) {
		win32shmem_lock(state.api);
		state.api->state->flexnetlock = 0;
		win32shmem_unlock(state.api);
		win32shmem_close(state.api);
		state.api = NULL;
		state.apiflags &= ~APIFLG_CLOSE;
		SetEvent(state.hdevlife);
	}
	LeaveCriticalSection(&state.apilock);
}

static void api_close(void)
{
	EnterCriticalSection(&state.apilock);
	if (state.api) {
		state.apiflags |= APIFLG_CLOSE;
		SetEvent(state.api->hflxrxevent);
		if (!(state.apiflags & APIFLG_COUNTMASK)) {
			win32shmem_lock(state.api);
			state.api->state->flexnetlock = 0;
			win32shmem_unlock(state.api);
			win32shmem_close(state.api);
			state.api = NULL;
			state.apiflags &= ~APIFLG_CLOSE;
			SetEvent(state.hdevlife);
		}
	}
	LeaveCriticalSection(&state.apilock);
}

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

/*
 * Treiber-Init. Sofern mehrfach aufgerufen, kommt vorher jeweils l1_exit()
 * Hier also alle Ressourcen allokieren, aber noch nicht starten sofern
 * dazu die Parameter gebraucht werden. Die kommen spaeter per l1_init_kanal()
 */

int init_device(HKEY hKey)
{
	DWORD regtype, reglen;

	verboselevel = ~0;
	syslogmsg = 1;
	/* get serial from registry */
	reglen = sizeof(state.serial);
	if (RegQueryValueEx(hKey, "SerialNumber", NULL, &regtype, state.serial, &reglen) != ERROR_SUCCESS ||
	    regtype != REG_SZ)
		state.serial[0] = 0;
	state.serial[sizeof(state.serial)-1] = 0;
	state.api = NULL;
	state.apiflags = 0;
	InitializeCriticalSection(&state.apilock);
	state.hdevlife = CreateEvent(NULL, FALSE, FALSE, "FlexNet Device State");
        return 1;
}

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

void l1_exit(HKEY hKey)
{
	EnterCriticalSection(&state.apilock);
	state.apiflags |= APIFLG_TERMINATE;
	LeaveCriticalSection(&state.apilock);
	api_close();
	l1_init_kanal(0, 0, MODE_off);
	RegSetValueEx(hKey, "SerialNumber", 0, REG_SZ, state.serial, strlen(state.serial)+1);
	EnterCriticalSection(&state.apilock);
	while (state.api) {
		LeaveCriticalSection(&state.apilock);
		Sleep(10);
		EnterCriticalSection(&state.apilock);
	}
	LeaveCriticalSection(&state.apilock);
	CloseHandle(state.hdevlife);
	DeleteCriticalSection(&state.apilock);
}

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

byte *config_info(byte kanal)
{
	if (kanal)
		return 0;
	return state.devname;
}

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

static void setdlgitems(HWND hDlg)
{
	struct trxapi *api;
	const struct trxapi_description *desc;
	struct trx_thread_state *servant;
        char buf[32];
	unsigned int i;
	HWND hcombo;

	GetDlgItemText(hDlg, IDC_SERIAL, buf, sizeof(buf));
	if (!strcmp(buf, "Any"))
		buf[0] = 0;
	if (!buf[0]) {
		api = win32shmem_open_byindex(0);
	} else {
		for (i = 0;; i++) {
			if (!(api = win32shmem_open_byindex(i)))
				break;
			desc = win32shmem_get_description(api);
			if (desc) {
				if (!strcmp(desc->instid, buf))
					break;
			}
			win32shmem_close(api);
		}
	}
	if (!api) {
		lprintf(10, "No modem found\n");
		/* gray out dialog */
		hcombo = GetDlgItem(hDlg, IDC_MODE);
		ComboBox_ResetContent(hcombo);
		SendMessage(hcombo, CB_ADDSTRING, 0, (LPARAM)"-");
		SendDlgItemMessage(hDlg, IDC_BITRATERX, EM_SETREADONLY, 1, 0);
		SendDlgItemMessage(hDlg, IDC_BITRATETX, EM_SETREADONLY, 1, 0);
		SendDlgItemMessage(hDlg, IDC_TXDELAY, EM_SETREADONLY, 1, 0);
		CheckDlgButton(hDlg, IDC_FULLDUPLEX, BST_INDETERMINATE);
		CheckDlgButton(hDlg, IDC_PTTMUTE, BST_INDETERMINATE);
		return;
	}
	servant = api->state;
	hcombo = GetDlgItem(hDlg, IDC_MODE);
	ComboBox_ResetContent(hcombo);
	SendMessage(hcombo, CB_ADDSTRING, 0, (LPARAM)"-");
	SendMessage(hcombo, CB_ADDSTRING, 0, (LPARAM)"fsk");
	SendMessage(hcombo, CB_ADDSTRING, 0, (LPARAM)"afsk");
	SendMessage(hcombo, CB_ADDSTRING, 0, (LPARAM)"external");
	SendMessage(hcombo, CB_ADDSTRING, 0, (LPARAM)"audio");
	switch (servant->cfg.adapt.mode) {
	case trxapi_baycomusb_mode_fsk:
		ComboBox_SetCurSel(hcombo, 1);
		break;

	case trxapi_baycomusb_mode_external:
		ComboBox_SetCurSel(hcombo, 3);
		break;

	case trxapi_baycomusb_mode_afsk:
		ComboBox_SetCurSel(hcombo, 2);
		break;

	case trxapi_baycomusb_mode_audio:
		ComboBox_SetCurSel(hcombo, 4);
		break;

	default:
		ComboBox_SetCurSel(hcombo, 0);
		break;
	}
	SendDlgItemMessage(hDlg, IDC_BITRATERX, EM_SETREADONLY, 0, 0);
	snprintf(buf, sizeof(buf), "%ld", servant->cfg.adapt.bitraterx);
	SetDlgItemText(hDlg, IDC_BITRATERX, buf);
	SendDlgItemMessage(hDlg, IDC_BITRATETX, EM_SETREADONLY, 0, 0);
	snprintf(buf, sizeof(buf), "%ld", servant->cfg.adapt.bitratetx);
	SetDlgItemText(hDlg, IDC_BITRATETX, buf);
	SendDlgItemMessage(hDlg, IDC_TXDELAY, EM_SETREADONLY, 0, 0);
	snprintf(buf, sizeof(buf), "%ld", servant->cfg.chacc.txdelay);
	SetDlgItemText(hDlg, IDC_TXDELAY, buf);
	CheckDlgButton(hDlg, IDC_FULLDUPLEX, servant->cfg.chacc.fulldup ? BST_CHECKED : BST_UNCHECKED);
	CheckDlgButton(hDlg, IDC_PTTMUTE, servant->cfg.adapt.pttmute ? BST_CHECKED : BST_UNCHECKED);
	win32shmem_close(api);
}

static unsigned int getdlgitems(HWND hDlg)
{
	struct trxapi *api;
	const struct trxapi_description *desc;
	struct trx_thread_state *servant;
	struct trxapi_baycomusb_adapter_config cf;
        char buf[32];
	unsigned int restart, i;

	GetDlgItemText(hDlg, IDC_SERIAL, buf, sizeof(buf));
	if (!strcmp(buf, "Any"))
		buf[0] = 0;
	restart = strcmp(buf, state.serial);
	strncpy(state.serial, buf, sizeof(state.serial));
	if (!buf[0]) {
		api = win32shmem_open_byindex(0);
	} else {
		for (i = 0;; i++) {
			if (!(api = win32shmem_open_byindex(i)))
				break;
			desc = win32shmem_get_description(api);
			if (desc) {
				if (!strcmp(desc->instid, buf))
					break;
			}
			win32shmem_close(api);
		}
	}
	if (!api) {
		lprintf(10, "No modem found\n");
		return restart;
	}
	servant = api->state;
	cf = servant->cfg.adapt;
	GetDlgItemText(hDlg, IDC_MODE, buf, sizeof(buf));
	if (!strcmp(buf, "fsk"))
		cf.mode = trxapi_baycomusb_mode_fsk;
	else if (!strcmp(buf, "external"))
		cf.mode = trxapi_baycomusb_mode_external;
	else if (!strcmp(buf, "afsk"))
		cf.mode =  trxapi_baycomusb_mode_afsk;
	else if (!strcmp(buf, "audio"))
		cf.mode = trxapi_baycomusb_mode_audio;
	GetDlgItemText(hDlg, IDC_BITRATERX, buf, sizeof(buf));
	cf.bitraterx = strtoul(buf, NULL, 0);
	GetDlgItemText(hDlg, IDC_BITRATETX, buf, sizeof(buf));
	cf.bitratetx = strtoul(buf, NULL, 0);
	GetDlgItemText(hDlg, IDC_TXDELAY, buf, sizeof(buf));
	servant->cfg.chacc.txdelay = strtoul(buf, NULL, 0);
	servant->cfg.chacc.fulldup = IsDlgButtonChecked(hDlg, IDC_FULLDUPLEX) == BST_CHECKED;
	cf.pttmute = IsDlgButtonChecked(hDlg, IDC_PTTMUTE) == BST_CHECKED;
	if (checkreload_adapt(servant, &cf))
                servant->flags |= FLG_RELOADMODEM;
	servant->cfg.adapt = cf;
	servant->flags |= FLG_SAVECONFIG;
	win32shmem_close(api);
	return restart;
}

static BOOL CALLBACK EdParmDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	struct trxapi *api;
	const struct trxapi_description *desc;
        HWND hcombo;
	unsigned int restart, i, index;
        int cursel;

	switch (uMsg) {
	case WM_INITDIALOG:
                hcombo = GetDlgItem(hDlg, IDC_SERIAL);
                SendMessage(hcombo, CB_ADDSTRING, 0, (LPARAM)"Any");
		cursel = state.serial[0] ? -1 : 0;
		for (index = 0, i = 1;; index++) {
			if (!(api = win32shmem_open_byindex(index)))
				break;
			desc = win32shmem_get_description(api);
			if (desc) {
				SendMessage(hcombo, CB_ADDSTRING, 0, (LPARAM)desc->instid);
				if (!strcmp(desc->instid, state.serial))
					cursel = i;
				i++;
			}
                        win32shmem_close(api);
                }
		ComboBox_SetCurSel(hcombo, cursel);
		SetWindowText(hDlg, DRIVER_NAME" Configuration");
		setdlgitems(hDlg);
		break;
                
        case WM_COMMAND:
                switch (GET_WM_COMMAND_ID(wParam, lParam)) {
                case IDCANCEL:
                        EndDialog(hDlg, 0);
                        break;

                case IDOK:
			restart = getdlgitems(hDlg);
			EndDialog(hDlg, restart);
                        break;
                        
		case IDC_SERIAL:
			setdlgitems(hDlg);
			break;

		case IDC_FULLDUPLEX:
		case IDC_PTTMUTE:
		        if (GET_WM_COMMAND_CMD(wParam, lParam) != BN_CLICKED)
				break;
			switch (IsDlgButtonChecked(hDlg, GET_WM_COMMAND_ID(wParam, lParam))) {
			case BST_CHECKED:
				CheckDlgButton(hDlg, GET_WM_COMMAND_ID(wParam, lParam), BST_UNCHECKED);
				break;

			case BST_UNCHECKED:
				CheckDlgButton(hDlg, GET_WM_COMMAND_ID(wParam, lParam), BST_CHECKED);
				break;
			}
			break;

                default:	
                        break;
                }
                break;
	
	default:
		return FALSE;
	}
	return TRUE;
}

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

int config_device(byte max_channels, HWND hDlg, byte channel)
{
	int restart = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hDlg, EdParmDlgProc, 0);

	if (restart)
		api_close();
	return restart;
}

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

u16 l1_get_ch_cnt(void)
{
	return 1;
}

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

byte get_txdelay(byte kanal)
{
	struct trx_thread_state *servant;
	byte ret = 0;

	if (kanal)
		return 0;
	servant = api_lock();
	if (!servant)
		return 0;
	ret = servant->cfg.chacc.txdelay / 10;
	api_unlock();
	return ret;
}

void set_txdelay(byte kanal, byte delay)
{
	struct trx_thread_state *servant;

	if (kanal)
		return;
	servant = api_lock();
	if (!servant)
		return;
	servant = state.api->state;
	win32shmem_lock(state.api);
	servant->cfg.chacc.txdelay = delay * 10;
	servant->flags |= FLG_SAVECONFIG;
	win32shmem_unlock(state.api);
	api_unlock();
}

u16 get_mode(byte kanal)
{
	struct trx_thread_state *servant;
	u16 ret = state.mode & ~MODE_d;

	if (kanal)
		return 0;
	servant = api_lock();
	if (!servant)
		return ret;
	if (servant->cfg.chacc.fulldup)
		ret |= MODE_d;
	api_unlock();
	return ret;
}

u16 get_baud(byte kanal)
{
	struct trx_thread_state *servant;
	u16 ret = 0;

	if (kanal)
		return 0;
	servant = api_lock();
	if (!servant)
		return 0;
	ret = servant->cfg.adapt.bitratetx / 100;
	api_unlock();
	return ret;
}

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

byte l1_init_kanal(byte kanal, u16 chbaud, u16 chmode)
{
	state.mode = chmode;
	return 1;
}

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

byte l1_ch_active(byte kanal)
{
	return state.api != NULL;
}

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

char far *l1_ident(byte kanal)
{
	return DRIVER_NAME;
}

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

char far *l1_version(byte kanal)
{
	return DRIVER_VERSION;
}

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

L1_STATISTICS far *l1_stat(byte kanal, byte delete)
{
	L1_STATISTICS *p = &state.stat;
	
	if (delete)
		p->tx_error = p->rx_overrun = p->rx_bufferoverflow =
		p->tx_frames = p->rx_frames = p->io_error = 0;
	return p;
}

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

void set_led(byte kanal, byte ledcode)
{
        struct trx_thread_state *servant;

	if (!(servant = api_lock()))
		return;
	win32shmem_lock(state.api);
	servant->leds = ((ledcode & LED_CON) ? 1 : 0) | ((ledcode & LED_STA) ? 2 : 0);
	servant->flags |= FLG_SETLEDS;
	win32shmem_unlock(state.api);
	api_unlock();
}

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

/*
 * state and servant must be locked!
 */

static unsigned int compute_state(struct trx_thread_state *servant, unsigned int *nextrx, unsigned int *nexttx)
{
	unsigned int nrtx = 0, nrrx = 0, nxttx, nxtrx, i;
	unsigned int st = 0;

	if (servant->mode != MODE_FSK && servant->mode != MODE_EXTERNAL && servant->mode != MODE_AFSK)
		goto nopkt;
	/* find tx packet */
	for (i = 0; i < NUMTXPACKETS; i++) {
		if (servant->du.p.txurb[i].urbstate == URBSTATE_UNUSED) {
			if (!nrtx)
				nxttx = i;
			nrtx++;
			continue;
		}
		if (servant->du.p.txurb[i].urbstate == URBSTATE_FILLED) {
			nrtx = 0;
			continue;
		}
	}
	/* find rx packet */
	i = servant->du.p.rxurbptr;
	do {
		if (servant->du.p.rxurb[i].urbstate == URBSTATE_CORBASEND)
			servant->du.p.rxurb[i].urbstate = URBSTATE_UNUSED;
		/* stop at INPROGRESS urbs */
		if (servant->du.p.rxurb[i].urbstate == URBSTATE_DONE) {
			if (!nrrx)
				nxtrx = i;
			nrrx++;
		} else if (servant->du.p.rxurb[i].urbstate != URBSTATE_UNUSED)
			break;
		i = (i + 1) % NUMRXPACKETS;
	} while (i != servant->du.p.rxurbptr);
  nopkt:
	if (!nrrx)
		nxtrx = ~0;
	if (!nrtx)
		nxttx = ~0;
	if (nextrx)
		*nextrx = nxtrx;
	if (nexttx)
		*nexttx = nxttx;
	/* update status */
	if (servant->ptt)
		st |= CH_PTT;
	if (servant->dcd)
		st |= CH_DCD;
	if (nrrx > 0)
		st |= CH_RXB;
	if (!nrtx)
		st |= CH_TBY;
	return st;
}

byte l1_ch_state(byte kanal)
{
        struct trx_thread_state *servant;
	byte ret;

	if (kanal)
		return CH_DEAD;
	if (!(servant = api_lock()))
		return CH_DEAD;
	win32shmem_lock(state.api);
	ret = compute_state(servant, NULL, NULL);
	win32shmem_unlock(state.api);
	api_unlock();
	return ret;
}

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

u16 l1_scale(byte kanal)
{
	struct trx_thread_state *servant;
	unsigned long br;
	u16 ret = 0;

	if (kanal)
		return 0;
	if (!(servant = api_lock()))
		return 0;
	win32shmem_lock(state.api);
	br = servant->cfg.adapt.bitratetx;
	if (br >= 10)
		ret = 614400 / br;
	win32shmem_unlock(state.api);
	api_unlock();
	return ret;
}

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

static void CALLBACK stopcalib(HWND hwnd, UINT umsg, UINT idevent, DWORD dwtime)
{
	if (!api_lock())
		return;
	win32shmem_set_ptt(state.api, 0);
	api_unlock();
}

void l1_tx_calib(byte kanal, byte minutes)
{
	if (state.calibtimer)
		KillTimer(NULL, state.calibtimer);
	if (!api_lock())
		return;
	if (win32shmem_set_ptt(state.api, 1)) {
		state.calibtimer = SetTimer(NULL, 0, minutes * 60000, stopcalib);
		if (!state.calibtimer)
			stopcalib(NULL, 0, 0, 0);
	}
	api_unlock();
}


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

L1FRAME far *l1_get_framebuf(byte kanal)
{
	if (kanal)
		return NULL;
	return &state.txframe;
}

byte l1_tx_frame(void)
{
	struct trx_thread_state *servant;
        unsigned int i, txd;

	if (!(servant = api_lock())) {
		state.stat.io_error++;
		return 1;
	}
	win32shmem_lock(state.api);
	compute_state(servant, NULL, &i);
	if (i >= NUMTXPACKETS)
		goto errpkt;
	if (servant->mode != MODE_FSK && servant->mode != MODE_EXTERNAL && servant->mode != MODE_AFSK)
		goto errio;
	if (state.txframe.len > sizeof(servant->du.p.txurb[i].b)-2)
		goto errio;
	txd = state.txframe.txdelay;
	if (!txd)
		txd = servant->cfg.chacc.txdelay / 10;
	txd *= servant->txdmul;
        if (!txd)
                txd = 1;
        servant->du.p.txurb[i].urb.buffer_length = state.txframe.len + 2;
	memcpy(&servant->du.p.txurb[i].b.buf[2], state.txframe.frame, state.txframe.len);
	servant->du.p.txurb[i].b.buf[0] = txd;
	servant->du.p.txurb[i].b.buf[1] = txd >> 8;
        servant->du.p.txurb[i].urbstate = URBSTATE_FILLED;
	win32shmem_unlock(state.api);
	win32shmem_wakeup(state.api);
	api_unlock();
	state.stat.tx_frames++;
	return 1;

  errio:
	win32shmem_unlock(state.api);
	api_unlock();
	state.stat.io_error++;
	return 1;

  errpkt:
	win32shmem_unlock(state.api);
	api_unlock();
	return 0;
}

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

/*
 * RX-Thread. Wartet auf RX-Bytes, verarbeitet sie und returned wenn
 * Handle zu oder Paket komplett
 */

L1FRAME far *l1_rx_frame(void)
{
	struct trx_thread_state *servant;
	unsigned int i;
	L1FRAME *rxframe;

	if (!(servant = api_lock()))
		return NULL;
	while (!(state.apiflags & (APIFLG_TERMINATE|APIFLG_CLOSE))) {
		win32shmem_lock(state.api);
		compute_state(servant, &i, NULL);
		if (i < NUMRXPACKETS) {
			rxframe = &servant->du.p.rxurb[i].b.frame;
			rxframe->kanal = 0;
			rxframe->txdelay = 0;
			rxframe->len = servant->du.p.rxurb[i].urb.actual_length-2;
			servant->du.p.rxurb[i].urbstate = URBSTATE_CORBASEND;
			win32shmem_unlock(state.api);
			api_unlock();
			return rxframe;
		}
		win32shmem_unlock(state.api);
		WaitForSingleObject(state.api->hflxrxevent, 10);
	}
	api_unlock();
	return NULL;
}

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