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

/*
 *      win32client.c  --  Transceiver control client (Win32 shmem 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
 *
 */

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

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

#include "trxapi.h"

#include <sys/types.h>
#include <stdio.h>

#if defined(WIN32)
#include <windows.h>
#else
#include <dirent.h>
#endif

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

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

#define VERBOSE

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

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

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

#if defined(WIN32)

#if 0
static int start_service(void)
{
        int ret = -1;
        SC_HANDLE hscm, hserv;
        DWORD err;
        char *args[5] = { "-S", "-v", "9", "-s", "-ORBBindAddr=127.0.0.1" };
        
        hscm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
        if (!hscm) {
                err = GetLastError();
                if (err != ERROR_CALL_NOT_IMPLEMENTED)
                        lprintf(0, "Cannot open SC manager, error %lu\n", err);
                return -1;
        }
        hserv = OpenService(hscm, SERVICENAME, SERVICE_ALL_ACCESS);
        if (!hserv) {
                err = GetLastError();
                lprintf(0, "Cannot open service, error %lu\n", err);
                goto closescm;
        }
        if (!StartService(hserv, 5, args)) {
                err = GetLastError();
                if (err != ERROR_SERVICE_ALREADY_RUNNING)
                        lprintf(0, "Cannot start service, error %lu\n", err);
                goto closeserv;
        }
        lprintf(0, "Service %s started successfully\n", SERVICENAME);
        ret = 0;

  closeserv:
        if (!CloseServiceHandle(hserv))
                lprintf(0, "Cannot close service handle, error %lu\n", GetLastError());
  closescm:
        if (!CloseServiceHandle(hscm))
                lprintf(0, "Cannot close service manager handle, error %lu\n", GetLastError());
        return ret;
}
#endif

#if 0
static void print_shmem(void)
{
	unsigned int i;
	HANDLE hshmem, hglobalshmem, hprocess;
	struct win32_global_state *globalstate;
	struct trx_thread_state *state;

	hglobalshmem = OpenFileMapping(FILE_MAP_READ, FALSE, "baycomusb");
	if (!hglobalshmem) {
		lprintf(0, "Cannot open baycomusb file mapping (error %lu)\n", GetLastError());
		return;
	}
	globalstate = MapViewOfFile(hglobalshmem, FILE_MAP_READ, 0, 0, 0);
	CloseHandle(hglobalshmem);
	if (!globalstate) {
		lprintf(0, "Cannot map shared memory segment (error %lu)\n", GetLastError());
		return;
	}
	if (strncmp(globalstate->signature, BAYUSBSIGNATURE, sizeof(globalstate->signature))) {
		lprintf(0, "Shared Memory Segment has invalid Signature %16s\n", globalstate->signature);
		goto err1;
	}
	hprocess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, globalstate->processid);
	if (!hprocess) {
		lprintf(0, "Cannot open server process %u (error %lu)\n", 
			globalstate->processid, GetLastError());
		goto err1;
	}
	printf("Modems:\n");
	for (i = 0; i < MAXUSBMODEMS; i++) {
		if (!globalstate->modem[i].hshmem)
			continue;
		if (!DuplicateHandle(hprocess, globalstate->modem[i].hshmem,
				     GetCurrentProcess(), &hshmem,
				     FILE_MAP_WRITE, FALSE, 0)) {
			lprintf(0, "Cannot duplicate handle (error %lu)\n", GetLastError());
			continue;
		}
		state = MapViewOfFile(hshmem, FILE_MAP_READ, 0, 0, 0);
		CloseHandle(hshmem);
		if (!state) {
			lprintf(0, "Cannot map shared memory segment (error %lu)\n", GetLastError());
			continue;
		}
		printf("dev %2u:  mode %u, serial %s\n", i, state->mode, state->serial);


		UnmapViewOfFile(state);
	}

  err2:
	CloseHandle(hprocess);
  err1:
	UnmapViewOfFile(globalstate);
}
/* MUTEX_ALL_ACCESS, EVENT_ALL_ACCESS, EVENT_MODIFY_STATE */
#endif

