/* * hamlib - (C) Frank Singleton 2000 (vk3fcs@ix.netcom.com) * * icom.c - Copyright (C) 2000 Stephane Fillod * This shared library provides an API for communicating * via serial interface to an ICOM using the "CI-V" interface. * * * $Id: icom.c,v 1.13 2001-01-28 22:08:41 f4cfe Exp $ * * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include /* Standard input/output definitions */ #include /* String function definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include /* Error number definitions */ #include /* POSIX terminal control definitions */ #include #include #include #include #include #include #include "icom.h" #include "icom_defs.h" #include "frame.h" const struct ts_sc_list r8500_ts_sc_list[] = { { 10, 0x00 }, { 50, 0x01 }, { 100, 0x02 }, { KHz(1), 0x03 }, { 12500, 0x04 }, { KHz(5), 0x05 }, { KHz(9), 0x06 }, { KHz(10), 0x07 }, { 12500, 0x08 }, { KHz(20), 0x09 }, { KHz(25), 0x10 }, { KHz(100), 0x11 }, { MHz(1), 0x12 }, { 0, 0 }, /* programmable tuning step not supported */ }; const struct ts_sc_list ic737_ts_sc_list[] = { { 10, 0x00 }, { KHz(1), 0x01 }, { KHz(2), 0x02 }, { KHz(3), 0x03 }, { KHz(4), 0x04 }, { KHz(5), 0x05 }, { KHz(6), 0x06 }, { KHz(7), 0x07 }, { KHz(8), 0x08 }, { KHz(9), 0x09 }, { KHz(10), 0x10 }, { 0, 0 }, }; const struct ts_sc_list r75_ts_sc_list[] = { { 10, 0x00 }, { 100, 0x01 }, { KHz(1), 0x02 }, { KHz(5), 0x03 }, { 6250, 0x04 }, { KHz(9), 0x05 }, { KHz(10), 0x06 }, { 12500, 0x07 }, { KHz(20), 0x08 }, { KHz(25), 0x09 }, { KHz(100), 0x10 }, { MHz(1), 0x11 }, { 0, 0 }, }; const struct ts_sc_list r7100_ts_sc_list[] = { { 100, 0x00 }, { KHz(1), 0x01 }, { KHz(5), 0x02 }, { KHz(10), 0x03 }, { 12500, 0x04 }, { KHz(20), 0x05 }, { KHz(25), 0x06 }, { KHz(100), 0x07 }, { 0, 0 }, }; const struct ts_sc_list ic756_ts_sc_list[] = { { 10, 0x00 }, { KHz(1), 0x01 }, { KHz(5), 0x02 }, { KHz(9), 0x03 }, { KHz(10), 0x04 }, { 0, 0 }, }; const struct ts_sc_list ic706_ts_sc_list[] = { { 10, 0x00 }, { 100, 0x01 }, { KHz(1), 0x02 }, { KHz(5), 0x03 }, { KHz(9), 0x04 }, { KHz(10), 0x05 }, { 12500, 0x06 }, { KHz(20), 0x07 }, { KHz(25), 0x08 }, { KHz(100), 0x09 }, { 0, 0 }, }; struct icom_addr { rig_model_t model; unsigned char re_civ_addr; }; /* * Please, if the default CI-V address of your rig is listed as UNKNOWN_ADDR, * send the value to for inclusion. Thanks --SF * * TODO: sort this list with most frequent rigs first. */ static const struct icom_addr icom_addr_list[] = { { RIG_MODEL_IC706, 0x48 }, { RIG_MODEL_IC706MKII, 0x4e }, { RIG_MODEL_IC706MKIIG, 0x58 }, { RIG_MODEL_IC271, 0x20 }, { RIG_MODEL_IC275, 0x10 }, { RIG_MODEL_IC375, 0x12 }, { RIG_MODEL_IC471, 0x22 }, { RIG_MODEL_IC475, 0x14 }, { RIG_MODEL_IC575, 0x16 }, { RIG_MODEL_IC707, 0x3e }, { RIG_MODEL_IC725, 0x28 }, { RIG_MODEL_IC726, 0x30 }, { RIG_MODEL_IC728, 0x38 }, { RIG_MODEL_IC729, 0x3a }, { RIG_MODEL_IC731, 0x02 }, /* need confirmation */ { RIG_MODEL_IC735, 0x04 }, { RIG_MODEL_IC736, 0x40 }, { RIG_MODEL_IC746, 0x56 }, { RIG_MODEL_IC737, 0x3c }, { RIG_MODEL_IC738, 0x44 }, { RIG_MODEL_IC751, 0x1c }, { RIG_MODEL_IC751A, 0x1c }, { RIG_MODEL_IC756, 0x50 }, { RIG_MODEL_IC756PRO, 0x5c }, { RIG_MODEL_IC761, 0x1e }, { RIG_MODEL_IC765, 0x2c }, { RIG_MODEL_IC775, 0x46 }, { RIG_MODEL_IC781, 0x26 }, { RIG_MODEL_IC820, 0x42 }, { RIG_MODEL_IC821, 0x4c }, { RIG_MODEL_IC821H, 0x4c }, { RIG_MODEL_IC970, 0x2e }, { RIG_MODEL_IC1271, 0x24 }, { RIG_MODEL_IC1275, 0x18 }, { RIG_MODEL_ICR71, 0x1a }, { RIG_MODEL_ICR72, 0x32 }, { RIG_MODEL_ICR75, 0x5a }, { RIG_MODEL_ICR7000, 0x08 }, { RIG_MODEL_ICR7100, 0x34 }, { RIG_MODEL_ICR8500, 0x4a }, { RIG_MODEL_ICR9000, 0x2a }, { RIG_MODEL_MINISCOUT, 0x94 }, { RIG_MODEL_IC718, 0x36 }, /* need confirmation */ { -1, 0 }, }; /* * This is a generic icom_init function. * You might want to define yours, so you can customize it for your rig * * Basically, it sets up *priv * REM: serial port is already open (rig->state->fd) */ int icom_init(RIG *rig) { struct icom_priv_data *priv; int i; if (!rig) return -RIG_EINVAL; priv = (struct icom_priv_data*)malloc(sizeof(struct icom_priv_data)); if (!priv) { /* whoops! memory shortage! */ return -RIG_ENOMEM; } rig->state.priv = (void*)priv; /* TODO: CI-V address should be customizable */ /* * init the priv_data from static struct * + override with preferences */ priv->re_civ_addr = 0x00; for (i=0; icom_addr_list[i].model >= 0; i++) { if (icom_addr_list[i].model == rig->caps->rig_model) { priv->re_civ_addr = icom_addr_list[i].re_civ_addr; break; } } if (rig->caps->rig_model == RIG_MODEL_IC731 || rig->caps->rig_model == RIG_MODEL_IC735) priv->civ_731_mode = 1; else priv->civ_731_mode = 0; switch (rig->caps->rig_model) { case RIG_MODEL_IC737: priv->ts_sc_list = ic737_ts_sc_list; break; case RIG_MODEL_ICR7100: case RIG_MODEL_ICR72: priv->ts_sc_list = r7100_ts_sc_list; break; case RIG_MODEL_IC756: priv->ts_sc_list = ic756_ts_sc_list; break; case RIG_MODEL_ICR75: priv->ts_sc_list = r75_ts_sc_list; break; case RIG_MODEL_ICR8500: priv->ts_sc_list = r8500_ts_sc_list; break; case RIG_MODEL_IC706MKIIG: ic706mkiig_str_cal_init(rig); case RIG_MODEL_IC706MKII: case RIG_MODEL_ICR9000: default: priv->ts_sc_list = ic706_ts_sc_list; } return RIG_OK; } /* * ICOM Generic icom_cleanup routine * the serial port is closed by the frontend */ int icom_cleanup(RIG *rig) { if (!rig) return -RIG_EINVAL; if (rig->state.priv) free(rig->state.priv); rig->state.priv = NULL; return RIG_OK; } /* * icom_set_freq * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_freq(RIG *rig, vfo_t vfo, freq_t freq) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char freqbuf[16], ackbuf[16]; int freq_len,ack_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; freq_len = priv->civ_731_mode ? 4:5; /* * to_bcd requires nibble len */ to_bcd(freqbuf, freq, freq_len*2); icom_transaction (rig, C_SET_FREQ, -1, freqbuf, freq_len, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_freq: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_freq * Assumes rig!=NULL, rig->state.priv!=NULL, freq!=NULL */ int icom_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char freqbuf[16]; int freq_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; icom_transaction (rig, C_RD_FREQ, -1, NULL, 0, freqbuf, &freq_len); /* * freqbuf should contain Cn,Data area */ freq_len--; /* * is it a blank mem channel ? */ if (freq_len == 1 && freqbuf[0] == 0xff) { *freq = -1; return RIG_OK; } if (freq_len != (priv->civ_731_mode ? 4:5)) { rig_debug(RIG_DEBUG_ERR,"icom_get_freq: wrong frame len=%d\n", freq_len); return -RIG_ERJCTED; } /* * from_bcd requires nibble len */ *freq = from_bcd(freqbuf+1, freq_len*2); return RIG_OK; } /* * icom_set_mode * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char ackbuf[16],icmode_ext[1]; int ack_len,icmode; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; icmode = hamlib2icom_mode(mode,width); icmode_ext[0] = (icmode>>8) & 0xff; icom_transaction (rig, C_SET_MODE, icmode & 0xff, icmode_ext, icmode_ext[0]?1:0, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_mode: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_mode * Assumes rig!=NULL, rig->state.priv!=NULL, mode!=NULL, width!=NULL */ int icom_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char modebuf[16]; int mode_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; icom_transaction (rig, C_RD_MODE, -1, NULL, 0, modebuf, &mode_len); /* * modebuf should contain Cn,Data area */ mode_len--; if (mode_len != 2 && mode_len != 1) { rig_debug(RIG_DEBUG_ERR,"icom_get_mode: wrong frame len=%d\n", mode_len); return -RIG_ERJCTED; } icom2hamlib_mode(modebuf[1]| modebuf[2]<<8, mode, width); return RIG_OK; } /* * icom_set_vfo * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_vfo(RIG *rig, vfo_t vfo) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char ackbuf[16]; int ack_len,icvfo; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; switch(vfo) { case RIG_VFO_A: icvfo = S_VFOA; break; case RIG_VFO_B: icvfo = S_VFOB; break; default: rig_debug(RIG_DEBUG_ERR,"icom: Unsupported VFO %d\n", vfo); return -RIG_EINVAL; } icom_transaction (rig, C_SET_VFO, icvfo, NULL, 0, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_vfo: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_strength * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) { return -RIG_ENIMPL; } /* * compensate strength using calibration * assume STR_CAL_LENGTH!=0 */ static int comp_cal_str(int str_cal_raw[], int str_cal_db[], int icom_val) { int i; int interpolation; for (i=0; i=STR_CAL_LENGTH) return str_cal_db[i-1]; if (str_cal_raw[i] == str_cal_raw[i-1]) return str_cal_db[i]; /* cheap, less accurate, but no fp involved */ interpolation = ((str_cal_raw[i]-icom_val)*(str_cal_db[i]-str_cal_db[i-1]))/(str_cal_raw[i]-str_cal_raw[i-1]); return str_cal_db[i] - interpolation; } /* * icom_get_level * Assumes rig!=NULL, rig->state.priv!=NULL, val!=NULL */ int icom_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char lvlbuf[16]; int lvl_len; int lvl_cn, lvl_sc; /* Command Number, Subcommand */ int icom_val; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; /* Optimize: * sort the switch cases with the most frequent first */ switch (level) { case RIG_LEVEL_STRENGTH: lvl_cn = C_RD_SQSM; lvl_sc = S_SML; break; case RIG_LEVEL_SQLSTAT: lvl_cn = C_RD_SQSM; lvl_sc = S_SQL; break; case RIG_LEVEL_PREAMP: lvl_cn = C_CTL_FUNC; lvl_sc = S_FUNC_PAMP; break; case RIG_LEVEL_ATT: lvl_cn = C_CTL_ATT; lvl_sc = -1; break; case RIG_LEVEL_ANT: lvl_cn = C_CTL_ANT; lvl_sc = -1; break; case RIG_LEVEL_AF: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_AF; break; case RIG_LEVEL_RF: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_RF; break; case RIG_LEVEL_SQL: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_SQL; break; case RIG_LEVEL_IF: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_IF; break; case RIG_LEVEL_APF: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_APF; break; case RIG_LEVEL_NR: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_NR; break; case RIG_LEVEL_PBT_IN: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_PBTIN; break; case RIG_LEVEL_PBT_OUT: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_PBTOUT; break; case RIG_LEVEL_CWPITCH: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_CWPITCH; break; case RIG_LEVEL_RFPOWER: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_RFPOWER; break; case RIG_LEVEL_MICGAIN: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_MICGAIN; break; case RIG_LEVEL_KEYSPD: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_KEYSPD; break; case RIG_LEVEL_NOTCHF: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_NOTCHF; break; case RIG_LEVEL_COMP: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_COMP; break; case RIG_LEVEL_AGC: lvl_cn = C_CTL_FUNC; lvl_sc = S_FUNC_AGC; break; case RIG_LEVEL_BKINDL: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_BKINDL; break; case RIG_LEVEL_BALANCE: lvl_cn = C_CTL_LVL; lvl_sc = S_LVL_BALANCE; break; case RIG_LEVEL_ANN: lvl_cn = C_CTL_ANN; lvl_sc = -1; break; default: rig_debug(RIG_DEBUG_ERR,"Unsupported get_level %d", level); return -RIG_EINVAL; } icom_transaction (rig, lvl_cn, lvl_sc, NULL, 0, lvlbuf, &lvl_len); /* * strbuf should contain Cn,Sc,Data area */ lvl_len-=2; #if 0 if (lvl_len != 2) { rig_debug(RIG_DEBUG_ERR,"icom_get_level: wrong frame len=%d\n", lvl_len); return -RIG_EPROTO; } #endif /* * The result is a 3 digit BCD, but in *big endian* order: 0000..0255 * (from_bcd is little endian) */ icom_val = from_bcd_be(lvlbuf+2, lvl_len*2); switch (level) { case RIG_LEVEL_STRENGTH: val->i = comp_cal_str(priv->str_cal_raw,priv->str_cal_db,icom_val); break; case RIG_LEVEL_SQLSTAT: /* * 0x00=sql closed, 0x01=sql open */ val->i = icom_val; break; case RIG_LEVEL_PREAMP: case RIG_LEVEL_ATT: case RIG_LEVEL_ANT: val->i = icom_val; break; default: val->f = (float)icom_val/255; } rig_debug(RIG_DEBUG_VERBOSE,"strength: %d %d %d\n",lvl_len,icom_val,val->i); /* this stuff is wrong, use calibrated data instead */ #if 0 #define STR_FLOOR 44.0 #define STR_CEILING 223.0 #define STR_MAX 114.0 /* S9+60db */ #ifndef max #define max(a,b) ((a)>(b)?(a):(b)) #endif /* translate to db */ if (level == RIG_LEVEL_STRENGTH) val->i = rint(STR_MAX/(STR_CEILING-STR_FLOOR)*max(icom_val-STR_FLOOR,0)); else val->i = icom_val; rig_debug(RIG_DEBUG_VERBOSE,"%d %d %d\n",lvl_len,icom_val,val->i); #endif return RIG_OK; } /* * icom_set_ptt * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_ptt(RIG *rig, vfo_t vfo, ptt_t ptt) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char ackbuf[16], ptt_sc; int ack_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; ptt_sc = ptt == RIG_PTT_ON ? S_PTT_ON:S_PTT_OFF; icom_transaction (rig, C_CTL_PTT, ptt_sc, NULL, 0, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_ptt: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_ptt * Assumes rig!=NULL, rig->state.priv!=NULL, ptt!=NULL */ int icom_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char pttbuf[16]; int ptt_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; icom_transaction (rig, C_CTL_PTT, -1, NULL, 0, pttbuf, &ptt_len); /* * freqbuf should contain Cn,Data area */ ptt_len--; if (ptt_len != 1) { rig_debug(RIG_DEBUG_ERR,"icom_get_ptt: wrong frame len=%d\n", ptt_len); return -RIG_ERJCTED; } *ptt = pttbuf[1] == S_PTT_ON ? RIG_PTT_ON : RIG_PTT_OFF; return RIG_OK; } /* * icom_set_rptr_shift * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_rptr_shift(RIG *rig, vfo_t vfo, rptr_shift_t rptr_shift) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char ackbuf[16]; int ack_len; int rptr_sc; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; switch (rptr_shift) { case RIG_RPT_SHIFT_NONE: rptr_sc = S_DUP_OFF; /* Simplex mode */ break; case RIG_RPT_SHIFT_MINUS: rptr_sc = S_DUP_M; /* Duplex - mode */ break; case RIG_RPT_SHIFT_PLUS: rptr_sc = S_DUP_P; /* Duplex + mode */ break; default: rig_debug(RIG_DEBUG_ERR,"Unsupported shift %d", rptr_shift); return -RIG_EINVAL; } icom_transaction (rig, C_CTL_SPLT, rptr_sc, NULL, 0, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_rptr_shift: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_rptr_shift * Assumes rig!=NULL, rig->state.priv!=NULL, rptr_shift!=NULL */ int icom_get_rptr_shift(RIG *rig, vfo_t vfo, rptr_shift_t *rptr_shift) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char rptrbuf[16]; int rptr_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; icom_transaction (rig, C_CTL_SPLT, -1, NULL, 0, rptrbuf, &rptr_len); /* * rptrbuf should contain Cn,Sc */ rptr_len--; if (rptr_len != 1) { rig_debug(RIG_DEBUG_ERR,"icom_get_rptr_shift: wrong frame len=%d\n", rptr_len); return -RIG_ERJCTED; } switch (rptrbuf[1]) { case S_DUP_OFF: *rptr_shift = RIG_RPT_SHIFT_NONE; /* Simplex mode */ break; case S_DUP_M: *rptr_shift = RIG_RPT_SHIFT_MINUS; /* Duples - mode */ break; case S_DUP_P: *rptr_shift = RIG_RPT_SHIFT_PLUS; /* Duplex + mode */ break; default: rig_debug(RIG_DEBUG_ERR,"Unsupported shift %d", rptrbuf[1]); return -RIG_EPROTO; } return RIG_OK; } /* * icom_set_rptr_offs * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_rptr_offs(RIG *rig, vfo_t vfo, shortfreq_t rptr_offs) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char offsbuf[16],ackbuf[16]; int ack_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; /* * Icoms are using a 100Hz unit (at least on 706MKIIg) -- SF */ to_bcd(offsbuf, rptr_offs/100, OFFS_LEN*2); icom_transaction (rig, C_SET_OFFS, -1, offsbuf, OFFS_LEN, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_rptr_offs: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_rptr_offs * Assumes rig!=NULL, rig->state.priv!=NULL, rptr_offs!=NULL */ int icom_get_rptr_offs(RIG *rig, vfo_t vfo, shortfreq_t *rptr_offs) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char offsbuf[16]; int offs_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; icom_transaction (rig, C_RD_OFFS, -1, NULL, 0, offsbuf, &offs_len); /* * offsbuf should contain Cn */ offs_len--; if (offs_len != OFFS_LEN) { rig_debug(RIG_DEBUG_ERR,"icom_get_rptr_offs: wrong frame len=%d\n", offs_len); return -RIG_ERJCTED; } /* * Icoms are using a 100Hz unit (at least on 706MKIIg) -- SF */ *rptr_offs = from_bcd(offsbuf+1, offs_len*2)*100; return RIG_OK; } /* * icom_set_split_freq * Assumes rig!=NULL, rig->state.priv!=NULL, * icom_set_vfo,icom_set_freq works for this rig * FIXME: status */ int icom_set_split_freq(RIG *rig, vfo_t vfo, freq_t rx_freq, freq_t tx_freq) { int status; status = icom_set_vfo(rig, RIG_VFO_B); status |= icom_set_freq(rig, RIG_VFO_CURR, tx_freq); status |= icom_set_vfo(rig, RIG_VFO_A); status |= icom_set_freq(rig, RIG_VFO_CURR, rx_freq); return status; } /* * icom_get_split_freq * Assumes rig!=NULL, rig->state.priv!=NULL, rx_freq!=NULL, tx_freq!=NULL * icom_set_vfo,icom_get_freq works for this rig * FIXME: status */ int icom_get_split_freq(RIG *rig, vfo_t vfo, freq_t *rx_freq, freq_t *tx_freq) { int status; status = icom_set_vfo(rig, RIG_VFO_B); status |= icom_get_freq(rig, RIG_VFO_CURR, tx_freq); status |= icom_set_vfo(rig, RIG_VFO_A); status |= icom_get_freq(rig, RIG_VFO_CURR, rx_freq); return status; } /* * icom_set_split * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_split(RIG *rig, vfo_t vfo, split_t split) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char ackbuf[16]; int ack_len; int split_sc; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; switch (split) { case RIG_SPLIT_OFF: split_sc = S_SPLT_OFF; break; case RIG_SPLIT_ON: split_sc = S_SPLT_ON; break; default: rig_debug(RIG_DEBUG_ERR,"Unsupported split %d", split); return -RIG_EINVAL; } icom_transaction (rig, C_CTL_SPLT, split_sc, NULL, 0, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_split: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_split * Assumes rig!=NULL, rig->state.priv!=NULL, split!=NULL */ int icom_get_split(RIG *rig, vfo_t vfo, split_t *split) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char splitbuf[16]; int split_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; icom_transaction (rig, C_CTL_SPLT, -1, NULL, 0, splitbuf, &split_len); /* * splitbuf should contain Cn,Sc */ split_len--; if (split_len != 1) { rig_debug(RIG_DEBUG_ERR,"icom_get_split: wrong frame len=%d\n", split_len); return -RIG_ERJCTED; } switch (splitbuf[1]) { case S_SPLT_OFF: *split = RIG_SPLIT_OFF; break; case S_SPLT_ON: *split = RIG_SPLIT_ON; break; default: rig_debug(RIG_DEBUG_ERR,"Unsupported split %d", splitbuf[1]); return -RIG_EPROTO; } return RIG_OK; } /* * icom_set_ts * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_ts(RIG *rig, vfo_t vfo, shortfreq_t ts) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char ackbuf[16]; int i, ack_len; int ts_sc; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; for (i=0; its_sc_list[i].ts == ts) { ts_sc = priv->ts_sc_list[i].sc; break; } } if (i >= TSLSTSIZ) { return -RIG_EINVAL; /* not found, unsupported */ } icom_transaction (rig, C_SET_TS, ts_sc, NULL, 0, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_ts: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_ts * Assumes rig!=NULL, rig->state.priv!=NULL, ts!=NULL */ int icom_get_ts(RIG *rig, vfo_t vfo, shortfreq_t *ts) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char tsbuf[16]; int ts_len,i; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; icom_transaction (rig, C_SET_TS, -1, NULL, 0, tsbuf, &ts_len); /* * tsbuf should contain Cn,Sc */ ts_len--; if (ts_len != 1) { rig_debug(RIG_DEBUG_ERR,"icom_get_ts: wrong frame len=%d\n", ts_len); return -RIG_ERJCTED; } for (i=0; its_sc_list[i].sc == tsbuf[1]) { *ts = priv->ts_sc_list[i].ts; break; } } if (i >= TSLSTSIZ) { return -RIG_EPROTO; /* not found, unsupported */ } return RIG_OK; } /* * icom_set_func * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_func(RIG *rig, vfo_t vfo, setting_t func, int status) { unsigned char fctbuf[16]; int fct_len; int fct_cn, fct_sc; /* Command Number, Subcommand */ /* * except for IC-R8500 */ fctbuf[0] = status? 0x00:0x01; fct_len = rig->caps->rig_model == RIG_MODEL_ICR8500 ? 0 : 1; /* Optimize: * sort the switch cases with the most frequent first */ switch (func) { case RIG_FUNC_FAGC: fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_AGC; /* default to 0x01 super-fast */ break; case RIG_FUNC_NB: fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_NB; break; case RIG_FUNC_COMP: fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_COMP; break; case RIG_FUNC_VOX: fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_VOX; break; case RIG_FUNC_TONE: /* repeater tone */ fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_TONE; break; case RIG_FUNC_TSQL: fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_TSQL; break; case RIG_FUNC_SBKIN: /* FIXME ? */ case RIG_FUNC_FBKIN: fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_BKIN; break; case RIG_FUNC_ANF: fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_ANF; break; case RIG_FUNC_NR: fct_cn = C_CTL_FUNC; fct_sc = S_FUNC_NR; break; default: rig_debug(RIG_DEBUG_ERR,"Unsupported set_func %d", func); return -RIG_EINVAL; } icom_transaction (rig, fct_cn, fct_sc, NULL, 0, fctbuf, &fct_len); if (fct_len != 2) { rig_debug(RIG_DEBUG_ERR,"icom_set_func: wrong frame len=%d\n", fct_len); return -RIG_EPROTO; } return RIG_OK; } /* * icom_set_channel * Assumes rig!=NULL, rig->state.priv!=NULL, chan!=NULL * TODO: still a WIP --SF */ int icom_set_channel(RIG *rig, const channel_t *chan) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char chanbuf[24], ackbuf[16]; int chan_len,freq_len,ack_len; int icmode; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; to_bcd_be(chanbuf,chan->channel_num,4); chanbuf[2] = S_MEM_CNTNT_SLCT; freq_len = priv->civ_731_mode ? 4:5; /* * to_bcd requires nibble len */ to_bcd(chanbuf+3, chan->freq, freq_len*2); chan_len = 3+freq_len+1; icmode = hamlib2icom_mode(chan->mode, RIG_PASSBAND_NORMAL); /* FIXME */ chanbuf[chan_len++] = icmode&0xff; chanbuf[chan_len++] = icmode>>8; to_bcd_be(chanbuf+chan_len++,chan->att,2); to_bcd_be(chanbuf+chan_len++,chan->preamp,2); to_bcd_be(chanbuf+chan_len++,chan->ant,2); memset(chanbuf+chan_len, 0, 8); strncpy(chanbuf+chan_len, chan->channel_desc, 8); chan_len += 8; icom_transaction (rig, C_CTL_MEM, S_MEM_CNTNT, chanbuf, chan_len, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_channel: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_get_channel * Assumes rig!=NULL, rig->state.priv!=NULL, chan!=NULL * TODO: still a WIP --SF */ int icom_get_channel(RIG *rig, channel_t *chan) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char chanbuf[24]; int chan_len,freq_len; pbwidth_t width; /* FIXME */ rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; to_bcd_be(chanbuf,chan->channel_num,4); chan_len = 2; freq_len = priv->civ_731_mode ? 4:5; icom_transaction (rig, C_CTL_MEM, S_MEM_CNTNT, chanbuf, chan_len, chanbuf, &chan_len); /* * freqbuf should contain Cn,Data area */ chan_len--; if (freq_len != freq_len+16) { rig_debug(RIG_DEBUG_ERR,"icom_get_channel: wrong frame len=%d\n", chan_len); return -RIG_ERJCTED; } /* * from_bcd requires nibble len */ chan->freq = from_bcd(chanbuf+4, freq_len*2); chan_len = 4+freq_len+1; icom2hamlib_mode(chanbuf[chan_len] | chanbuf[chan_len+1], &chan->mode, &width); chan_len += 2; chan->att = from_bcd_be(chanbuf+chan_len++,2); chan->preamp = from_bcd_be(chanbuf+chan_len++,2); chan->ant = from_bcd_be(chanbuf+chan_len++,2); strncpy(chan->channel_desc, chanbuf+chan_len, 8); return RIG_OK; } /* * icom_set_poweron * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_poweron(RIG *rig) { unsigned char ackbuf[16]; int ack_len; icom_transaction(rig, C_SET_PWR, S_PWR_ON, NULL, 0, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_poweron: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_set_poweroff * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_poweroff(RIG *rig) { unsigned char ackbuf[16]; int ack_len; icom_transaction(rig, C_SET_PWR, S_PWR_OFF, NULL, 0, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_poweroff: ack NG (%#.2x), len=%d\n", ackbuf[0],ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_set_mem * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_mem(RIG *rig, vfo_t vfo, int ch) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char membuf[2]; unsigned char ackbuf[16]; int ack_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; to_bcd_be(membuf, ch, CHAN_NB_LEN*2); icom_transaction (rig, C_SET_MEM, -1, membuf, CHAN_NB_LEN, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_mem: ack NG (%#.2x), len=%d\n", ackbuf[0], ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_set_bank * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_set_bank(RIG *rig, vfo_t vfo, int bank) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char bankbuf[2]; unsigned char ackbuf[16]; int ack_len; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; to_bcd_be(bankbuf, bank, BANK_NB_LEN*2); icom_transaction (rig, C_SET_MEM, S_BANK, bankbuf, CHAN_NB_LEN, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_set_bank: ack NG (%#.2x), len=%d\n", ackbuf[0], ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_mv_ctl, Mem/VFO operation * Assumes rig!=NULL, rig->state.priv!=NULL */ int icom_mv_ctl(RIG *rig, vfo_t vfo, mv_op_t op) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char mvbuf[16]; unsigned char ackbuf[16]; int mv_len, ack_len; int mv_cn, mv_sc; rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; mv_len = 0; switch(op) { case RIG_MVOP_VFO_MODE: mv_cn = C_SET_VFO; mv_sc = -1; break; case RIG_MVOP_MEM_MODE: mv_cn = C_SET_MEM; mv_sc = -1; break; case RIG_MVOP_VFO_CPY: mv_cn = C_SET_VFO; mv_sc = S_BTOA; break; case RIG_MVOP_VFO_XCHG: mv_cn = C_SET_VFO; mv_sc = S_XCHNG; break; case RIG_MVOP_DUAL_OFF: mv_cn = C_SET_VFO; mv_sc = S_DUAL_OFF; break; case RIG_MVOP_DUAL_ON: mv_cn = C_SET_VFO; mv_sc = S_DUAL_ON; break; case RIG_MVOP_FROM_VFO: mv_cn = C_WR_MEM; mv_sc = -1; break; case RIG_MVOP_TO_VFO: mv_cn = C_MEM2VFO; mv_sc = -1; break; case RIG_MVOP_MCL: mv_cn = C_CLR_MEM; mv_sc = -1; break; default: rig_debug(RIG_DEBUG_ERR,"Unsupported mem/vfo op %d", op); return -RIG_EINVAL; } icom_transaction (rig, mv_cn, mv_sc, mvbuf, mv_len, ackbuf, &ack_len); if (ack_len != 1 || ackbuf[0] != ACK) { rig_debug(RIG_DEBUG_ERR,"icom_mv_ctl: ack NG (%#.2x), len=%d\n", ackbuf[0], ack_len); return -RIG_ERJCTED; } return RIG_OK; } /* * icom_decode is called by sa_sigio, when some asynchronous * data has been received from the rig */ int icom_decode_event(RIG *rig) { struct icom_priv_data *priv; struct rig_state *rig_s; unsigned char buf[32]; int frm_len; freq_t freq; rmode_t mode; pbwidth_t width; rig_debug(RIG_DEBUG_VERBOSE, "icom: icom_decode called\n"); rig_s = &rig->state; priv = (struct icom_priv_data*)rig_s->priv; frm_len = read_icom_frame(rig_s->stream, buf, rig_s->timeout); /* * the first 2 bytes must be 0xfe * the 3rd one the emitter * the 4rd one 0x00 since this is transceive mode * then the command number * the rest is data * and don't forget one byte at the end for the EOM */ switch (buf[4]) { case C_SND_FREQ: /* TODO: check the read freq len is 4 or 5 bytes */ if (rig->callbacks.freq_event) { freq = from_bcd(buf+5, (priv->civ_731_mode ? 4:5)*2); return rig->callbacks.freq_event(rig,RIG_VFO_CURR,freq); } else return -RIG_ENAVAIL; break; case C_SND_MODE: if (rig->callbacks.mode_event) { icom2hamlib_mode(buf[5]| buf[6]<<8, &mode, &width); return rig->callbacks.mode_event(rig,RIG_VFO_CURR,mode,width); } else return -RIG_ENAVAIL; break; default: rig_debug(RIG_DEBUG_VERBOSE,"icom_decode: tranceive cmd " "unsupported %#2.2x\n",buf[4]); return -RIG_ENIMPL; } return RIG_OK; } /* * init_icom is called by rig_backend_load */ int init_icom(void *be_handle) { rig_debug(RIG_DEBUG_VERBOSE, "icom: _init called\n"); rig_register(&ic706_caps); rig_register(&ic706mkii_caps); rig_register(&ic706mkiig_caps); rig_register(&icr8500_caps); return RIG_OK; }