/*
 *  Hamlib Kenwood backend - IC-10 interface for:
 *    TS-940, TS-811, TS-711, TS-440, and R-5000
 *
 *  Copyright (c) 2000-2010 by Stephane Fillod and others
 *
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public
 *   License as published by the Free Software Foundation; either
 *   version 2.1 of the License, or (at your option) any later version.
 *
 *   This library 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
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <math.h>
#include <ctype.h>   /* character class tests */

#include "hamlib/rig.h"
#include "serial.h"
#include "misc.h"
#include "register.h"

#include "kenwood.h"
#include "ic10.h"


/****
 * ic10_cmd_trim
 * gobbles up additional spaces at the end of a line
 *
****/
int ic10_cmd_trim(char *data, int data_len)
{

    int i;
    rig_debug(RIG_DEBUG_TRACE, "%s: incoming data_len is '%d'\n",
              __func__, data_len);

    /* suck up additional spaces at end of the data buffer */
    for (i = data_len; !isdigit((int)data[i - 1]); i--)
    {
        data_len = data_len - 1;
        rig_debug(RIG_DEBUG_TRACE, "%s: data['%d'] is '%c'\n",
                  __func__, i - 1, data[i - 1]);
        rig_debug(RIG_DEBUG_TRACE, "%s: For i='%d' data_len is now '%d'\n",
                  __func__, i, data_len);
    }

    rig_debug(RIG_DEBUG_TRACE,
              "%s: finished loop.. i='%d' data_len='%d' data[i-1]='%c'\n",
              __func__, i, data_len, data[i - 1]);

    return data_len;
}


/**
 * ic10_transaction
 * Assumes rig!=NULL rig->state!=NULL rig->caps!=NULL
**/
int ic10_transaction(RIG *rig, const char *cmd, int cmd_len, char *data,
                     int *data_len)
{
    int retval;
    int retry_cmd = 0;
    struct rig_state *rs;

    rig_debug(RIG_DEBUG_TRACE,
              "%s: called cmd='%s', len=%d, data=%p, data_len=%p\n", __func__, cmd, cmd_len,
              data, data_len);

    rs = &rig->state;

transaction:
    rig_flush(&rs->rigport);

    retval = write_block(&rs->rigport, cmd, cmd_len);

    if (retval != RIG_OK)
    {
        return retval;
    }

    if (!data)
    {
        char buffer[50];
        struct kenwood_priv_data *priv = rig->state.priv;

        if (RIG_OK != (retval = write_block(&rs->rigport, priv->verify_cmd
                                            , strlen(priv->verify_cmd))))
        {
            return retval;
        }

        // this should be the ID response
        retval = read_string(&rs->rigport, buffer, sizeof(buffer), ";", 1);

        // might be ?; too
        if (buffer[0] == '?' && retry_cmd++ < rs->rigport.retry)
        {
            rig_debug(RIG_DEBUG_ERR, "%s: retrying cmd #%d\n", __func__, retry_cmd);
            goto transaction;
        }

        if (strncmp("ID", buffer, 2))
        {
            rig_debug(RIG_DEBUG_ERR, "%s: expected ID response and got %s\n", __func__,
                      buffer);
            return retval;
        }

        return RIG_OK;
    }

    retval = read_string(&rs->rigport, data, 50, ";", 1);

    if (retval == -RIG_ETIMEOUT)
    {
        retval = 0;
    }

    if (retval < 0)
    {
        return retval;
    }

    *data_len = retval;

    return RIG_OK;
}

/*
 * Get the answer of IF command, with retry handling
 */
static int get_ic10_if(RIG *rig, char *data)
{
    struct kenwood_priv_caps *priv = (struct kenwood_priv_caps *)rig->caps->priv;
    int i, data_len, retval = RIG_EINVAL;

    rig_debug(RIG_DEBUG_TRACE, "%s: called\n", __func__);

    for (i = 0; retval != RIG_OK && i < rig->caps->retry; i++)
    {
        data_len = 37;
        retval = ic10_transaction(rig, "IF;", 3, data, &data_len);

        if (retval != RIG_OK)
        {
            continue;
        }

        if (retval == RIG_OK &&
                (data_len < priv->if_len ||
                 data[0] != 'I' || data[1] != 'F'))
        {
            rig_debug(RIG_DEBUG_WARN, "%s: unexpected answer %s, len=%d\n",
                      __func__, data, data_len);
            retval = -RIG_ERJCTED;
        }
    }

    return retval;
}