#if 0
void win32shmem_runserver(void)
{
        char windir[512], name[512], cmdname[512], cmdline[512], *cp;
        STARTUPINFO sti;
        PROCESS_INFORMATION pi;
	HANDLE hglobalshmem;

	hglobalshmem = OpenFileMapping(FILE_MAP_READ, FALSE, "baycomusb");
	if (hglobalshmem) {
		CloseHandle(hglobalshmem);
		return;
	}
	if (!GetWindowsDirectory(windir, sizeof(windir))) {
                lprintf(0, "Cannot get windows directory path\n");
                return;
        }
        if (!GetModuleFileName(GetModuleHandle(NULL), name, sizeof(name))) {
                lprintf(0, "Cannot determine executable name\n");
                return;
        }
        if ((cp = strrchr(name, '\\')))
                *cp = 0;
        else
                name[0] = 0;
        snprintf(cmdname, sizeof(cmdname), "%s\\rundll32.exe", windir);
	/*
	 * Database calls (getXbyY) seem to hang for a very looooong time under
	 * w9x if DNS is not or misconfigured. So we set the binding address to
	 * 127.0.0.1, which only accepts connections from 127.0.0.1
	 * and does no database calls
	 */
        snprintf(cmdline, sizeof(cmdline), "%s %s\\bayusb.dll,server_entry_cmdline -v 9 -s" /*" -ORBBindAddr=127.0.0.1"*/, cmdname, name);
        GetStartupInfo(&sti);
        if (!CreateProcess(cmdname, cmdline, NULL, NULL, FALSE,
                           DETACHED_PROCESS | CREATE_DEFAULT_ERROR_MODE | HIGH_PRIORITY_CLASS,
                           NULL, NULL, &sti, &pi)) {
                lprintf(0, "Cannot start command %s, Error %lu\n", cmdname, GetLastError());
        } else
		Sleep(2000);
}
#else
void win32shmem_runserver(void)
{
        char name[512], cmdname[512], cmdline[512], *cp;
        STARTUPINFO sti;
        PROCESS_INFORMATION pi;
	HANDLE hglobalshmem;

	hglobalshmem = OpenFileMapping(FILE_MAP_READ, FALSE, "baycomusb");
	if (hglobalshmem) {
		CloseHandle(hglobalshmem);
		return;
	}
        if (!GetModuleFileName(GetModuleHandle(NULL), name, sizeof(name))) {
                lprintf(0, "Cannot determine executable name\n");
                return;
        }
        if ((cp = strrchr(name, '\\')))
                *cp = 0;
        else
                name[0] = 0;
        snprintf(cmdname, sizeof(cmdname), "%s\\baycomusbserv.exe", name);
	/*
	 * Database calls (getXbyY) seem to hang for a very looooong time under
	 * w9x if DNS is not or misconfigured. So we set the binding address to
	 * 127.0.0.1, which only accepts connections from 127.0.0.1
	 * and does no database calls
	 */
        snprintf(cmdline, sizeof(cmdline), "%s -v 9 -s" /*" -ORBBindAddr=127.0.0.1"*/, cmdname);
        GetStartupInfo(&sti);
        if (!CreateProcess(cmdname, cmdline, NULL, NULL, FALSE,
                           DETACHED_PROCESS | CREATE_DEFAULT_ERROR_MODE | HIGH_PRIORITY_CLASS,
                           NULL, NULL, &sti, &pi)) {
                lprintf(0, "Cannot start command %s, Error %lu\n", cmdname, GetLastError());
        } else
		Sleep(2000);
}
#endif


#endif

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

