From 263c16984ad0a15b0b51c4095611fffac47beac6 Mon Sep 17 00:00:00 2001 From: Mikael Nousiainen Date: Sun, 30 May 2021 01:36:49 +0300 Subject: [PATCH] Attempt to handle asynchronously pushed Icom CI-V frames (transceive and spectrum data) as these frame will get interleaved with command responses --- rigs/icom/frame.c | 94 ++++++++++++++++++++++--- rigs/icom/frame.h | 1 + rigs/icom/icom.c | 170 +++++++++++++++++++++++++--------------------- rigs/icom/icom.h | 2 + src/misc.c | 2 +- 5 files changed, 180 insertions(+), 89 deletions(-) diff --git a/rigs/icom/frame.c b/rigs/icom/frame.c index 3030b5031..967535c2b 100644 --- a/rigs/icom/frame.c +++ b/rigs/icom/frame.c @@ -23,9 +23,7 @@ #include "config.h" #endif -#include #include /* String function definitions */ -#include /* UNIX standard function definitions */ #include "hamlib/rig.h" #include "serial.h" @@ -90,6 +88,26 @@ int make_cmd_frame(char frame[], char re_id, char ctrl_id, char cmd, int subcmd, RETURNFUNC(i); } +int icom_frame_fix_preamble(int frame_len, unsigned char *frame) +{ + if (frame[0] == PR) + { + // Sometimes the second preamble byte is missing -> TODO: Find out why! + if (frame[1] != PR) + { + memmove(frame + 1, frame, frame_len); + frame_len++; + } + } + else + { + rig_debug(RIG_DEBUG_WARN, "%s: invalid Icom CI-V frame, no preamble found\n", __func__); + RETURNFUNC(-RIG_EPROTO); + } + + return frame_len; +} + /* * icom_one_transaction * @@ -109,11 +127,12 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, struct icom_priv_data *priv; const struct icom_priv_caps *priv_caps; struct rig_state *rs; + struct timeval start_time, current_time, elapsed_time; // this buf needs to be large enough for 0xfe strings for power up // at 115,200 this is now at least 150 unsigned char buf[200]; unsigned char sendbuf[MAXFRAMELEN]; - int frm_len, retval; + int frm_len, frm_data_len, retval; int ctrl_id; ENTERFUNC; @@ -168,12 +187,14 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, if (retval < 0) { + Unhold_Decode(rig); /* Other error, return it */ RETURNFUNC(retval); } if (retval < 1) { + Unhold_Decode(rig); RETURNFUNC(-RIG_EPROTO); } @@ -223,6 +244,9 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, RETURNFUNC(RIG_OK); } + gettimeofday(&start_time, NULL); + +read_another_frame: /* * wait for ACK ... * FIXME: handle padding/collisions @@ -245,10 +269,9 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, #endif - Unhold_Decode(rig); - if (frm_len < 0) { + Unhold_Decode(rig); /* RIG_TIMEOUT: timeout getting response, return timeout */ /* other error: return it */ RETURNFUNC(frm_len); @@ -256,12 +279,23 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, if (frm_len < 1) { + Unhold_Decode(rig); RETURNFUNC(-RIG_EPROTO); } + retval = icom_frame_fix_preamble(frm_len, buf); + if (retval < 0) + { + Unhold_Decode(rig); + RETURNFUNC(retval); + } + + frm_len = retval; + switch (buf[frm_len - 1]) { case COL: + Unhold_Decode(rig); /* Collision */ RETURNFUNC(-RIG_BUSBUSY); @@ -270,30 +304,70 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, break; case NAK: + Unhold_Decode(rig); RETURNFUNC(-RIG_ERJCTED); default: + Unhold_Decode(rig); /* Timeout after reading at least one character */ /* Problem on ci-v bus? */ RETURNFUNC(-RIG_EPROTO); } - if (frm_len < ACKFRMLEN) { RETURNFUNC(-RIG_EPROTO); } + if (frm_len < ACKFRMLEN) + { + Unhold_Decode(rig); + RETURNFUNC(-RIG_EPROTO); + } // if we send a bad command we will get back a NAK packet // e.g. fe fe e0 50 fa fd - if (frm_len == 6 && NAK == buf[frm_len - 2]) { RETURNFUNC(-RIG_ERJCTED); } + if (frm_len == 6 && NAK == buf[frm_len - 2]) + { + Unhold_Decode(rig); + RETURNFUNC(-RIG_ERJCTED); + } rig_debug(RIG_DEBUG_TRACE, "%s: frm_len=%d, frm_len-1=%02x, frm_len-2=%02x\n", __func__, frm_len, buf[frm_len - 1], buf[frm_len - 2]); // has to be one of these two now or frame is corrupt - if (FI != buf[frm_len - 1] && ACK != buf[frm_len - 1]) { RETURNFUNC(-RIG_BUSBUSY); } + if (FI != buf[frm_len - 1] && ACK != buf[frm_len - 1]) + { + Unhold_Decode(rig); + RETURNFUNC(-RIG_BUSBUSY); + } - *data_len = frm_len - (ACKFRMLEN - 1); + frm_data_len = frm_len - (ACKFRMLEN - 1); - if (*data_len <= 0) { RETURNFUNC(-RIG_EPROTO); } + if (frm_data_len <= 0) + { + Unhold_Decode(rig); + RETURNFUNC(-RIG_EPROTO); + } + if (icom_is_async_frame(rig, frm_len, buf)) + { + int elapsed_ms; + icom_process_async_frame(rig, frm_len, buf); + + gettimeofday(¤t_time, NULL); + timersub(¤t_time, &start_time, &elapsed_time); + + elapsed_ms = (int) (elapsed_time.tv_sec * 1000 + elapsed_time.tv_usec / 1000); + + if (elapsed_ms > rs->rigport.timeout) + { + Unhold_Decode(rig); + RETURNFUNC(-RIG_ETIMEOUT); + } + + goto read_another_frame; + } + + Unhold_Decode(rig); + + *data_len = frm_data_len; memcpy(data, buf + 4, *data_len); /* diff --git a/rigs/icom/frame.h b/rigs/icom/frame.h index b5d84621c..0ffc7e0e9 100644 --- a/rigs/icom/frame.h +++ b/rigs/icom/frame.h @@ -28,6 +28,7 @@ * helper functions */ int make_cmd_frame(char frame[], char re_id, char ctrl_id, char cmd, int subcmd, const unsigned char *data, int data_len); +int icom_frame_fix_preamble(int frame_len, unsigned char *frame); int icom_transaction (RIG *rig, int cmd, int subcmd, const unsigned char *payload, int payload_len, unsigned char *data, int *data_len); int read_icom_frame(hamlib_port_t *p, unsigned char rxbuffer[], int rxbuffer_len); diff --git a/rigs/icom/icom.c b/rigs/icom/icom.c index b81609d35..8f09cc214 100644 --- a/rigs/icom/icom.c +++ b/rigs/icom/icom.c @@ -7933,7 +7933,7 @@ int icom_mW2power(RIG *rig, float *power, unsigned int mwpower, freq_t freq, RETURNFUNC(RIG_OK); } -static int icom_parse_spectrum_frame(RIG *rig, int length, unsigned char *frame_data) +static int icom_parse_spectrum_frame(RIG *rig, int length, const unsigned char *frame_data) { struct rig_caps *caps = rig->caps; struct icom_priv_caps *priv_caps = (struct icom_priv_caps *) caps->priv; @@ -8059,6 +8059,88 @@ static int icom_parse_spectrum_frame(RIG *rig, int length, unsigned char *frame_ RETURNFUNC(RIG_OK); } +int icom_is_async_frame(RIG *rig, int frame_len, const unsigned char *frame) +{ + struct rig_state *rs = &rig->state; + struct icom_priv_data *priv = (struct icom_priv_data *) rs->priv; + + if (frame_len < ACKFRMLEN) + { + return 0; + } + + /* Spectrum scope data is not CI-V transceive data, but handled the same way as it is pushed by the rig */ + return frame[2] == BCASTID || (frame[2] == CTRLID && frame[4] == C_CTL_SCP && frame[5] == S_SCP_DAT); +} + +int icom_process_async_frame(RIG *rig, int frame_len, const unsigned char *frame) +{ + struct rig_state *rs = &rig->state; + struct icom_priv_data *priv = (struct icom_priv_data *) rs->priv; + rmode_t mode; + pbwidth_t width; + + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + /* + * the first 2 bytes must be 0xfe + * the 3rd one 0x00 since this is transceive mode + * the 4rd one the emitter + * then the command number + * the rest is data + * and don't forget one byte at the end for the EOM + */ + switch (frame[4]) + { + case C_SND_FREQ: + /* + * TODO: the freq length might be less than 4 or 5 bytes + * on older rigs! + */ + if (rig->callbacks.freq_event) + { + freq_t freq; + freq = from_bcd(frame + 5, (priv->civ_731_mode ? 4 : 5) * 2); + RETURNFUNC(rig->callbacks.freq_event(rig, RIG_VFO_CURR, freq, + rig->callbacks.freq_arg)); + } + else + { + RETURNFUNC(-RIG_ENAVAIL); + } + + break; + + case C_SND_MODE: + if (rig->callbacks.mode_event) + { + icom2rig_mode(rig, frame[5], frame[6], &mode, &width); + RETURNFUNC(rig->callbacks.mode_event(rig, RIG_VFO_CURR, + mode, width, rig->callbacks.mode_arg)); + } + else + { + RETURNFUNC(-RIG_ENAVAIL); + } + + break; + + case C_CTL_SCP: + if (frame[5] == S_SCP_DAT) + { + icom_parse_spectrum_frame(rig, frame_len - (6 + 1), frame + 6); + } + break; + + default: + rig_debug(RIG_DEBUG_VERBOSE, "%s: transceive cmd unsupported %#2.2x\n", + __func__, frame[4]); + RETURNFUNC(-RIG_ENIMPL); + } + + RETURNFUNC(RIG_OK); +} + /* * icom_decode is called by sa_sigio, when some asynchronous * data has been received from the rig @@ -8068,9 +8150,7 @@ int icom_decode_event(RIG *rig) struct icom_priv_data *priv; struct rig_state *rs; unsigned char buf[MAXFRAMELEN]; - int frm_len; - rmode_t mode; - pbwidth_t width; + int retval, frm_len; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); @@ -8090,21 +8170,14 @@ int icom_decode_event(RIG *rig) RETURNFUNC(0); } - if (buf[0] == PR) + retval = icom_frame_fix_preamble(frm_len, buf); + if (retval < 0) { - // Sometimes the second preamble byte is missing -> TODO: Find out why! - if (buf[1] != PR) - { - memmove(buf + 1, buf, frm_len); - frm_len++; - } - } - else - { - rig_debug(RIG_DEBUG_WARN, "%s: invalid Icom CI-V frame, no preamble found\n", __func__); - RETURNFUNC(-RIG_EPROTO); + RETURNFUNC(retval); } + frm_len = retval; + switch (buf[frm_len - 1]) { case COL: @@ -8122,72 +8195,13 @@ int icom_decode_event(RIG *rig) RETURNFUNC(-RIG_EPROTO); } - /* Spectrum scope data is not CI-V transceive data, but handled the same way as it is pushed by the rig */ - if (buf[3] != BCASTID && buf[3] != priv->re_civ_addr && buf[4] != C_CTL_SCP) + if (!icom_is_async_frame(rig, frm_len, buf)) { rig_debug(RIG_DEBUG_WARN, "%s: CI-V %#x called for %#x!\n", __func__, - priv->re_civ_addr, buf[3]); + priv->re_civ_addr, buf[2]); } - /* - * 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: the freq length might be less than 4 or 5 bytes - * on older rigs! - */ - if (rig->callbacks.freq_event) - { - freq_t freq; - freq = from_bcd(buf + 5, (priv->civ_731_mode ? 4 : 5) * 2); - RETURNFUNC(rig->callbacks.freq_event(rig, RIG_VFO_CURR, freq, - rig->callbacks.freq_arg)); - } - else - { - RETURNFUNC(-RIG_ENAVAIL); - } - - break; - - case C_SND_MODE: - if (rig->callbacks.mode_event) - { - icom2rig_mode(rig, buf[5], buf[6], &mode, &width); - RETURNFUNC(rig->callbacks.mode_event(rig, RIG_VFO_CURR, - mode, width, - rig->callbacks.mode_arg)); - } - else - { - RETURNFUNC(-RIG_ENAVAIL); - } - - break; - - case C_CTL_SCP: - if (buf[5] == S_SCP_DAT) - { - icom_parse_spectrum_frame(rig, frm_len - (6 + 1), buf + 6); - } - break; - - default: - rig_debug(RIG_DEBUG_VERBOSE, "%s: transceive cmd unsupported %#2.2x\n", - __func__, buf[4]); - RETURNFUNC(-RIG_ENIMPL); - } - - RETURNFUNC(RIG_OK); + RETURNFUNC(icom_process_async_frame(rig, frm_len, buf)); } int icom_set_raw(RIG *rig, int cmd, int subcmd, int subcmdbuflen, diff --git a/rigs/icom/icom.h b/rigs/icom/icom.h index d9a2974bc..296978893 100644 --- a/rigs/icom/icom.h +++ b/rigs/icom/icom.h @@ -387,6 +387,8 @@ int icom_set_custom_parm_time(RIG *rig, int parmbuflen, unsigned char *parmbuf, int icom_get_custom_parm_time(RIG *rig, int parmbuflen, unsigned char *parmbuf, int *seconds); int icom_get_freq_range(RIG *rig); +int icom_is_async_frame(RIG *rig, int frame_len, const unsigned char *frame); +int icom_process_async_frame(RIG *rig, int frame_len, const unsigned char *frame); extern const struct confparams icom_cfg_params[]; extern const struct confparams icom_ext_levels[]; diff --git a/src/misc.c b/src/misc.c index 1cc7d65f5..5e8510169 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1904,7 +1904,7 @@ int HAMLIB_API parse_hoststr(char *hoststr, char host[256], char port[6]) return -1; } -#undef RIG_FLUSH_REMOVE +#define RIG_FLUSH_REMOVE int HAMLIB_API rig_flush(hamlib_port_t *port) { #ifndef RIG_FLUSH_REMOVE