diff --git a/include/hamlib/rig.h b/include/hamlib/rig.h index f58dfa387..9e174d0fa 100644 --- a/include/hamlib/rig.h +++ b/include/hamlib/rig.h @@ -1984,6 +1984,17 @@ struct rig_caps { const char *clone_combo_set; /*!< String describing key combination to enter load cloning mode */ const char *clone_combo_get; /*!< String describing key combination to enter save cloning mode */ const char *macro_name; /*!< Rig model macro name */ + + int async_data_supported; + int (*read_frame_direct)(RIG *rig, + size_t buffer_length, + const unsigned char *buffer); + int (*is_async_frame)(RIG *rig, + size_t frame_length, + const unsigned char *frame); + int (*process_async_frame)(RIG *rig, + size_t frame_length, + const unsigned char *frame); }; //! @endcond @@ -2186,6 +2197,10 @@ typedef struct hamlib_port { } parm; /*!< Port parameter union */ int client_port; /*!< client socket port for tcp connection */ RIG *rig; /*!< our parent RIG device */ + + int async; /*!< enable asynchronous data handling if true */ + int fd_sync_write; /*!< file descriptor for writing synchronous data */ + int fd_sync_read; /*!< file descriptor for reading synchronous data */ } hamlib_port_t; //! @endcond diff --git a/rigs/icom/frame.c b/rigs/icom/frame.c index 964277a13..2137df20e 100644 --- a/rigs/icom/frame.c +++ b/rigs/icom/frame.c @@ -46,7 +46,8 @@ * NB: the frame array must be big enough to hold the frame. * The smallest frame is 6 bytes, the biggest is at least 13 bytes. */ -int make_cmd_frame(char frame[], char re_id, char ctrl_id, char cmd, int subcmd, +int make_cmd_frame(unsigned char frame[], unsigned char re_id, unsigned char ctrl_id, + unsigned char cmd, int subcmd, const unsigned char *data, int data_len) { int i = 0; @@ -121,7 +122,7 @@ int icom_frame_fix_preamble(int frame_len, unsigned char *frame) * return RIG_OK if transaction completed, * or a negative value otherwise indicating the error. */ -int icom_one_transaction(RIG *rig, int cmd, int subcmd, +int icom_one_transaction(RIG *rig, unsigned char cmd, int subcmd, const unsigned char *payload, int payload_len, unsigned char *data, int *data_len) { @@ -134,7 +135,7 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, unsigned char buf[200]; unsigned char sendbuf[MAXFRAMELEN]; int frm_len, frm_data_len, retval; - int ctrl_id; + unsigned char ctrl_id; ENTERFUNC; rig_lock(); @@ -146,7 +147,7 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, ctrl_id = priv_caps->serial_full_duplex == 0 ? CTRLID : 0x80; - frm_len = make_cmd_frame((char *) sendbuf, priv->re_civ_addr, ctrl_id, cmd, + frm_len = make_cmd_frame(sendbuf, priv->re_civ_addr, ctrl_id, cmd, subcmd, payload, payload_len); /* @@ -158,7 +159,7 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, if (data_len) { *data_len = 0; } - retval = write_block(&rs->rigport, (char *) sendbuf, frm_len); + retval = write_block(&rs->rigport, sendbuf, frm_len); if (retval != RIG_OK) { @@ -234,7 +235,7 @@ int icom_one_transaction(RIG *rig, int cmd, int subcmd, RETURNFUNC(-RIG_EPROTO); } - if (memcmp(buf, sendbuf, frm_len)) + if (memcmp(buf, sendbuf, frm_len) != 0) { /* Frames are different? */ /* Problem on ci-v bus? */ @@ -466,17 +467,17 @@ static const char icom_block_end[2] = { FI, COL}; #define icom_block_end_length 2 /* - * read_icom_frame - * read a whole CI-V frame (until 0xfd is encountered) + * Read a whole CI-V frame (until 0xfd is encountered). + * * TODO: strips padding/collisions * FIXME: check return codes/bytes read */ -int read_icom_frame(hamlib_port_t *p, unsigned char rxbuffer[], - int rxbuffer_len) +static int read_icom_frame_generic(hamlib_port_t *p, const unsigned char rxbuffer[], + size_t rxbuffer_len, int direct) { int read = 0; int retries = 10; - char *rx_ptr = (char *)rxbuffer; + unsigned char *rx_ptr = (unsigned char *) rxbuffer; ENTERFUNC; // zeroize the buffer so we can still check contents after timeouts @@ -484,13 +485,20 @@ int read_icom_frame(hamlib_port_t *p, unsigned char rxbuffer[], /* * OK, now sometimes we may time out, e.g. the IC7000 can time out - * during a PTT operation. So, we will insure that the last thing we + * during a PTT operation. So, we will ensure that the last thing we * read was a proper end marker - if not, we will try again. */ do { - int i = read_string(p, rx_ptr, MAXFRAMELEN - read, - icom_block_end, icom_block_end_length, 0); + int i; + if (direct) + { + i = read_string_direct(p, rx_ptr, MAXFRAMELEN - read, icom_block_end, icom_block_end_length, 0); + } + else + { + i = read_string(p, rx_ptr, MAXFRAMELEN - read, icom_block_end, icom_block_end_length, 0); + } if (i < 0 && i != RIG_BUSBUSY) /* die on errors */ { @@ -518,6 +526,17 @@ int read_icom_frame(hamlib_port_t *p, unsigned char rxbuffer[], RETURNFUNC(read); } +int read_icom_frame(hamlib_port_t *p, const unsigned char rxbuffer[], + size_t rxbuffer_len) +{ + return read_icom_frame_generic(p, rxbuffer, rxbuffer_len, 0); +} + +int read_icom_frame_direct(hamlib_port_t *p, const unsigned char rxbuffer[], + size_t rxbuffer_len) +{ + return read_icom_frame_generic(p, rxbuffer, rxbuffer_len, 1); +} /* * convert mode and width as expressed by Hamlib frontend diff --git a/rigs/icom/frame.h b/rigs/icom/frame.h index 0ffc7e0e9..acd03c5cd 100644 --- a/rigs/icom/frame.h +++ b/rigs/icom/frame.h @@ -27,11 +27,14 @@ /* * 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 make_cmd_frame(unsigned char frame[], unsigned char re_id, unsigned char ctrl_id, + unsigned 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); +int read_icom_frame(hamlib_port_t *p, const unsigned char rxbuffer[], size_t rxbuffer_len); +int read_icom_frame_direct(hamlib_port_t *p, const unsigned char rxbuffer[], size_t rxbuffer_len); int rig2icom_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width, unsigned char *md, signed char *pd); void icom2rig_mode(RIG *rig, unsigned char md, int pd, rmode_t *mode, pbwidth_t *width); diff --git a/rigs/icom/ic7300.c b/rigs/icom/ic7300.c index d2c6b79e7..e9fb53598 100644 --- a/rigs/icom/ic7300.c +++ b/rigs/icom/ic7300.c @@ -692,6 +692,11 @@ const struct rig_caps ic7300_caps = }, }, + .async_data_supported = 1, + .read_frame_direct = icom_read_frame_direct, + .is_async_frame = icom_is_async_frame, + .process_async_frame = icom_process_async_frame, + .cfgparams = icom_cfg_params, .set_conf = icom_set_conf, .get_conf = icom_get_conf, diff --git a/rigs/icom/icom.c b/rigs/icom/icom.c index 4a3e17ac0..6ad781007 100644 --- a/rigs/icom/icom.c +++ b/rigs/icom/icom.c @@ -929,6 +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 echo_off; ENTERFUNC; @@ -937,9 +938,9 @@ icom_rig_open(RIG *rig) rig_debug(RIG_DEBUG_VERBOSE, "%s: %s v%s\n", __func__, rig->caps->model_name, rig->caps->version); retry_open: - retval = icom_get_usb_echo_off(rig); + echo_off = icom_get_usb_echo_off(rig); - if (retval == RIG_OK) // then echo is on so let's try freq now + if (echo_off == 0) // then echo is on so let's try freq now { rig->state.current_vfo = icom_current_vfo(rig); // some rigs like the IC7100 still echo when in standby @@ -967,9 +968,9 @@ retry_open: } // Now that we're powered up let's try again - retval = icom_get_usb_echo_off(rig); + echo_off = icom_get_usb_echo_off(rig); - if (retval < 0) + if (echo_off < 0) { rig_debug(RIG_DEBUG_ERR, "%s: Unable to determine USB echo status\n", __func__); RETURNFUNC(retval); @@ -7675,7 +7676,7 @@ int icom_set_powerstat(RIG *rig, powerstat_t status) // we'll just send a few more to be sure for all speeds memset(fe_buf, 0xfe, fe_max); // sending more than enough 0xfe's to wake up the rs232 - write_block(&rs->rigport, (char *) fe_buf, fe_max); + write_block(&rs->rigport, fe_buf, fe_max); // we'll try 0x18 0x01 now -- should work on STBY rigs too pwr_sc = S_PWR_ON; @@ -8601,9 +8602,9 @@ static int icom_parse_spectrum_frame(RIG *rig, int length, RETURNFUNC(RIG_OK); } -int icom_is_async_frame(RIG *rig, int frame_len, const unsigned char *frame) +int icom_is_async_frame(RIG *rig, size_t frame_length, const unsigned char *frame) { - if (frame_len < ACKFRMLEN) + if (frame_length < ACKFRMLEN) { return 0; } @@ -8613,7 +8614,7 @@ int icom_is_async_frame(RIG *rig, int frame_len, const unsigned char *frame) && frame[5] == S_SCP_DAT); } -int icom_process_async_frame(RIG *rig, int frame_len, +int icom_process_async_frame(RIG *rig, size_t frame_length, const unsigned char *frame) { struct rig_state *rs = &rig->state; @@ -8670,7 +8671,7 @@ int icom_process_async_frame(RIG *rig, int frame_len, case C_CTL_SCP: if (frame[5] == S_SCP_DAT) { - icom_parse_spectrum_frame(rig, frame_len - (6 + 1), frame + 6); + icom_parse_spectrum_frame(rig, frame_length - (6 + 1), frame + 6); } break; @@ -8706,6 +8707,7 @@ int icom_decode_event(RIG *rig) { rig_debug(RIG_DEBUG_VERBOSE, "%s: got a timeout before the first character\n", __func__); + RETURNFUNC(-RIG_ETIMEOUT); } if (frm_len < 1) @@ -8755,6 +8757,11 @@ int icom_decode_event(RIG *rig) RETURNFUNC(icom_process_async_frame(rig, frm_len, buf)); } +int icom_read_frame_direct(RIG *rig, size_t buffer_length, const unsigned char *buffer) +{ + return read_icom_frame_direct(&rig->state.rigport, buffer, buffer_length); +} + int icom_set_raw(RIG *rig, int cmd, int subcmd, int subcmdbuflen, unsigned char *subcmdbuf, int val_bytes, int val) { @@ -9216,11 +9223,11 @@ DECLARE_PROBERIG_BACKEND(icom) for (civ_addr = 0x01; civ_addr <= 0x7f; civ_addr++) { - frm_len = make_cmd_frame((char *) buf, civ_addr, CTRLID, + frm_len = make_cmd_frame(buf, civ_addr, CTRLID, C_RD_TRXID, S_RD_TRXID, NULL, 0); rig_flush(port); - write_block(port, (char *) buf, frm_len); + write_block(port, buf, frm_len); /* read out the bytes we just sent * TODO: check this is what we expect @@ -9290,11 +9297,11 @@ DECLARE_PROBERIG_BACKEND(icom) for (civ_addr = 0x80; civ_addr <= 0x8f; civ_addr++) { - frm_len = make_cmd_frame((char *) buf, civ_addr, CTRLID, + frm_len = make_cmd_frame(buf, civ_addr, CTRLID, C_CTL_MISC, S_OPTO_RDID, NULL, 0); rig_flush(port); - write_block(port, (char *) buf, frm_len); + write_block(port, buf, frm_len); /* read out the bytes we just sent * TODO: check this is what we expect diff --git a/rigs/icom/icom.h b/rigs/icom/icom.h index 20176f2a2..10d23fdd9 100644 --- a/rigs/icom/icom.h +++ b/rigs/icom/icom.h @@ -398,8 +398,9 @@ 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); +int icom_is_async_frame(RIG *rig, size_t frame_length, const unsigned char *frame); +int icom_process_async_frame(RIG *rig, size_t frame_length, const unsigned char *frame); +int icom_read_frame_direct(RIG *rig, size_t buffer_length, const unsigned char *buffer); extern const struct confparams icom_cfg_params[]; extern const struct confparams icom_ext_levels[]; diff --git a/rigs/kenwood/tmd710.c b/rigs/kenwood/tmd710.c index 8012f654f..f8e7e58a5 100644 --- a/rigs/kenwood/tmd710.c +++ b/rigs/kenwood/tmd710.c @@ -588,6 +588,7 @@ static int tmd710_resolve_vfo(RIG *rig, vfo_t vfo, vfo_t *resolved_vfo, switch (vfo) { case RIG_VFO_CURR: + case RIG_VFO_MEM: return tmd710_get_vfo_num(rig, resolved_vfonum, resolved_vfo); case RIG_VFO_A: diff --git a/src/iofunc.c b/src/iofunc.c index 232bc3fb0..6483d2e18 100644 --- a/src/iofunc.c +++ b/src/iofunc.c @@ -55,6 +55,18 @@ #include "cm108.h" #include "gpio.h" +static void close_sync_data_pipe(hamlib_port_t *p) +{ + if (p->fd_sync_read != -1) { + close(p->fd_sync_read); + p->fd_sync_read = -1; + } + if (p->fd_sync_write != -1) { + close(p->fd_sync_write); + p->fd_sync_write = -1; + } +} + /** * \brief Open a hamlib_port based on its rig port type * \param p rig port descriptor @@ -64,10 +76,30 @@ int HAMLIB_API port_open(hamlib_port_t *p) { int status; int want_state_delay = 0; + int sync_pipe_fds[2]; ENTERFUNC; p->fd = -1; + p->fd_sync_write = -1; + p->fd_sync_read = -1; + + if (p->async) + { + status = pipe2(sync_pipe_fds, O_NONBLOCK); + if (status != 0) + { + rig_debug(RIG_DEBUG_ERR, "%s: synchronous data pipe open status=%d, err=%s\n", __func__, + status, strerror(errno)); + close_sync_data_pipe(p); + RETURNFUNC(-RIG_EINTERNAL); + } + + p->fd_sync_read = sync_pipe_fds[0]; + p->fd_sync_write = sync_pipe_fds[1]; + + rig_debug(RIG_DEBUG_VERBOSE, "%s: created synchronous data pipe\n", __func__); + } switch (p->type.rig) { @@ -78,6 +110,7 @@ int HAMLIB_API port_open(hamlib_port_t *p) { rig_debug(RIG_DEBUG_ERR, "%s: serial_open(%s) status=%d, err=%s\n", __func__, p->pathname, status, strerror(errno)); + close_sync_data_pipe(p); RETURNFUNC(status); } @@ -92,6 +125,7 @@ int HAMLIB_API port_open(hamlib_port_t *p) if (status != 0) { rig_debug(RIG_DEBUG_ERR, "%s: set_rts status=%d\n", __func__, status); + close_sync_data_pipe(p); RETURNFUNC(status); } @@ -105,6 +139,7 @@ int HAMLIB_API port_open(hamlib_port_t *p) if (status != 0) { rig_debug(RIG_DEBUG_ERR, "%s: set_dtr status=%d\n", __func__, status); + close_sync_data_pipe(p); RETURNFUNC(status); } @@ -124,6 +159,7 @@ int HAMLIB_API port_open(hamlib_port_t *p) if (status < 0) { + close_sync_data_pipe(p); RETURNFUNC(status); } @@ -134,6 +170,7 @@ int HAMLIB_API port_open(hamlib_port_t *p) if (status < 0) { + close_sync_data_pipe(p); RETURNFUNC(status); } @@ -144,6 +181,7 @@ int HAMLIB_API port_open(hamlib_port_t *p) if (status < 0) { + close_sync_data_pipe(p); RETURNFUNC(-RIG_EIO); } @@ -157,6 +195,7 @@ int HAMLIB_API port_open(hamlib_port_t *p) if (status < 0) { + close_sync_data_pipe(p); RETURNFUNC(status); } @@ -174,12 +213,14 @@ int HAMLIB_API port_open(hamlib_port_t *p) if (status < 0) { + close_sync_data_pipe(p); RETURNFUNC(status); } break; default: + close_sync_data_pipe(p); RETURNFUNC(-RIG_EINVAL); } @@ -231,6 +272,8 @@ int HAMLIB_API port_close(hamlib_port_t *p, rig_port_t port_type) p->fd = -1; } + close_sync_data_pipe(p); + RETURNFUNC(ret); } @@ -370,16 +413,18 @@ static int port_select(hamlib_port_t *p, /* POSIX */ -static ssize_t port_read(hamlib_port_t *p, void *buf, size_t count) +static ssize_t port_read_generic(hamlib_port_t *p, void *buf, size_t count, int direct) { + int fd = direct ? p->fd : p->fd_sync_read; + if (p->type.rig == RIG_PORT_SERIAL && p->parm.serial.data_bits == 7) { unsigned char *pbuf = buf; - int ret = read(p->fd, buf, count); + ssize_t ret = read(fd, buf, count); /* clear MSB */ - int i; + ssize_t i; for (i = 0; i < ret; i++) { @@ -390,12 +435,12 @@ static ssize_t port_read(hamlib_port_t *p, void *buf, size_t count) } else { - return read(p->fd, buf, count); + return read(fd, buf, count); } } //! @cond Doxygen_Suppress -#define port_write(p,b,c) write((p)->fd,(b),(c)) +#define port_write_generic(p,b,c,d) write((d) ? (p)->fd : (p)->fd_sync_write,(b),(c)) #define port_select(p,n,r,w,e,t) select((n),(r),(w),(e),(t)) //! @endcond @@ -430,7 +475,7 @@ static ssize_t port_read(hamlib_port_t *p, void *buf, size_t count) * it could work very well also with any file handle, like a socket. */ -int HAMLIB_API write_block(hamlib_port_t *p, const char *txbuffer, size_t count) +static int write_block_generic(hamlib_port_t *p, const unsigned char *txbuffer, size_t count, int direct) { int ret; @@ -468,7 +513,7 @@ int HAMLIB_API write_block(hamlib_port_t *p, const char *txbuffer, size_t count) for (i = 0; i < count; i++) { - ret = port_write(p, txbuffer + i, 1); + ret = port_write_generic(p, txbuffer + i, 1, direct); if (ret != 1) { @@ -487,7 +532,7 @@ int HAMLIB_API write_block(hamlib_port_t *p, const char *txbuffer, size_t count) } else { - ret = port_write(p, txbuffer, count); + ret = port_write_generic(p, txbuffer, count, direct); if (ret != count) { @@ -530,31 +575,55 @@ int HAMLIB_API write_block(hamlib_port_t *p, const char *txbuffer, size_t count) /** - * \brief Read bytes from an fd + * \brief Write a block of characters to an fd. * \param p rig port descriptor - * \param rxbuffer buffer to receive text - * \param count number of bytes - * \return count of bytes received + * \param txbuffer command sequence to be sent + * \param count number of bytes to send + * \return 0 = OK, <0 = NOK * - * Read "num" bytes from "fd" and put results into - * an array of unsigned char pointed to by "rxbuffer" + * Write a block of count characters to port file descriptor, + * with a pause between each character if write_delay is > 0 * - * Blocks on read until timeout hits. + * The write_delay is for Yaesu type rigs..require 5 character + * sequence to be sent with 50-200msec between each char. * - * It then reads "num" bytes into rxbuffer. + * Also, post_write_delay is for some Yaesu rigs (eg: FT747) that + * get confused with sequential fast writes between cmd sequences. + * + * input: + * + * fd - file descriptor to write to + * txbuffer - pointer to a command sequence array + * count - count of byte to send from the txbuffer + * write_delay - write delay in ms between 2 chars + * post_write_delay - minimum delay between two writes + * post_write_date - timeval of last write * * Actually, this function has nothing specific to serial comm, * it could work very well also with any file handle, like a socket. */ -int HAMLIB_API read_block(hamlib_port_t *p, char *rxbuffer, size_t count) +int HAMLIB_API write_block(hamlib_port_t *p, const unsigned char *txbuffer, size_t count) +{ + return write_block_generic(p, txbuffer, count, 1); +} + +int HAMLIB_API write_block_sync(hamlib_port_t *p, const unsigned char *txbuffer, size_t count) +{ + return write_block_generic(p, txbuffer, count, 0); +} + +static int read_block_generic(hamlib_port_t *p, unsigned char *rxbuffer, size_t count, int direct) { fd_set rfds, efds; + int fd; struct timeval tv, tv_timeout, start_time, end_time, elapsed_time; int total_count = 0; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + fd = direct ? p->fd : p->fd_sync_read; + /* * Wait up to timeout ms. */ @@ -571,10 +640,10 @@ int HAMLIB_API read_block(hamlib_port_t *p, char *rxbuffer, size_t count) tv = tv_timeout; /* select may have updated it */ FD_ZERO(&rfds); - FD_SET(p->fd, &rfds); + FD_SET(fd, &rfds); efds = rfds; - retval = port_select(p, p->fd + 1, &rfds, NULL, &efds, &tv); + retval = port_select(p, fd + 1, &rfds, NULL, &efds, &tv); if (retval == 0) { @@ -605,7 +674,7 @@ int HAMLIB_API read_block(hamlib_port_t *p, char *rxbuffer, size_t count) return -RIG_EIO; } - if (FD_ISSET(p->fd, &efds)) + if (FD_ISSET(fd, &efds)) { rig_debug(RIG_DEBUG_ERR, "%s(): fd error after %d chars\n", @@ -619,7 +688,7 @@ int HAMLIB_API read_block(hamlib_port_t *p, char *rxbuffer, size_t count) * grab bytes from the rig * The file descriptor must have been set up non blocking. */ - rd_count = port_read(p, rxbuffer + total_count, count); + rd_count = (int) port_read_generic(p, rxbuffer + total_count, count, direct); if (rd_count < 0) { @@ -643,38 +712,61 @@ int HAMLIB_API read_block(hamlib_port_t *p, char *rxbuffer, size_t count) /** - * \brief Read a string from an fd - * \param p Hamlib port descriptor - * \param rxbuffer buffer to receive string - * \param rxmax maximum string size + 1 - * \param stopset string of recognized end of string characters - * \param stopset_len length of stopset - * \return number of characters read if the operation has been successful, - * otherwise a negative value if an error occurred (in which case, cause is - * set appropriately). + * \brief Read bytes from the device directly or from the synchronous data pipe, depending on the device caps + * \param p rig port descriptor + * \param rxbuffer buffer to receive text + * \param count number of bytes + * \return count of bytes received * - * Read a string from "fd" and put result into + * Read "num" bytes from "fd" and put results into * an array of unsigned char pointed to by "rxbuffer" * * Blocks on read until timeout hits. * - * It then reads characters until one of the characters in - * "stopset" is found, or until "rxmax-1" characters was copied - * into rxbuffer. String termination character is added at the end. + * It then reads "num" bytes into rxbuffer. * * Actually, this function has nothing specific to serial comm, * it could work very well also with any file handle, like a socket. - * - * Assumes rxbuffer!=NULL */ -int HAMLIB_API read_string(hamlib_port_t *p, - char *rxbuffer, - size_t rxmax, - const char *stopset, - int stopset_len, - int flush_flag) + +int HAMLIB_API read_block(hamlib_port_t *p, unsigned char *rxbuffer, size_t count) +{ + return read_block_generic(p, rxbuffer, count, !p->async); +} + +/** + * \brief Read bytes directly from the device file descriptor + * \param p rig port descriptor + * \param rxbuffer buffer to receive text + * \param count number of bytes + * \return count of bytes received + * + * Read "num" bytes from "fd" and put results into + * an array of unsigned char pointed to by "rxbuffer" + * + * Blocks on read until timeout hits. + * + * It then reads "num" bytes into rxbuffer. + * + * Actually, this function has nothing specific to serial comm, + * it could work very well also with any file handle, like a socket. + */ + +int HAMLIB_API read_block_direct(hamlib_port_t *p, unsigned char *rxbuffer, size_t count) +{ + return read_block_generic(p, rxbuffer, count, 1); +} + +static int read_string_generic(hamlib_port_t *p, + unsigned char *rxbuffer, + size_t rxmax, + const char *stopset, + int stopset_len, + int flush_flag, + int direct) { fd_set rfds, efds; + int fd; struct timeval tv, tv_timeout, start_time, end_time, elapsed_time; int total_count = 0; int i=0; @@ -694,6 +786,8 @@ int HAMLIB_API read_string(hamlib_port_t *p, return 0; } + fd = direct ? p->fd : p->fd_sync_read; + /* * Wait up to timeout ms. */ @@ -707,15 +801,16 @@ int HAMLIB_API read_string(hamlib_port_t *p, while (total_count < rxmax - 1) // allow 1 byte for end-of-string { - int rd_count; + ssize_t rd_count; int retval; + tv = tv_timeout; /* select may have updated it */ FD_ZERO(&rfds); - FD_SET(p->fd, &rfds); + FD_SET(fd, &rfds); efds = rfds; - retval = port_select(p, p->fd + 1, &rfds, NULL, &efds, &tv); + retval = port_select(p, fd + 1, &rfds, NULL, &efds, &tv); if (retval == 0) { @@ -743,7 +838,7 @@ int HAMLIB_API read_string(hamlib_port_t *p, if (retval < 0) { - dump_hex((unsigned char *) rxbuffer, total_count); + dump_hex(rxbuffer, total_count); rig_debug(RIG_DEBUG_ERR, "%s(): select() error after %d chars: %s\n", __func__, @@ -753,7 +848,7 @@ int HAMLIB_API read_string(hamlib_port_t *p, return -RIG_EIO; } - if (FD_ISSET(p->fd, &efds)) + if (FD_ISSET(fd, &efds)) { rig_debug(RIG_DEBUG_ERR, "%s(): fd error after %d chars\n", @@ -769,13 +864,12 @@ int HAMLIB_API read_string(hamlib_port_t *p, */ do { - rd_count = port_read(p, &rxbuffer[total_count], 1); + rd_count = port_read_generic(p, &rxbuffer[total_count], 1, direct); if (errno == EAGAIN) { hl_usleep(5*1000); rig_debug(RIG_DEBUG_WARN, "%s: port_read is busy?\n", __func__); } - } while( ++i < 10 && errno == EBUSY); // 50ms should be enough /* if we get 0 bytes or an error something is wrong */ @@ -793,7 +887,7 @@ int HAMLIB_API read_string(hamlib_port_t *p, // check to see if our string startis with \...if so we need more chars if (total_count == 0 && rxbuffer[total_count] == '\\') { rxmax = (rxmax - 1) * 5; } - total_count += rd_count; + total_count += (int) rd_count; if (stopset && memchr(stopset, rxbuffer[total_count - 1], stopset_len)) { @@ -817,4 +911,77 @@ int HAMLIB_API read_string(hamlib_port_t *p, return total_count; /* return bytes count read */ } +/** + * \brief Read a string from the device directly or from the synchronous data pipe, depending on the device caps + * \param p Hamlib port descriptor + * \param rxbuffer buffer to receive string + * \param rxmax maximum string size + 1 + * \param stopset string of recognized end of string characters + * \param stopset_len length of stopset + * \return number of characters read if the operation has been successful, + * otherwise a negative value if an error occurred (in which case, cause is + * set appropriately). + * + * Read a string from "fd" and put result into + * an array of unsigned char pointed to by "rxbuffer" + * + * Blocks on read until timeout hits. + * + * It then reads characters until one of the characters in + * "stopset" is found, or until "rxmax-1" characters was copied + * into rxbuffer. String termination character is added at the end. + * + * Actually, this function has nothing specific to serial comm, + * it could work very well also with any file handle, like a socket. + * + * Assumes rxbuffer!=NULL + */ + +int HAMLIB_API read_string(hamlib_port_t *p, + unsigned char *rxbuffer, + size_t rxmax, + const char *stopset, + int stopset_len, + int flush_flag) +{ + return read_string_generic(p, rxbuffer, rxmax, stopset, stopset_len, flush_flag, !p->async); +} + + +/** + * \brief Read a string directly from the device file descriptor + * \param p Hamlib port descriptor + * \param rxbuffer buffer to receive string + * \param rxmax maximum string size + 1 + * \param stopset string of recognized end of string characters + * \param stopset_len length of stopset + * \return number of characters read if the operation has been successful, + * otherwise a negative value if an error occurred (in which case, cause is + * set appropriately). + * + * Read a string from "fd" and put result into + * an array of unsigned char pointed to by "rxbuffer" + * + * Blocks on read until timeout hits. + * + * It then reads characters until one of the characters in + * "stopset" is found, or until "rxmax-1" characters was copied + * into rxbuffer. String termination character is added at the end. + * + * Actually, this function has nothing specific to serial comm, + * it could work very well also with any file handle, like a socket. + * + * Assumes rxbuffer!=NULL + */ + +int HAMLIB_API read_string_direct(hamlib_port_t *p, + unsigned char *rxbuffer, + size_t rxmax, + const char *stopset, + int stopset_len, + int flush_flag) +{ + return read_string_generic(p, rxbuffer, rxmax, stopset, stopset_len, flush_flag, 1); +} + /** @} */ diff --git a/src/iofunc.h b/src/iofunc.h index 8055acfa8..5def45bf4 100644 --- a/src/iofunc.h +++ b/src/iofunc.h @@ -25,24 +25,38 @@ #include #include - extern HAMLIB_EXPORT(int) port_open(hamlib_port_t *p); extern HAMLIB_EXPORT(int) port_close(hamlib_port_t *p, rig_port_t port_type); extern HAMLIB_EXPORT(int) read_block(hamlib_port_t *p, - char *rxbuffer, + unsigned char *rxbuffer, size_t count); +extern HAMLIB_EXPORT(int) read_block_direct(hamlib_port_t *p, + unsigned char *rxbuffer, + size_t count); + extern HAMLIB_EXPORT(int) write_block(hamlib_port_t *p, - const char *txbuffer, + const unsigned char *txbuffer, size_t count); +extern HAMLIB_EXPORT(int) write_block_sync(hamlib_port_t *p, + const unsigned char *txbuffer, + size_t count); + extern HAMLIB_EXPORT(int) read_string(hamlib_port_t *p, - char *rxbuffer, + unsigned char *rxbuffer, size_t rxmax, const char *stopset, int stopset_len, int flush_flag); +extern HAMLIB_EXPORT(int) read_string_direct(hamlib_port_t *p, + unsigned char *rxbuffer, + size_t rxmax, + const char *stopset, + int stopset_len, + int flush_flag); + #endif /* _IOFUNC_H */ diff --git a/src/rig.c b/src/rig.c index c709b1857..3c22b6976 100644 --- a/src/rig.c +++ b/src/rig.c @@ -227,6 +227,18 @@ void rig_lock() {}; void rig_unlock() {}; #endif +#ifdef HAVE_PTHREAD +volatile int async_data_handler_thread_run = 0; +pthread_t async_data_handler_thread_id; + +struct async_data_handler_args_s +{ + RIG *rig; +} async_data_handler_args; + +void *async_data_handler(void *arg); +#endif + /* * track which rig is opened (with rig_open) * needed at least for transceive mode @@ -365,13 +377,13 @@ static int rig_check_rig_caps() if (&caps_test.rig_model != caps_test_rig_model) { rc = -RIG_EINTERNAL; - rig_debug(RIG_DEBUG_WARN, "%s: shared libary change#1\n", __func__); + rig_debug(RIG_DEBUG_WARN, "%s: shared library change#1\n", __func__); } if (&caps_test.macro_name != caps_test_macro_name) { rc = -RIG_EINTERNAL; - rig_debug(RIG_DEBUG_WARN, "%s: shared libary change#2\n", __func__); + rig_debug(RIG_DEBUG_WARN, "%s: shared library change#2\n", __func__); } //if (rc != RIG_OK) @@ -467,6 +479,9 @@ RIG *HAMLIB_API rig_init(rig_model_t rig_model) rs->pttport.fd = -1; rs->comm_state = 0; rs->rigport.type.rig = caps->port_type; /* default from caps */ + rs->rigport.async = caps->async_data_supported; + rs->rigport.fd_sync_write = -1; + rs->rigport.fd_sync_read = -1; switch (caps->port_type) { @@ -1039,6 +1054,22 @@ int HAMLIB_API rig_open(RIG *rig) RETURNFUNC(status); } + if (caps->async_data_supported) + { + async_data_handler_thread_run = 1; + async_data_handler_args.rig = rig; + int err = pthread_create(&async_data_handler_thread_id, NULL, + async_data_handler, &async_data_handler_args); + + if (err) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d) pthread_create error: %s\n", __FILE__, __LINE__, + strerror(errno)); + port_close(&rs->rigport, rs->rigport.type.rig); + return -RIG_EINTERNAL; + } + } + add_opened_rig(rig); rs->comm_state = 1; @@ -1053,6 +1084,8 @@ int HAMLIB_API rig_open(RIG *rig) if (status != RIG_OK) { + // TODO: stop async reader + port_close(&rs->rigport, rs->rigport.type.rig); RETURNFUNC(status); } } @@ -1177,6 +1210,22 @@ int HAMLIB_API rig_close(RIG *rig) #endif + async_data_handler_thread_run = 0; + + if (async_data_handler_thread_id != 0) + { + int err = pthread_join(async_data_handler_thread_id, NULL); + + if (err) + { + rig_debug(RIG_DEBUG_ERR, "%s(%d): pthread_join error: %s\n", __FILE__, __LINE__, + strerror(errno)); + // just ignore the error + } + + async_data_handler_thread_id = 0; + } + if (!rig || !rig->caps) { RETURNFUNC(-RIG_EINVAL); @@ -7139,3 +7188,64 @@ HAMLIB_EXPORT(void) sync_callback(int lock) /*! @} */ +#ifdef HAVE_PTHREAD + +#define MAX_FRAME_LENGTH 1024 + +void *async_data_handler(void *arg) +{ + struct async_data_handler_args_s *args = (struct async_data_handler_args_s *)arg; + unsigned char frame[MAX_FRAME_LENGTH]; + RIG *rig = args->rig; + struct rig_state *rs = &rig->state; + int result; + + rig_debug(RIG_DEBUG_TRACE, "%s(%d): Starting async data handler thread\n", __FILE__, + __LINE__); + + while (async_data_handler_thread_run) + { + int frame_length; + int async_frame; + + result = rig->caps->read_frame_direct(rig, sizeof(frame), frame); + if (result < 0) + { + // TODO: error handling + rig_debug(RIG_DEBUG_ERR, "%s: read_frame_direct() failed, result=%d\n", __func__, result); + continue; + } + + frame_length = result; + + async_frame = rig->caps->is_async_frame(rig, frame_length, frame); + + rig_debug(RIG_DEBUG_VERBOSE, "%s: received frame: len=%d async=%d\n", __func__, frame_length, async_frame); + + if (async_frame) + { + result = rig->caps->process_async_frame(rig, frame_length, frame); + if (result < 0) + { + // TODO: error handling + continue; + } + } + else + { + result = write_block_sync(&rs->rigport, frame, frame_length); + if (result < 0) + { + // TODO: error handling + rig_debug(RIG_DEBUG_ERR, "%s: write_block_sync() failed, result=%d\n", __func__, result); + continue; + } + } + } + + rig_debug(RIG_DEBUG_TRACE, "%s(%d): Stopping async data handler thread\n", __FILE__, + __LINE__); + + return NULL; +} +#endif diff --git a/src/serial.c b/src/serial.c index 959b546a9..9af2624d9 100644 --- a/src/serial.c +++ b/src/serial.c @@ -643,6 +643,7 @@ int HAMLIB_API serial_flush(hamlib_port_t *p) int timeout_save; unsigned char buf[4096]; ENTERFUNC; + RETURNFUNC(RIG_OK); if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd || p->flushx) { diff --git a/tests/rigctl_parse.c b/tests/rigctl_parse.c index bf8798d49..df9f281cd 100644 --- a/tests/rigctl_parse.c +++ b/tests/rigctl_parse.c @@ -4144,11 +4144,11 @@ static int myspectrum_event(RIG *rig, struct rig_spectrum_line *line, { ENTERFUNC; - if (rig_need_debug(RIG_DEBUG_TRACE)) + if (rig_need_debug(RIG_DEBUG_ERR)) { char spectrum_debug[line->spectrum_data_length * 4]; print_spectrum_line(spectrum_debug, sizeof(spectrum_debug), line); - rig_debug(RIG_DEBUG_TRACE, "%s: ASCII Spectrum Scope: %s\n", __func__, + rig_debug(RIG_DEBUG_ERR, "%s: ASCII Spectrum Scope: %s\n", __func__, spectrum_debug); } @@ -4198,7 +4198,8 @@ declare_proto_rig(set_trn) rig_set_spectrum_callback(rig, myspectrum_event, NULL); } - RETURNFUNC(rig_set_trn(rig, trn)); + RETURNFUNC(RIG_OK); + //RETURNFUNC(rig_set_trn(rig, trn)); } @@ -4932,7 +4933,7 @@ declare_proto_rig(send_cmd) rig_debug(RIG_DEBUG_TRACE, "%s: rigport=%d, bufcmd=%s, cmd_len=%d\n", __func__, rs->rigport.fd, hasbinary(bufcmd, cmd_len) ? "BINARY" : bufcmd, cmd_len); - retval = write_block(&rs->rigport, (char *)bufcmd, cmd_len); + retval = write_block(&rs->rigport, (unsigned char *) bufcmd, cmd_len); if (retval != RIG_OK) { @@ -4957,7 +4958,7 @@ declare_proto_rig(send_cmd) } /* Assumes CR or LF is end of line char for all ASCII protocols. */ - retval = read_string(&rs->rigport, (char *)buf, rxbytes, eom_buf, + retval = read_string(&rs->rigport, buf, rxbytes, eom_buf, strlen(eom_buf), 0); if (retval < 0)