/*
 * ic10_set_vfo
 * Assumes rig!=NULL
 */
int ic10_set_vfo(RIG *rig, vfo_t vfo)
{
    char cmdbuf[6];
    int cmd_len, retval;
    char vfo_function;

    switch (vfo)
    {
    case RIG_VFO_VFO:
    case RIG_VFO_MAIN:
    case RIG_VFO_A: vfo_function = '0'; break;

    case RIG_VFO_SUB:
    case RIG_VFO_B: vfo_function = '1'; break;

    case RIG_VFO_MEM: vfo_function = '2'; break;

    case RIG_VFO_CURR: return RIG_OK;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n",
                  __func__, rig_strvfo(vfo));
        return -RIG_EINVAL;
    }

    cmd_len = sprintf(cmdbuf, "FN%c;", vfo_function);

    retval = ic10_transaction(rig, cmdbuf, cmd_len, NULL, 0);
    return retval;
}


/*
 * ic10_get_vfo
 * Assumes rig!=NULL, !vfo
 */
int ic10_get_vfo(RIG *rig, vfo_t *vfo)
{
    struct kenwood_priv_caps *priv = (struct kenwood_priv_caps *)rig->caps->priv;
    char vfobuf[50];
    unsigned char c;
    int retval, iflen;

    rig_debug(RIG_DEBUG_VERBOSE, "%s: called\n", __func__);

    /* query RX VFO */
    retval = get_ic10_if(rig, vfobuf);

    if (retval != RIG_OK)
    {
        return retval;
    }

    /* trim extra spaces */
    iflen = ic10_cmd_trim(vfobuf, priv->if_len);

    /* IFggmmmkkkhhh snnnzrx yytdfcp */
    /* IFggmmmkkkhhhxxxxxrrrrrssxcctmfcp */

    c = vfobuf[iflen - 3];

    switch (c)
    {
    case '0': *vfo = RIG_VFO_A; break;

    case '1': *vfo = RIG_VFO_B; break;

    case '2': *vfo = RIG_VFO_MEM; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %c\n",
                  __func__, c);
        return -RIG_EPROTO;
    }

    rig_debug(RIG_DEBUG_VERBOSE, "%s: returning VFO=%s\n", __func__,
              rig_strvfo(*vfo));

    return RIG_OK;
}


int ic10_set_split_freq(RIG *rig, vfo_t vfo, freq_t tx_freq)
{
    rig_debug(RIG_DEBUG_VERBOSE, "%s: called vfo=%s tx_freq=%.0f\n", __func__,
              rig_strvfo(vfo), tx_freq);
    return ic10_set_freq(rig, RIG_VFO_B, tx_freq);
}

int ic10_get_split_freq(RIG *rig, vfo_t vfo, freq_t *tx_freq)
{
    rig_debug(RIG_DEBUG_VERBOSE, "%s: called vfo=%s\n", __func__, rig_strvfo(vfo));
    return ic10_get_freq(rig, RIG_VFO_B, tx_freq);
}

int ic10_set_split_vfo(RIG *rig, vfo_t vfo, split_t split, vfo_t txvfo)
{
    rig_debug(RIG_DEBUG_VERBOSE, "%s: called\n", __func__);
    return ic10_transaction(rig, split == RIG_SPLIT_ON ? "SP1;" : "SP0;", 4,
                            NULL, 0);
}


