X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=blobdiff_plain;f=src%2Fserial.c;h=106090b72927e230a6dd389f04a640fe312f94ab;hp=1c0870d9f61cf9c1fe168cc76d21339a6b1a69a2;hb=HEAD;hpb=6762401d2b992a7b10585653ab07ccf1a336fe05 diff --git a/src/serial.c b/src/serial.c index 1c0870d9..d78dea20 100644 --- a/src/serial.c +++ b/src/serial.c @@ -99,6 +99,8 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) serial->lib_funcs = ser_lib_funcs_hid; else if (ser_name_is_bt(serial)) serial->lib_funcs = ser_lib_funcs_bt; + else if (ser_name_is_tcpraw(serial)) + serial->lib_funcs = ser_lib_funcs_tcpraw; else serial->lib_funcs = ser_lib_funcs_libsp; if (!serial->lib_funcs) @@ -122,10 +124,24 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) if (ret != SR_OK) return ret; - if (serial->serialcomm) - return serial_set_paramstr(serial, serial->serialcomm); - else - return SR_OK; + if (serial->serialcomm) { + ret = serial_set_paramstr(serial, serial->serialcomm); + if (ret != SR_OK) + return ret; + } + + /* + * Flush potentially dangling RX data. Availability of the + * flush primitive depends on the transport/cable, absense + * is non-fatal. + */ + ret = serial_flush(serial); + if (ret == SR_ERR_NA) + ret = SR_OK; + if (ret != SR_OK) + return ret; + + return SR_OK; } /** @@ -547,6 +563,37 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, return ret; } +/** + * Manipulate handshake state for the specified serial port. + * + * @param serial Previously initialized serial port structure. + * @param[in] rts Status of RTS line (0 or 1; or -1 to ignore). + * @param[in] dtr Status of DTR line (0 or 1; or -1 to ignore). + * + * @retval SR_OK Success. + * @retval SR_ERR Failure. + * + * @private + */ +SR_PRIV int serial_set_handshake(struct sr_serial_dev_inst *serial, + int rts, int dtr) +{ + int ret; + + if (!serial) { + sr_dbg("Invalid serial port."); + return SR_ERR; + } + + sr_spew("Modifying serial parameters on port %s.", serial->port); + + if (!serial->lib_funcs || !serial->lib_funcs->set_handshake) + return SR_ERR_NA; + ret = serial->lib_funcs->set_handshake(serial, rts, dtr); + + return ret; +} + /** * Set serial parameters for the specified serial port from parameter string. * @@ -574,7 +621,7 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial, const char *paramstr) { /** @cond PRIVATE */ -#define SERIAL_COMM_SPEC "^(\\d+)/([5678])([neo])([12])(.*)$" +#define SERIAL_COMM_SPEC "^(\\d+)(/([5678])([neo])([12]))?(.*)$" /** @endcond */ GRegex *reg; @@ -582,7 +629,10 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial, int speed, databits, parity, stopbits, flow, rts, dtr, i; char *mstr, **opts, **kv; - speed = databits = parity = stopbits = flow = 0; + speed = flow = 0; + databits = 8; + parity = SP_PARITY_NONE; + stopbits = 1; rts = dtr = -1; sr_spew("Parsing parameters from \"%s\".", paramstr); reg = g_regex_new(SERIAL_COMM_SPEC, 0, 0, NULL); @@ -590,10 +640,10 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial, if ((mstr = g_match_info_fetch(match, 1))) speed = strtoul(mstr, NULL, 10); g_free(mstr); - if ((mstr = g_match_info_fetch(match, 2))) + if ((mstr = g_match_info_fetch(match, 3)) && mstr[0]) databits = strtoul(mstr, NULL, 10); g_free(mstr); - if ((mstr = g_match_info_fetch(match, 3))) { + if ((mstr = g_match_info_fetch(match, 4)) && mstr[0]) { switch (mstr[0]) { case 'n': parity = SP_PARITY_NONE; @@ -607,10 +657,10 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial, } } g_free(mstr); - if ((mstr = g_match_info_fetch(match, 4))) + if ((mstr = g_match_info_fetch(match, 5)) && mstr[0]) stopbits = strtoul(mstr, NULL, 10); g_free(mstr); - if ((mstr = g_match_info_fetch(match, 5)) && mstr[0] != '\0') { + if ((mstr = g_match_info_fetch(match, 6)) && mstr[0] != '\0') { if (mstr[0] != '/') { sr_dbg("missing separator before extra options"); speed = 0; @@ -658,14 +708,17 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial, } g_match_info_unref(match); g_regex_unref(reg); + sr_spew("Got params: rate %d, frame %d/%d/%d, flow %d, rts %d, dtr %d.", + speed, databits, parity, stopbits, flow, rts, dtr); - if (speed) { - return serial_set_params(serial, speed, databits, parity, - stopbits, flow, rts, dtr); - } else { + if (!speed) { sr_dbg("Could not infer speed from parameter string."); return SR_ERR_ARG; } + + return serial_set_params(serial, speed, + databits, parity, stopbits, + flow, rts, dtr); } /** @@ -736,14 +789,24 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, /** * Try to find a valid packet in a serial data stream. * - * @param serial Previously initialized serial port structure. - * @param buf Buffer containing the bytes to write. - * @param buflen Size of the buffer. + * @param[in] serial Previously initialized serial port structure. + * @param[in] buf Buffer containing the bytes to write. + * @param[in] buflen Size of the buffer. * @param[in] packet_size Size, in bytes, of a valid packet. - * @param is_valid Callback that assesses whether the packet is valid or not. + * @param[in] is_valid Callback that assesses whether the packet is valid or not. + * @param[in] is_valid_len Callback which checks a variable length packet. + * @param[out] return_size Detected packet size in case of successful match. * @param[in] timeout_ms The timeout after which, if no packet is detected, to * abort scanning. * + * Data is received from the serial port and into the caller provided + * buffer, until the buffer is exhausted, or the timeout has expired, + * or a valid packet was found. Receive data is passed to the caller + * provided validity check routine, assuming either fixed size packets + * (#is_valid parameter, exact match to the #packet_size length) or + * packets of variable length (#is_valid_len parameter, minimum length + * #packet_size required for first invocation). + * * @retval SR_OK Valid packet was found within the given timeout. * @retval SR_ERR Failure. * @@ -751,86 +814,138 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, */ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, uint8_t *buf, size_t *buflen, - size_t packet_size, - packet_valid_callback is_valid, + size_t packet_size, packet_valid_callback is_valid, + packet_valid_len_callback is_valid_len, size_t *return_size, uint64_t timeout_ms) { - uint64_t start, time, byte_delay_us; - size_t ibuf, i, maxlen; - ssize_t len; - - maxlen = *buflen; + uint64_t start_us, elapsed_ms, byte_delay_us; + size_t fill_idx, check_idx, max_fill_idx; + ssize_t recv_len; + const uint8_t *check_ptr; + size_t check_len, pkt_len; + gboolean do_dump; + int ret; sr_dbg("Detecting packets on %s (timeout = %" PRIu64 "ms).", serial->port, timeout_ms); - if (maxlen < (packet_size * 2) ) { - sr_err("Buffer size must be at least twice the packet size."); - return SR_ERR; + max_fill_idx = *buflen; + if (max_fill_idx < 2 * packet_size) { + /* + * The heuristics in this check is only useful for fixed + * packet length scenarios, but for variable length setups + * we don't know the packets' sizes up front. + */ + sr_err("Small stream detect RX buffer, want 2x packet size."); + return SR_ERR_ARG; } - /* Assume 8n1 transmission. That is 10 bits for every byte. */ byte_delay_us = serial_timeout(serial, 1) * 1000; - start = g_get_monotonic_time(); + start_us = g_get_monotonic_time(); + + check_idx = fill_idx = 0; + while (fill_idx < max_fill_idx) { + /* + * Read bytes individually. Lets callers continue to + * successfully process next RX data after first match. + * Run full loop bodies for empty or failed reception + * in an iteration, to have timeouts checked. + */ + recv_len = serial_read_nonblocking(serial, &buf[fill_idx], 1); + if (recv_len > 0) + fill_idx += recv_len; + + /* Dump receive data when (a minimum) size is reached. */ + check_ptr = &buf[check_idx]; + check_len = fill_idx - check_idx; + do_dump = check_len >= packet_size; + do_dump &= sr_log_loglevel_get() >= SR_LOG_SPEW; + if (do_dump) { + GString *text; - i = ibuf = len = 0; - while (ibuf < maxlen) { - len = serial_read_nonblocking(serial, &buf[ibuf], 1); - if (len > 0) { - ibuf += len; - } else if (len == 0) { - /* No logging, already done in serial_read(). */ - } else { - /* Error reading byte, but continuing anyway. */ + text = sr_hexdump_new(check_ptr, check_len); + sr_spew("Trying packet: len %zu, bytes %s", + check_len, text->str); + sr_hexdump_free(text); } - time = g_get_monotonic_time() - start; - time /= 1000; - - if ((ibuf - i) >= packet_size) { - GString *text; - /* We have at least a packet's worth of data. */ - text = sr_hexdump_new(&buf[i], packet_size); - sr_spew("Trying packet: %s", text->str); - sr_hexdump_free(text); - if (is_valid(&buf[i])) { - sr_spew("Found valid %zu-byte packet after " - "%" PRIu64 "ms.", (ibuf - i), time); - *buflen = ibuf; + /* A packet's (minimum) length was received, check its data. */ + elapsed_ms = g_get_monotonic_time() - start_us; + elapsed_ms /= 1000; + if (is_valid_len && check_len >= packet_size) { + pkt_len = packet_size; + ret = is_valid_len(NULL, check_ptr, check_len, &pkt_len); + if (ret == SR_PACKET_VALID) { + /* Exact match. Terminate with success. */ + sr_spew("Valid packet after %" PRIu64 "ms.", + elapsed_ms); + sr_spew("RX count %zu, packet len %zu.", + fill_idx, pkt_len); + *buflen = fill_idx; + if (return_size) + *return_size = pkt_len; return SR_OK; + } + if (ret == SR_PACKET_NEED_RX) { + /* Incomplete, keep accumulating RX data. */ + sr_spew("Checker needs more RX data."); } else { - sr_spew("Got %zu bytes, but not a valid " - "packet.", (ibuf - i)); + /* Not a valid packet. Continue searching. */ + sr_spew("Invalid packet, advancing read pos."); + check_idx++; + } + } + if (is_valid && check_len >= packet_size) { + if (is_valid(check_ptr)) { + /* Exact match. Terminate with success. */ + sr_spew("Valid packet after %" PRIu64 "ms.", + elapsed_ms); + sr_spew("RX count %zu, packet len %zu.", + fill_idx, packet_size); + *buflen = fill_idx; + if (return_size) + *return_size = packet_size; + return SR_OK; } /* Not a valid packet. Continue searching. */ - i++; + sr_spew("Invalid packet, advancing read pointer."); + check_idx++; } - if (time >= timeout_ms) { - /* Timeout */ - sr_dbg("Detection timed out after %" PRIu64 "ms.", time); + + /* Check for packet search timeout. */ + if (elapsed_ms >= timeout_ms) { + sr_dbg("Detection timed out after %" PRIu64 "ms.", + elapsed_ms); break; } - if (len < 1) + if (recv_len < 1) g_usleep(byte_delay_us); } - - *buflen = ibuf; - - sr_err("Didn't find a valid packet (read %zu bytes).", *buflen); + sr_info("Didn't find a valid packet (read %zu bytes).", fill_idx); + *buflen = fill_idx; return SR_ERR; } +#endif + /** * Extract the serial device and options from the options linked list. * - * @param options List of options passed from the command line. - * @param serial_device Pointer where to store the extracted serial device. - * @param serial_options Pointer where to store the optional extracted serial + * The caller's passed in references get updated when the list of options + * contains one of the desired parameters. This lets callers pre-assign + * default values which take effect in the absence of user specifications. + * Either reference is optional, passing #NULL is acceptable. + * + * Callers must not free returned strings. These shall be considered + * read-only handles to data that is managed elsewhere. + * + * @param[in] options List of options passed from the command line. + * @param[out] serial_device Pointer where to store the extracted serial device. + * @param[out] serial_options Pointer where to store the optional extracted serial * options. * - * @return SR_OK if a serial_device is found, SR_ERR if no device is found. The - * returned string should not be freed by the caller. + * @return SR_OK if a serial_device is found, SR_ERR if no device is found. * * @private */ @@ -840,23 +955,25 @@ SR_PRIV int sr_serial_extract_options(GSList *options, GSList *l; struct sr_config *src; - *serial_device = NULL; - for (l = options; l; l = l->next) { src = l->data; switch (src->key) { case SR_CONF_CONN: + if (!serial_device) + break; *serial_device = g_variant_get_string(src->data, NULL); sr_dbg("Parsed serial device: %s.", *serial_device); break; case SR_CONF_SERIALCOMM: + if (!serial_options) + break; *serial_options = g_variant_get_string(src->data, NULL); sr_dbg("Parsed serial options: %s.", *serial_options); break; } } - if (!*serial_device) { + if (serial_device && !*serial_device) { sr_dbg("No serial device specified."); return SR_ERR; } @@ -864,6 +981,8 @@ SR_PRIV int sr_serial_extract_options(GSList *options, return SR_OK; } +#ifdef HAVE_SERIAL_COMM + /** @private */ SR_PRIV int serial_source_add(struct sr_session *session, struct sr_serial_dev_inst *serial, int events, int timeout,