/*
 *  Hamlib Kenwood backend - TS2000 description
 *  Copyright (c) 2000-2002 by Stephane Fillod
 *
 *		$Id: ts2k.c,v 1.5 2002-06-30 10:17:03 dedmons Exp $
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library 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 Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
 * This code is has been substatiantially altered from the original
 * author's.  Also, many new functions have been added and are
 * (C) Copyrighted 2002 by Dale E. Edmons (KD7ENI).  The license
 * is unchanged, and fitness disclaimers still apply.
 */

/*
 * Copied kenwood.c to end of this file.  When I change a function for the
 * ts2000 I will already have the kenwood version with the function prefix
 * changed to ts2k.  Unique functions will be pointed to in this file,
 * whereas the unmodified version will be in kenwood.c.  This simplifies
 * things during development and unused ts2k_() functions should go away
 * as soon as possible.
 *
 *	Dale KD7ENI
 */

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

#include <ctype.h>
#include <hamlib/rig.h>
#include "kenwood.h"
#include "ts2k.h"


/*
 * I just read in kenwood.c and will modify the functions here.  This way,
 * kenwood functions that actually work on other rigs won't be broken by
 * my hacks.  Anything that works for all can be sent back over to kenwood.c
 *
 * Note: due to my compulsive laziness, I often abbreviate Kenwood TS-2000
 *	as simply ts2k, especially for code!
 *
 * Dale kd7eni
 */

/*
 *  Hamlib Kenwood backend - main file
 *  Copyright (c) 2000-2002 by Stephane Fillod
 *
 *		$Id: ts2k.c,v 1.5 2002-06-30 10:17:03 dedmons Exp $
 */


#include <stdlib.h>
#include <stdio.h>		/* Standard input/output definitions */
#include <string.h>		/* String function definitions */
#include <unistd.h>		/* UNIX standard function definitions */
#include <fcntl.h>		/* File control definitions */
#include <errno.h>		/* Error number definitions */
#include <termios.h>		/* POSIX terminal control definitions */
#include <sys/ioctl.h>
#include <math.h>

#include <serial.h>
#include <misc.h>


/*
 * modes in use by the "MD" command
 */
#define MD_NONE	'0'
#define MD_LSB	'1'
#define MD_USB	'2'
#define MD_CW	'3'
#define MD_FM	'4'
#define MD_AM	'5'
#define MD_FSK	'6'
#define MD_CWR	'7'
#define MD_FSKR	'9'


// Added the following two lists --Dale, kd7eni
// FIXME: RIG_MODE_[FSKR|CWR] undefined in rig.h
const int ts2k_mode_list[] = {
	RIG_MODE_NONE, RIG_MODE_LSB, RIG_MODE_USB, RIG_MODE_CW,
	RIG_MODE_FM, RIG_MODE_AM, RIG_MODE_RTTY, RIG_MODE_CW,
	RIG_MODE_RTTY
};

long int ts2k_steps[2][10] = {
	{1000, 2500, 5000, 10000, 0, 0, 0, 0, 0, 0},	// ssb, cw, fsk
	{5000, 6250, 10000, 12500, 15000, 20000,
	 25000, 30000, 50000, 100000}	// am/fm
};


struct ts2k_id {
	rig_model_t model;
	int id;
};

struct ts2k_id_string {
	rig_model_t model;
	const char *id;
};


#define UNKNOWN_ID -1

/*
 * Identification number as returned by "ID;"
 * Please, if the model number of your rig is listed as UNKNOWN_ID,
 * send the value to <fillods@users.sourceforge.net> for inclusion. Thanks --SF
 *
 * TODO: sort this list with most frequent rigs first.
 */
static const struct ts2k_id ts2k_id_list[] = {
	{RIG_MODEL_R5000, 5},
	{RIG_MODEL_TS870S, 15},
	{RIG_MODEL_TS570D, 17},
	{RIG_MODEL_TS570S, 18},
	{RIG_MODEL_TS2000, 19},	/* correct --kd7eni */
	{RIG_MODEL_NONE, UNKNOWN_ID},	/* end marker */
};

static const struct ts2k_id_string ts2k_id_string_list[] = {
	{RIG_MODEL_THD7A, "TH-D7"},
	{RIG_MODEL_THD7AG, "TH-D7G"},
	{RIG_MODEL_THF6A, "TH-F6"},
	{RIG_MODEL_THF7E, "TH-F7"},
	{RIG_MODEL_NONE, NULL},	/* end marker */
};


/*
 * 38 CTCSS sub-audible tones  (17500 invalid for ctcss --kd7eni)
  */
const int ts2k_ctcss_list[] = {
	670, 719, 744, 770, 797, 825, 854, 885, 915, 948,
	974, 1000, 1035, 1072, 1109, 1148, 1188, 1230, 1273, 1318,
	1365, 1413, 1462, 1514, 1567, 1622, 1679, 1738, 1799, 1862,
	1928, 2035, 2107, 2181, 2257, 2336, 2418, 2503, // 17500,
	/* Note: 17500 is not available as ctcss, only tone. --kd7eni */
	0,
};

#define cmd_trm(rig) ((struct ts2k_priv_caps *)(rig)->caps->priv)->cmdtrm
#define ta_quit	rs->hold_decode = 0; return retval

/**
 * kenwood_transaction
 * Assumes rig!=NULL rig->state!=NULL rig->caps!=NULL
 *
 * cmdstr - Command to be sent to the rig. Cmdstr can also be NULL, indicating
 *          that only a reply is needed (nothing will be send).
 * data - Buffer for reply string.  Can be NULL, indicating that no reply is
 *        is needed and will return with RIG_OK after command was sent.
 * datasize - in: Size of buffer. It is the caller's responsibily to provide
 *            a large enough buffer for all possible replies for a command.
 *            out: location where to store number of bytes read.
 *
 * returns:
 *   RIG_OK  -  if no error occured.
 *   RIG_EIO  -  if an I/O error occured while sending/receiving data.
 *   RIG_ETIMEOUT  -  if timeout expires without any characters received.
 *   RIG_REJECTED  -  if a negative acknowledge was received or command not
 *                    recognized by rig.
 */

/* FIXME: cmd_len appears to change and needs set every invocation? --Dale */

int
ts2k_transaction(RIG * rig, const char *cmdstr, int cmd_len,
		 char *data, size_t * datasize)
{
//      return kenwood_transaction(rig, cmdstr, cmd_len, data, datasize);

	struct rig_state *rs;
	int retval;
	const char *cmdtrm = EOM_KEN;	/* Default Command/Reply termination char */
	int retry_read = 0;
	char *errtxt;

#define MAX_RETRY_READ 5 

	rs = &rig->state;
	rs->hold_decode = 1;

	serial_flush(&rs->rigport);

	cmdtrm = cmd_trm(rig);

	if (cmdstr != NULL) {
		//      rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 1) sending '%s'\n\n", cmdstr);
		retval = write_block(&rs->rigport, cmdstr, strlen(cmdstr));
		if (retval != RIG_OK)
			{ ta_quit; }
#undef	TH_ADD_CMDTRM
#ifdef	TH_ADD_CMDTRM
		//      rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 2) sending '%s'\n\n", cmdtrm);
		retval = write_block(&rs->rigport, cmdtrm, strlen(cmdtrm));
		if (retval != RIG_OK)
			{ ta_quit; }
#endif
	}

/* FIXME: Everything below this line wants to be completely rewritten!!!! */

	if (data == NULL || datasize <= 0) {
		rig->state.hold_decode = 0;
		return RIG_OK;	/* don't want a reply */
	}

      transaction_read:
	/* FIXME : TS-2000 gets alot of 'timedout' on read_string()! */
	//rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 3a) reading %u bytes...\n", *datasize);
	retval =
	    read_string(&rs->rigport, data, *datasize, cmdtrm,
			strlen(cmdtrm));
	//rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 3b) read '%s', retval=%u\n\n", data, retval);
	*datasize = retval;
	if (retval > 0) {
		//rig_debug(RIG_DEBUG_ERR, __FUNCTION__": 3b) read cmd '%s', retval=%u\n\n", data, retval);
		retval = RIG_OK;
		goto transaction_check;
	}

	/* Check that command termination is correct */
	if (!strchr(cmdtrm, data[strlen(data)])) {
		if (retry_read++ < MAX_RETRY_READ)
			goto transaction_read;
		rig_debug(RIG_DEBUG_ERR,
			  __FUNCTION__
			  ": Command is not correctly terminated '%s'\n",
			  data);
		retval = -RIG_EPROTO;
		ta_quit;
	}

	/* Errors */
	if (strlen(data) == 2 && data[0] == 'E') {
		switch (data[0]) {
		case 'E':
			rig_debug(RIG_DEBUG_ERR,
				  __FUNCTION__
				  ": Communication Error for '%s'\n",
				  cmdstr);
			break;

		case 'O':
			rig_debug(RIG_DEBUG_ERR,
				  __FUNCTION__
				  ": Communication Error for '%s'\n",
				  cmdstr);
			break;

		case '?':
			rig_debug(RIG_DEBUG_ERR,
				  __FUNCTION__
				  ": Communication Error for '%s'\n",
				  cmdstr);
			break;

		default:
			rig_debug(RIG_DEBUG_ERR,
				  __FUNCTION__ ": Hamlib Error for '%s'\n",
				  cmdstr);
			break;

		}
		retval = -RIG_ERJCTED;
		ta_quit;
	}
#undef	CONFIG_STRIP_CMDTRM
#ifdef	CONFIG_STRIP_CMDTRM
	if (strlen(data) > 0)
		data[strlen(data) - 1] = '\0';	/* not very elegant, but should work. */
	else
		data[0] = '\0';
#endif
	/*
	 * Check that received the correct reply. The first two characters
	 * should be the same as command.
	 */
      transaction_check:
	if (cmdstr && (toupper(data[0]) != toupper(cmdstr[0])
		       || toupper(data[1]) != toupper(cmdstr[1]))) {
		/*
		 * TODO: When RIG_TRN is enabled, we can pass the string
		 *       to the decoder for callback. That way we don't ignore
		 *       any commands.
		 */
		if (retry_read++ < MAX_RETRY_READ)
			goto transaction_read;

		rig_debug(RIG_DEBUG_ERR,
			  __FUNCTION__ ": Unexpected reply '%s'\n", data);
		retval = -RIG_EPROTO;
		{ ta_quit; }
	}

	retval = RIG_OK;

      transaction_quit:
	{ ta_quit; }
}

/*
 * kenwood_set_vfo
 * Assumes rig!=NULL
 *
 *	status:		VFOA, VFOB, VFOC, Main, Sub,
 *			MEMA, MEMC, CALLA, CALLC 
 *			VFO_AB, VFO_BA, ...
 *		They all work!		--Dale
 */
