X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fserial.c;h=27244da34b6513c6e0cc4b9461fd4d417b3f652b;hb=bee2b0168c087676c1b365861d8c2d4714afa9b9;hp=705c52df08b504ddc1cc0651bf8cf9b56d6ef02d;hpb=01f6e330f8a49a80f2c7fdf94af79bffbe279cba;p=libsigrok.git diff --git a/src/serial.c b/src/serial.c index 705c52df..27244da3 100644 --- a/src/serial.c +++ b/src/serial.c @@ -4,6 +4,7 @@ * Copyright (C) 2010-2012 Bert Vermeulen * Copyright (C) 2010-2012 Uwe Hermann * Copyright (C) 2012 Alexandru Gagniuc + * Copyright (C) 2014 Uffe Jakobsen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,28 +20,50 @@ * along with this program. If not, see . */ +#include #include #include #include #include #include -#include "libsigrok.h" +#include #include "libsigrok-internal.h" +#ifdef G_OS_WIN32 +#include /* for HANDLE */ +#endif +/** @cond PRIVATE */ #define LOG_PREFIX "serial" +/** @endcond */ + +/** + * @file + * + * Serial port handling. + */ + +/** + * @defgroup grp_serial Serial port handling + * + * Serial port handling functions. + * + * @{ + */ /** * Open the specified serial port. * * @param serial Previously initialized serial port structure. * @param[in] flags Flags to use when opening the serial port. Possible flags - * include SERIAL_RDWR, SERIAL_RDONLY, SERIAL_NONBLOCK. + * include SERIAL_RDWR, SERIAL_RDONLY. * * If the serial structure contains a serialcomm string, it will be * passed to serial_set_paramstr() after the port is opened. * * @retval SR_OK Success. * @retval SR_ERR Failure. + * + * @private */ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) { @@ -62,8 +85,6 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) else if (flags & SERIAL_RDONLY) sp_flags = SP_MODE_READ; - serial->nonblocking = (flags & SERIAL_NONBLOCK) ? 1 : 0; - ret = sp_open(serial->data, sp_flags); switch (ret) { @@ -91,6 +112,8 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) * * @retval SR_OK Success. * @retval SR_ERR Failure. + * + * @private */ SR_PRIV int serial_close(struct sr_serial_dev_inst *serial) { @@ -136,6 +159,8 @@ SR_PRIV int serial_close(struct sr_serial_dev_inst *serial) * * @retval SR_OK Success. * @retval SR_ERR Failure. + * + * @private */ SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial) { @@ -171,8 +196,48 @@ SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial) return SR_OK; } +/** + * Drain serial port buffers. + * + * @param serial Previously initialized serial port structure. + * + * @retval SR_OK Success. + * @retval SR_ERR Failure. + * + * @private + */ +SR_PRIV int serial_drain(struct sr_serial_dev_inst *serial) +{ + int ret; + char *error; + + if (!serial) { + sr_dbg("Invalid serial port."); + return SR_ERR; + } + + if (!serial->data) { + sr_dbg("Cannot drain unopened serial port %s.", serial->port); + return SR_ERR; + } + + sr_spew("Draining serial port %s.", serial->port); + + ret = sp_drain(serial->data); + + if (ret == SP_ERR_FAIL) { + error = sp_last_error_message(); + sr_err("Error draining port (%d): %s.", + sp_last_error_code(), error); + sp_free_error_message(error); + return SR_ERR; + } + + return SR_OK; +} + static int _serial_write(struct sr_serial_dev_inst *serial, - const void *buf, size_t count, int nonblocking) + const void *buf, size_t count, int nonblocking, unsigned int timeout_ms) { ssize_t ret; char *error; @@ -190,7 +255,7 @@ static int _serial_write(struct sr_serial_dev_inst *serial, if (nonblocking) ret = sp_nonblocking_write(serial->data, buf, count); else - ret = sp_blocking_write(serial->data, buf, count, 0); + ret = sp_blocking_write(serial->data, buf, count, timeout_ms); switch (ret) { case SP_ERR_ARG: @@ -203,50 +268,53 @@ static int _serial_write(struct sr_serial_dev_inst *serial, return SR_ERR; } - sr_spew("Wrote %d/%d bytes.", ret, count); + sr_spew("Wrote %zd/%zu bytes.", ret, count); return ret; } /** - * Write a number of bytes to the specified serial port. + * Write a number of bytes to the specified serial port, blocking until finished. * * @param serial Previously initialized serial port structure. * @param[in] buf Buffer containing the bytes to write. * @param[in] count Number of bytes to write. + * @param[in] timeout_ms Timeout in ms, or 0 for no timeout. * * @retval SR_ERR_ARG Invalid argument. * @retval SR_ERR Other error. - * @retval other The number of bytes written. - */ -SR_PRIV int serial_write(struct sr_serial_dev_inst *serial, - const void *buf, size_t count) -{ - return _serial_write(serial, buf, count, serial->nonblocking); -} - -/** - * Write a number of bytes to the specified serial port, blocking until finished. - * @copydetails serial_write() + * @retval other The number of bytes written. If this is less than the number + * specified in the call, the timeout was reached. + * + * @private */ SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial, - const void *buf, size_t count) + const void *buf, size_t count, unsigned int timeout_ms) { - return _serial_write(serial, buf, count, 0); + return _serial_write(serial, buf, count, 0, timeout_ms); } /** * Write a number of bytes to the specified serial port, return immediately. - * @copydetails serial_write() -*/ + * + * @param serial Previously initialized serial port structure. + * @param[in] buf Buffer containing the bytes to write. + * @param[in] count Number of bytes to write. + * + * @retval SR_ERR_ARG Invalid argument. + * @retval SR_ERR Other error. + * @retval other The number of bytes written. + * + * @private + */ SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial, const void *buf, size_t count) { - return _serial_write(serial, buf, count, 1); + return _serial_write(serial, buf, count, 1, 0); } static int _serial_read(struct sr_serial_dev_inst *serial, void *buf, - size_t count, int nonblocking) + size_t count, int nonblocking, unsigned int timeout_ms) { ssize_t ret; char *error; @@ -264,7 +332,7 @@ static int _serial_read(struct sr_serial_dev_inst *serial, void *buf, if (nonblocking) ret = sp_nonblocking_read(serial->data, buf, count); else - ret = sp_blocking_read(serial->data, buf, count, 0); + ret = sp_blocking_read(serial->data, buf, count, timeout_ms); switch (ret) { case SP_ERR_ARG: @@ -278,47 +346,50 @@ static int _serial_read(struct sr_serial_dev_inst *serial, void *buf, } if (ret > 0) - sr_spew("Read %d/%d bytes.", ret, count); + sr_spew("Read %zd/%zu bytes.", ret, count); return ret; } /** - * Read a number of bytes from the specified serial port. + * Read a number of bytes from the specified serial port, block until finished. * * @param serial Previously initialized serial port structure. * @param buf Buffer where to store the bytes that are read. * @param[in] count The number of bytes to read. + * @param[in] timeout_ms Timeout in ms, or 0 for no timeout. * * @retval SR_ERR_ARG Invalid argument. * @retval SR_ERR Other error. - * @retval other The number of bytes read. - */ -SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf, - size_t count) -{ - return _serial_read(serial, buf, count, serial->nonblocking); -} - -/** - * Read a number of bytes from the specified serial port, block until finished. - * @copydetails serial_read() + * @retval other The number of bytes read. If this is less than the number + * requested, the timeout was reached. + * + * @private */ SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf, - size_t count) + size_t count, unsigned int timeout_ms) { - return _serial_read(serial, buf, count, 0); + return _serial_read(serial, buf, count, 0, timeout_ms); } /** * Try to read up to @a count bytes from the specified serial port, return * immediately with what's available. - * @copydetails serial_read() + * + * @param serial Previously initialized serial port structure. + * @param buf Buffer where to store the bytes that are read. + * @param[in] count The number of bytes to read. + * + * @retval SR_ERR_ARG Invalid argument. + * @retval SR_ERR Other error. + * @retval other The number of bytes read. + * + * @private */ SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf, size_t count) { - return _serial_read(serial, buf, count, 1); + return _serial_read(serial, buf, count, 1, 0); } /** @@ -336,6 +407,8 @@ SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf * * @retval SR_OK Success. * @retval SR_ERR Failure. + * + * @private */ SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate, int bits, int parity, int stopbits, @@ -415,13 +488,18 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate, * rts=0|1 Set RTS off resp. on.\n * Please note that values and combinations of these parameters must be * supported by the concrete serial interface hardware and the drivers for it. + * * @retval SR_OK Success. * @retval SR_ERR Failure. + * + * @private */ 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])(.*)$" +/** @endcond */ GRegex *reg; GMatchInfo *match; @@ -442,13 +520,13 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial, if ((mstr = g_match_info_fetch(match, 3))) { switch (mstr[0]) { case 'n': - parity = SERIAL_PARITY_NONE; + parity = SP_PARITY_NONE; break; case 'e': - parity = SERIAL_PARITY_EVEN; + parity = SP_PARITY_EVEN; break; case 'o': - parity = SERIAL_PARITY_ODD; + parity = SP_PARITY_ODD; break; } } @@ -526,11 +604,13 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial, * * @retval SR_OK Success. * @retval SR_ERR Failure. + * + * @private */ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf, int *buflen, gint64 timeout_ms) { - gint64 start; + gint64 start, remaining; int maxlen, len; if (!serial) { @@ -543,16 +623,16 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf, return -1; } - timeout_ms *= 1000; start = g_get_monotonic_time(); + remaining = timeout_ms; maxlen = *buflen; *buflen = len = 0; - while(1) { + while (1) { len = maxlen - *buflen - 1; if (len < 1) break; - len = serial_read(serial, *buf + *buflen, 1); + len = sp_blocking_read(serial->data, *buf + *buflen, 1, remaining); if (len > 0) { *buflen += len; *(*buf + *buflen) = '\0'; @@ -563,7 +643,9 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf, break; } } - if (g_get_monotonic_time() - start > timeout_ms) + /* Reduce timeout by time elapsed. */ + remaining = timeout_ms - ((g_get_monotonic_time() - start) / 1000); + if (remaining <= 0) /* Timeout */ break; if (len < 1) @@ -591,6 +673,8 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf, * * @retval SR_OK Valid packet was found within the given timeout. * @retval SR_ERR Failure. + * + * @private */ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, uint8_t *buf, size_t *buflen, @@ -600,7 +684,7 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, { uint64_t start, time, byte_delay_us; size_t ibuf, i, maxlen; - int len; + ssize_t len; maxlen = *buflen; @@ -613,12 +697,12 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, } /* Assume 8n1 transmission. That is 10 bits for every byte. */ - byte_delay_us = 10 * (1000000 / baudrate); + byte_delay_us = 10 * ((1000 * 1000) / baudrate); start = g_get_monotonic_time(); i = ibuf = len = 0; while (ibuf < maxlen) { - len = serial_read(serial, &buf[ibuf], 1); + len = serial_read_nonblocking(serial, &buf[ibuf], 1); if (len > 0) { ibuf += len; } else if (len == 0) { @@ -633,12 +717,12 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, if ((ibuf - i) >= packet_size) { /* We have at least a packet's worth of data. */ if (is_valid(&buf[i])) { - sr_spew("Found valid %d-byte packet after " + sr_spew("Found valid %zu-byte packet after " "%" PRIu64 "ms.", (ibuf - i), time); *buflen = ibuf; return SR_OK; } else { - sr_spew("Got %d bytes, but not a valid " + sr_spew("Got %zu bytes, but not a valid " "packet.", (ibuf - i)); } /* Not a valid packet. Continue searching. */ @@ -646,7 +730,7 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, } if (time >= timeout_ms) { /* Timeout */ - sr_dbg("Detection timed out after %dms.", time); + sr_dbg("Detection timed out after %" PRIu64 "ms.", time); break; } if (len < 1) @@ -655,7 +739,7 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, *buflen = ibuf; - sr_err("Didn't find a valid packet (read %d bytes).", *buflen); + sr_err("Didn't find a valid packet (read %zu bytes).", *buflen); return SR_ERR; } @@ -664,12 +748,14 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, * 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 exctracted serial device. + * @param serial_device Pointer where to store the extracted serial device. * @param 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. + * + * @private */ SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_device, const char **serial_options) @@ -684,38 +770,47 @@ SR_PRIV int sr_serial_extract_options(GSList *options, const char **serial_devic switch (src->key) { case SR_CONF_CONN: *serial_device = g_variant_get_string(src->data, NULL); - sr_dbg("Parsed serial device: %s", *serial_device); + sr_dbg("Parsed serial device: %s.", *serial_device); break; - case SR_CONF_SERIALCOMM: *serial_options = g_variant_get_string(src->data, NULL); - sr_dbg("Parsed serial options: %s", *serial_options); + sr_dbg("Parsed serial options: %s.", *serial_options); break; } } if (!*serial_device) { - sr_dbg("No serial device specified"); + sr_dbg("No serial device specified."); return SR_ERR; } return SR_OK; } -#ifdef _WIN32 +/** @cond PRIVATE */ +#ifdef G_OS_WIN32 typedef HANDLE event_handle; #else typedef int event_handle; #endif +/** @endcond */ +/** @private */ SR_PRIV int serial_source_add(struct sr_session *session, struct sr_serial_dev_inst *serial, int events, int timeout, sr_receive_data_callback cb, void *cb_data) { + struct sp_event_set *event_set; + gintptr poll_fd; + unsigned int poll_events; enum sp_event mask = 0; - unsigned int i; - if (sp_new_event_set(&serial->event_set) != SP_OK) + if ((events & (G_IO_IN|G_IO_ERR)) && (events & G_IO_OUT)) { + sr_err("Cannot poll input/error and output simultaneously."); + return SR_ERR_ARG; + } + + if (sp_new_event_set(&event_set) != SP_OK) return SR_ERR; if (events & G_IO_IN) @@ -725,50 +820,114 @@ SR_PRIV int serial_source_add(struct sr_session *session, if (events & G_IO_ERR) mask |= SP_EVENT_ERROR; - if (sp_add_port_events(serial->event_set, serial->data, mask) != SP_OK) { - sp_free_event_set(serial->event_set); + if (sp_add_port_events(event_set, serial->data, mask) != SP_OK) { + sp_free_event_set(event_set); + return SR_ERR; + } + if (event_set->count != 1) { + sr_err("Unexpected number (%u) of event handles to poll.", + event_set->count); + sp_free_event_set(event_set); return SR_ERR; } - serial->pollfds = (GPollFD *) g_malloc0(sizeof(GPollFD) * serial->event_set->count); + poll_fd = (gintptr) ((event_handle *)event_set->handles)[0]; + mask = event_set->masks[0]; + + sp_free_event_set(event_set); + + poll_events = 0; + if (mask & SP_EVENT_RX_READY) + poll_events |= G_IO_IN; + if (mask & SP_EVENT_TX_READY) + poll_events |= G_IO_OUT; + if (mask & SP_EVENT_ERROR) + poll_events |= G_IO_ERR; + /* + * Using serial->data as the key for the event source is not quite + * proper, as it makes it impossible to create another event source + * for the same serial port. However, these fixed keys will soon be + * removed from the API anyway, so this is OK for now. + */ + return sr_session_fd_source_add(session, serial->data, + poll_fd, poll_events, timeout, cb, cb_data); +} - for (i = 0; i < serial->event_set->count; i++) { +/** @private */ +SR_PRIV int serial_source_remove(struct sr_session *session, + struct sr_serial_dev_inst *serial) +{ + return sr_session_source_remove_internal(session, serial->data); +} - serial->pollfds[i].fd = ((event_handle *) serial->event_set->handles)[i]; +/** + * Create/allocate a new sr_serial_port structure. + * + * @param name The OS dependent name of the serial port. Must not be NULL. + * @param description An end user friendly description for the serial port. + * Can be NULL (in that case the empty string is used + * as description). + * + * @return The newly allocated sr_serial_port struct. + */ +static struct sr_serial_port *sr_serial_new(const char *name, + const char *description) +{ + struct sr_serial_port *serial; - mask = serial->event_set->masks[i]; + if (!name) + return NULL; - if (mask & SP_EVENT_RX_READY) - serial->pollfds[i].events |= G_IO_IN; - if (mask & SP_EVENT_TX_READY) - serial->pollfds[i].events |= G_IO_OUT; - if (mask & SP_EVENT_ERROR) - serial->pollfds[i].events |= G_IO_ERR; + serial = g_malloc(sizeof(struct sr_serial_port)); + serial->name = g_strdup(name); + serial->description = g_strdup(description ? description : ""); - if (sr_session_source_add_pollfd(session, &serial->pollfds[i], - timeout, cb, cb_data) != SR_OK) - return SR_ERR; - } + return serial; +} - return SR_OK; +/** + * Free a previously allocated sr_serial_port structure. + * + * @param serial The sr_serial_port struct to free. Must not be NULL. + */ +SR_API void sr_serial_free(struct sr_serial_port *serial) +{ + if (!serial) + return; + g_free(serial->name); + g_free(serial->description); + g_free(serial); } -SR_PRIV int serial_source_remove(struct sr_session *session, - struct sr_serial_dev_inst *serial) +/** + * List available serial devices. + * + * @return A GSList of strings containing the path of the serial devices or + * NULL if no serial device is found. The returned list must be freed + * by the caller. + */ +SR_API GSList *sr_serial_list(const struct sr_dev_driver *driver) { - unsigned int i; + GSList *tty_devs = NULL; + struct sp_port **ports; + struct sr_serial_port *port; + int i; - for (i = 0; i < serial->event_set->count; i++) - if (sr_session_source_remove_pollfd(session, &serial->pollfds[i]) != SR_OK) - return SR_ERR; + /* Currently unused, but will be used by some drivers later on. */ + (void)driver; - g_free(serial->pollfds); - sp_free_event_set(serial->event_set); + if (sp_list_ports(&ports) != SP_OK) + return NULL; - serial->pollfds = NULL; - serial->event_set = NULL; + for (i = 0; ports[i]; i++) { + port = sr_serial_new(sp_get_port_name(ports[i]), + sp_get_port_description(ports[i])); + tty_devs = g_slist_append(tty_devs, port); + } - return SR_OK; + sp_free_port_list(ports); + + return tty_devs; } /** @@ -780,6 +939,8 @@ SR_PRIV int serial_source_remove(struct sr_session *session, * @return A GSList of strings containing the path of the serial device or * NULL if no serial device is found. The returned list must be freed * by the caller. + * + * @private */ SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id) { @@ -790,13 +951,58 @@ SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id) if (sp_list_ports(&ports) != SP_OK) return NULL; - for (i=0; ports[i]; i++) + for (i = 0; ports[i]; i++) if (sp_get_port_transport(ports[i]) == SP_TRANSPORT_USB && sp_get_port_usb_vid_pid(ports[i], &vid, &pid) == SP_OK && - vid == vendor_id && pid == product_id) + vid == vendor_id && pid == product_id) { tty_devs = g_slist_prepend(tty_devs, - g_strdup(sp_get_port_name(ports[i]))); + g_strdup(sp_get_port_name(ports[i]))); + } sp_free_port_list(ports); + return tty_devs; } + +/** @private */ +SR_PRIV int serial_timeout(struct sr_serial_dev_inst *port, int num_bytes) +{ + struct sp_port_config *config; + int timeout_ms, bits, baud, tmp; + + /* Default to 1s. */ + timeout_ms = 1000; + + if (sp_new_config(&config) < 0) + return timeout_ms; + + bits = baud = 0; + do { + if (sp_get_config(port->data, config) < 0) + break; + + /* Start bit. */ + bits = 1; + if (sp_get_config_bits(config, &tmp) < 0) + break; + bits += tmp; + if (sp_get_config_stopbits(config, &tmp) < 0) + break; + bits += tmp; + if (sp_get_config_baudrate(config, &tmp) < 0) + break; + baud = tmp; + } while (FALSE); + + if (bits && baud) { + /* Throw in 10ms for misc OS overhead. */ + timeout_ms = 10; + timeout_ms += ((1000.0 / baud) * bits) * num_bytes; + } + + sp_free_config(config); + + return timeout_ms; +} + +/** @} */