int ic10_get_split_vfo(RIG *rig, vfo_t vfo, split_t *split, vfo_t *txvfo)
{
    struct kenwood_priv_caps *priv = (struct kenwood_priv_caps *)rig->caps->priv;
    char infobuf[50];
    int retval, iflen;

    retval = get_ic10_if(rig, infobuf);

    if (retval != RIG_OK)
    {
        return retval;
    }

    /* trim extra spaces */
    iflen = ic10_cmd_trim(infobuf, priv->if_len);

    /* IFggmmmkkkhhh snnnzrx yytdfcp */
    /* IFggmmmkkkhhhxxxxxrrrrrssxcctmfcp */

    *split = infobuf[iflen - 1] == '0' ? RIG_SPLIT_OFF : RIG_SPLIT_ON;

    return RIG_OK;
}


/*
 * ic10_get_mode
 * Assumes rig!=NULL, !vfo
 */
int ic10_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width)
{
    struct kenwood_priv_caps *priv = (struct kenwood_priv_caps *)rig->caps->priv;
    char modebuf[50];
    unsigned char c;
    int retval, iflen;

    /* query RX VFO */
    retval = get_ic10_if(rig, modebuf);

    if (retval != RIG_OK)
    {
        return retval;
    }

    /* trim extra spaces */
    iflen = ic10_cmd_trim(modebuf, priv->if_len);

    /* IFggmmmkkkhhh snnnzrx yytdfcp */
    /* IFggmmmkkkhhhxxxxxrrrrrssxcctmfcp */

    c = modebuf[iflen - 4];

    switch (c)
    {
    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;

    case MD_NONE:   *mode = RIG_MODE_NONE; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode '%c'\n",
                  __func__, c);
        return -RIG_EINVAL;
    }

    *width = rig_passband_normal(rig, *mode);

    return RIG_OK;
}


/*
 * ic10_set_mode
 * Assumes rig!=NULL
 */
int ic10_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width)
{
    char modebuf[6];
    int mode_len, retval;
    char mode_letter;

    switch (mode)
    {
    case RIG_MODE_LSB  :    mode_letter = MD_LSB; break;

    case RIG_MODE_USB  :    mode_letter = MD_USB; break;

    case RIG_MODE_CW   :    mode_letter = MD_CW; break;

    case RIG_MODE_FM   :    mode_letter = MD_FM; break;

    case RIG_MODE_AM   :    mode_letter = MD_AM; break;

    case RIG_MODE_RTTY :    mode_letter = MD_FSK; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode %s\n",
                  __func__, rig_strrmode(mode));
        return -RIG_EINVAL;
    }

    mode_len = sprintf(modebuf, "MD%c;", mode_letter);
    retval = ic10_transaction(rig, modebuf, mode_len, NULL, 0);

    return retval;
}


/*
 * ic10_get_freq
 * Assumes rig!=NULL, freq!=NULL
 */
int ic10_get_freq(RIG *rig, vfo_t vfo, freq_t *freq)
{
    char infobuf[50];
    int retval;

    if (vfo != RIG_VFO_CURR)
    {
        /* targeted freq retrieval */
        return kenwood_get_freq(rig, vfo, freq);
    }

    retval = get_ic10_if(rig, infobuf);

    if (retval != RIG_OK)
    {
        return retval;
    }

    /* IFggmmmkkkhhh snnnzrx yytdfcp */
    /* IFggmmmkkkhhhxxxxxrrrrrssxcctmfcp */

    infobuf[13] = '\0';
    sscanf(infobuf + 2, "%011"SCNfreq, freq);

    return RIG_OK;
}


/*
 * ic10_set_freq
 * Assumes rig!=NULL
 */
int ic10_set_freq(RIG *rig, vfo_t vfo, freq_t freq)
{
    char freqbuf[64];
    int freq_len, retval;
    unsigned char vfo_letter;
    vfo_t   tvfo;

    if (vfo == RIG_VFO_CURR)
    {
        tvfo = rig->state.current_vfo;
    }
    else
    {
        tvfo = vfo;
    }

    switch (tvfo)
    {
    case RIG_VFO_A:
    case RIG_VFO_MAIN: vfo_letter = 'A'; break;

    case RIG_VFO_B:
    case RIG_VFO_SUB: vfo_letter = 'B'; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %s\n",
                  __func__, rig_strvfo(vfo));
        return -RIG_EINVAL;
    }

    // cppcheck-suppress *
    freq_len = sprintf(freqbuf, "F%c%011"PRIll";", vfo_letter, (int64_t)freq);
    retval = ic10_transaction(rig, freqbuf, freq_len, NULL, 0);

    return retval;
}