int ts2k_set_vfo(RIG * rig, vfo_t vfo)
{
	unsigned char cmdbuf[10];
	int ptt, ctrl, v,cmd_len, retval;
	static int sat_on;	// temporary! to be removed!
	char vfo_function;

	// trivial case, but needs checked if enabled
/*//	if( (vfo == RIG_CTRL_MODE(RIG_CTRL_MAIN,RIG_VFO_ALL))
//		|| (vfo == RIG_CTRL_MODE(RIG_CTRL_SUB,RIG_VFO_ALL)) ) {
//		rig_debug(RIG_DEBUG_ERR, __FUNCTION__ \
//			  ": Geez, you can't set *all* VFO's!\n");
//		return -RIG_EINVAL;
//	}
*/
	cmd_len = 10;
	ptt = ctrl = v = 0;

	// be optimistic (and ensure initialization)
	retval = RIG_OK;

	// Main/Sub Active Transceiver
	switch (vfo) {
	case RIG_VFO_A:
	case RIG_VFO_B:
	case RIG_VFO_AB:	// split
	case RIG_VFO_BA:
	case RIG_CTRL_SAT:	// FIXME: Not even close to correct
	case RIG_VFO_MAIN:
	case RIG_VFO_MEM_A:
	case RIG_VFO_CALL_A:
		ctrl = TS2K_CTRL_ON_MAIN;	// FIXME : these are independent!
		ptt = TS2K_PTT_ON_MAIN;
		break;
	case RIG_VFO_C:
	case RIG_VFO_SUB:
	case RIG_VFO_MEM_C:
	case RIG_VFO_CALL_C:
		ctrl = TS2K_CTRL_ON_SUB;
		ptt = TS2K_PTT_ON_SUB;
		break;

	default:
		break;
	}

	// set now so "ft...;" and "fr...;" don't fail
	retval = ts2k_set_ctrl(rig, ptt, ctrl);
	if (retval != RIG_OK)
		return -RIG_EINVAL;

	// check if we need to skip the remainder
	v  = (vfo == RIG_VFO_SUB)
		|| (vfo == RIG_VFO_MAIN)
		|| (vfo == RIG_VFO_CURR)
		|| (vfo == RIG_VFO_VFO)
//		|| (vfo == RIG_VFO_ALL)		// yea, I know
		/* bit mask checks */
		|| (vfo & RIG_CTRL_SAT)	// "fr...;", "ft...;" won't do!
		;

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__ \
		  ": starting check.... vfo = 0x%X, v=%d\n", vfo, v);

	if (!v) {	// start check

		// FIXME: this is a speed-up kludge but won't *always* work!
		if(sat_on) {
			retval = ts2k_sat_off(rig, vfo);	// we gotta do it.
			if(retval != RIG_OK)
				return retval;
			sat_on = 0;
		}

		// RX Active Tuning
		switch (vfo) {
		case RIG_VFO_AB:	// TX is opposite
		case RIG_VFO_A:
		case RIG_VFO_C:
			vfo_function = '0';
			break;
		case RIG_VFO_BA:	// TX is opposite
		case RIG_VFO_B:
			vfo_function = '1';
			break;
		case RIG_VFO_MEM_A:
		case RIG_VFO_MEM_C:
			vfo_function = '2';
			break;
		case RIG_VFO_CALL_A:
		case RIG_VFO_CALL_C:
			vfo_function = '3';
			break;

		default:
			rig_debug(RIG_DEBUG_ERR, __FUNCTION__
				  ": unsupported VFO %u\n", vfo);
			return -RIG_EINVAL;
			break;
		}

		// ack_len is tmp
		cmd_len =
		    sprintf(cmdbuf, "fr%c%s", vfo_function, cmd_trm(rig));

		/* set RX VFO */
		retval =
		    ts2k_transaction(rig, cmdbuf, cmd_len, NULL, NULL);
		if (retval != RIG_OK)
			return -RIG_EINVAL;

		// TX Active tuning
		switch (vfo) {
		case RIG_VFO_A:
		case RIG_VFO_C:
		case RIG_VFO_BA:	// opposite of above
			vfo_function = '0';
			break;
		case RIG_VFO_AB:	// opposite of above
		case RIG_VFO_B:
			vfo_function = '1';
			break;
		case RIG_VFO_MEM_A:
		case RIG_VFO_MEM_C:	// FIXME: need to handle vfo/mem split
			vfo_function = '2';
			break;
		case RIG_VFO_CALL_A:
		case RIG_VFO_CALL_C:
			vfo_function = '3';
			break;

		default:
			rig_debug(RIG_DEBUG_ERR, __FUNCTION__
				  ": unsupported VFO %u\n",
				  vfo);
			return -RIG_EINVAL;
		}

		/* set TX VFO */
		cmdbuf[1] = 't';

		// removing this causes split to not function!!!!
		cmd_len =
		    sprintf(cmdbuf, "ft%c%s", vfo_function, cmd_trm(rig));

		retval =
		    ts2k_transaction(rig, cmdbuf, cmd_len, NULL, NULL);
		if (retval != RIG_OK)
			return retval;

	} else {	// Check further for special modes not using "fr...;", "ft...;"
		if(vfo & RIG_CTRL_SAT) {	// test the SAT bit
			retval = ts2k_sat_on(rig, vfo);
				if (retval != RIG_OK)
					return retval;
			sat_on = 1;			
		} else {
			rig_debug(RIG_DEBUG_ERR, __FUNCTION__ \
				  ": VFO not changed, only PTT/CTRL\n");
		}
	}
/*
 * FIXME: some items like scan, satellite need checked and turned off here.
 *	I've got a simple kludge to turn SAT *off* but it wants to be done
 *	here.  It's a bit expensive though and I'm trying to find a better
 *	way to do SAT as well as others.  ts2k_sat_off() reads the current,
 *	sets it off, then writes it back so the user selected stuff don't
 *	change unexpectedly.  Now, SAT won't get turned off if first turned
 *	on via the front panel.  MEM and SCAN have similar quirks.
 *							--Dale
 */
	return retval;
}

/* we just turn SAT on here. It'll take some doing to run it! */
int ts2k_sat_on(RIG *rig, vfo_t vfo)
{
	char cmd[20], ack[20];
	int cmdlen, acklen;

	acklen = 20;

	if(!(vfo & RIG_CTRL_SAT))
		return -RIG_EINTERNAL;	// All right.  Who called us!?

//	cmdlen = sprintf(cmd, "sa%07u;", 0);	// Initial string to modify
	acklen = ts2k_transaction(rig, "sa;", 3, ack, &acklen);

	// Sat mode ON
	ack[2] = '1';	// Everything below is *nice*, this is *required*

	goto STest;	// testing

/* cmd is already full of '0's, but we set them again explicitly */
	// SAT_VFO or SAT_MEM?
	if(vfo & RIG_CTRL_MEM)
		ack[8] = '1';	// sat mem ch 0-9
	else
		ack[8] = '0';	// sat vfo

	/* Main or Sub as uplink? */
	// Note: if both are set, Main is still uplink
	if(vfo & RIG_CTRL_MAIN) {
		ack[4] = '0';	// sat main=uplink
	} else if(vfo & RIG_CTRL_SUB) {
		ack[4] = '1';	// sat sub=uplink
	}

	// FIXME: Add Sat Trace here!

	// Trace REV
	if(vfo & RIG_CTRL_REV) 
		ack[7] = '1';	// sat trace REV 
	else
		ack[7] = '0';

	// CTRL to main or sub?
	if ((vfo & RIG_VFO_CTRL) && (vfo & RIG_CTRL_SUB)) 	
		ack[5] = '1';	// sat CTRL on sub
	else
		ack[5] = '0';	// sat CTRL on main

STest:
	rig_debug(RIG_DEBUG_ERR, __FUNCTION__ \
		  ": sat = %s, vfo = 0x%X\n", cmd, vfo);
	// of coure, this is *required* too!
	return ts2k_transaction(rig, ack, acklen, NULL, NULL);
}

int ts2k_sat_off(RIG *rig, vfo_t vfo)
{
	char cmd[20], ack[20];
	int cmdlen, acklen, retval;

	acklen = 10;

	cmdlen = sprintf(cmd, "sa;");
	retval = ts2k_transaction(rig, cmd, cmdlen, ack, &acklen);
	if(retval != RIG_OK)
		return retval;

	ack[2] = '0';
	cmdlen = 20;
	return ts2k_transaction(rig, ack, acklen, NULL, NULL);
}

/*
 * kenwood_get_vfo
 * Assumes rig!=NULL, !vfo
 *
 *	status:	works perfect for implemented modes!  --Dale
 *		code's getting a little ugly.  okay, real ugly.
 */
int ts2k_get_vfo(RIG * rig, vfo_t * vfo)
{
	char vfobuf[50], r_vfo;
	char *ctrl_ptt;
	int vfo_len, retval, tmp;

	/*
	 * FIXME: if FR != FT && rcvr==main, the mode = split!  --kd7eni
	 */

	// Check which receiver so VFO_C may be detected (PTT/CTRL)

	ctrl_ptt = ts2k_get_ctrl(rig);
	if (ctrl_ptt == NULL)
		return -RIG_EINVAL;
//	rig_debug(RIG_DEBUG_ERR, "ts2k_get_vfo: PTT/CTRL is %s\n", ctrl_ptt);

	/* query RX VFO */
	vfo_len = 50;
	rig_debug(RIG_DEBUG_ERR, __FUNCTION__
		": sending fr; cmd/checking SAT.  Expect TIMEDOUT if in SAT mode!\n");
	retval = ts2k_transaction(rig, "fr;", 3, vfobuf, &vfo_len);

							/* "fr;" fails in satellite mode; interesting... */
	if (retval != RIG_OK) {
		rig_debug(RIG_DEBUG_ERR, __FUNCTION__": kenwood/ts2k.c\n"
			"FIXME: ts2k.c,\tThis is timeout cannot be prevented.\n");
		tmp = retval;
		retval = ts2k_transaction(rig, "sa;", 3, vfobuf, &vfo_len);
		if (retval == RIG_OK) {
			rig_debug(RIG_DEBUG_ERR, __FUNCTION__": SAT=%s\n", vfobuf);
			if(vfobuf[2] == '1') {
							/* yes, we're in satellite mode! */
				*vfo = RIG_CTRL_SAT;	// FIXME: set the rest!
							/* TODO: write get_sat() and let it do the work */
				return RIG_OK;
			}
		}
		return tmp;	// return original "fr;" error!
	}

//	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": checking fr; cmd.\n");
	r_vfo = vfobuf[2];

	//if (vfo_len != 4 || vfobuf[1] != 'R') {
	if (vfobuf[1] != 'R') {
		rig_debug(RIG_DEBUG_ERR, __FUNCTION__
			  ": unexpected answer %s, "
			  "len=%u\n", vfobuf, vfo_len);
		return -RIG_ERJCTED;
	}

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": sending ft; cmd.\n");
	vfo_len = 50;
	retval = ts2k_transaction(rig, "ft;", 3, vfobuf, &vfo_len);
	if (retval != RIG_OK)
		return retval;

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": checking ft; cmd.\n");
	if (vfobuf[2] == r_vfo) {	// check most common first
		rig_debug(RIG_DEBUG_ERR, "ts2k_get_vfo: Non-Split.\n");

		/* TODO: replace 0,1,2,.. constants by defines */
		/* FIXME: return based on RIG_PTT_ON_???? or RIG_CTRL_ON_????
		 * may be different.  We need to specify actual status.
		 * Right now we pretend things are simpler. --kd7eni
		 */
		switch (vfobuf[2]) {
		case '0':
			if (ctrl_ptt[3] == '0')	// we use CTRL as Active Transceiver
				*vfo = RIG_VFO_A;
			else if (ctrl_ptt[3] == '1')
				*vfo = RIG_VFO_C;
			else {	// "There be errors here!"
				rig_debug(RIG_DEBUG_ERR,
					  "ts2k_get_vfo: VFO1 on erroneous xcvr %c\n",
					  vfobuf[2]);
				return -RIG_EPROTO;
			}
			break;
		case '1':
			// only valid on Main--no checks required.
			*vfo = RIG_VFO_B;
			break;
		case '2':
			if (ctrl_ptt[3] == '0')	// we use CTRL as Active Transceiver. 
				*vfo = RIG_VFO_MEM_A;
			else if (ctrl_ptt[3] == '1')
				*vfo = RIG_VFO_MEM_C;
			else
				return -RIG_EPROTO;
			break;
		case '3':
			if (ctrl_ptt[3] == '0')	// we use CTRL as current
				*vfo = RIG_VFO_CALL_A;
			else if (ctrl_ptt[3] == '1')	// we use CTRL as current
				*vfo = RIG_VFO_CALL_C;
			else
				return -RIG_EPROTO;
			break;

		default:	// Different or newer rig types...
			rig_debug(RIG_DEBUG_ERR,
				  "ts2k_get_vfo: unsupported VFO %c\n",
				  vfobuf[2]);
			return -RIG_EPROTO;

		}		// end switch
	} else {		// end rx == tx; start split checks. 
		rig_debug(RIG_DEBUG_ERR, "ts2k_get_vfo: Split.\n");

		if (r_vfo == '0' && vfobuf[2] == '1')
			*vfo = RIG_VFO_AB;
		else if (r_vfo == '1' && vfobuf[2] == '0')
			*vfo = RIG_VFO_BA;
		else {		// FIXME: need vfo <--> mem split
			rig_debug(RIG_DEBUG_ERR,
				  __FUNCTION__
				  ":FIXME: vfo<->mem split! -kd7eni!\n");
			return -RIG_EPROTO;
		}
	}

	return RIG_OK;
}