struct trxapi *win32shmem_open_byindex(unsigned int index)
{
	struct trxapi_description *d;
	struct trxapi *api;
	unsigned int i;
	HANDLE hshmem, hglobalshmem, hprocess, hmutex, hflxtxevent, hflxrxevent;
	struct win32_global_state *globalstate;
	struct trx_thread_state *state;
	static int audioglobalinit = 1;

	win32shmem_runserver();
	/* kludge!! */
	if (audioglobalinit) {
	  audioglobalinit = 0;
	  audio_globalinit();
	}
	hglobalshmem = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "baycomusb");
	if (!hglobalshmem) {
		verbose(lprintf(1, "Cannot open baycomusb file mapping (error %lu)\n", GetLastError()));
		return NULL;
	}
	globalstate = MapViewOfFile(hglobalshmem, FILE_MAP_ALL_ACCESS, 0, 0, 0);
	CloseHandle(hglobalshmem);
	if (!globalstate) {
		verbose(lprintf(1, "Cannot map shared memory segment (error %lu)\n", GetLastError()));
		return NULL;
	}
	if (strncmp(globalstate->signature, BAYUSBSIGNATURE, sizeof(globalstate->signature))) {
		verbose(lprintf(1, "Shared Memory Segment has invalid Signature %16s\n", globalstate->signature));
		goto err1;
	}
	hprocess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, globalstate->processid);
	if (!hprocess) {
		verbose(lprintf(1, "Cannot open server process %u (error %lu)\n", 
			globalstate->processid, GetLastError()));
		goto err1;
	}
	for (i = 0; i < MAXUSBMODEMS; i++) {
		if (!globalstate->modem[i].hshmem)
			continue;
		if (index) {
			index--;
			continue;
		}
		if (!DuplicateHandle(hprocess, globalstate->modem[i].hshmem,
				     GetCurrentProcess(), &hshmem,
				     FILE_MAP_ALL_ACCESS, FALSE, 0)) {
			verbose(lprintf(1, "Cannot duplicate shmem handle (error %lu)\n", GetLastError()));
			continue;
		}
		state = MapViewOfFile(hshmem, FILE_MAP_ALL_ACCESS, 0, 0, 0);
		CloseHandle(hshmem);
		if (!state) {
			verbose(lprintf(1, "Cannot map shared memory segment (error %lu)\n", GetLastError()));
			continue;
		}
		if (!DuplicateHandle(hprocess, state->hmutex,
				     GetCurrentProcess(), &hmutex,
				     MUTEX_ALL_ACCESS, FALSE, 0)) {
			verbose(lprintf(1, "Cannot duplicate mutex handle (error %lu)\n", GetLastError()));
			UnmapViewOfFile(state);
			continue;
		}
		if (!DuplicateHandle(hprocess, state->hflxtxevent,
				     GetCurrentProcess(), &hflxtxevent,
				     EVENT_MODIFY_STATE, FALSE, 0)) {
			verbose(lprintf(1, "Cannot duplicate tx event handle (error %lu)\n", GetLastError()));
			UnmapViewOfFile(state);
			CloseHandle(hmutex);
			continue;
		}
		if (!DuplicateHandle(hprocess, state->hflxrxevent,
				     GetCurrentProcess(), &hflxrxevent,
				     EVENT_ALL_ACCESS, FALSE, 0)) {
			verbose(lprintf(1, "Cannot duplicate rx event handle (error %lu)\n", GetLastError()));
			UnmapViewOfFile(state);
			CloseHandle(hmutex);
			CloseHandle(hflxtxevent);
			continue;
		}
		/* allocate new device */
		if (!(api = calloc(1, sizeof(struct trxapi))))
			goto err3;
		api->state = state;
		api->hmutex = hmutex;
		api->hflxtxevent = hflxtxevent;
		api->hflxrxevent = hflxrxevent;
		/* retrieve description */
		if (!(d = calloc(1, sizeof(struct trxapi_description) + 
				 (1 + 1) * sizeof(struct trxapi_banddescription))))
			goto errdesc;
		api->desc = d;
		d->rxbands = (struct trxapi_banddescription *)(d + 1);
		d->txbands = &d->rxbands[1];
		strncpy(d->manufacturer, "Holger Eckart/Baycom", sizeof(d->manufacturer));
		d->manufacturer[sizeof(d->manufacturer)-1] = 0;
		strncpy(d->model, "T7F/Baycom USB", sizeof(d->model));
		d->model[sizeof(d->model)-1] = 0;
		strncpy(d->copyright, "(C) 1999-2000 Holger Eckart/Baycom", sizeof(d->copyright));
		d->copyright[sizeof(d->copyright)-1] = 0;
		strncpy(d->instid, state->serial, sizeof(d->instid));
		d->instid[sizeof(d->instid)-1] = 0;
		d->nrrxbands = 1;
		d->nrtxbands = 1;
		d->rssimin = RSSI_MIN;
		d->rssimax = RSSI_MAX;
		for (i = 0; i < d->nrrxbands; i++) {
			d->rxbands[i].low = 430000000;
			d->rxbands[i].high = 440000000;
			d->rxbands[i].step = 12500;
		}
		for (i = 0; i < d->nrtxbands; i++) {
			d->txbands[i].low = 430000000;
			d->txbands[i].high = 440000000;
			d->txbands[i].step = 12500;
		}
	  errdesc:
		/* retrieve audio devs */
		api->audiodevs = audio_get_device_list();
		return api;
	}

  err2:
	CloseHandle(hprocess);
  err1:
	UnmapViewOfFile(globalstate);
	return NULL;

  err3:
	UnmapViewOfFile(state);
	CloseHandle(hmutex);
	CloseHandle(hflxtxevent);
	CloseHandle(hflxrxevent);
	goto err2;
}
/* MUTEX_ALL_ACCESS, EVENT_ALL_ACCESS, EVENT_MODIFY_STATE */