/*
 * ic10_set_ant
 * Assumes rig!=NULL
 */
int ic10_set_ant(RIG *rig, vfo_t vfo, ant_t ant, value_t option)
{
    char buf[6], ackbuf[64];
    int len, ack_len, retval;

    len = sprintf(buf, "AN%c;", ant == RIG_ANT_1 ? '1' : '2');

    retval = ic10_transaction(rig, buf, len, ackbuf, &ack_len);

    return retval;
}


/*
 * ic10_get_ant
 * Assumes rig!=NULL, ptt!=NULL
 */
int ic10_get_ant(RIG *rig, vfo_t vfo, ant_t dummy, value_t *option,
                 ant_t *ant_curr, ant_t *ant_tx, ant_t *ant_rx)
{
    char infobuf[50];
    int info_len, retval;

    info_len = 4;
    retval = ic10_transaction(rig, "AN;", 3, infobuf, &info_len);

    if (retval != RIG_OK)
    {
        return retval;
    }

    if (info_len < 4 || infobuf[0] != 'A' || infobuf[1] != 'N')
    {
        rig_debug(RIG_DEBUG_ERR, "%s: wrong answer len=%d\n",
                  __func__, info_len);
        return -RIG_ERJCTED;
    }

    *ant_curr = infobuf[2] == '1' ? RIG_ANT_1 : RIG_ANT_2;

    return RIG_OK;
}


/*
 * ic10_get_ptt
 * Assumes rig!=NULL, ptt!=NULL
 */
int ic10_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt)
{
    struct kenwood_priv_caps *priv = (struct kenwood_priv_caps *)rig->caps->priv;
    char infobuf[50];
    int retval, iflen;

    retval = get_ic10_if(rig, infobuf);

    if (retval != RIG_OK)
    {
        return retval;
    }

    /* trim extra spaces */
    iflen = ic10_cmd_trim(infobuf, priv->if_len);

    /* IFggmmmkkkhhh snnnzrx yytdfcp */
    /* IFggmmmkkkhhhxxxxxrrrrrssxcctmfcp */

    *ptt = infobuf[iflen - 5] == '0' ? RIG_PTT_OFF : RIG_PTT_ON;

    return RIG_OK;
}


// Not referenced anywhere
/*
 * ic10_set_ptt
 * Assumes rig!=NULL
 */
int ic10_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt)
{
    char pttbuf[4];
    int ptt_len, retval;
    unsigned char ptt_letter;

    rig_debug(RIG_DEBUG_VERBOSE, "%s: called\n", __func__);

    switch (ptt)
    {
    case RIG_PTT_OFF: ptt_letter = 'R'; break;

    case RIG_PTT_ON : ptt_letter = 'T'; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported PTT %d\n",
                  __func__, ptt);
        return -RIG_EINVAL;
    }

    ptt_len = sprintf(pttbuf, "%cX;", ptt_letter);
    retval = ic10_transaction(rig, pttbuf, ptt_len, NULL, 0);

    return retval;
}


/*
 * ic10_get_mem
 * Assumes rig!=NULL
 */
int ic10_get_mem(RIG *rig, vfo_t vfo, int *ch)
{
    struct kenwood_priv_caps *priv = (struct kenwood_priv_caps *)rig->caps->priv;
    char membuf[50];
    int retval, iflen;

    retval = get_ic10_if(rig, membuf);

    if (retval != RIG_OK)
    {
        return retval;
    }

    /* trim extra spaces */
    iflen = ic10_cmd_trim(membuf, priv->if_len);

    /* IFggmmmkkkhhh snnnzrx yytdfcp */
    /* IFggmmmkkkhhhxxxxxrrrrrssxcctmfcp */
    membuf[iflen - 5] = '\0';
    *ch = atoi(membuf + priv->if_len - 7);

    return RIG_OK;
}


