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

/*
 *      server.c  --  Transceiver control server object implementation (UNIX only).
 *
 *      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
 *   0.2  09.03.2001  Now requiring T7F firmware V1.44
 *
 */

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

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

#define _REENTRANT

#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

#include "sysdeps.h"
#include "trxctrl.h"
#include "trx.h"

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

typedef struct trx_thread_state impl_POA_Trx_Control;

static CORBA_ORB orb;
static PortableServer_POA poa;

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

/*** Stub implementations ***/

CORBA_char *corba_strdup(const char *str)
{
	CORBA_char *s = CORBA_string_alloc(strlen(str));
	strcpy(s, str);
	return s;
}

static void raise_exception(CORBA_Environment *ev, const char *reason)
{
	Trx_TrxError *err = Trx_TrxError__alloc();
	err->reason = corba_strdup(reason);
	CORBA_exception_set(ev, CORBA_USER_EXCEPTION, ex_Trx_TrxError, err);
}

static void raise_oom_exception(CORBA_Environment *ev)
{
	raise_exception(ev, "Out of memory");
}

static void
impl_Trx_Control_GetDescription(impl_POA_Trx_Control * servant,
                                Trx_TrxDescription ** desc,
                                CORBA_Environment * ev)
{
	Trx_TrxDescription *d = Trx_TrxDescription__alloc();
	*desc = d;
	if (!d) {
		raise_oom_exception(ev);
		return;
	}
	d->manufacturer = corba_strdup("Holger Eckart/Baycom");
	d->model = corba_strdup("T7F/Baycom USB");
	d->copyright = corba_strdup("(C) 1999-2000 Holger Eckart/Baycom");
	d->instid = corba_strdup(servant->serial);
	d->rssimin = RSSI_MIN;
	d->rssimax = RSSI_MAX;
	d->rxbands._maximum = d->rxbands._length = 1;
	d->rxbands._buffer = CORBA_sequence_Trx_TrxBandDescription_allocbuf(1);
	CORBA_sequence_set_release(&d->rxbands, CORBA_TRUE);
	d->rxbands._buffer[0].low  = 430000000;
	d->rxbands._buffer[0].high = 440000000;
	d->rxbands._buffer[0].step =     12500;
	d->txbands._maximum = d->txbands._length = 1;
	d->txbands._buffer = CORBA_sequence_Trx_TrxBandDescription_allocbuf(1);
	CORBA_sequence_set_release(&d->txbands, CORBA_TRUE);
	d->txbands._buffer[0].low  = 430000000;
	d->txbands._buffer[0].high = 440000000;
	d->txbands._buffer[0].step =     12500;
}