/*
 * kenwood_set_freq
 * Assumes rig!=NULL
 *
 *	status:	correctly sets FA, FB, FC	--Dale
 */
int ts2k_set_freq(RIG * rig, vfo_t vfo, freq_t freq)
{
	unsigned char freqbuf[16];
	int freq_len, ack_len = 0, retval;
	char vfo_letter;

	/*
	 * better FIXME: vfo==RIG_VFO_CURR
	 */
	if (vfo == RIG_VFO_CURR) {
		retval = ts2k_get_vfo(rig, &vfo);
		if (retval != RIG_OK)
			return retval;
	}

	switch (vfo) {
	case RIG_VFO_A:
		vfo_letter = 'A';
		break;
	case RIG_VFO_B:
		vfo_letter = 'B';
		break;
	case RIG_VFO_C:
		vfo_letter = 'C';
		break;
	default:
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_set_freq: unsupported VFO %u\n", vfo);
		return -RIG_EINVAL;
	}
	freq_len = sprintf(freqbuf, "F%c%011Lu;", vfo_letter, freq);

	ack_len = 14;
	retval = ts2k_transaction(rig, freqbuf, freq_len, NULL, NULL);

	return retval;
}

/*
 * kenwood_get_freq
 * Assumes rig!=NULL, freq!=NULL
 */
int ts2k_get_freq(RIG * rig, vfo_t vfo, freq_t * freq)
{
	unsigned char freqbuf[50];
	unsigned char cmdbuf[4];
	int cmd_len, freq_len, retval;
	char vfo_letter;

	/*
	 * FIXME: need to handle RIG_VFO_MEM, etc...
	 */
	if (vfo == RIG_VFO_CURR) {
		retval = ts2k_get_vfo(rig, &vfo);
		if (retval != RIG_OK)
			return retval;
	}

	switch (vfo) {
	case RIG_VFO_A:
		vfo_letter = 'a';
		break;
	case RIG_VFO_B:
		vfo_letter = 'b';
		break;
	case RIG_VFO_C:
		vfo_letter = 'c';
		break;
	default:
		rig_debug(RIG_DEBUG_ERR,
			  __FUNCTION__": unsupported VFO %u\n", vfo);
		return -RIG_EPROTO;
	}

	cmd_len = sprintf(cmdbuf, "f%c%s", vfo_letter, cmd_trm(rig));

	freq_len = 15;
	retval =
	    ts2k_transaction(rig, cmdbuf, cmd_len, freqbuf, &freq_len);
	//rig_debug(RIG_DEBUG_ERR,"__FUNCTION__: received %s\n", cmdbuf);
	if (retval != RIG_OK)
		return retval;

	//if (freq_len != 14 || freqbuf[0] != 'F') {
	if (freqbuf[0] != 'F') {
		rig_debug(RIG_DEBUG_ERR,
			  __FUNCTION__": unexpected answer '%s', "
			  "len=%u\n", freqbuf, freq_len);
		return -RIG_ERJCTED;
	}

	sscanf(freqbuf + 2, "%llu", freq);

	return RIG_OK;
}

/*
 * kenwood_set_mode
 * Assumes rig!=NULL
 */
int ts2k_set_mode(RIG * rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
{
	unsigned char mdbuf[16], ackbuf[16];
	int mdbuf_len, ack_len = 0, kmode, retval;

	switch (mode) {
	case RIG_MODE_CW:
		kmode = MD_CW;
		break;
	case RIG_MODE_USB:
		kmode = MD_USB;
		break;
	case RIG_MODE_LSB:
		kmode = MD_LSB;
		break;
	case RIG_MODE_FM:
		kmode = MD_FM;
		break;
	case RIG_MODE_AM:
		kmode = MD_AM;
		break;
	case RIG_MODE_RTTY:
		kmode = MD_FSK;
		break;
	default:
		rig_debug(RIG_DEBUG_ERR, "ts2k_set_mode: "
			  "unsupported mode %u\n", mode);
		return -RIG_EINVAL;
	}
	mdbuf_len = sprintf(mdbuf, "MD%c;", kmode);
	ack_len = 16;
	rig_debug(RIG_DEBUG_ERR, "ts2k_set_mode: sending %s\n", mdbuf);
	retval = ts2k_transaction(rig, mdbuf, mdbuf_len, NULL, NULL);

	return retval;
}

/*
 * kenwood_get_mode
 * Assumes rig!=NULL, mode!=NULL
 */
int ts2k_get_mode(RIG * rig, vfo_t vfo, rmode_t * mode, pbwidth_t * width)
{
	unsigned char modebuf[50];
	int mode_len, retval;


	mode_len = 50;
	retval = ts2k_transaction(rig, "MD;", 3, modebuf, &mode_len);
	if (retval != RIG_OK)
		return retval;

	if (mode_len != 4 || modebuf[1] != 'D') {
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_get_mode: unexpected answer, len=%u\n",
			  mode_len);
		return -RIG_ERJCTED;
	}

	*width = RIG_PASSBAND_NORMAL;	/* FIXME */
	switch (modebuf[2]) {
	case MD_CW:
		*mode = RIG_MODE_CW;
		break;
	case MD_USB:
		*mode = RIG_MODE_USB;
		break;
	case MD_LSB:
		*mode = RIG_MODE_LSB;
		break;
	case MD_FM:
		*mode = RIG_MODE_FM;
		break;
	case MD_AM:
		*mode = RIG_MODE_AM;
		break;
	case MD_FSK:
		*mode = RIG_MODE_RTTY;
		break;
#ifdef RIG_MODE_CWR
	case MD_CWR:
		*mode = RIG_MODE_CWR;
		break;
#endif
#ifdef RIG_MODE_RTTYR
	case MD_FSKR:
		*mode = RIG_MODE_RTTY;
		break;
#endif
	case MD_NONE:
		*mode = RIG_MODE_NONE;
		break;
	default:
		rig_debug(RIG_DEBUG_ERR, "ts2k_get_mode: "
			  "unsupported mode '%c'\n", modebuf[2]);
		return -RIG_EINVAL;
	}

	return RIG_OK;
}

/* added a whole buch of stuff --Dale */
int ts2k_set_level(RIG * rig, vfo_t vfo, setting_t level, value_t val)
{
	unsigned char levelbuf[16], ackbuf[16], ctrl;
	char *dc;	// to be replaced.
//	char dc[10];	// use this when ts2k_get_ctrl() is fixed.
//	int dc_len;	// use this when ts2k_get_ctrl() is fixed.
	int level_len, ack_len = 0, retval, reply;
	int ts2k_val;

	if (RIG_LEVEL_IS_FLOAT(level))
		ts2k_val = val.f * 255;
	else
		ts2k_val = val.i;

/* Several commands need to know status of CTRL */

//	dc_len = 10;	// these are to be used when ts2k_get_ctrl() is changed.
//	if( ts2k_get_ctrl(rig, dc, 10) != RIG_OK)
//		return -RIG_EINVAL;
// we shouldn't really change dc; its a static and will get changed.
	dc = ts2k_get_ctrl(rig);	// will get fixed
//	m = dc[2]; s = dc[3];		// set defaults

// FIXME: Just handles simple stuff RIG_VFO_CURR,VFOA,VFOB,VFOC; *may* work as intended. 
	ctrl = '0';			// Assume Main
	if(vfo & RIG_CTRL_SUB)		// Change only if sub (bitwise, not logical)
		ctrl = '1';

	// assume no replies
	reply = 0;
	switch (level) {
	case RIG_LEVEL_RFPOWER:
		level_len = sprintf(levelbuf, "pc%03d;", ts2k_val);
		break;

	case RIG_LEVEL_AF:	// I call this volume.  :)
		level_len = sprintf(levelbuf, "ag%c%03u;", ctrl, ts2k_val);
		break;

	case RIG_LEVEL_RF:
		level_len = sprintf(levelbuf, "rg%03d;", ts2k_val);
		break;

	case RIG_LEVEL_SQL:	// fixed: uses main/sub	--Dale
		level_len = sprintf(levelbuf, "sq%c%03u;", ctrl, ts2k_val);
		break;


	/* I added the following levels --Dale */
	case RIG_LEVEL_PREAMP:
		level_len = sprintf(levelbuf, "pa%c;", (ts2k_val==0)? '0' : '1');
		break;

	case RIG_LEVEL_ATT:
		if(ts2k_val<1 || ts2k_val>2)	// only 1 or 2, 0=read
			return -RIG_EINVAL;
		level_len = sprintf(levelbuf, "an%01u;", ts2k_val);
		break;

	case RIG_LEVEL_NR:
		if(ts2k_val<0 || ts2k_val>2)	// only 1 or 2, 0=read
			return -RIG_EINVAL;
		level_len = sprintf(levelbuf, "nr%01u;", ts2k_val);
		break;

	/* FIXME: FM mic gain is low/mid/high; cmd="ex0410000n;" 0=low --Dale */
	case RIG_LEVEL_MICGAIN:
		level_len = sprintf(levelbuf, "mg%03u;", (ts2k_val>100)? 100 : ts2k_val);
		break;

	/* no rig error on invalid values */
	case RIG_LEVEL_KEYSPD:
		if(ts2k_val<10 || ts2k_val>60)	// only 1 or 2, 0=read
			return -RIG_EINVAL;
		level_len = sprintf(levelbuf, "ks%03u;", ts2k_val);
		break;

	case RIG_LEVEL_NOTCHF:	// actually, this is autonotch--same thing?
		level_len = sprintf(levelbuf, "al%03u;", (ts2k_val>4)? 4 : ts2k_val);
		break;

	case RIG_LEVEL_AGC:
		level_len = sprintf(levelbuf, "gt%03u;", (ts2k_val>20)? 20 : ts2k_val);
		break;

	case RIG_LEVEL_BKINDL:	// 0-1000ms in 50ms steps; this'll be good 'nuff.
		level_len = sprintf(levelbuf, "sd%04u;",(int)((float) ts2k_val*1000.0/255.0));
		break;

	case RIG_LEVEL_VOXGAIN:
		level_len = sprintf(levelbuf, "vg%03u;",(ts2k_val>9)? 9 : ts2k_val);
// alternate
//		level_len = sprintf(levelbuf, "vg%03u;",(int)((float) ts2k_val*9.0/255.0));
		break;

//	case RIG_LEVEL_VOX:	// header file declares these the same
	case RIG_LEVEL_VOXDELAY:
		level_len = sprintf(levelbuf, "vg%03u;",(int)((float) ts2k_val*3000.0/255.0));
		break;
// vox on/off
//		level_len = sprintf(levelbuf, "vx%c;", (ts2k_val==0)? '0' : '1');
//		break;

	/* Check unsupported just so we can complain if one is found.  :) */
	case RIG_LEVEL_APF:

	/* readonly */
	case RIG_LEVEL_SWR:
	case RIG_LEVEL_ALC:
	case RIG_LEVEL_STRENGTH:
		return -RIG_EINTERNAL;	// and complain loud!

	/* not currently implemented */
	case RIG_LEVEL_METER:	// readonly + needs rechecked.
	case RIG_LEVEL_PBT_IN:	// nnn
	case RIG_LEVEL_PBT_OUT: // NNN; "plnnnNNN;"
	case RIG_LEVEL_IF:	// hmmmm....
	case RIG_LEVEL_CWPITCH:	// "ex0310000n;" and a fair bit of programming...
	case RIG_LEVEL_COMP:	// available?, processor's not the same is it?
	case RIG_LEVEL_BALANCE:	// AF R/L balance?  See "ex01600002;" for similar(?)
	case RIG_LEVEL_SQLSTAT:	// need more info; avail?
		return -RIG_ENIMPL;

	default:
		rig_debug(RIG_DEBUG_ERR,
			  "Unsupported set_level %u", level);
		return -RIG_EINVAL;
	}
	// The following might make a nifty macro	--Dale
	ack_len = reply? 16 : 0;	// This'd work by itself wouldn't it?  --Dale
	retval = ts2k_transaction(rig, levelbuf, level_len,
			reply? ackbuf : NULL, reply? &ack_len : NULL);

	if (retval != RIG_OK)
		return retval;

	return RIG_OK;
}