/*
 * ic10_set_mem
 * Assumes rig!=NULL
 */
int ic10_set_mem(RIG *rig, vfo_t vfo, int ch)
{
    char membuf[8], ackbuf[64];
    int mem_len, ack_len, retval;

    mem_len = sprintf(membuf, "MC%02d;", ch);

    retval = ic10_transaction(rig, membuf, mem_len, ackbuf, &ack_len);

    return retval;
}


int ic10_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only)
{
    char membuf[16], infobuf[32];
    int retval, info_len, len;

    len = sprintf(membuf, "MR00%02d;", chan->channel_num);
    info_len = 24;
    retval = ic10_transaction(rig, membuf, len, infobuf, &info_len);

    if (retval != RIG_OK)
    {
        return retval;
    }

    /* MRs-ccfffffffffffml----; */
    /* 012345678901234567890123 */
    switch (infobuf[17])
    {
    case MD_CW  :   chan->mode = RIG_MODE_CW; break;

    case MD_USB :   chan->mode = RIG_MODE_USB; break;

    case MD_LSB :   chan->mode = RIG_MODE_LSB; break;

    case MD_FM  :   chan->mode = RIG_MODE_FM; break;

    case MD_AM  :   chan->mode = RIG_MODE_AM; break;

    case MD_FSK :   chan->mode = RIG_MODE_RTTY; break;

    case MD_NONE:   chan->mode = RIG_MODE_NONE; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode '%c'\n",
                  __func__, infobuf[17]);
        return -RIG_EINVAL;
    }

    chan->width = rig_passband_normal(rig, chan->mode);

    sscanf(infobuf + 6, "%011"SCNfreq, &chan->freq);
    chan->vfo = RIG_VFO_MEM;

    if (chan->channel_num >= 90)
    {
        chan->split = 1;
        /* TX VFO (Split channel only) */
        len = sprintf(membuf, "MR10%02d;", chan->channel_num);
        info_len = 24;
        retval = ic10_transaction(rig, membuf, len, infobuf, &info_len);

        if (retval == RIG_OK && info_len > 17)
        {

            /* MRn rrggmmmkkkhhhdz    ; */
            switch (infobuf[17])
            {
            case MD_CW  :   chan->tx_mode = RIG_MODE_CW; break;

            case MD_USB :   chan->tx_mode = RIG_MODE_USB; break;

            case MD_LSB :   chan->tx_mode = RIG_MODE_LSB; break;

            case MD_FM  :   chan->tx_mode = RIG_MODE_FM; break;

            case MD_AM  :   chan->tx_mode = RIG_MODE_AM; break;

            case MD_FSK :   chan->tx_mode = RIG_MODE_RTTY; break;

            case MD_NONE:   chan->tx_mode = RIG_MODE_NONE; break;

            default:
                rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode '%c'\n",
                          __func__, infobuf[17]);
                return -RIG_EINVAL;
            }

            chan->tx_width = rig_passband_normal(rig, chan->tx_mode);

            sscanf(infobuf + 6, "%011"SCNfreq, &chan->tx_freq);
        }
    }

    if (!read_only)
    {
        // Set rig to channel values
        rig_debug(RIG_DEBUG_ERR,
                  "%s: please contact hamlib mailing list to implement this\n", __func__);
        rig_debug(RIG_DEBUG_ERR,
                  "%s: need to know if rig updates when channel read or not\n", __func__);
        return -RIG_ENIMPL;
    }

    return RIG_OK;
}


