Attempt to handle asynchronously pushed Icom CI-V frames (transceive and spectrum data) as these frame will get interleaved with command responses

pull/715/head
Mikael Nousiainen 2021-05-30 01:36:49 +03:00
rodzic 503897c41c
commit 263c16984a
5 zmienionych plików z 180 dodań i 89 usunięć

Wyświetl plik

@ -23,9 +23,7 @@
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h> /* String function definitions */
#include <unistd.h> /* 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(&current_time, NULL);
timersub(&current_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);
/*

Wyświetl plik

@ -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);

Wyświetl plik

@ -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,

Wyświetl plik

@ -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[];

Wyświetl plik

@ -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