/* 
 * assumes f!=NULL
 */
static int
get_ts2k_level(RIG * rig, const char *cmd, int cmd_len, float *f)
{
	unsigned char lvlbuf[50];
	int lvl_len, retval;
	int lvl;

	lvl_len = 50;
	retval = ts2k_transaction(rig, cmd, cmd_len, lvlbuf, &lvl_len);
	if (retval != RIG_OK)
		return retval;

	if (lvl_len != 6) {
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_get_level: wrong answer len=%u\n",
			  lvl_len);
		return -RIG_ERJCTED;
	}

	/*
	 * 000..255
	 */
	sscanf(lvlbuf + 2, "%u", &lvl);
	*f = (float) lvl / 255.0;

	return RIG_OK;
};


/*
 * kenwood_get_level
 * Assumes rig!=NULL, val!=NULL
 */
int ts2k_get_level(RIG * rig, vfo_t vfo, setting_t level, value_t * val)
{
	unsigned char lvlbuf[50];
	int lvl_len, retval;
	int lvl;
	int i;

	lvl_len = 50;
	/* Optimize:
	 *   sort the switch cases with the most frequent first
	 */
	switch (level) {
	case RIG_LEVEL_STRENGTH:
		// fixme: "sm0;" VFO must be specified!  --kd7eni
		retval = ts2k_transaction(rig, "SM;", 3, lvlbuf, &lvl_len);
		if (retval != RIG_OK)
			return retval;

		if (lvl_len != 7 || lvlbuf[1] != 'M') {
			rig_debug(RIG_DEBUG_ERR, "ts2k_get_level: "
				  "wrong answer len=%u\n", lvl_len);
			return -RIG_ERJCTED;
		}

		/*
		 * Frontend expects:
		 *    -54 = S0
		 *              0 = S9
		 */
		sscanf(lvlbuf + 2, "%u", &val->i);
		val->i = (val->i * 4) - 54;
		break;

	case RIG_LEVEL_SQLSTAT:
		return -RIG_ENIMPL;	/* get_dcd ? */

	case RIG_LEVEL_PREAMP:
		return -RIG_ENIMPL;

	case RIG_LEVEL_ATT:
		retval = ts2k_transaction(rig, "RA;", 3, lvlbuf, &lvl_len);
		if (retval != RIG_OK)
			return retval;

		if (lvl_len != 5) {
			rig_debug(RIG_DEBUG_ERR, "ts2k_get_level: "
				  "unexpected answer len=%u\n", lvl_len);
			return -RIG_ERJCTED;
		}

		sscanf(lvlbuf + 2, "%u", &lvl);
		if (lvl == 0) {
			val->i = 0;
		} else {
			for (i = 0; i < lvl && i < MAXDBLSTSIZ; i++)
				if (rig->state.attenuator[i] == 0) {
					rig_debug(RIG_DEBUG_ERR,
						  "ts2k_get_level: "
						  "unexpected att level %u\n",
						  lvl);
					return -RIG_EPROTO;
				}
			if (i != lvl)
				return -RIG_EINTERNAL;
			val->i = rig->state.attenuator[i - 1];
		}
		break;
	case RIG_LEVEL_RFPOWER:
		return get_ts2k_level(rig, "PC;", 3, &val->f);

	case RIG_LEVEL_AF:
		return get_ts2k_level(rig, "AG;", 3, &val->f);

	case RIG_LEVEL_RF:
		return get_ts2k_level(rig, "RG;", 3, &val->f);

	case RIG_LEVEL_SQL:
		return get_ts2k_level(rig, "SQ;", 3, &val->f);

	case RIG_LEVEL_MICGAIN:
		return get_ts2k_level(rig, "MG;", 3, &val->f);

	case RIG_LEVEL_AGC:
		return get_ts2k_level(rig, "GT;", 3, &val->f);

	case RIG_LEVEL_IF:
	case RIG_LEVEL_APF:
	case RIG_LEVEL_NR:
	case RIG_LEVEL_PBT_IN:
	case RIG_LEVEL_PBT_OUT:
	case RIG_LEVEL_CWPITCH:
	case RIG_LEVEL_KEYSPD:
	case RIG_LEVEL_NOTCHF:
	case RIG_LEVEL_COMP:
	case RIG_LEVEL_BKINDL:
	case RIG_LEVEL_BALANCE:
		return -RIG_ENIMPL;

	default:
		rig_debug(RIG_DEBUG_ERR,
			  "Unsupported get_level %u", level);
		return -RIG_EINVAL;
	}

	return RIG_OK;
}