int ic10_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan)
{
    char membuf[64];
    int retval, len, md;
    int64_t freq;

    freq = (int64_t) chan->freq;

    if (chan->channel_num < 90 &&  chan->tx_freq != 0)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: Transmit/split can only be on channels 90-99\n",
                  __func__);
        return -RIG_EINVAL;
    }

    switch (chan->mode)
    {
    case RIG_MODE_CW  : md = MD_CW; break;

    case RIG_MODE_USB : md = MD_USB; break;

    case RIG_MODE_LSB : md = MD_LSB; break;

    case RIG_MODE_FM  : md = MD_FM; break;

    case RIG_MODE_AM  : md = MD_AM; break;

    case RIG_MODE_RTTY: md = MD_FSK; break;

    case RIG_MODE_NONE: md = MD_NONE; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode %s\n",
                  __func__, rig_strrmode(chan->mode));
        return -RIG_EINVAL;
    }

    /* MWnxrrggmmmkkkhhhdzxxxx; */
    len = sprintf(membuf, "MW0 %02d%011"PRIll"%c0    ;",
                  chan->channel_num,
                  freq,
                  md
                 );
    retval = ic10_transaction(rig, membuf, len, NULL, 0);

    if (retval != RIG_OK)
    {
        return retval;
    }

    /* TX VFO (Split channel only) */
    freq = chan->tx_freq;

    switch (chan->tx_mode)
    {
    case RIG_MODE_CW:   md = MD_CW; break;

    case RIG_MODE_USB:  md = MD_USB; break;

    case RIG_MODE_LSB:  md = MD_LSB; break;

    case RIG_MODE_FM:   md = MD_FM; break;

    case RIG_MODE_AM:   md = MD_AM; break;

    case RIG_MODE_RTTY: md = MD_FSK; break;

    case RIG_MODE_NONE: md = MD_NONE; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode %s\n",
                  __func__, rig_strrmode(chan->tx_mode));
        return -RIG_EINVAL;
    }

    if (chan->channel_num >= 90)
    {
        /* MWnxrrggmmmkkkhhhdzxxxx; */
        len = sprintf(membuf, "MW1 %02d%011"PRIll"%c0    ;",
                      chan->channel_num,
                      freq,
                      md
                     );
        retval = ic10_transaction(rig, membuf, len, NULL, 0);

        // I assume we need to check the retval here -- W9MDB
        // This was found from cppcheck
        if (retval != RIG_OK)
        {
            rig_debug(RIG_DEBUG_ERR, "%s: transaction failed: %s\n", __func__,
                      rigerror(retval));
            return retval;
        }
    }

    return RIG_OK;
}


/*
 * ic10_get_func
 * Assumes rig!=NULL, val!=NULL
 */
int ic10_get_func(RIG *rig, vfo_t vfo, setting_t func, int *status)
{
    char cmdbuf[6], fctbuf[50];
    int cmdlen, fct_len, retval;

    fct_len = 4;

    switch (func)
    {
    case RIG_FUNC_LOCK: cmdlen = sprintf(cmdbuf, "LK;"); break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported get_func %s",
                  __func__, rig_strfunc(func));
        return -RIG_EINVAL;
    }

    retval = ic10_transaction(rig, cmdbuf, cmdlen, fctbuf, &fct_len);

    if (retval != RIG_OK)
    {
        return retval;
    }

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

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

    return RIG_OK;
}


/*
 * ic10_set_func
 * Assumes rig!=NULL, val!=NULL
 */
int ic10_set_func(RIG *rig, vfo_t vfo, setting_t func, int status)
{
    char cmdbuf[4], fctbuf[16], ackbuf[64];
    int cmdlen, fct_len, ack_len;

    switch (func)
    {
    case RIG_FUNC_LOCK:
        cmdlen = sprintf(cmdbuf, "LK");

        if (cmdlen < 0)
        {
            return -RIG_ETRUNC;
        }

        break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported set_func %s",
                  __func__, rig_strfunc(func));
        return -RIG_EINVAL;
    }

    fct_len = sprintf(fctbuf, "%s%c;", cmdbuf, status == 0 ? '0' : '1');

    if (fct_len < 0)
    {
        return -RIG_ETRUNC;
    }

    return ic10_transaction(rig, fctbuf, fct_len, ackbuf, &ack_len);
}


/*
 * ic10_set_parm
 * Assumes rig!=NULL
 */
