Allow icom_rig_open() to succeed even if the rig is powered off. Fix issues with async I/O error code propagation. Allow rigctld daemon to start even if rig is powered off. Add mutex locks around rig_open()/rig_close() calls as multiple client connections could end up calling them concurrently, which could lead to all kinds of issues as the functions are not meant to be thread-safe. This also blocks execution of rigctl commands during rig open/close.

pull/943/head
Mikael Nousiainen 2022-01-22 23:19:07 +02:00
rodzic be992b8a36
commit 1f428c8d95
4 zmienionych plików z 93 dodań i 56 usunięć

Wyświetl plik

@ -929,7 +929,7 @@ icom_rig_open(RIG *rig)
struct rig_state *rs = &rig->state;
struct icom_priv_data *priv = (struct icom_priv_data *) rs->priv;
int retry_flag = 1;
int retry_save = rs->rigport.retry;
short retry_save = rs->rigport.retry;
ENTERFUNC;
@ -969,7 +969,7 @@ retry_open:
retval = abs(rig_set_powerstat(rig, 1));
// this is only a fatal error if powerstat is implemented
// if not iplemented than we're at an error here
// if not implemented than we're at an error here
if (retval != RIG_OK && retval != RIG_ENIMPL && retval != RIG_ENAVAIL)
{
rig_debug(RIG_DEBUG_WARN, "%s: unexpected retval here: %s\n",
@ -978,17 +978,18 @@ retry_open:
rig_debug(RIG_DEBUG_WARN, "%s: rig_set_powerstat failed: =%s\n", __func__,
rigerror(retval));
rs->rigport.retry = retry_save;
RETURNFUNC(retval);
}
// Now that we're powered up let's try again
retval_echo = icom_get_usb_echo_off(rig);
if (retval_echo != 0 && retval_echo != 1)
if (retval == RIG_OK)
{
rig_debug(RIG_DEBUG_ERR, "%s: Unable to determine USB echo status\n", __func__);
rs->rigport.retry = retry_save;
RETURNFUNC(retval);
// Now that we're powered up let's try again
retval_echo = icom_get_usb_echo_off(rig);
if (retval_echo != 0 && retval_echo != 1)
{
rig_debug(RIG_DEBUG_ERR, "%s: Unable to determine USB echo status\n", __func__);
rs->rigport.retry = retry_save;
}
}
}
else if (retval != RIG_OK)
@ -1005,11 +1006,14 @@ retry_open:
}
rs->rigport.retry = retry_save;
RETURNFUNC(retval);
}
rig->state.current_vfo = icom_current_vfo(rig);
priv->poweron = 1;
priv->poweron = (retval == RIG_OK) ? 1 : 0;
if (priv->poweron)
{
rig->state.current_vfo = icom_current_vfo(rig);
}
if (rig->caps->has_get_func & RIG_FUNC_SATMODE)
{
@ -7834,6 +7838,7 @@ int icom_set_powerstat(RIG *rig, powerstat_t status)
if (retval == RIG_OK)
{
rig->state.current_vfo = icom_current_vfo(rig);
RETURNFUNC(retval);
}
else

Wyświetl plik

@ -437,7 +437,7 @@ extern int is_uh_radio_fd(int fd);
static int port_read_sync_data_error_code(hamlib_port_t *p)
{
ssize_t total_bytes_read = 0;
unsigned char data;
signed char data;
int result;
do {
@ -810,13 +810,14 @@ static ssize_t port_read_generic(hamlib_port_t *p, void *buf, size_t count, int
#define port_select(p,n,r,w,e,t,d) select((n),(r),(w),(e),(t))
//! @endcond
static int flush_and_read_last_byte(hamlib_port_t *p, int fd, int direct)
static int port_read_sync_data_error_code(hamlib_port_t *p, int fd, int direct)
{
fd_set rfds, efds;
ssize_t total_bytes_read = 0;
ssize_t bytes_read;
struct timeval tv_timeout;
int result;
char data;
signed char data;
do {
tv_timeout.tv_sec = 0;
@ -829,21 +830,32 @@ static int flush_and_read_last_byte(hamlib_port_t *p, int fd, int direct)
result = port_select(p, fd + 1, &rfds, NULL, &efds, &tv_timeout, direct);
if (result < 0)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s(): select() timeout, direct=%d\n", __func__, direct);
return -RIG_ETIMEOUT;
}
if (result == 0)
{
if (total_bytes_read > 0) {
rig_debug(RIG_DEBUG_VERBOSE, "%s(): returning error code %d, direct=%d\n", __func__, (int) data, direct);
return data;
}
rig_debug(RIG_DEBUG_ERR, "%s(): no error code available\n", __func__);
return -RIG_EIO;
}
if (FD_ISSET(fd, &efds))
{
rig_debug(RIG_DEBUG_ERR, "%s(): select() indicated error\n", __func__);
return -RIG_EIO;
}
bytes_read = read(fd, &data, 1);
total_bytes_read += bytes_read;
} while (bytes_read > 0);
rig_debug(RIG_DEBUG_VERBOSE, "%s(): returning error code %d\n", __func__, data);
return data;
}
@ -886,28 +898,30 @@ static int port_wait_for_data(hamlib_port_t *p, int direct)
else if (result < 0)
{
rig_debug(RIG_DEBUG_ERR,
"%s(): select() error: %s\n",
"%s(): select() error, direct=%d: %s\n",
__func__,
direct,
strerror(errno));
return -RIG_EIO;
}
if (FD_ISSET(fd, &efds))
{
rig_debug(RIG_DEBUG_ERR, "%s(): fd error\n", __func__);
rig_debug(RIG_DEBUG_ERR, "%s(): fd error, direct=%d\n", __func__, direct);
return -RIG_EIO;
}
if (!direct)
{
if (FD_ISSET(errorfd, &efds))
{
rig_debug(RIG_DEBUG_ERR, "%s(): fd error from sync error pipe\n", __func__);
rig_debug(RIG_DEBUG_ERR, "%s(): fd error from sync error pipe, direct=%d\n", __func__, direct);
return -RIG_EIO;
}
if (FD_ISSET(errorfd, &rfds))
{
return flush_and_read_last_byte(p, errorfd, 0);
rig_debug(RIG_DEBUG_VERBOSE, "%s(): attempting to read error code, direct=%d\n", __func__, direct);
return port_read_sync_data_error_code(p, errorfd, 0);
}
}
@ -1078,7 +1092,7 @@ static int read_block_generic(hamlib_port_t *p, unsigned char *rxbuffer, size_t
struct timeval start_time, end_time, elapsed_time;
int total_count = 0;
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
rig_debug(RIG_DEBUG_VERBOSE, "%s called, direct=%d\n", __func__, direct);
#ifdef ASYNC_BUG
if (!p->async && !direct)
@ -1111,11 +1125,12 @@ static int read_block_generic(hamlib_port_t *p, unsigned char *rxbuffer, size_t
}
rig_debug(RIG_DEBUG_WARN,
"%s(): Timed out %d.%d seconds after %d chars\n",
"%s(): Timed out %d.%d seconds after %d chars, direct=%d\n",
__func__,
(int)elapsed_time.tv_sec,
(int)elapsed_time.tv_usec,
total_count);
total_count,
direct);
return -RIG_ETIMEOUT;
}
@ -1126,7 +1141,7 @@ static int read_block_generic(hamlib_port_t *p, unsigned char *rxbuffer, size_t
{
dump_hex((unsigned char *) rxbuffer, total_count);
}
rig_debug(RIG_DEBUG_ERR, "%s(): I/O error after %d chars: %d\n", __func__, total_count, result);
rig_debug(RIG_DEBUG_ERR, "%s(): I/O error after %d chars, direct=%d: %d\n", __func__, total_count, direct, result);
return result;
}
@ -1138,7 +1153,7 @@ static int read_block_generic(hamlib_port_t *p, unsigned char *rxbuffer, size_t
if (rd_count < 0)
{
rig_debug(RIG_DEBUG_ERR, "%s(): read failed - %s\n", __func__, strerror(errno));
rig_debug(RIG_DEBUG_ERR, "%s(): read failed, direct=%d - %s\n", __func__, direct, strerror(errno));
return -RIG_EIO;
}
@ -1148,7 +1163,7 @@ static int read_block_generic(hamlib_port_t *p, unsigned char *rxbuffer, size_t
if (direct)
{
rig_debug(RIG_DEBUG_TRACE, "%s(): RX %d bytes\n", __func__, total_count);
rig_debug(RIG_DEBUG_TRACE, "%s(): RX %d bytes, direct=%d\n", __func__, total_count, direct);
dump_hex((unsigned char *) rxbuffer, total_count);
}
@ -1229,7 +1244,7 @@ static int read_string_generic(hamlib_port_t *p,
return -RIG_EINTERNAL;
}
rig_debug(RIG_DEBUG_TRACE, "%s called, rxmax=%d\n", __func__, (int)rxmax);
rig_debug(RIG_DEBUG_TRACE, "%s called, rxmax=%d direct=%d\n", __func__, (int)rxmax, direct);
if (!p || !rxbuffer)
{
@ -1272,11 +1287,12 @@ static int read_string_generic(hamlib_port_t *p,
if (!flush_flag)
{
rig_debug(RIG_DEBUG_WARN,
"%s(): Timed out %d.%03d seconds after %d chars\n",
"%s(): Timed out %d.%03d seconds after %d chars, direct=%d\n",
__func__,
(int)elapsed_time.tv_sec,
(int)elapsed_time.tv_usec / 1000,
total_count);
total_count,
direct);
}
return -RIG_ETIMEOUT;
@ -1291,7 +1307,7 @@ static int read_string_generic(hamlib_port_t *p,
{
dump_hex(rxbuffer, total_count);
}
rig_debug(RIG_DEBUG_ERR, "%s(): I/O error after %d chars: %d\n", __func__, total_count, result);
rig_debug(RIG_DEBUG_ERR, "%s(): I/O error after %d chars, direct=%d: %d\n", __func__, total_count, direct, result);
return result;
}
@ -1306,7 +1322,7 @@ static int read_string_generic(hamlib_port_t *p,
if (errno == EAGAIN)
{
hl_usleep(5 * 1000);
rig_debug(RIG_DEBUG_WARN, "%s: port_read is busy?\n", __func__);
rig_debug(RIG_DEBUG_WARN, "%s: port_read is busy? direct=%d\n", __func__, direct);
}
}
while (++i < 10 && errno == EBUSY); // 50ms should be enough
@ -1318,7 +1334,7 @@ static int read_string_generic(hamlib_port_t *p,
{
dump_hex((unsigned char *) rxbuffer, total_count);
}
rig_debug(RIG_DEBUG_ERR, "%s(): read failed - %s\n", __func__, strerror(errno));
rig_debug(RIG_DEBUG_ERR, "%s(): read failed, direct=%d - %s\n", __func__, direct, strerror(errno));
return -RIG_EIO;
}
@ -1348,14 +1364,15 @@ static int read_string_generic(hamlib_port_t *p,
*/
rxbuffer[total_count] = '\000';
if (direct)
{
// if (direct)
// {
rig_debug(RIG_DEBUG_TRACE,
"%s(): RX %d characters\n",
"%s(): RX %d characters, direct=%d\n",
__func__,
total_count);
total_count,
direct);
dump_hex((unsigned char *) rxbuffer, total_count);
}
// }
return total_count; /* return bytes count read */
}

Wyświetl plik

@ -1070,10 +1070,12 @@ int HAMLIB_API rig_open(RIG *rig)
if (status != RIG_OK)
{
remove_opened_rig(rig);
#ifdef ASYNC_BUG
async_data_handler_stop(rig);
#endif
port_close(&rs->rigport, rs->rigport.type.rig);
rs->comm_state = 0;
RETURNFUNC(status);
}
}
@ -6992,15 +6994,16 @@ void *async_data_handler(void *arg)
if (result < 0)
{
// TODO: it may be necessary to have mutex locking on transaction_active flag
if (rs->transaction_active)
{
unsigned char data = (unsigned char) result;
write_block_sync_error(&rs->rigport, &data, 1);
}
// Timeouts occur always if there is nothing to receive, so they are not really errors in this case
if (result != -RIG_ETIMEOUT)
{
// TODO: it may be necessary to have mutex locking on transaction_active flag
if (rs->transaction_active)
{
unsigned char data = (unsigned char) result;
write_block_sync_error(&rs->rigport, &data, 1);
}
// TODO: error handling -> store errors in rig state -> to be exposed in async snapshot packets
rig_debug(RIG_DEBUG_ERR, "%s: read_frame_direct() failed, result=%d\n",
__func__, result);

Wyświetl plik

@ -135,6 +135,7 @@ static unsigned client_count;
#endif
static RIG *my_rig; /* handle to rig (instance) */
static volatile int rig_opened = 0;
static int verbose;
#ifdef HAVE_SIG_ATOMIC_T
@ -687,14 +688,15 @@ int main(int argc, char *argv[])
exit(0);
}
/* open and close rig connection to check early for issues */
/* attempt to open rig to check early for issues */
retcode = rig_open(my_rig);
rig_opened = retcode == RIG_OK ? 1 : 0;
if (retcode != RIG_OK)
{
fprintf(stderr, "rig_open: error = %s %s %s \n", rigerror(retcode), rig_file,
strerror(errno));
exit(2);
// continue even if opening the rig fails, because it may be powered off
}
if (verbose > RIG_DEBUG_ERR)
@ -1010,8 +1012,6 @@ int main(int argc, char *argv[])
}
while (retcode == 0 && !ctrl_c);
network_multicast_publisher_stop(my_rig);
#ifdef HAVE_PTHREAD
/* allow threads to finish current action */
mutex_rigctld(1);
@ -1026,6 +1026,9 @@ int main(int argc, char *argv[])
#else
rig_close(my_rig); /* close port */
#endif
network_multicast_publisher_stop(my_rig);
rig_cleanup(my_rig); /* if you care about memory */
#ifdef __MINGW32__
@ -1132,10 +1135,9 @@ void *handle_socket(void *arg)
#endif
int rig_opened = 1; // our rig is already open
do
{
mutex_rigctld(1);
if (!rig_opened)
{
retcode = rig_open(my_rig);
@ -1143,6 +1145,7 @@ void *handle_socket(void *arg)
rig_debug(RIG_DEBUG_ERR, "%s: rig_open reopened retcode=%d\n", __func__,
retcode);
}
mutex_rigctld(0);
if (rig_opened) // only do this if rig is open
{
@ -1168,15 +1171,24 @@ void *handle_socket(void *arg)
do
{
mutex_rigctld(1);
retcode = rig_close(my_rig);
hl_usleep(1000 * 1000);
rig_opened = 0;
mutex_rigctld(0);
rig_debug(RIG_DEBUG_ERR, "%s: rig_close retcode=%d\n", __func__, retcode);
retcode = rig_open(my_rig);
rig_opened = retcode == RIG_OK ? 1 : 0;
rig_debug(RIG_DEBUG_ERR, "%s: rig_open retcode=%d, opened=%d\n", __func__,
retcode, rig_opened);
hl_usleep(1000 * 1000);
mutex_rigctld(1);
if (!rig_opened) {
retcode = rig_open(my_rig);
rig_opened = retcode == RIG_OK ? 1 : 0;
rig_debug(RIG_DEBUG_ERR, "%s: rig_open retcode=%d, opened=%d\n", __func__,
retcode, rig_opened);
}
mutex_rigctld(0);
}
while (retry-- > 0 && retcode != RIG_OK);
while (!rig_opened && retry-- > 0 && retcode != RIG_OK);
}
}
while (retcode == RIG_OK || RIG_IS_SOFT_ERRCODE(-retcode));