int ts2k_set_func(RIG * rig, vfo_t vfo, setting_t func, int status)
{
	unsigned char fctbuf[16], ackbuf[16];
	int fct_len, ack_len = 16;

	/* Optimize:
	 *   sort the switch cases with the most frequent first
	 */
	switch (func) {
	case RIG_FUNC_NB:
		fct_len =
		    sprintf(fctbuf, "NB%c;",
			    status == RIG_FUNC_NB ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	case RIG_FUNC_ABM:
		fct_len =
		    sprintf(fctbuf, "AM%c;",
			    status == RIG_FUNC_ABM ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	case RIG_FUNC_COMP:
		fct_len =
		    sprintf(fctbuf, "PR%c;",
			    status == RIG_FUNC_COMP ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	case RIG_FUNC_TONE:
		fct_len =
		    sprintf(fctbuf, "TO%c;",
			    status == RIG_FUNC_TONE ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	case RIG_FUNC_TSQL:
		// fixme: see ts2000.doc and follow the proceedure!  --kdeni
		// rigbug!
		fct_len =
		    sprintf(fctbuf, "CT%c;",
			    status == RIG_FUNC_TSQL ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len,
					ackbuf, &ack_len);

	case RIG_FUNC_VOX:
		fct_len =
		    sprintf(fctbuf, "VX%c;",
			    status == RIG_FUNC_VOX ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	case RIG_FUNC_NR:
		fct_len =
		    sprintf(fctbuf, "NR%c;",
			    status == RIG_FUNC_NR ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	case RIG_FUNC_BC:
		fct_len =
		    sprintf(fctbuf, "BC%c;",
			    status == RIG_FUNC_BC ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	case RIG_FUNC_ANF:
		fct_len =
		    sprintf(fctbuf, "NT%c;",
			    status == RIG_FUNC_ANF ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	case RIG_FUNC_LOCK:
		fct_len =
		    sprintf(fctbuf, "LK%c0;",
			    status == RIG_FUNC_LOCK ? '0' : '1');
		return ts2k_transaction(rig, fctbuf, fct_len, NULL, NULL);

	default:
		rig_debug(RIG_DEBUG_ERR, "Unsupported set_func %#x", func);
		return -RIG_EINVAL;
	}
	return RIG_OK;
}


/* 
 * assumes status!=NULL
 * works for any 'format 1' command
 */
static int
get_ts2k_func(RIG * rig, const char *cmd, int cmd_len, int *status)
{
	unsigned char fctbuf[50];
	int fct_len, retval;

	fct_len = 50;
	retval = ts2k_transaction(rig, cmd, cmd_len, fctbuf, &fct_len);
	if (retval != RIG_OK)
		return retval;

	if (fct_len != 4) {
		rig_debug(RIG_DEBUG_ERR,
			  __FUNCTION__": wrong answer len=%u\n", fct_len);
		return -RIG_ERJCTED;
	}

	*status = fctbuf[2] == '0' ? 0 : 1;

	return RIG_OK;
};


/*
 * kenwood_get_func
 * Assumes rig!=NULL, val!=NULL
 */
int ts2k_get_func(RIG * rig, vfo_t vfo, setting_t func, int *status)
{
	unsigned char fctbuf[50];
	int fct_len, retval;

	fct_len = 50;
	/* Optimize:
	 *   sort the switch cases with the most frequent first
	 */
	switch (func) {
	case RIG_FUNC_FAGC:
		retval = ts2k_transaction(rig, "GT;", 3, fctbuf, &fct_len);
		if (retval != RIG_OK)
			return retval;

		if (fct_len != 6) {
			rig_debug(RIG_DEBUG_ERR, "ts2k_get_func: "
				  "wrong answer len=%u\n", fct_len);
			return -RIG_ERJCTED;
		}

		*status = fctbuf[4] != '4' ? 1 : 0;
		break;

	case RIG_FUNC_NB:
		return get_ts2k_func(rig, "NB;", 3, status);

	case RIG_FUNC_ABM:
		return get_ts2k_func(rig, "AM;", 3, status);

	case RIG_FUNC_COMP:
		return get_ts2k_func(rig, "PR;", 3, status);

	case RIG_FUNC_TONE:
		return get_ts2k_func(rig, "TO;", 3, status);

	case RIG_FUNC_TSQL:
		return get_ts2k_func(rig, "CT;", 3, status);

	case RIG_FUNC_VOX:
		return get_ts2k_func(rig, "VX;", 3, status);

	case RIG_FUNC_NR:
		return get_ts2k_func(rig, "NR;", 3, status);

		/* FIXME on TS2000 */
	case RIG_FUNC_BC:
		return get_ts2k_func(rig, "BC;", 3, status);

	case RIG_FUNC_ANF:
		return get_ts2k_func(rig, "NT;", 3, status);

	case RIG_FUNC_LOCK:
		return get_ts2k_func(rig, "LK;", 3, status);

	default:
		rig_debug(RIG_DEBUG_ERR, "Unsupported get_func %#x", func);
		return -RIG_EINVAL;
	}

	return RIG_OK;
}

/*
 * kenwood_set_ctcss_tone
 * Assumes rig!=NULL, rig->caps->ctcss_list != NULL
 *
 * Warning! This is untested stuff! May work at least on TS-870S
 * 	Please owners report to me <fillods@users.sourceforge.net>, thanks. --SF
 *
 * TODO: TS-2000 uses CN/CT
 *	ex057 menu is AutoPower off for TS-2000	--kd7eni
 */
int ts2k_set_ctcss_tone(RIG * rig, vfo_t vfo, tone_t tone)
{
	return ts2k_set_Tones(rig, vfo, tone, (char)'c');
}

int ts2k_set_tone(RIG * rig, vfo_t vfo, tone_t tone)
{
	return ts2k_set_Tones(rig, vfo, tone, (char)'t');
}

int ts2k_set_Tones(RIG * rig, vfo_t vfo, tone_t tone, const char ct)
{
	const struct rig_caps *caps;
	unsigned char tonebuf[16], ackbuf[16];
	int tone_len, ack_len = 0;
	int i;

	caps = rig->caps;

/* TODO: replace 200 by something like RIGTONEMAX */
	for (i = 0; ts2k_ctcss_list[i] != 0 && i < 38; i++) {
		if ((ts2k_ctcss_list[i] >= tone)
			&& (ts2k_ctcss_list[i-1] < tone))	// at least get close
			break;
	}
	if (ts2k_ctcss_list[i-1] == tone)
		i--;
	if (ts2k_ctcss_list[i] > tone)
		return -RIG_EINVAL;

	tone_len = sprintf(tonebuf, "%cn%02u;", ct, i+1);

	ack_len = 16;
	return ts2k_transaction(rig, tonebuf, tone_len, NULL, NULL);
	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": sent %s", tonebuf);
}

/*
 * kenwood_get_ctcss_tone
 * Assumes rig!=NULL, rig->state.priv!=NULL
 */
int ts2k_get_ctcss_tone(RIG * rig, vfo_t vfo, tone_t * tone)
{
	return ts2k_get_Tones(rig, vfo, tone, "cn;");
}

int ts2k_get_tone(RIG * rig, vfo_t vfo, tone_t * tone)
{
	return ts2k_get_Tones(rig, vfo, tone, "tn;");
}

int ts2k_get_Tones(RIG * rig, vfo_t vfo, tone_t * tone, const char *ct)
{
	const struct rig_caps *caps;
	unsigned char tonebuf[10];
	int tone_len, i, retval;
	unsigned int tone_idx;

	caps = rig->caps;

	tone_len = 10;
	retval = ts2k_transaction(rig, ct, 3, tonebuf, &tone_len);
	if (retval != RIG_OK)
		return retval;

	if (tone_len != 5) {
		rig_debug(RIG_DEBUG_ERR,
			  __FUNCTION__": unexpected reply "
			  "'%s', len=%u\n", tonebuf, tone_len);
		return -RIG_ERJCTED;
	}

	sscanf(tonebuf + 2, "%u", (int *) &tone_idx);

	if (tone_idx == 0) {
		rig_debug(RIG_DEBUG_ERR,
			  __FUNCTION__": Unexpected Tone "
			  "no (%04d)\n", tone_idx);
		return -RIG_EPROTO;
	}

	/* check this tone exists. That's better than nothing. */
	for (i = 0; i < tone_idx; i++) {
		if (caps->ctcss_list[i] == 0) {
			rig_debug(RIG_DEBUG_ERR,
				  __FUNCTION__": Tone NG "
				  "(%04d)\n", tone_idx);
			return -RIG_EPROTO;
		}
	}
	*tone = caps->ctcss_list[tone_idx - 1];

	return RIG_OK;
}

/*
 * kenwood_get_ptt
 * Assumes rig!=NULL, ptt!=NULL
 */
int ts2k_get_ptt(RIG * rig, vfo_t vfo, ptt_t * ptt)
{
	unsigned char infobuf[50];
	int info_len, retval;

	info_len = 50;
	retval = ts2k_transaction(rig, "IF;", 3, infobuf, &info_len);
	if (retval != RIG_OK)
		return retval;

	if (info_len != 38 || infobuf[1] != 'F') {
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_get_ptt: wrong answer len=%u\n", info_len);
		return -RIG_ERJCTED;
	}

	*ptt = infobuf[28] == '0' ? RIG_PTT_OFF : RIG_PTT_ON;

	return RIG_OK;
}

/*
 * kenwood_set_ptt
 * Assumes rig!=NULL
 */
int ts2k_set_ptt(RIG * rig, vfo_t vfo, ptt_t ptt)
{
	unsigned char ackbuf[16];
	int ack_len = 16;


	return ts2k_transaction(rig,
				ptt ==
				RIG_PTT_ON ? "TX;" : "RX;", 3, NULL, NULL);
}
/*
 * kenwood_get_dcd
 * Assumes rig!=NULL, dcd!=NULL
 */
int ts2k_get_dcd(RIG * rig, vfo_t vfo, dcd_t * dcd)
{
	unsigned char busybuf[50];
	int busy_len, retval;

	busy_len = 50;
	retval = ts2k_transaction(rig, "BY;", 3, busybuf, &busy_len);
	if (retval != RIG_OK)
		return retval;

	if (busy_len != 4) {
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_get_dcd: wrong answer len=%u\n", busy_len);
		return -RIG_ERJCTED;
	}

	*dcd = (busybuf[2] == 0x01) ? RIG_DCD_ON : RIG_DCD_OFF;

	return RIG_OK;
}

/*
 * kenwood_set_trn
 * Assumes rig!=NULL
 */
int ts2k_set_trn(RIG * rig, int trn)
{
	unsigned char trnbuf[16], ackbuf[16];
	int trn_len, ack_len = 16;

	/* changed to TS-2000 --D.E. kd7eni */

	trn_len = sprintf(trnbuf, "AI%c;", trn == RIG_TRN_RIG ? '2' : '0');

	return ts2k_transaction(rig, trnbuf, trn_len, NULL, NULL);
	// No reply on "ai2;"--how quaint!
}
/*
 * kenwood_get_trn
 * Assumes rig!=NULL, trn!=NULL
 */ int ts2k_get_trn(RIG * rig, int *trn)
{
	unsigned char trnbuf[50];
	int trn_len, retval;

	trn_len = 50;
	retval = ts2k_transaction(rig, "AI;", 3, trnbuf, &trn_len);
	if (retval != RIG_OK)
		return retval;

	if (trn_len != 4) {
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_get_trn: wrong answer" "len=%u\n",
			  trn_len);
		return -RIG_ERJCTED;
	}
	*trn = trnbuf[2] != '0' ? RIG_TRN_RIG : RIG_TRN_OFF;

	return RIG_OK;
}

/*
 * kenwood_set_powerstat
 * Assumes rig!=NULL
 */
int ts2k_set_powerstat(RIG * rig, powerstat_t status)
{
	unsigned char pwrbuf[16], ackbuf[16];
	int pwr_len, ack_len = 16;

	pwr_len =
	    sprintf(pwrbuf, "PS%c;", status == RIG_POWER_ON ? '1' : '0');

	return ts2k_transaction(rig, pwrbuf, pwr_len, ackbuf, &ack_len);
}
/*
 * kenwood_get_powerstat
 * Assumes rig!=NULL, trn!=NULL
 */
int ts2k_get_powerstat(RIG * rig, powerstat_t * status)
{
	unsigned char pwrbuf[50];
	int pwr_len = 50, retval;

	// No reply when powered off, 1=power on.  Geez!
	retval = ts2k_transaction(rig, "PS;", 3, pwrbuf, &pwr_len);
	if (retval != RIG_OK)
		return retval;

	if (pwr_len != 4) {
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_get_powerstat: wrong answer "
			  "len=%u\n", pwr_len);
		return -RIG_ERJCTED;
	}
	*status = pwrbuf[2] == '0' ? RIG_POWER_OFF : RIG_POWER_ON;

	return RIG_OK;
}

/*
 * kenwood_reset
 * Assumes rig!=NULL
 */
int ts2k_reset(RIG * rig, reset_t reset)
{
	unsigned char rstbuf[16], ackbuf[16];
	int rst_len, ack_len = 16;
	char rst;

	switch (reset) {
	case RIG_RESET_VFO:
		rst = '1';
		break;
	case RIG_RESET_MASTER:
		rst = '2';
		break;
	default:
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_reset: unsupported reset %u\n", reset);
		return -RIG_EINVAL;
	}
	rst_len = sprintf(rstbuf, "SR%c;", rst);

	// Largely untested!    ;)      --kd7eni
	return ts2k_transaction(rig, rstbuf, rst_len, ackbuf, &ack_len);
}

/*
 * kenwood_send_morse
 * Assumes rig!=NULL
 */
int ts2k_send_morse(RIG * rig, vfo_t vfo, const char *msg)
{
	unsigned char morsebuf[30], ackbuf[16];
	int morse_len, ack_len = 0;
	int msg_len, buff_len, retval;
	const char *p;

	p = msg;
	msg_len = strlen(msg);

	while (msg_len > 0) {
		/*
		 * TODO: check with "KY;" if char buffer is available.
		 *              if not, sleep.
		 */
		buff_len = msg_len > 24 ? 24 : msg_len;

		strcpy(morsebuf, "KY ");
		strncat(morsebuf, p, buff_len);
		strcat(morsebuf, ";");
		morse_len = 4 + buff_len;
		ack_len = 16;
		retval =
		    ts2k_transaction(rig, morsebuf, morse_len,
				     ackbuf, &ack_len);
		if (retval != RIG_OK)
			return retval;

		msg_len -= buff_len;
		p += buff_len;
	}
	return RIG_OK;
}

/*
 * kenwood_vfo_op
 * Assumes rig!=NULL
 */
int ts2k_vfo_op(RIG * rig, vfo_t vfo, vfo_op_t op)
{
	unsigned char *cmd, ackbuf[16];
	int ack_len = 0;

	switch (op) {
	case RIG_OP_UP:
		cmd = "UP;";
		break;
	case RIG_OP_DOWN:
		cmd = "DN;";
		break;
	case RIG_OP_BAND_UP:
		cmd = "BD;";
		break;
	case RIG_OP_BAND_DOWN:
		cmd = "BU;";
		break;
	default:
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_vfo_op: unsupported op %#x\n", op);
		return -RIG_EINVAL;
	}
	ack_len = 16;
	return ts2k_transaction(rig, cmd, 3, NULL, NULL);
}

/*
 * kenwood_set_mem
 * Assumes rig!=NULL
 */
int ts2k_set_mem(RIG * rig, vfo_t vfo, int ch)
{
	unsigned char membuf[16], ackbuf[16];
	int mem_len, ack_len = 16;

	/*
	 * "MCbmm;"
	 * where b is the bank number, mm the memory number.
	 * b can be a space ( *not*! manual wrong. --kd7eni
	 */
	mem_len = sprintf(membuf, "MC%03d;", ch);

	return ts2k_transaction(rig, membuf, mem_len, NULL, NULL);
}
/*
 * kenwood_get_mem
 * Assumes rig!=NULL
 */ int ts2k_get_mem(RIG * rig, vfo_t vfo, int *ch)
{
	unsigned char membuf[50];
	int retval, mem_len;

	/*
	 * "MCbmm;"
	 * where b is the bank number, mm the memory number.
	 * b can be a space (*not* --kd7eni)
	 */

	mem_len = 10;
	retval = ts2k_transaction(rig, "MC;", 3, membuf, &mem_len);
	if (retval != RIG_OK)
		return retval;

	if (mem_len != 6) {
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_get_mem: wrong answer " "len=%u\n",
			  mem_len);
		return -RIG_ERJCTED;
	}
	membuf[5] = '\0';
	*ch = atoi(membuf + 2);

	return RIG_OK;
}

/*
 * kenwood_get_info
 * supposed to work only for TS2000...
 * Assumes rig!=NULL
 */
const char *ts2k_get_info(RIG * rig)
{
	unsigned char firmbuf[50];
	int firm_len, retval;

	firm_len = 50;
	retval = ts2k_transaction(rig, "TY;", 3, firmbuf, &firm_len);
	if (retval != RIG_OK)
		return NULL;

	if (firm_len != 6) {
		rig_debug(RIG_DEBUG_ERR,
			  "ts2k_get_info: wrong answer len=%u\n",
			  firm_len);
		return NULL;
	}

	switch (firmbuf[4]) {
	case '0':
		return "Firmware: Overseas type";
	case '1':
		return "Firmware: Japanese 100W type";
	case '2':
		return "Firmware: Japanese 20W type";
	default:
		return "Firmware: unknown";
	}
}

#define IDBUFSZ 16

/*
 * probe_kenwood
 */
rig_model_t probe_ts2k(port_t * port)
{
	unsigned char idbuf[IDBUFSZ];
	int id_len, i, k_id;
	int retval;

	if (!port)
		return RIG_MODEL_NONE;

	port->write_delay = port->post_write_delay = 0;
	port->timeout = 50;
	port->retry = 1;

	retval = serial_open(port);
	if (retval != RIG_OK)
		return RIG_MODEL_NONE;

	retval = write_block(port, "ID;", 3);
	id_len = read_string(port, idbuf, IDBUFSZ, EOM_KEN EOM_TH, 2);

	close(port->fd);

	if (retval != RIG_OK)
		return RIG_MODEL_NONE;

	/* 
	 * reply should be something like 'IDxxx;'
	 */
	if (id_len != 5 || id_len != 6) {
		idbuf[7] = '\0';
		rig_debug(RIG_DEBUG_VERBOSE,
			  "probe_ts2k: protocol error,"
			  " expected %u, received %u: %s\n", 6,
			  id_len, idbuf);
		return RIG_MODEL_NONE;
	}


	/* first, try ID string */
	for (i = 0; ts2k_id_string_list[i].model != RIG_MODEL_NONE; i++) {
		if (!strncmp(ts2k_id_string_list[i].id, idbuf + 2, 16)) {
			rig_debug(RIG_DEBUG_VERBOSE,
				  "probe_ts2k: " "found %s\n", idbuf + 2);
			return ts2k_id_string_list[i].model;
		}
	}

	/* then, try ID numbers */

	k_id = atoi(idbuf + 2);

	for (i = 0; ts2k_id_list[i].model != RIG_MODEL_NONE; i++) {
		if (ts2k_id_list[i].id == k_id) {
			rig_debug(RIG_DEBUG_VERBOSE,
				  "probe_ts2k: " "found %03d\n", k_id);
			return ts2k_id_list[i].model;
		}
	}
	/*
	 * not found in known table.... 
	 * update ts2k_id_list[]!
	 */
	rig_debug(RIG_DEBUG_WARN,
		  "probe_ts2k: found unknown device "
		  "with ID %03d, please report to Hamlib "
		  "developers.\n", k_id);

	return RIG_MODEL_NONE;
}


/* kenwood_init
 *
 * Basically, it sets up *priv
 * REM: serial port is already open (rig->state.rigport.fd)
 */
int ts2k_init(RIG * rig)
{
	const struct rig_caps *caps;
	const struct ts2k_priv_caps *priv_caps;
	rig_debug(RIG_DEBUG_TRACE, __FUNCTION__ ": called\n");

	if (!rig || !rig->caps)
		return -RIG_EINVAL;

	caps = rig->caps;
	if (!caps->priv)
		return -RIG_ECONF;

	priv_caps = (const struct ts2k_priv_caps *) caps->priv;

#if 0				/* No private data for Kenwood backends */
	priv = (struct ts2k_priv_data *)
	    malloc(sizeof(struct ts2k_priv_data));
	if (!priv) {
		/* whoops! memory shortage! */
		return -RIG_ENOMEM;
	}

	rig->state.priv = (void *) priv;
	/* Assign default values */
	priv->dummy = -1;	// placeholder for real entries.
#endif

	return RIG_OK;
}

/* kenwood_cleanup
 * the serial port is closed by the frontend
 */
int ts2k_cleanup(RIG * rig)
{
	rig_debug(RIG_DEBUG_TRACE, __FUNCTION__ ": called\n");

	if (!rig)
		return -RIG_EINVAL;

	if (rig->state.priv)
		free(rig->state.priv);
	rig->state.priv = NULL;

	return RIG_OK;
}
/*
 * initrigs_kenwood is called by rig_backend_load
 */ int initrigs_ts2k(void *be_handle)
{
	rig_debug(RIG_DEBUG_VERBOSE, "ts2k: _init called\n");

	rig_register(&ts950sdx_caps);
	rig_register(&ts50s_caps);
	rig_register(&ts450s_caps);
	rig_register(&ts570d_caps);
	rig_register(&ts570s_caps);
	rig_register(&ts790_caps);
	rig_register(&ts850_caps);
	rig_register(&ts870s_caps);
	rig_register(&ts2000_caps);
	rig_register(&thd7a_caps);
	rig_register(&thf7e_caps);

	return RIG_OK;
}

/*****************************************************************************
	Added the following functions.
 	(C) Copyright 2002 by Dale E. Edmons.   All rights Reserved.
	License:	Identical to all other Hamlib code.
*****************************************************************************/

#define CHKERR(c)	if((c) != RIG_OK) return c
#define STUFF(c)	int retval, acklen=(c); char ack[c]
//#define STUFF(c)	static int retval, acklen=(c); static char ack[c]

// The following two are expensive but convenient!

int ncpy(char *tmp, char *src, int cnt)
{
	strncpy(tmp, src, cnt);
	tmp[cnt] = '\0';
	return RIG_OK;
}

int int_n(char *tmp, char *src, const int cnt)
{
	ncpy(tmp, src, cnt);
	return atoi(tmp);
}

/*
 * ts2k_get_ctrl() ts2000 transceiver check.  Tests and returns the value of the current
 *	PTT/CTRL (using "dc;") for main and sub transceivers.  The settings are:
 *
 *	    PTT	__    __ CTRL	'0' = main; '1' = sub
 *		   \ /
 *		"dc00;"	PTT && CTRL both on main
 *		"dc01;"	PTT on main; CTRL on sub 
 *		"dc10;"	PTT on sub; CTRL both on main
 *		"dc11;"	PTT && CTRL both on sub
 */
//int ts2k_get_ctrl(RIG * rig, char *dc_buf, int dc_len)	// use this when static removed.
char *ts2k_get_ctrl(RIG * rig)
{
	// FIXME: I guess this shouldn't be static for re-entrancy --Dale
	static char dc_buf[16];
	static int dc_len;

	static int retval;
	dc_len = 16;

 //	rig_debug(RIG_DEBUG_VERBOSE, "ts2k_get_ctrl: getting PTT/CTRL bytes\n");

	retval = ts2k_transaction(rig, "dc;", 3, dc_buf, &dc_len);
	if (retval != RIG_OK) {
		rig_debug(RIG_DEBUG_VERBOSE,
			  "ts2k_get_ctrl:error: retval=%u, dc_buf=%s\n",
			  retval, dc_buf);
		return NULL;
	}
//      rig_debug(RIG_DEBUG_VERBOSE, "ts2k_get_ctrl: returning %u PTT/CTRL bytes\n", dc_len);

//	return RIG_OK;	// use this when static variable removed and all other code changed.
	return dc_buf;
}

/* NOTE: PTT/CTRL enable is set only if ptt != 0 (or ctrl) */
int ts2k_set_ctrl(RIG * rig, int ptt, int ctrl)
{
	STUFF(10);
	char *buf;
	int buflen = 10;

	buf = ts2k_get_ctrl(rig);
	if(buf==NULL) {
		rig_debug(RIG_DEBUG_VERBOSE, __FUNCTION__ \
			": returned NULL!\n"); 
		return -RIG_EINVAL;
	}
	rig_debug(RIG_DEBUG_VERBOSE, __FUNCTION__": curr='%s',"
			"ptt=%u, ctrl=%u\n", buf, ptt, ctrl);

	if (ptt != 0)
		buf[2] = (TS2K_PTT_ON_SUB == ptt) ? '1' : '0';
	if (ctrl != 0)
		buf[3] = (TS2K_CTRL_ON_SUB == ctrl) ? '1' : '0';

	buf[4] = ';';
	buf[5] = '\0';	// just for printing debug

	acklen=10;
	retval = ts2k_transaction(rig, buf, 5, NULL, NULL);
	CHKERR(retval);

	return RIG_OK;
}

/*
 * FIXME: simple ascii to integer converter--expensive!
 *	Prevents trashing original string, otherwise
 *	just drop a (char)0 where you need it.
 */
int ts2k_get_int(char *src, int cnt)
{
	static char buf[20];

	strncpy(buf, src, cnt);
	buf[cnt] = (char) 0;

	return atoi(buf);
}

int ts2k_get_rit(RIG * rig, vfo_t vfo, shortfreq_t * rit)
{
	int retval, acklen;
	char ack[40];
	char *ctrl;

	ctrl = ts2k_get_ctrl(rig);
	if (ctrl == NULL)	// do we have valid "dc;" ?
		return -RIG_EINVAL;	// don't know proper errors yet!

	// sub shows main's rit!  We return 0 if subreceiver.
	if (ctrl[3] == '1') {
		*rit = 0;
		return RIG_OK;	// not really, but it's paid for...
	}
	retval = ts2k_transaction(rig, "if;", 3, ack, &acklen);
	CHKERR(retval);

	ack[23] = (char) 0;
	*rit = atoi(&ack[17]);

	return RIG_OK;
}

/*
 * ts2k_get_xit()  We just call ts2k_get_rit()
 * On the ts2k, they're the same.  The rig
 * acts this way.
*/
int ts2k_get_xit(RIG * rig, vfo_t vfo, shortfreq_t * rit)
{
	return ts2k_get_rit(rig, vfo, rit);
}

int ts2k_set_rit(RIG * rig, vfo_t vfo, shortfreq_t rit)
{
	char buf[40], c;
	int retval, i, len;

	// Clear current rit/xit.
	retval = ts2k_transaction(rig, "rc;", 3, NULL, NULL);
	CHKERR(retval);

	// Execute up/down request.
	if (rit > 0) {
		c = 'u';
		i = rit;
	} else {
		c = 'd';
		i = -rit;
	}

	len = sprintf(buf, "r%c%6d;", c, i);

	return ts2k_transaction(rig, buf, len, NULL, NULL);
}

int ts2k_set_xit(RIG * rig, vfo_t vfo, shortfreq_t rit)
{
	return ts2k_set_rit(rig, vfo, rit);
}

int ts2k_get_ts(RIG * rig, vfo_t vfo, shortfreq_t * ts)
{
	STUFF(10);
	int m, s;

	retval = ts2k_transaction(rig, "ST;", 5, ack, &acklen);
	CHKERR(retval);

	ack[4] = '\0';
	s = atoi(&ack[2]);

	rig_debug(RIG_DEBUG_VERBOSE, __FUNCTION__": received: '%s', %u\n", ack, s);

	retval = ts2k_transaction(rig, "MD;", 5, ack, &acklen);
	CHKERR(retval);

	// fm or am mode selects 1
	m = (ack[2] == '4' || ack[2] == '5') ? 1 : 0;

	rig_debug(RIG_DEBUG_VERBOSE, __FUNCTION__": received: '%s', %u\n", ack, m);

	*ts = ts2k_steps[m][s];
	return RIG_OK;
}

// FIXME: should get nearest, not easiest.  :)
/*
 *	status:	working.  fixed timeout.	--Dale
 */
int ts2k_set_ts(RIG * rig, vfo_t vfo, shortfreq_t ts)
{
	STUFF(10);
	char st[10];
	int m, s, k;
	long int aver, diff[2];

	retval = ts2k_transaction(rig, "md;", 5, ack, &acklen);
	CHKERR(retval);

	// fm or am mode selects 1
	m = (ack[2] == '4' || ack[2] == '5') ? 1 : 0;

	// fm or am selects 10 step freq. else 4.
	k = ( m )? 10 : 4;

	s=0;
	if( ts < ts2k_steps[m][0] ) {
		k=s;
	}

	if( ts > ts2k_steps[m][9] ) {
		s=9;
	}

	for ( s=1 ; s<k; s++) {
		if ( ts2k_steps[m][s-1] <= ts && ts <= ts2k_steps[m][s]) {
			diff[0] = ts - ts2k_steps[m][s-1];
			diff[1] = ts2k_steps[m][s] - ts;
			if( diff[0] > diff[1] ) {	// closer to [s]
				break;
			} else {			// closer to [s-1]
				s--;
				break;
			}
		}
	}
	// m is tmp now
	m = sprintf(st, "st0%1u;", s);

	// no reply!
	retval = ts2k_transaction(rig, st, m, NULL, NULL);
	CHKERR(retval);

	return RIG_OK;
}

/* Rig truncates in kHz(50) steps, so we don't. */
int ts2k_get_rptr_offs(RIG * rig, vfo_t vfo, shortfreq_t * rptr_offs)
{
	STUFF(20);

	retval = ts2k_transaction(rig, "of;", 3, ack, &acklen);
	CHKERR(retval);

	if (ack[0] != 'O' || ack[1] != 'F')
		return -RIG_EINVAL;
	if (acklen != 12)
		return -RIG_EINVAL;

	ack[11] = '\0';
	*rptr_offs = atoi(&ack[2]);

	return RIG_OK;
}

/* Rig truncates in kHz(50) steps, so we don't. */
int ts2k_set_rptr_offs(RIG * rig, vfo_t vfo, shortfreq_t rptr_offs)
{
	STUFF(20);
	char buf[20];
	int buflen = 20, i;

	i = sprintf(buf, "of%09u;", (unsigned int) rptr_offs);

	retval = ts2k_transaction(rig, buf, i, NULL, NULL);
	CHKERR(retval);

	return RIG_OK;
}

/*
 *	status:	working, leaves vfo intact.
 */
int ts2k_get_rptr_shift(RIG * rig, vfo_t vfo, rptr_shift_t * rptr_shift)
{
	STUFF(20);
	vfo_t vfo_tmp;

	// FIXME: I don't know if I should change back to currVFO, but I do.
	retval = ts2k_get_vfo(rig, &vfo_tmp);
	CHKERR(retval);

	retval = ts2k_transaction(rig, "os;", 3, ack, &acklen);
	CHKERR(retval);

	retval = ts2k_vfo_ctrl(rig, vfo);

	if (ack[0] != 'O' || ack[1] != 'S')
		return -RIG_EINVAL;
	//if(acklen != 4) return -RIG_EINVAL;

	switch (ack[2]) {
	case '0':
		*rptr_shift = RIG_RPT_SHIFT_NONE;
		break;
	case '1':
		*rptr_shift = RIG_RPT_SHIFT_MINUS;
		break;
	case '2':
		*rptr_shift = RIG_RPT_SHIFT_PLUS;
		break;
	case '3':
		*rptr_shift = RIG_RPT_SHIFT_1750;
		break;

	default:
		return -RIG_EINVAL;
		break;
	}

	// FIXME: I don't know if I should change back to currVFO, but I do.
	retval = ts2k_set_vfo(rig, vfo_tmp);
	CHKERR(retval);

	return RIG_OK;
}

/*
 * ts2k_set_rptr_shift()
 *
 *	status:		doesn't check VFO yet	--Dale
 */
int ts2k_set_rptr_shift(RIG * rig, vfo_t vfo, rptr_shift_t rptr_shift)
{
	STUFF(10);
	char c;

	switch (rptr_shift) {
	case RIG_RPT_SHIFT_NONE:
		c = '0';
		break;
	case RIG_RPT_SHIFT_PLUS:
		c = '1';
		break;
	case RIG_RPT_SHIFT_MINUS:
		c = '2';
		break;
	case RIG_RPT_SHIFT_1750:	// FIXME: invalid for mine! (non-Etype)
		c = '3';
		break;

	default:
		return -RIG_EINVAL;
		break;
	}
	acklen = sprintf(ack, "os%c;", c);

	retval = ts2k_transaction(rig, ack, 4, NULL, NULL);
	CHKERR(retval);

	return RIG_OK;
}

int ts2k_get_split(RIG * rig, vfo_t vfo, split_t * split)
{
	STUFF(10);
	char ack2[10];
	int ack2len = 10;

	retval = ts2k_transaction(rig, "fr;", 3, ack, &acklen);
	CHKERR(retval);

	retval = ts2k_transaction(rig, "ft;", 3, ack2, &ack2len);
	CHKERR(retval);

	if (ack[2] != ack2[2])
		*split = RIG_SPLIT_ON;
	else
		*split = RIG_SPLIT_OFF;

	return RIG_OK;
}

/* no VFO check, and no mem.  Yet. */
int ts2k_set_split(RIG * rig, vfo_t vfo, split_t split)
{
	STUFF(10);
	char ack2[10];
	int ack2len = 10;

	retval = ts2k_transaction(rig, "fr;", 3, ack, &acklen);
	CHKERR(retval);

	retval = ts2k_transaction(rig, "ft;", 3, ack2, &ack2len);
	CHKERR(retval);

	if (split == RIG_SPLIT_ON) {	// RX/TX on different vfo's
		if (ack[2] == '0')
			ack2[2] = '1';
		else if (ack[2] == '1')
			ack2[2] = '0';

		/* FIXME: mem split.  mem/vfo split must enable menu 6a
		 * with the "ex...;" or mem/vfo won't work.  A split
		 * memory operates simply by storing a memory with the
		 * freq with RX != TX then recalling it.  Don't know if
		 * if two separate memories can be used (e.g. 100/101)
		 */
		else
			return -RIG_EINVAL;
	} else {		// RX/TX on same vfo (or mem etc...)
		ack2[2] = ack[2];
	}

	// now, just send back the rig's strings :)
	retval = ts2k_transaction(rig, ack, acklen, NULL, NULL);
	CHKERR(retval);

	retval = ts2k_transaction(rig, ack2, ack2len, NULL, NULL);
	CHKERR(retval);

	return RIG_OK;
}

int ts2k_get_split_freq(RIG * rig, vfo_t vfo, freq_t * tx_freq)
{
	// FIXME : This makes too many assumptions--I'll fix it later...d PTT/CTRL bytes\n", dc_len);*//* The following are functions I've added for the TS-2000.  (C) 2002 D. Edmons and all that. *//*-------------------------------------------------------------------------------------------*/
	return ts2k_get_freq(rig, vfo, tx_freq);
}

int ts2k_set_split_freq(RIG * rig, vfo_t vfo, freq_t tx_freq)
{
	// FIXME : This makes too many assumptions--I'll fix it later...d PTT/CTRL bytes\n", dc_len);*//* The following are functions I've added for the TS-2000.  (C) 2002 D. Edmons and all that. *//*-------------------------------------------------------------------------------------------*/
	return ts2k_set_freq(rig, vfo, tx_freq);
}

/* ts2k_get_channel()
 *
 *	status:	working!  reading unset memory is an error.  this
 *		is the rig.  vfo_t is not needed for regular memory
 *		and we don't check it now.  when we start to access
 *		other memory we'll split into multiple functions
 *		each dedicated to the unique type (e.g. quick/tmp).
 *
 *		memory range not checked.
 *
 *	Note: the manual erroneously states bank should be ' ' if
 *		memory is <100.  The "correct" way is to *never*
 *		send a ' ' or you'll get a "?;" response.  Always
 *		set memory with (e.g.) "mc1020;".	--kd7eni
 */
int ts2k_get_channel(RIG * rig, channel_t *chan)
{
//	channel_t tch;	// needed?
	char rxtx, mrtxt[2][60], mrcmd[15], ack[60], tmp[20];
	int i, check, retval, curr_mem, mrtxt_len, mrcmd_len, ack_len;
	vfo_t curr_vfo;
#ifdef _USEVFO
	vfo_t v;

	v = vfo;
	// check the memory bit
	if( !(v & RIG_VFO_MEM) ) {
		rig_debug(RIG_DEBUG_ERR, __FUNCTION__
			": Non Memory VFO='%u', v=%u\n", vfo, v);
		return -RIG_EINVAL;	// not a channel!
	}

// get needed info if rig's mem pointers used
	if( (	vfo == RIG_VFO_MEM_A
		|| vfo == RIG_VFO_MEM_C ) ) {
		rig_debug(RIG_DEBUG_ERR, __FUNCTION__": using rig's ptr\n");
		retval = ts2k_get_vfo(rig, &curr_vfo);
		CHKERR(retval);
		retval = ts2k_get_mem(rig, curr_vfo, &curr_mem);
		CHKERR(retval);
		chan->channel_num = curr_mem;
	}
#endif

	mrtxt_len = ack_len = 60;
	mrcmd_len=15;

	if(chan==NULL)
		return -RIG_EINVAL;

// send request for both rx mem and tx mem
	for(i=0; i<2; i++) {	// 0=rx; 1=tx

		mrcmd_len = sprintf(mrcmd, "mr%01d%03u;", i, chan->channel_num); 
		ack_len = 60;	// must be reset inside loop!
		retval = ts2k_transaction(rig, mrcmd, mrcmd_len, ack, &ack_len); 
		CHKERR(retval);

		rig_debug(RIG_DEBUG_ERR, __FUNCTION__": read \n\t'%s'\n", ack);

		ack[50] = '\0';		// May be too far, but helps.

		// watch out.  |= and != look the same!
		// Perform checks on data.
		check = (ack[0] != 'M');
		check |= (ack[1] != 'R');
		check |= (ack[2] != ((i == 0)? '0' : '1'));
		check |= (chan->channel_num != int_n(tmp, &ack[3], 3));
		if( check ) {
			rig_debug(RIG_DEBUG_ERR, __FUNCTION__
				":check failed!\n");
			return -RIG_EINVAL;	// correct error type?
		}

		// all is well, save string
		strncpy( &mrtxt[i][0], ack, 52);
	}
	// FIXME: handle mem split
	// final check on data. (if RX!=TX mem split, or limit!)
	if( strncmp( &mrtxt[0][3], &mrtxt[1][3], 41-3) != 0 ) {
		rig_debug(RIG_DEBUG_ERR, "\n"__FUNCTION__
			": MEM split and band limits not yet supported!\n");
		return -RIG_EINVAL;	// FIXME: sending proper error?
	}

	// FIXME: 1) Since chan is not an array, we fudge and only do TX!
	//		even if split!!!!!!!!
	// FIXME: 2) we only handle regular memories, not everything
	// FIXME: 3) I store only data ts2k actually saves in memory
	//		some values are the ts2k value, not the hamlib value!

	// (keep same order as channel struct to ease debugging!)
//	chan->channel_num = ;	// already set?

// The following may be used to indicate we're reading limits (290-299).
// At any rate, it's currently unused. 
	chan->bank_num = 0;	// I merge the two--do not use! --Dale

	chan->lock = int_n(tmp, &mrtxt[0][18], 1);
	chan->freq = int_n(tmp, &mrtxt[0][06], 11);
	chan->mode = ts2k_mode_list[ int_n(tmp, &mrtxt[0][17], 1) ];
	if(chan->mode == RIG_MODE_AM || chan->tx_mode == RIG_MODE_FM)
		i = 1;
	else
		i = 0;
	chan->width = 0;
	chan->tx_freq = int_n(tmp, &mrtxt[1][06], 11);
	chan->tx_mode = ts2k_mode_list[ int_n(tmp, &mrtxt[1][17], 1)];
	chan->tx_width = 0;
	chan->split = 0;
	chan->rptr_shift = int_n(tmp, &mrtxt[1][28], 1);
	chan->rptr_offs = int_n(tmp, &mrtxt[1][29], 9);
	if(chan->tx_mode == RIG_MODE_AM || chan->tx_mode == RIG_MODE_FM)
		i = 1;
	else
		i = 0;
	chan->tuning_step = ts2k_steps[i][ int_n(tmp,& mrtxt[1][38], 2) ];
	chan->rit = 0;
	chan->xit = 0;
	chan->funcs = 0;
	for( i=0; i<RIG_SETTING_MAX; i++) {
		// the following shamelessly stolen from rigctl.c  --Dale
		setting_t level = rig_idx2setting(i);	// now, I understand
		if(RIG_LEVEL_IS_FLOAT(level))
			chan->levels[i].f = 0.0;	// I'd figured this out.
		else
			chan->levels[i].f = 0.0;
	}
	chan->ctcss_tone = ts2k_ctcss_list[ int_n(tmp, &mrtxt[1][22], 2) + 1 ];
	chan->ctcss_sql = int_n(tmp, &mrtxt[1][19], 1);
	chan->dcs_code = ts2k_dcs_list[ int_n(tmp, &mrtxt[1][24], 3) ];
	chan->dcs_sql = int_n(tmp, &mrtxt[1][19], 1);
	chan->scan_group = int_n(tmp, &mrtxt[1][40], 1);
//	chan->flags = curr_vfo;	// n/a
	// FIXME : The following may have trailing garbage
	strncpy( chan->channel_desc, &mrtxt[1][41], 8);
	chan->channel_desc[8] = '\0';

#ifdef _USEVFO
// if curr mem is changed at top, this'll restore it
	if( (	vfo == RIG_VFO_MEM_A
		|| vfo == RIG_VFO_MEM_C ) ) {
	}

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": restoring mem=%i\n", curr_mem);
	retval = ts2k_set_mem(rig, curr_vfo, curr_mem);
	CHKERR(retval);
#endif

	return RIG_OK;
}

/*
 * ts2k_set_channel()	Here I read a channel_t and memory data.  I'm
 *	assuming this is correct.  Anyway, this is a useful function.
 *	The only quirk is that the TS-2000 saves both TX as well
 *	as RX data separately.  This function can easily be modified
 *	to write as if there were 600 memories but that would lead
 *	to confusion and the rig certainly don't operate that way.
 *	I hope to channel_t *chan changed to channel_t *chan[2].
 *	(As well as vfo_t too.)  Much thanks to Stephane for already
 *	having the most important stuff working from the start
 *	(mostly anyway).
 *
 *	From here on out I'm gonna try to use hamlib-1.1.0.pdf
 *	for the design document and hopefully I'll know which way
 *	to go when things aren't the way they should be.  We'll
 *	see how things go.
 *						--Dale kd7e
 */
int ts2k_set_channel(RIG * rig, const channel_t *chan)
{
	char mrtxt[2][60], mrcmd[10], ack[60];
	int retval, i, j, mr_len[2], ack_len;

	// the following are the actual memory data to be written.
	unsigned int
		p1,	// RX/TX (bool)
		p2p3,	// this is not a bug--I combine bank/channel
		p4,	// freq
		p5,	// mode
		p6,	// lockout
		p7,	// tone, ctcss, dcs
		p8,	// tone #
		p9,	// CTCSS #
		p10,	// DCS code
		p11,	// revers status
		p12,	// repeater shift
		p13,	// offset freq
		p14,	// step size
		p15;	// memory group (0-9)
	char	*p16;	// 8 char + 1 null byte

	ack_len = 10;
	/*
	 * Write everthing in order.  We only set things that
	 * the rig actually puts in memory.  This way, a set
	 * followed by a get will match exactly.  Otherwise,
	 * we'll have to fudge and won't know when we have
	 * a valid save.  (FIXME: delete all this useless
	 * comment stuff after the code is working. --Dale)
	 */

	/* FIXME: we are required to have RX/TX match */
	if(chan->freq != chan->tx_freq)
		return -RIG_EINVAL;	// should be 'unimplemented'

	for(i=0; i<2; i++) {
		p1 = (unsigned int) i; // 0=RX, 1=TX

		p2p3 = (unsigned int) chan->channel_num;	// we'll compare 'em later
		if( i == 0 )
			p4 = (unsigned int) chan->freq;
		else
			p4 = (unsigned int) chan->tx_freq;

		for(j=0; j<(sizeof(ts2k_mode_list)/sizeof(int)); j++) {
			if(chan->mode == ts2k_mode_list[j])
				break;
		}
		p5 = (unsigned int) j;	// FIXME: either not found, or last!
		p6 = (unsigned int) chan->lock;
		p7 = 0;	// FIXME: to lazy to sort this out right now
		p8 = 0; //	"	"	"	"	"
		p9 = 0; //	"	"	"	"	"
		p10 = 0; //	"	"	"	"	"
		p11 = 0; //	"	"	"	"	"
		p12 = 0; //	"	"	"	"	"
		p13 = 0; //	"	"	"	"	"
		p14 = 0; //	"	"	"	"	"
		p15 = (unsigned int) chan->scan_group;
		p16 = &(chan->channel_desc[0]);

		mr_len[i] = sprintf( &(mrtxt[i][0]),
			"mr%1u%3u%11u%1u%1u%1u%2u%2u%3u%1u%1u%9u%2u%1u%s;",
			p1, p2p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13,
			p14, p15,
			p16
			);	// yikes!
		retval = ts2k_transaction(rig, &mrtxt[i][0], mr_len[i],
				 ack, &ack_len);
		CHKERR(retval);

		// FIXME: now readback the string and make sure it worked!
	}
	
	return retval;
}

/*
 * ts2k_vfo_ctrl()	set PTT/CTRL based on VFO arg
 *
 *	(Taken from my revision of ts2k_set_vfo())
 *
 *	status:		VFOA, VFOB, VFOC, Main, Sub,
 *			MEMA, MEMC, CALLA, CALLC 
 *			VFO_AB, VFO_BA, ...
 *		They all work!		--Dale
 */
int ts2k_vfo_ctrl(RIG * rig, vfo_t vfo)
{
	int ptt, ctrl;
	int retval;

	ptt = ctrl = 0;

	// Main/Sub Active Transceiver
	switch (vfo) {
	case RIG_VFO_A:
	case RIG_VFO_B:
	case RIG_VFO_AB:	// split
	case RIG_VFO_BA:
	case RIG_CTRL_SAT:	// Should be PTT on main CTRL on sub (?)
	case RIG_VFO_MAIN:
	case RIG_VFO_MEM_A:
	case RIG_VFO_CALL_A:
		ctrl = TS2K_CTRL_ON_MAIN;	// FIXME : these are independent!
		ptt = TS2K_PTT_ON_MAIN;
		break;
	case RIG_VFO_C:
	case RIG_VFO_SUB:
	case RIG_VFO_MEM_C:
	case RIG_VFO_CALL_C:
		ctrl = TS2K_CTRL_ON_SUB;
		ptt = TS2K_PTT_ON_SUB;
		break;

	default:
		break;
	}

	// set PTT/CTRL
	retval = ts2k_set_ctrl(rig, ptt, ctrl);
	if (retval != RIG_OK)
		return -RIG_EINVAL;

	return retval;
}

/*
 *	status:	ok, no vfo checks
 */
int ts2k_get_dcs_code(RIG * rig, vfo_t vfo, tone_t *code)
{
	char ack[10], tmp[10];
	int retval, acklen, i;

	acklen = 10;
	retval = ts2k_transaction(rig, "qc;", 6, ack, &acklen);
	CHKERR(retval);

	i = int_n(tmp, &ack[2], 3);
	*code = ts2k_dcs_list[i];

	return RIG_OK;
}

/*
 *	status:	ok, no vfo checks
 */
int ts2k_set_dcs_code(RIG * rig, vfo_t vfo, tone_t code)
{
	char ack[10], cmd[10], tmp[10];
	int retval, acklen, cmdlen, i;

	// we only allow exact matches here
	i=0;
	while(code != ts2k_dcs_list[i]) {
		if(ts2k_dcs_list[i] == 0)
			return -RIG_EINVAL;
		i++;
	}
	cmdlen = sprintf(cmd, "qc%03u;", i);
	return ts2k_transaction(rig, cmd, cmdlen, NULL, NULL);
}

/*
 *	status:	new, all guesses and assumptions!
 *		know nothing about txwidth (which one?)
 *		or tx = rx + txwidth?
 */
int ts2k_set_split_mode(RIG * rig,
	vfo_t vfo, rmode_t txmode, pbwidth_t txwidth)
{
	vfo_t vtmp;

	switch(vfo) {
	case RIG_VFO_AB:
		vtmp = RIG_VFO_B; break;
	case RIG_VFO_BA:
		vtmp = RIG_VFO_A; break;
	default:
		return -RIG_EINVAL;
	}
	return ts2k_set_mode(rig, vtmp, txmode, txwidth);
}

int ts2k_get_split_mode(RIG *rig,
	vfo_t vfo, rmode_t *txmode, pbwidth_t *txwidth)
{
	vfo_t vtmp;
	switch(vfo) {
	case RIG_VFO_AB:
		vtmp = RIG_VFO_B; break;
	case RIG_VFO_BA:
		vtmp = RIG_VFO_A; break;
	default:
		return -RIG_EINVAL;
	}
	return ts2k_get_mode(rig, vtmp, txmode, txwidth);
}

/*
 *	status:	new
 */
int ts2k_scan(RIG *rig, vfo_t vfo, scan_t scan, int ch)
{
	int retval;
	vfo_t v;

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": starting...\n");

	if(vfo==RIG_VFO_CURR) {
		retval = ts2k_get_vfo(rig, &v);
		CHKERR(retval);
	} else
		v = vfo;

	// hopefully, this'll work.  rig does nothing if already in scan!
	retval = ts2k_scan_off(rig);
	CHKERR(retval);

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": got VFO = %x\n", v);
	// set proper vfo first (already done?)
	switch(v) {
	case RIG_VFO_MEM:	// Currently selected Main/Sub
	case RIG_VFO_MEM_A:	// Main
	case RIG_VFO_MEM_C:	// Sub
		// FIXME: we should set the group and fall through
		/* nobreak */
//	case RIG_VFO_VFO:	// Currently selected Main/Sub
	case RIG_VFO_A:		// Main
	case RIG_VFO_B:		// Main
	case RIG_VFO_C:		// Sub
		retval = ts2k_set_vfo(rig, v);	// already set?
		CHKERR(retval);
		break;
		
	case RIG_VFO_CALL_A:	// 
	case RIG_VFO_CALL_C:
	default:
		rig_debug(RIG_DEBUG_ERR, __FUNCTION__": vfo 'defaulted'\n");
		return -RIG_ENIMPL;	// unimplemented, but valid scan
	}

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": VFO set!\n");
	retval = ts2k_scan_off(rig);
	CHKERR(retval);

	switch(scan) {

	case RIG_SCAN_STOP: 
		return ts2k_scan_off(rig);
		break;

	case RIG_SCAN_PROG:
		if(ch<290)
			return -RIG_EINVAL;
		retval = ts2k_set_mem(rig, vfo, ch);
		CHKERR(retval);
		/* nobreak */
	case RIG_SCAN_MEM:
		/* nobreak */
	case RIG_SCAN_VFO:
		return ts2k_scan_on(rig, '1');	// Look Ma, I'm scanning!
		break;

	case RIG_SCAN_SLCT:
		/* nobreak */
	case RIG_SCAN_PRIO:
		/* nobreak */
	default:
		rig_debug(RIG_DEBUG_ERR, __FUNCTION__": scan 'defaulted'\n");
		return -RIG_ENIMPL;	// unimplemented, but valid scan
	}
}