int ic10_set_parm(RIG *rig, setting_t parm, value_t val)
{
    char cmdbuf[50];
    int cmd_len;
    int hours;
    int minutes;
    int seconds;

    switch (parm)
    {
    case RIG_PARM_TIME:
        minutes = val.i / 60;
        hours = minutes / 60;
        seconds = val.i - (minutes * 60);
        minutes = minutes % 60;
        cmd_len = sprintf(cmdbuf, "CK1%02d%02d%02d;", hours, minutes, seconds);
        return ic10_transaction(rig, cmdbuf, cmd_len, NULL, NULL);
        break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: Unsupported set_parm %s\n",
                  __func__, rig_strparm(parm));
        return -RIG_EINVAL;
    }

    return RIG_OK;
}


/*
 * ic10_get_parm
 * Assumes rig!=NULL, val!=NULL
 */
int ic10_get_parm(RIG *rig, setting_t parm, value_t *val)
{
    int retval, lvl_len, i;
    char lvlbuf[50];

    switch (parm)
    {
    case RIG_PARM_TIME:
        lvl_len = 10;
        retval = ic10_transaction(rig, "CK1;", 4, lvlbuf, &lvl_len);

        if (retval != RIG_OK)
        {
            return retval;
        }

        /* "CK1hhmmss;"*/
        if (lvl_len != 10)
        {
            rig_debug(RIG_DEBUG_ERR, "%s: wrong answer len=%d\n",
                      __func__, lvl_len);
            return -RIG_ERJCTED;
        }

        /* convert ASCII to numeric 0..9 */
        for (i = 3; i < 9; i++)
        {
            lvlbuf[i] -= '0';
        }

        val->i = ((10 * lvlbuf[3] + lvlbuf[4]) * 60 + /* hours */
                  10 * lvlbuf[5] + lvlbuf[6]) * 60 + /* minutes */
                 10 * lvlbuf[7] + lvlbuf[8]; /* seconds */
        break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: Unsupported get_parm %s\n",
                  __func__, rig_strparm(parm));
        return -RIG_EINVAL;
    }

    return RIG_OK;
}


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

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

    return ic10_transaction(rig, pwrbuf, pwr_len, ackbuf, &ack_len);
}


/*
 * ic10_get_powerstat
 * Assumes rig!=NULL, trn!=NULL
 */
int ic10_get_powerstat(RIG *rig, powerstat_t *status)
{
    char pwrbuf[50];
    int pwr_len, retval;

    pwr_len = 4;
    retval = ic10_transaction(rig, "PS;", 3, pwrbuf, &pwr_len);

    if (retval != RIG_OK)
    {
        return retval;
    }

    if (pwr_len != 4)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: wrong answer len=%d\n",
                  __func__, pwr_len);
        return -RIG_ERJCTED;
    }

    *status = pwrbuf[2] == '0' ? RIG_POWER_OFF : RIG_POWER_ON;

    return RIG_OK;
}


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

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

    return ic10_transaction(rig, trnbuf, trn_len, ackbuf, &ack_len);
}


/*
 * ic10_get_trn
 * Assumes rig!=NULL, trn!=NULL
 */
int ic10_get_trn(RIG *rig, int *trn)
{
    char trnbuf[50];
    int trn_len, retval;

    trn_len = 38;
    retval = ic10_transaction(rig, "AI;", 3, trnbuf, &trn_len);

    if (retval != RIG_OK)
    {
        return retval;
    }

    if (trn_len != 38)
    {
        rig_debug(RIG_DEBUG_ERR, "%s: wrong answer len=%d\n",
                  __func__, trn_len);
        return -RIG_ERJCTED;
    }

    *trn = trnbuf[2] != '0' ? RIG_TRN_RIG : RIG_TRN_OFF;

    return RIG_OK;
}


/*
 * ic10_vfo_op
 * Assumes rig!=NULL
 */
int ic10_vfo_op(RIG *rig, vfo_t vfo, vfo_op_t op)
{
    char *cmd, ackbuf[64];
    int ack_len;

    switch (op)
    {
    case RIG_OP_UP    : cmd = "UP;"; break;

    case RIG_OP_DOWN  : cmd = "DN;"; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported op %#x\n",
                  __func__, op);
        return -RIG_EINVAL;
    }

    return ic10_transaction(rig, cmd, 3, ackbuf, &ack_len);
}