void win32shmem_close(struct trxapi *api)
{
	if (!api)
		return;
	if (api->desc)
		free(api->desc);
	if (api->audiodevs)
		free(api->audiodevs);
	UnmapViewOfFile(api->state);
	CloseHandle(api->hmutex);
	CloseHandle(api->hflxtxevent);
	CloseHandle(api->hflxrxevent);
	free(api);
}

void win32shmem_lock(struct trxapi *api)
{
	if (!api)
		return;
	WaitForSingleObject(api->hmutex, INFINITE);
}

void win32shmem_unlock(struct trxapi *api)
{
	if (!api)
		return;
	ReleaseMutex(api->hmutex);
}

void win32shmem_wakeup(struct trxapi *api)
{
	if (!api)
		return;
	SetEvent(api->hflxtxevent);
}

int win32shmem_init(int *argc, char **argv)
{
	return 0;
}

const struct trxapi_description *win32shmem_get_description(struct trxapi *api)
{
	if (!api)
		return NULL;
	return api->desc;
}

int win32shmem_get_state(struct trxapi *api, struct trxapi_state *state)
{
	struct trx_thread_state *servant;

	if (!state || !api)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
	win32shmem_lock(api);
        switch (servant->mode) {
        case MODE_FSK:
        case MODE_AFSK:
        case MODE_EXTERNAL:
                state->mode = trxapi_mode_packet;
                break;
 
        case MODE_AUDIO:
                state->mode = trxapi_mode_audio;
                break;
 
        default:
                state->mode = trxapi_mode_unspec;
        }
        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 ? 1 : 0;
        state->manualptt = (servant->flags & FLG_MANUALPTT) ? 1 : 0;
        state->dcd = servant->dcd ? 1 : 0;
        state->rssi = RSSI_MIN+(servant->rssi*((RSSI_MAX-RSSI_MIN)/255.0));
	win32shmem_unlock(api);
	return 0;
}