int ts2k_scan_on(RIG *rig, char sc)
{
	char cmd[]="sc0;", ack[10];
	int retval, cmdlen, acklen, i;

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": turning scan on\n");
	cmd[2] = sc;
	retval = ts2k_transaction(rig, cmd, 4, NULL, NULL);
	CHKERR(retval);

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": turned scan on\n");
	// force reply when turning scan on
	if(sc != '0') {
		cmd[2] = ';'; cmd[3] = '\0';
		retval = ts2k_transaction(rig, cmd, 3, ack, &acklen);
		CHKERR(retval);

		if(ack[2] != sc)
			return -RIG_EINVAL;
	}

	return RIG_OK;
}

int ts2k_scan_off(RIG *rig)
{
	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": turning scan off\n");
	return ts2k_scan_on(rig, '0');
}


int ts2k_get_parm(RIG *rig, setting_t parm, value_t *val)
{
	int retval, acklen, cmdlen;
	char ack[30], cmd[30];

	switch( parm ) {
	case RIG_PARM_BEEP:
		cmdlen = sprintf(cmd, "ex0120000;"); break;
	case RIG_PARM_BACKLIGHT:
		cmdlen = sprintf(cmd, "ex0000000;"); break;
	case RIG_PARM_KEYLIGHT:
		cmdlen = sprintf(cmd, "ex0010000;"); break;
	case RIG_PARM_APO:
		cmdlen = sprintf(cmd, "ex0570000;"); break;
	case RIG_PARM_ANN:
		return -RIG_ENIMPL;
	case RIG_PARM_TIME:
		return -RIG_ENAVAIL;
	default:
		return -RIG_EINTERNAL;	
	}
	acklen = 30;
	retval = ts2k_transaction(rig, cmd, cmdlen, ack, &acklen);
	if(retval == RIG_OK) {
		switch( parm ) {
		case RIG_PARM_BEEP:
			val->i = (int)(ack[9] - '0');
			break;
		case RIG_PARM_BACKLIGHT:
		case RIG_PARM_KEYLIGHT:
			val->f = (float)(ack[9] - '0');
			break;
		case RIG_PARM_APO:
			val->i = (int) int_n(cmd, &ack[9], 2);	// cmd is TMP now
			break;
		default:
			return -RIG_EINTERNAL;
		}
	}
	return retval;
}