/*
 * ic10_scan
 * Assumes rig!=NULL, val!=NULL
 */
int ic10_scan(RIG *rig, vfo_t vfo, scan_t scan, int ch)
{
    char ackbuf[64];
    int ack_len;

    return ic10_transaction(rig, scan == RIG_SCAN_STOP ? "SC0;" : "SC1;", 4,
                            ackbuf, &ack_len);
}


/*
 * ic10_get_info
 * Assumes rig!=NULL
 */
const char *ic10_get_info(RIG *rig)
{
    char firmbuf[50];
    int firm_len, retval;

    firm_len = 6;
    retval = ic10_transaction(rig, "ID;", 3, firmbuf, &firm_len);

    if (retval != RIG_OK)
    {
        return NULL;
    }

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

    switch (firmbuf[4])
    {
    case '4': return "ID: TS-440S";

    case '5': return "ID: R-5000";

    default: return "ID: unknown";
    }
}


/*
 * ic10_decode_event is called by sa_sigio, when some asynchronous
 * data has been received from the rig.
 */
int ic10_decode_event(RIG *rig)
{
    struct kenwood_priv_caps *priv = (struct kenwood_priv_caps *)rig->caps->priv;
    char asyncbuf[128], c;
    int retval, async_len = 128, iflen;
    vfo_t vfo;
    freq_t freq;
    rmode_t mode;
    ptt_t ptt;

    rig_debug(RIG_DEBUG_TRACE, "%s: called\n", __func__);

    retval = ic10_transaction(rig, NULL, 0, asyncbuf, &async_len);

    if (retval != RIG_OK)
    {
        return retval;
    }

    rig_debug(RIG_DEBUG_TRACE, "%s: Decoding message\n", __func__);



    /* --------------------------------------------------------------------- */
    if (async_len < priv->if_len || asyncbuf[0] != 'I' || asyncbuf[1] != 'F')
    {

        rig_debug(RIG_DEBUG_ERR, "%s: Unsupported transceive cmd '%s'\n",
                  __func__, asyncbuf);
        return -RIG_ENIMPL;
    }

    /* trim extra spaces */
    iflen = ic10_cmd_trim(asyncbuf, priv->if_len);

    /* IFggmmmkkkhhh snnnzrx yytdfcp */
    /* IFggmmmkkkhhhxxxxxrrrrrssxcctmfcp */

    c = asyncbuf[iflen - 3];

    switch (c)
    {
    case '0': vfo = RIG_VFO_A; break;

    case '1': vfo = RIG_VFO_B; break;

    case '2': vfo = RIG_VFO_MEM; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported VFO %c\n",
                  __func__, c);
        return -RIG_EPROTO;
    }

    c = asyncbuf[iflen - 4];

    switch (c)
    {
    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;

    case MD_NONE:   mode = RIG_MODE_NONE; break;

    default:
        rig_debug(RIG_DEBUG_ERR, "%s: unsupported mode '%c'\n",
                  __func__, c);
        return -RIG_EINVAL;
    }

    ptt = asyncbuf[iflen - 5] == '0' ? RIG_PTT_OFF : RIG_PTT_ON;

    asyncbuf[13] = '\0';
    sscanf(asyncbuf + 2, "%011"SCNfreq, &freq);

    /* Callback execution */
    if (rig->callbacks.vfo_event)
    {
        rig->callbacks.vfo_event(rig, vfo, rig->callbacks.vfo_arg);
    }

    if (rig->callbacks.freq_event)
    {
        rig->callbacks.freq_event(rig, vfo, freq, rig->callbacks.freq_arg);
    }

    if (rig->callbacks.mode_event)
    {
        rig->callbacks.mode_event(rig, vfo, mode, RIG_PASSBAND_NORMAL,
                                  rig->callbacks.mode_arg);
    }

    if (rig->callbacks.ptt_event)
    {
        rig->callbacks.ptt_event(rig, vfo, ptt, rig->callbacks.ptt_arg);
    }

    return RIG_OK;
}