int win32shmem_set_ptt(struct trxapi *api, unsigned int ptt)
{
	struct trx_thread_state *servant;
	
	if (!api)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
	win32shmem_lock(api);
        if (ptt)
                servant->flags |= FLG_MANUALPTT;
        else
                servant->flags &= ~FLG_MANUALPTT;
        servant->flags |= FLG_SETPTT;
	win32shmem_unlock(api);
	return 0;
}

int win32shmem_set_frequency(struct trxapi *api, trxapi_frequency_t rx, trxapi_frequency_t tx)
{
	struct trx_thread_state *servant;
        unsigned int dr, dt;
 	
	if (!api)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
        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;
	win32shmem_lock(api);
        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;
	win32shmem_unlock(api);
	return 0;
}

/* Raw UART */
int win32shmem_uart_send(struct trxapi *api, const char *str)
{
	struct trx_thread_state *servant;
        int r;

	if (!api || !str)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
	win32shmem_lock(api);
        r = addtxfifo(servant, str, strlen(str));
	win32shmem_unlock(api);
	if (r)
		return -1;
	return 0;
}

int win32shmem_uart_receive(struct trxapi *api, unsigned long *ptr, char *str, unsigned int strsz)
{
	struct trx_thread_state *servant;
	unsigned int i, p;
                                                        	
	if (!api || !str || strsz < 2 || !ptr)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
	win32shmem_lock(api);
	p = *ptr;
        i = getrxfifo(servant, &p, str, strsz-1);
	*ptr = p;
	win32shmem_unlock(api);
	str[i] = 0;
	return 0;
}

/* Configuration stuff */
int win32shmem_baycomusb_adapter_get_config(struct trxapi *api, struct trxapi_baycomusb_adapter_config *cfg)
{
	struct trx_thread_state *servant;

	if (!api || !cfg)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
	win32shmem_lock(api);
	*cfg = servant->cfg.adapt;
	win32shmem_unlock(api);
	return 0;
}

int win32shmem_baycomusb_adapter_set_config(struct trxapi *api, const struct trxapi_baycomusb_adapter_config *cfg)
{
	struct trx_thread_state *servant;
	unsigned char rfsq;

	if (!api || !cfg)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
	win32shmem_lock(api);
	if (checkreload_adapt(servant, cfg))
		servant->flags |= FLG_RELOADMODEM;	
	servant->cfg.adapt = *cfg;
        /* clamp values */
        if (servant->cfg.adapt.rfsquelch <= RSSI_MIN) {
                servant->cfg.adapt.rfsquelch = RSSI_MIN;
                rfsq = 0;
        } else if (servant->cfg.adapt.rfsquelch >= RSSI_MAX) {
                servant->cfg.adapt.rfsquelch = RSSI_MAX;
                rfsq = 255;
        } else
                rfsq = (servant->cfg.adapt.rfsquelch - RSSI_MIN) * (255.0/(RSSI_MAX-RSSI_MIN));
#if defined(WIN32) || defined(USERMODEDRV)
        servant->audioparam.rfsquelch = rfsq;
#endif
	servant->flags |= FLG_SAVECONFIG;
	win32shmem_unlock(api);
	return 0;
}

const struct trxapi_baycomusb_adapter_audio_devs *win32shmem_baycomusb_adapter_get_audiodevices(struct trxapi *api)
{
	if (!api)
		return NULL;
	return api->audiodevs;
}


int win32shmem_channel_access_get_config(struct trxapi *api, struct trxapi_channel_access_config *cfg)
{
	return -1;
}

int win32shmem_channel_access_set_config(struct trxapi *api, const struct trxapi_channel_access_config *cfg)
{
	return -1;
}


int win32shmem_interface_get_config(struct trxapi *api, struct trxapi_interface_config *cfg)
{
	return -1;
}

int win32shmem_interface_set_config(struct trxapi *api, const struct trxapi_interface_config *cfg)
{
	return -1;
}