static void
impl_Trx_Control_GetState(impl_POA_Trx_Control * servant,
                          Trx_TrxState * state, CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	switch (servant->mode) {
	case MODE_FSK:
	case MODE_AFSK:
	case MODE_EXTERNAL:
		state->mode = Trx_trxpacket;
		break;

	case MODE_AUDIO:
		state->mode = Trx_trxaudio;
		break;

	default:
		state->mode = Trx_trxunspec;
	}
	if (servant->flags & (FLG_T7FERROR | FLG_MODEMERROR)) {
		state->rxfreq = 0;
		state->txfreq = 0;
	} else {
		state->rxfreq = 430000000 + 12500*servant->rxdiv;
		state->txfreq = 430000000 + 12500*servant->txdiv;
	}
	state->ptt = servant->ptt ? CORBA_TRUE : CORBA_FALSE;
	state->manualptt = (servant->flags & FLG_MANUALPTT) ? CORBA_TRUE : CORBA_FALSE;
	state->dcd = servant->dcd ? CORBA_TRUE : CORBA_FALSE;
	state->rssi = RSSI_MIN+(servant->rssi*((RSSI_MAX-RSSI_MIN)/255.0));
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_SetPTT(impl_POA_Trx_Control * servant,
                        CORBA_boolean ptt, CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	if (ptt)
		servant->flags |= FLG_MANUALPTT;
	else
		servant->flags &= ~FLG_MANUALPTT;
	servant->flags |= FLG_SETPTT;
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_SetFrequency(impl_POA_Trx_Control * servant,
                              Trx_frequency rx,
                              Trx_frequency tx, CORBA_Environment * ev)
{
	unsigned int dr, dt;

	if (rx < 430000000)
		rx = 0;
	else
		rx -= (430000000-12500/2);
	if (tx < 430000000)
		tx = 0;
	else
		tx -= (430000000-12500/2);
	if (rx > 10000000)
		dr = 800;
	else
		dr = ((unsigned int)rx) / 12500;
	if (tx > 10000000)
		dt = 800;
	else
		dt = ((unsigned int)tx) / 12500;
	acquire_trxdata_mutex(servant);
	servant->newrxdiv = dr;
	servant->newtxdiv = dt;
	servant->flags |= FLG_SETFREQ | FLG_SAVECONFIG;
	servant->cfg.freq.rx = 430000000 + 12500 * dr;
	servant->cfg.freq.tx = 430000000 + 12500 * dt;
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_UartSend(impl_POA_Trx_Control * servant,
                          CORBA_char * str, CORBA_Environment * ev)
{
	int r;

	if (!str)
		return;
	acquire_trxdata_mutex(servant);
	r = addtxfifo(servant, str, strlen(str));
	release_trxdata_mutex(servant);
	if (r)
		raise_exception(ev, "FIFO full");
}

static void
impl_Trx_Control_UartReceive(impl_POA_Trx_Control * servant,
                             CORBA_unsigned_long * ptr,
                             CORBA_char ** str, CORBA_Environment * ev)
{
	CORBA_char *cp;
	unsigned int i;

	cp = CORBA_string_alloc(512);
	acquire_trxdata_mutex(servant);
	i = getrxfifo(servant, ptr, cp, 512-1);
	release_trxdata_mutex(servant);
	cp[i] = 0;
	*str = cp;
}

static void
impl_Trx_Control_BaycomUSBAdapterGetConfig(impl_POA_Trx_Control * servant,
                                           Trx_BaycomUSBAdapterConfig * cfg,
                                           CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	switch (servant->cfg.adapt.mode) {
	case trxapi_baycomusb_mode_external:
		cfg->mode = Trx_external;
	        break;

	case trxapi_baycomusb_mode_afsk:
		cfg->mode = Trx_afsk;
	        break;

	case trxapi_baycomusb_mode_audio:
		cfg->mode = Trx_audio;
	        break;

	default:
		cfg->mode = Trx_fsk;
	        break;
	}
	cfg->fclk = servant->cfg.adapt.fclk;
	cfg->bitraterx = servant->cfg.adapt.bitraterx;
	cfg->bitratetx = servant->cfg.adapt.bitratetx;
	switch (servant->cfg.adapt.loopback) {
	default:
		cfg->loopback = Trx_loopback_off;
		break;

	case trxapi_baycomusb_loopback_scrambler:
		cfg->loopback = Trx_loopback_scrambler;
		break;

	case trxapi_baycomusb_loopback_modem:
		cfg->loopback = Trx_loopback_modem;
		break;
	}
	cfg->pttmute = servant->cfg.adapt.pttmute;
	cfg->filtmode = servant->cfg.adapt.filtmode;
	cfg->samplerate = servant->cfg.adapt.samplerate;
	cfg->gain = servant->cfg.adapt.gain;
	strncpy(cfg->audiodevin, servant->cfg.adapt.audiodevin, sizeof(cfg->audiodevin));
	cfg->audiodevin[sizeof(cfg->audiodevin)-1] = 0;
	strncpy(cfg->audiodevout, servant->cfg.adapt.audiodevout, sizeof(cfg->audiodevout));
	cfg->audiodevout[sizeof(cfg->audiodevout)-1] = 0;
	cfg->rfsquelch = servant->cfg.adapt.rfsquelch;
	cfg->audiosquelch = servant->cfg.adapt.audiosquelch;
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_BaycomUSBAdapterSetConfig(impl_POA_Trx_Control * servant,
                                           Trx_BaycomUSBAdapterConfig * cfg,
                                           CORBA_Environment * ev)
{
	struct trxapi_baycomusb_adapter_config cf;
	unsigned char rfsq;

	acquire_trxdata_mutex(servant);
	/* clamp values */
	if (cfg->rfsquelch <= RSSI_MIN) {
		cfg->rfsquelch = RSSI_MIN;
		rfsq = 0;
	} else if (cfg->rfsquelch >= RSSI_MAX) {
		cfg->rfsquelch = RSSI_MAX;
		rfsq = 255;
	} else
		rfsq = (cfg->rfsquelch - RSSI_MIN) * (255.0/(RSSI_MAX-RSSI_MIN));
#if defined(WIN32) || defined(USERMODEDRV)
	servant->audioparam.rfsquelch = rfsq;
#endif
	/* check for significant changes requiring modem reboot */
	servant->flags |= FLG_SAVECONFIG;
	switch (cfg->mode) {
	case Trx_external:
		cf.mode = trxapi_baycomusb_mode_external;
		break;

	case Trx_afsk:
		cf.mode = trxapi_baycomusb_mode_afsk;
		break;

	case Trx_audio:
		cf.mode = trxapi_baycomusb_mode_audio;
		break;

	default:
		cf.mode = trxapi_baycomusb_mode_fsk;
		break;
	}
	cf.fclk = cfg->fclk;
	cf.bitraterx = cfg->bitraterx;
	cf.bitratetx = cfg->bitratetx;
	switch (cfg->loopback) {
	default:
		cf.loopback = trxapi_baycomusb_loopback_off;
		break;

	case Trx_loopback_scrambler:
		cf.loopback = trxapi_baycomusb_loopback_scrambler;
		break;

	case Trx_loopback_modem:
		cf.loopback = trxapi_baycomusb_loopback_modem;
		break;
	}
	cf.pttmute = cfg->pttmute;
	cf.filtmode = cfg->filtmode;
	cf.samplerate = cfg->samplerate;
	cf.gain = cfg->gain;
	strncpy(cf.audiodevin, cfg->audiodevin, sizeof(cf.audiodevin));
	cf.audiodevin[sizeof(cf.audiodevin)-1] = 0;
	strncpy(cf.audiodevout, cfg->audiodevout, sizeof(cf.audiodevout));
	cf.audiodevout[sizeof(cf.audiodevout)-1] = 0;
	cf.rfsquelch = cfg->rfsquelch;
	cf.audiosquelch = cfg->audiosquelch;
	if (checkreload_adapt(servant, &cf))
		servant->flags |= FLG_RELOADMODEM;
	servant->cfg.adapt = cf;
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_BaycomUSBAdapterGetAudioDevices(impl_POA_Trx_Control *
                                                 servant,
                                                 Trx_BaycomUSBAdapterAudioDevs
                                                 ** devs,
                                                 CORBA_Environment * ev)
{
	Trx_BaycomUSBAdapterAudioDevs *d;
	struct trxapi_baycomusb_adapter_audio_devs *ad;
	unsigned int i;

	*devs = NULL;
	ad = audio_get_device_list();
	if (!ad) {
		raise_exception(ev, "not implemented");
		return;
	}
	*devs = d = Trx_BaycomUSBAdapterAudioDevs__alloc();
	if (!d) {
		free(ad);
		raise_exception(ev, "out of memory");
		return;
	}
	d->audiodevsin._maximum = d->audiodevsin._length = ad->nraudiodevsin;
	d->audiodevsout._maximum = d->audiodevsout._length = ad->nraudiodevsout;
	d->audiodevsin._buffer = CORBA_sequence_Trx_BaycomUSBAdapterAudioDev_allocbuf(d->audiodevsin._length);
	CORBA_sequence_set_release(&d->audiodevsin, CORBA_TRUE);
	d->audiodevsout._buffer = CORBA_sequence_Trx_BaycomUSBAdapterAudioDev_allocbuf(d->audiodevsout._length);
	CORBA_sequence_set_release(&d->audiodevsout, CORBA_TRUE);
	for (i = 0; i < d->audiodevsin._length; i++)
		strncpy(d->audiodevsin._buffer[i], ad->audiodevsin[i], sizeof(d->audiodevsin._buffer[i]));
	for (i = 0; i < d->audiodevsout._length; i++)
		strncpy(d->audiodevsout._buffer[i], ad->audiodevsout[i], sizeof(d->audiodevsout._buffer[i]));
	free(ad);
}

static void
impl_Trx_Control_ModemDisconnectGetConfig(impl_POA_Trx_Control * servant,
                                          Trx_ModemDisconnectConfig * cfg,
                                          CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	cfg->output = servant->cfg.mdisc.output;
	cfg->direction = servant->cfg.mdisc.direction;
	switch (servant->cfg.mdisc.rxc) {
	default:
		cfg->rxc = Trx_mdisc_rxc_normal;
		break;

	case trxapi_modem_disconnect_rxc_rxclock:
		cfg->rxc = Trx_mdisc_rxc_rxclock;
		break;
	}
	switch (servant->cfg.mdisc.txc) {
	default:
		cfg->txc = Trx_mdisc_txc_normal;
		break;

	case trxapi_modem_disconnect_txc_txclock:
		cfg->txc = Trx_mdisc_txc_txclock;
		break;
	}
	switch (servant->cfg.mdisc.txd) {
	default:
		cfg->txd = Trx_mdisc_txd_normal;
		break;

	case trxapi_modem_disconnect_txd_txdata:
		cfg->txd = Trx_mdisc_txd_txdata;
		break;
	}
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_ModemDisconnectSetConfig(impl_POA_Trx_Control * servant,
                                          Trx_ModemDisconnectConfig * cfg,
                                          CORBA_Environment * ev)
{
	struct trxapi_modem_disconnect_config cf;

	cf.output = cfg->output;
	cf.direction = cfg->direction;
	switch (cfg->rxc) {
	default:
		cf.rxc = trxapi_modem_disconnect_rxc_normal;
		break;

	case Trx_mdisc_rxc_rxclock:
		cf.rxc = trxapi_modem_disconnect_rxc_rxclock;
		break;
	}
	switch (cfg->txc) {
	default:
		cf.txc = trxapi_modem_disconnect_txc_normal;
		break;

	case Trx_mdisc_txc_txclock:
		cf.txc = trxapi_modem_disconnect_txc_txclock;
		break;
	}
	switch (cfg->txd) {
	default:
		cf.txd = trxapi_modem_disconnect_txd_normal;
		break;

	case Trx_mdisc_txd_txdata:
		cf.txd = trxapi_modem_disconnect_txd_txdata;
		break;
	}
	acquire_trxdata_mutex(servant);
	if (checkreload_mdisc(servant, &cf))
		servant->flags |= FLG_RELOADMODEM;
	servant->cfg.mdisc = cf;
	servant->flags |= FLG_SETMDISCDIR | FLG_SETMDISCOUT;
	release_trxdata_mutex(servant);
}

#if !defined(WIN32)

static void
impl_Trx_Control_ChannelAccessGetConfig(impl_POA_Trx_Control * servant,
                                        Trx_ChannelAccessConfig * cfg,
                                        CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	cfg->txdelay = servant->cfg.chacc.txdelay;
	cfg->slottime = servant->cfg.chacc.slottime;
	cfg->ppersistence = servant->cfg.chacc.ppersistence;
	cfg->txtail = servant->cfg.chacc.txtail;
	cfg->fullduplex = servant->cfg.chacc.fullduplex;
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_ChannelAccessSetConfig(impl_POA_Trx_Control * servant,
                                        Trx_ChannelAccessConfig * cfg,
                                        CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	servant->cfg.chacc.txdelay = cfg->txdelay;
	servant->cfg.chacc.slottime = cfg->slottime;
	servant->cfg.chacc.ppersistence = cfg->ppersistence;
	servant->cfg.chacc.txtail = cfg->txtail;
	servant->cfg.chacc.fullduplex = cfg->fullduplex;
	servant->flags |= FLG_RECONFCHACC | FLG_SAVECONFIG;
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_InterfaceGetConfig(impl_POA_Trx_Control * servant,
                                    Trx_InterfaceConfig * cfg,
                                    CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	strncpy(cfg->ifname, servant->cfg.intf.ifname, sizeof(cfg->ifname));
	cfg->ifname[sizeof(cfg->ifname)-1] = 0;
	strncpy(cfg->hwaddr, servant->cfg.intf.hwaddr, sizeof(cfg->hwaddr));
	cfg->hwaddr[sizeof(cfg->hwaddr)-1] = 0;
	cfg->ipaddr[0] = servant->cfg.intf.ipaddr[0];
	cfg->ipaddr[1] = servant->cfg.intf.ipaddr[1];
	cfg->ipaddr[2] = servant->cfg.intf.ipaddr[2];
	cfg->ipaddr[3] = servant->cfg.intf.ipaddr[3];
	cfg->netmask[0] = servant->cfg.intf.netmask[0];
	cfg->netmask[1] = servant->cfg.intf.netmask[1];
	cfg->netmask[2] = servant->cfg.intf.netmask[2];
	cfg->netmask[3] = servant->cfg.intf.netmask[3];
	cfg->broadcast[0] = servant->cfg.intf.broadcast[0];
	cfg->broadcast[1] = servant->cfg.intf.broadcast[1];
	cfg->broadcast[2] = servant->cfg.intf.broadcast[2];
	cfg->broadcast[3] = servant->cfg.intf.broadcast[3];
	strncpy(cfg->gwhwaddr, servant->cfg.intf.gwhwaddr, sizeof(cfg->gwhwaddr));
	cfg->gwhwaddr[sizeof(cfg->gwhwaddr)-1] = 0;
	cfg->gwipaddr[0] = servant->cfg.intf.gwipaddr[0];
	cfg->gwipaddr[1] = servant->cfg.intf.gwipaddr[1];
	cfg->gwipaddr[2] = servant->cfg.intf.gwipaddr[2];
	cfg->gwipaddr[3] = servant->cfg.intf.gwipaddr[3];
	cfg->gwnetmask[0] = servant->cfg.intf.gwnetmask[0];
	cfg->gwnetmask[1] = servant->cfg.intf.gwnetmask[1];
	cfg->gwnetmask[2] = servant->cfg.intf.gwnetmask[2];
	cfg->gwnetmask[3] = servant->cfg.intf.gwnetmask[3];
	cfg->gwipmode = servant->cfg.intf.gwipmode;
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_InterfaceSetConfig(impl_POA_Trx_Control * servant,
                                    Trx_InterfaceConfig * cfg,
                                    CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	strncpy(servant->cfg.intf.ifname, cfg->ifname, sizeof(servant->cfg.intf.ifname));
	servant->cfg.intf.ifname[sizeof(servant->cfg.intf.ifname)-1] = 0;
	strncpy(servant->cfg.intf.hwaddr, cfg->hwaddr, sizeof(servant->cfg.intf.hwaddr));
	servant->cfg.intf.hwaddr[sizeof(servant->cfg.intf.hwaddr)-1] = 0;
	servant->cfg.intf.ipaddr[0] = cfg->ipaddr[0];
	servant->cfg.intf.ipaddr[1] = cfg->ipaddr[1];
	servant->cfg.intf.ipaddr[2] = cfg->ipaddr[2];
	servant->cfg.intf.ipaddr[3] = cfg->ipaddr[3];
	servant->cfg.intf.netmask[0] = cfg->netmask[0];
	servant->cfg.intf.netmask[1] = cfg->netmask[1];
	servant->cfg.intf.netmask[2] = cfg->netmask[2];
	servant->cfg.intf.netmask[3] = cfg->netmask[3];
	servant->cfg.intf.broadcast[0] = cfg->broadcast[0];
	servant->cfg.intf.broadcast[1] = cfg->broadcast[1];
	servant->cfg.intf.broadcast[2] = cfg->broadcast[2];
	servant->cfg.intf.broadcast[3] = cfg->broadcast[3];
	strncpy(servant->cfg.intf.gwhwaddr, cfg->gwhwaddr, sizeof(servant->cfg.intf.gwhwaddr));
	servant->cfg.intf.gwhwaddr[sizeof(servant->cfg.intf.gwhwaddr)-1] = 0;
	servant->cfg.intf.gwipaddr[0] = cfg->gwipaddr[0];
	servant->cfg.intf.gwipaddr[1] = cfg->gwipaddr[1];
	servant->cfg.intf.gwipaddr[2] = cfg->gwipaddr[2];
	servant->cfg.intf.gwipaddr[3] = cfg->gwipaddr[3];
	servant->cfg.intf.gwnetmask[0] = cfg->gwnetmask[0];
	servant->cfg.intf.gwnetmask[1] = cfg->gwnetmask[1];
	servant->cfg.intf.gwnetmask[2] = cfg->gwnetmask[2];
	servant->cfg.intf.gwnetmask[3] = cfg->gwnetmask[3];
	servant->cfg.intf.gwipmode = cfg->gwipmode;
	servant->flags |= FLG_RECONFINTF | FLG_SAVECONFIG;
	release_trxdata_mutex(servant);
}

#else

static void
impl_Trx_Control_ChannelAccessGetConfig(impl_POA_Trx_Control * servant,
                                        Trx_ChannelAccessConfig * cfg,
                                        CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

static void
impl_Trx_Control_ChannelAccessSetConfig(impl_POA_Trx_Control * servant,
                                        Trx_ChannelAccessConfig * cfg,
                                        CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

static void
impl_Trx_Control_InterfaceGetConfig(impl_POA_Trx_Control * servant,
                                    Trx_InterfaceConfig * cfg,
                                    CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

static void
impl_Trx_Control_InterfaceSetConfig(impl_POA_Trx_Control * servant,
                                    Trx_InterfaceConfig * cfg,
                                    CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

#endif

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

#if !defined(WIN32) && !defined(USERMODEDRV)

static void
impl_Trx_Control_GetAudioState(impl_POA_Trx_Control * servant,
                               Trx_TrxAudioState * state,
                               CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

static void
impl_Trx_Control_GetAudioMicSamples(impl_POA_Trx_Control * servant,
                                    CORBA_unsigned_long ptr,
                                    CORBA_unsigned_long len,
                                    Trx_TrxAudioSamples ** samples,
                                    CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

static void
impl_Trx_Control_GetAudioSpkSamples(impl_POA_Trx_Control * servant,
                                    CORBA_unsigned_long ptr,
                                    CORBA_unsigned_long len,
                                    Trx_TrxAudioSamples ** samples,
                                    CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

static void
impl_Trx_Control_SetAudioDTMF(impl_POA_Trx_Control * servant,
                              CORBA_long ch, CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

static void
impl_Trx_Control_SetAudioParams(impl_POA_Trx_Control * servant,
                                Trx_TrxAudioParams * params,
                                CORBA_Environment * ev)
{
	raise_exception(ev, "not implemented");
}

#else

static void
impl_Trx_Control_GetAudioState(impl_POA_Trx_Control * servant,
                               Trx_TrxAudioState * state,
                               CORBA_Environment * ev)
{
	acquire_trxdata_mutex(servant);
	if (servant->mode != MODE_AUDIO) {
		raise_exception(ev, "invalid mode");
		goto out;
	}
	state->samplerate = AUDIOSAMPLINGRATE;
	state->micbufsz = sizeof(servant->du.a.isrc.abuf);
	state->micbufptr = servant->du.a.isrc.aptr;
	state->spkbufsz = sizeof(servant->du.a.osrc.abuf);
	state->spkbufptr = servant->du.a.osrc.aptr;
	state->micsigpwr = servant->du.a.isrc.sigpwr;
	state->spksigpwr = servant->du.a.osrc.sigpwr;
	state->spknoisepwr = servant->du.a.osrc.noisepwr;
  out:
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_GetAudioSpkSamples(impl_POA_Trx_Control * servant,
                                    CORBA_unsigned_long ptr,
                                    CORBA_unsigned_long len,
                                    Trx_TrxAudioSamples ** samples,
                                    CORBA_Environment * ev)
{
	Trx_TrxAudioSamples *buf;
	unsigned int x;

	acquire_trxdata_mutex(servant);
	if (servant->mode != MODE_AUDIO) {
		raise_exception(ev, "invalid mode");
		goto out;
	}
	if (len > sizeof(servant->du.a.osrc.abuf) || ptr >= sizeof(servant->du.a.osrc.abuf)) {
		raise_exception(ev, "invalid parameter");
		goto out;
	}
	if (!(buf = Trx_TrxAudioSamples__alloc())) {
		raise_exception(ev, "out of memory");
		goto out;
	}
	*samples = buf;
	if (!len) {
		buf->_maximum = buf->_length = 0;
		buf->_buffer = NULL;
		CORBA_sequence_set_release(buf, CORBA_FALSE);
		goto out;
	}
	buf->_maximum = buf->_length = len;
	buf->_buffer = CORBA_sequence_CORBA_octet_allocbuf(len);
	CORBA_sequence_set_release(buf, CORBA_TRUE);
	x = sizeof(servant->du.a.osrc.abuf) - ptr;
	if (x > len)
		x = len;
       	memcpy(buf->_buffer, &servant->du.a.osrc.abuf[ptr], x);
	len -= x;
	if (len)
		memcpy(&buf->_buffer[x], servant->du.a.osrc.abuf, len);
  out:
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_GetAudioMicSamples(impl_POA_Trx_Control * servant,
                                    CORBA_unsigned_long ptr,
                                    CORBA_unsigned_long len,
                                    Trx_TrxAudioSamples ** samples,
                                    CORBA_Environment * ev)
{
	Trx_TrxAudioSamples *buf;
	unsigned int x;

	acquire_trxdata_mutex(servant);
	if (servant->mode != MODE_AUDIO) {
		raise_exception(ev, "invalid mode");
		goto out;
	}
	if (len > sizeof(servant->du.a.isrc.abuf) || ptr >= sizeof(servant->du.a.isrc.abuf)) {
		raise_exception(ev, "invalid parameter");
		goto out;
	}
	if (!(buf = Trx_TrxAudioSamples__alloc())) {
		raise_exception(ev, "out of memory");
		goto out;
	}
	*samples = buf;
	if (!len) {
		buf->_maximum = buf->_length = 0;
		buf->_buffer = NULL;
		CORBA_sequence_set_release(buf, CORBA_FALSE);
		goto out;
	}
	buf->_maximum = buf->_length = len;
	buf->_buffer = CORBA_sequence_CORBA_octet_allocbuf(len);
	CORBA_sequence_set_release(buf, CORBA_TRUE);
	x = sizeof(servant->du.a.isrc.abuf) - ptr;
	if (x > len)
		x = len;
       	memcpy(buf->_buffer, &servant->du.a.isrc.abuf[ptr], x);
	len -= x;
	if (len)
		memcpy(&buf->_buffer[x], servant->du.a.isrc.abuf, len);
  out:
	release_trxdata_mutex(servant);
}

/*
 * char:
 * -1     off
 * 0..15  DTMF
 * 16     1750
 */

/*
 *
 * DTMF frequencies
 *
 *      1209 1336 1477 1633
 *  697   1    2    3    A
 *  770   4    5    6    B
 *  852   7    8    9    C
 *  941   *    0    #    D
 * 
 */

static void
impl_Trx_Control_SetAudioDTMF(impl_POA_Trx_Control * servant,
                              CORBA_long ch, CORBA_Environment * ev)
{
	static const unsigned char dtmftransl[0x10] = {
		0x13, 0x00, 0x10, 0x20, 0x01, 0x11, 0x21, 0x02,
		0x12, 0x22, 0x30, 0x31, 0x32, 0x33, 0x03, 0x23
	};
	static const unsigned int freqlogrp[4] = { 697, 770, 852, 941 };
	static const unsigned int freqhigrp[4] = { 1209, 1336, 1477, 1633 };
	unsigned int sr = servant->du.a.p.srateusb, freq0 = 0, freq1 = 0, tr;

	acquire_trxdata_mutex(servant);
	if (servant->mode != MODE_AUDIO) {
		raise_exception(ev, "invalid mode");
		goto out;
	}
	if (ch == 16) {
		if (sr > 0)
			freq0 = (1750*65536 + sr/2) / sr;
	} else if (ch >= 0 && ch <= 15) {
		tr = dtmftransl[ch & 15];
		freq0 = (freqlogrp[tr & 15]*65536+sr/2)/sr;
		freq1 = (freqhigrp[tr >> 4]*65536+sr/2)/sr;
	} else if (ch != -1) {
		raise_exception(ev, "invalid parameter");
		goto out;
	}
	servant->du.a.isrc.freq0 = freq0;
	servant->du.a.isrc.freq1 = freq1;
	servant->du.a.isrc.ph0 = 0;
	servant->du.a.isrc.ph1 = 0;
  out:
	release_trxdata_mutex(servant);
}

static void
impl_Trx_Control_SetAudioParams(impl_POA_Trx_Control * servant,
                                Trx_TrxAudioParams * params,
                                CORBA_Environment * ev)
{
	if (params->rfsquelch <= RSSI_MIN)
		servant->audioparam.rfsquelch = 0;
	else if (params->rfsquelch >= RSSI_MAX)
		servant->audioparam.rfsquelch = 255;
	else
		servant->audioparam.rfsquelch = (params->rfsquelch - RSSI_MIN) * (255.0/(RSSI_MAX-RSSI_MIN));
	servant->audioparam.audiosquelch = params->audiosquelch;
}

#endif

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

/*** epv structures ***/

static PortableServer_ServantBase__epv impl_Trx_Control_base_epv = {
	NULL,                        /* _private data */
	NULL,                        /* finalize routine */
	NULL,                        /* default_POA routine */
};

static POA_Trx_Control__epv impl_Trx_Control_epv = {
	NULL,                        /* _private */
        (gpointer) & impl_Trx_Control_GetDescription,
        (gpointer) & impl_Trx_Control_GetState,
        (gpointer) & impl_Trx_Control_SetPTT,
        (gpointer) & impl_Trx_Control_SetFrequency,
        (gpointer) & impl_Trx_Control_UartSend,
        (gpointer) & impl_Trx_Control_UartReceive,
	(gpointer) & impl_Trx_Control_BaycomUSBAdapterGetConfig,
        (gpointer) & impl_Trx_Control_BaycomUSBAdapterSetConfig,
	(gpointer) & impl_Trx_Control_BaycomUSBAdapterGetAudioDevices,
        (gpointer) & impl_Trx_Control_ChannelAccessGetConfig,
        (gpointer) & impl_Trx_Control_ChannelAccessSetConfig,
        (gpointer) & impl_Trx_Control_InterfaceGetConfig,
        (gpointer) & impl_Trx_Control_InterfaceSetConfig,
        (gpointer) & impl_Trx_Control_ModemDisconnectGetConfig,
        (gpointer) & impl_Trx_Control_ModemDisconnectSetConfig,
	(gpointer) & impl_Trx_Control_GetAudioState,
	(gpointer) & impl_Trx_Control_GetAudioMicSamples,
	(gpointer) & impl_Trx_Control_GetAudioSpkSamples,
	(gpointer) & impl_Trx_Control_SetAudioDTMF,
	(gpointer) & impl_Trx_Control_SetAudioParams,
};

/*** vepv structures ***/

static POA_Trx_Control__vepv impl_Trx_Control_vepv = {
	&impl_Trx_Control_base_epv,
	&impl_Trx_Control_epv,
};

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

#if defined(WIN32)

static int deletekeyx(HKEY key)
{
        char name[128];
        LONG err;
        DWORD len;
        HKEY key2;
        int ret = 0;
        
        for (;;) {
                len = sizeof(name);
                if ((RegEnumKeyEx(key, 0, name, &len, NULL, NULL, NULL, NULL)) != ERROR_SUCCESS)
                        return ret;
                if ((err = RegOpenKeyEx(key, name, 0, KEY_ALL_ACCESS, &key2)) != ERROR_SUCCESS) {
                        lprintf(1, "RegOpenKeyEx(%s) returned 0x%lx\n", name, err);
                        return -1;
                }
                ret |= deletekeyx(key2);
                RegCloseKey(key2);
                RegDeleteKey(key, name);
        }
        return ret;
}

static int deletekey(const char *name)
{
        HKEY key;
        int ret;
        DWORD err;
        
        if ((err = RegOpenKeyEx(IORREGHKEY, name, 0, KEY_ALL_ACCESS, &key)) != ERROR_SUCCESS) {
                lprintf(1, "RegOpenKeyEx(%s) returned 0x%lx\n", name, err);
                return -1;
        }
        ret = deletekeyx(key);
        RegCloseKey(key);
        RegDeleteKey(IORREGHKEY, name);
        return ret;
}

int publishior(struct trx_thread_state *state)
{
        HKEY key, key2;
        DWORD err;


	snprintf(state->iorrname, sizeof(state->iorrname), "%08X", g_str_hash(state->ior));
        if ((err = RegCreateKeyEx(IORREGHKEY, IORREGPATH, 0, "", 0, KEY_ALL_ACCESS, NULL, &key, NULL)) != ERROR_SUCCESS) {
                lprintf(1, "RegCreateKeyEx(" IORREGPATH ") returned 0x%lx\n", err);
                return -1;
        }
	if ((err = RegCreateKeyEx(key, state->iorrname, 0, "", REG_OPTION_VOLATILE, KEY_WRITE, NULL, &key2, NULL)) != ERROR_SUCCESS) {
                lprintf(1, "RegCreateKeyEx(%s) returned 0x%lx\n", state->ior, err);
                return -1;
        }
        err = RegSetValueEx(key2, "IOR", 0, REG_SZ, state->ior, strlen(state->ior)+1);
        RegCloseKey(key2);
        RegCloseKey(key);
        if (err != ERROR_SUCCESS) {
                lprintf(1, "RegSetValueEx(IOR,%s) returned 0x%lx\n", state->ior, err);
                return -1;
        }
	return 0;
}

int unpublishior(struct trx_thread_state *state)
{
        char name[256];

	snprintf(name, sizeof(name), "%s\\%s", IORREGPATH, state->iorrname);
	deletekey(name);
	return 0;
}

#else

int publishior(struct trx_thread_state *state)
{
	int fd;
	FILE *f;

	strncpy(state->iorfname, IORIDPATH "/trx-XXXXXX", sizeof(state->iorfname));
	fd = mkstemp(state->iorfname);
	if (fd == -1) {
		state->iorfname[0] = 0;
		lprintf(1, "publish IOR %s failed %s (%u)\n", state->ior, strerror(errno), errno);
		return -1;
	}
	fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
	if (!(f = fdopen(fd, "w"))) {
		lprintf(1, "publish IOR %s failed %s (%u)\n", state->ior, strerror(errno), errno);
		close(fd);
		return -1;
	}
	fprintf(f, "%s\n", state->ior);
	fclose(f);
	return 0;
}

int unpublishior(struct trx_thread_state *state)
{
	if (unlink(state->iorfname) == -1) {
		lprintf(1, "unpublish IOR %s failed %s (%u)\n", state->ior, strerror(errno), errno);
		return -1;
	}
	return 0;
}

#endif

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

void init_object(struct trx_thread_state *state)
{
	Trx_Control obj;
	PortableServer_ObjectId *objid;
	CORBA_Environment ev;
	char *str;

	state->servant.vepv = &impl_Trx_Control_vepv;
	state->poa = poa;
	acquire_orbit_mutex();
	CORBA_exception_init(&ev);
	POA_Trx_Control__init((PortableServer_Servant)state, &ev);
	objid = PortableServer_POA_activate_object(poa, state, &ev);
	CORBA_free(objid);
	obj = PortableServer_POA_servant_to_reference(poa, state, &ev);
	str = CORBA_ORB_object_to_string(orb, obj, &ev);
	strncpy(state->ior, str, sizeof(state->ior));
	CORBA_free(str);
	lprintf(5, "New object: %s\n", state->ior);
	publishior(state);
	CORBA_Object_release(obj, &ev);
	if (ev._major != CORBA_NO_EXCEPTION)
		lprintf(1, "Error during object initialization\n");
	release_orbit_mutex();
}

void destroy_object(struct trx_thread_state *state)
{
	PortableServer_ObjectId *objid;
	CORBA_Environment ev;

	acquire_orbit_mutex();
	CORBA_exception_init(&ev);
	lprintf(5, "Destroying object: %s\n", state->ior);
	unpublishior(state);
	objid = PortableServer_POA_servant_to_id(state->poa, state, &ev);
	PortableServer_POA_deactivate_object(state->poa, objid, &ev);
	CORBA_free(objid);
	POA_Trx_Control__fini((PortableServer_Servant)state, &ev);
	if (ev._major != CORBA_NO_EXCEPTION)
		lprintf(1, "Error during object finalization\n");
	release_orbit_mutex();

}

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

int server_init1(int *argc, char **argv)
{
	CORBA_Environment ev;
	PortableServer_POAManager poamgr;

	/* init CORBA exception handling */
	CORBA_exception_init(&ev);
	/* normal ORB init */
	orb = CORBA_ORB_init(argc, argv, "orbit-local-orb", &ev);
	if (ev._major != CORBA_NO_EXCEPTION) {
		lprintf(0, "cannot initialize CORBA ORB\n");
		return -1;
	}
	poa = (PortableServer_POA)CORBA_ORB_resolve_initial_references(orb, "RootPOA", &ev);
	if (ev._major != CORBA_NO_EXCEPTION) {
		lprintf(0, "cannot resolve initial CORBA references\n");
		return -1;
	}
	poamgr = PortableServer_POA__get_the_POAManager(poa, &ev);
	if (ev._major != CORBA_NO_EXCEPTION) {
		lprintf(0, "cannot get the CORBA POA manager\n");
		return -1;
	}
	PortableServer_POAManager_activate(poamgr, &ev);
	if (ev._major != CORBA_NO_EXCEPTION) {
		lprintf(0, "cannot activate CORBA POA manager\n");
		return -1;
	}
       	return 0;
}

int server_init2(void)
{
	CORBA_Environment ev;
	
#if defined(WIN32)
	deletekey(IORREGPATH);
#endif
	CORBA_exception_init(&ev);
        acquire_orbit_mutex();
        ORBit_custom_run_setup(orb, &ev);
        release_orbit_mutex();
	if (ev._major == CORBA_NO_EXCEPTION)
		return 0;
	CORBA_exception_free(&ev);
	return 1;
}