int ts2k_set_parm(RIG *rig, setting_t parm, value_t val)
{
	char cmd[30];
	int retval, cmdlen, i;

	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": val.i = %u\n", val.i);
	rig_debug(RIG_DEBUG_ERR, __FUNCTION__": val.f = %f\n", val.f);

	switch( parm ) {
	case RIG_PARM_APO:
		cmdlen = sprintf(cmd, "ex0570000%01u;",
					(val.i>180)? 3: val.i/60);
		break;
	case RIG_PARM_BEEP:
		cmdlen = sprintf(cmd, "ex0120000%01u;", (val.i>9)? 9: val.i);
		break;

	case RIG_PARM_BACKLIGHT:
		cmdlen = sprintf(cmd, "ex0000000%01u;",
				(int) ((val.f>1.0)? 4.0 : val.f*4.0) );
		break;

	case RIG_PARM_KEYLIGHT:
		cmdlen = sprintf(cmd, "ex0010000%01u;", (val.i==0)? 0: 1);
		break;

	case RIG_PARM_ANN:
		return -RIG_ENIMPL;

	case RIG_PARM_TIME:
	case RIG_PARM_BAT:
		return -RIG_ENAVAIL;

	default:
		return -RIG_EINTERNAL;
	}

	retval = ts2k_transaction(rig, cmd, cmdlen, NULL, NULL);

	return retval;
}

#undef CHKERR
#undef STUFF

// End