int win32shmem_modem_disconnect_get_config(struct trxapi *api, struct trxapi_modem_disconnect_config *cfg)
{
	struct trx_thread_state *servant;

	if (!api || !cfg)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
	win32shmem_lock(api);
	*cfg = servant->cfg.mdisc;
	win32shmem_unlock(api);
	return 0;
}

int win32shmem_modem_disconnect_set_config(struct trxapi *api, const struct trxapi_modem_disconnect_config *cfg)
{
	struct trx_thread_state *servant;

	if (!api || !cfg)
		return -1;
	servant = api->state;
	if (servant->mode == MODE_DEAD)
		return -1;
	win32shmem_lock(api);
	if (checkreload_mdisc(servant, cfg))
		servant->flags |= FLG_RELOADMODEM;
	servant->cfg.mdisc = *cfg;
	servant->flags |= FLG_SAVECONFIG;
	win32shmem_unlock(api);
	return 0;
}

/* Audio Stuff */

int win32shmem_get_audio_state(struct trxapi *api, struct trxapi_audio_state *state)
{
	struct trx_thread_state *servant;
	
	if (!api || !state)
		return -1;
	servant = api->state;
	if (servant->mode != MODE_AUDIO)
		return -1;
	win32shmem_lock(api);
        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;
	win32shmem_unlock(api);
	return 0;
}

int win32shmem_get_audio_mic_samples(struct trxapi *api, unsigned long ptr, unsigned long len, unsigned char *samples)
{
	struct trx_thread_state *servant;
	unsigned int x;

	if (!api || !samples || len <= 0)
		return -1;
	servant = api->state;
	if (servant->mode != MODE_AUDIO)
		return -1;
	win32shmem_lock(api);
        if (len > sizeof(servant->du.a.isrc.abuf) || ptr >= sizeof(servant->du.a.isrc.abuf))
		goto err;
        x = sizeof(servant->du.a.isrc.abuf) - ptr;
        if (x > len)
                x = len;
        memcpy(samples, &servant->du.a.isrc.abuf[ptr], x);
        len -= x;
        if (len)
                memcpy(&samples[x], servant->du.a.isrc.abuf, len);
	win32shmem_unlock(api);
	return 0;

  err:
	win32shmem_unlock(api);
	return -1;
}

int win32shmem_get_audio_spk_samples(struct trxapi *api, unsigned long ptr, unsigned long len, unsigned char *samples)
{
	struct trx_thread_state *servant;
	unsigned int x;
	
	if (!api || !samples || len <= 0)
		return -1;
	servant = api->state;
	if (servant->mode != MODE_AUDIO)
		return -1;
	win32shmem_lock(api);
        if (len > sizeof(servant->du.a.osrc.abuf) || ptr >= sizeof(servant->du.a.osrc.abuf))
		goto err;
        x = sizeof(servant->du.a.osrc.abuf) - ptr;
        if (x > len)
                x = len;
        memcpy(samples, &servant->du.a.osrc.abuf[ptr], x);
        len -= x;
        if (len)
                memcpy(&samples[x], servant->du.a.osrc.abuf, len);
	win32shmem_unlock(api);
	return 0;

  err:
	win32shmem_unlock(api);
	return -1;
}

int win32shmem_set_audio_dtmf(struct trxapi *api, long ch)
{
	struct trx_thread_state *servant;
        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, freq0 = 0, freq1 = 0, tr;

	if (!api)
		return -1;
	servant = api->state;
	if (servant->mode != MODE_AUDIO)
		return -1;
	sr = servant->du.a.p.srateusb;
	win32shmem_lock(api);
        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)
		goto err;
        servant->du.a.isrc.freq0 = freq0;
        servant->du.a.isrc.freq1 = freq1;
        servant->du.a.isrc.ph0 = 0;
        servant->du.a.isrc.ph1 = 0;
	win32shmem_unlock(api);
	return 0;

  err:
	win32shmem_unlock(api);
	return -1;
}

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