X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fserial.c;h=192b18a7245a4caffa17eb3a23ab3801bd16c33e;hb=cf3db38193bf1b2d36628ebe57a0204d77f68da3;hp=9a9a0d94fd3b3058e84ba74202db681de8397885;hpb=155b680da482cea2381becb73c51cfb838bff31e;p=libsigrok.git diff --git a/src/serial.c b/src/serial.c index 9a9a0d94..192b18a7 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 @@ -34,7 +35,7 @@ * * @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. @@ -62,8 +63,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) { @@ -171,8 +170,46 @@ 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. + */ +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 +227,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: @@ -209,44 +246,43 @@ static int _serial_write(struct sr_serial_dev_inst *serial, } /** - * 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. */ 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. */ 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 +300,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: @@ -284,41 +320,40 @@ static int _serial_read(struct sr_serial_dev_inst *serial, void *buf, } /** - * 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. */ 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. */ 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); } /** @@ -530,7 +565,7 @@ SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial, 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,8 +578,8 @@ 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; @@ -552,7 +587,7 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf, 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 +598,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) @@ -618,7 +655,7 @@ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, 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) { @@ -783,129 +820,60 @@ SR_PRIV int serial_source_remove(struct sr_session *session, */ SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id) { -#ifdef __linux__ - const gchar *usb_dev; - const char device_tree[] = "/sys/bus/usb/devices/"; - GDir *devices_dir, *device_dir; - GSList *l = NULL; - GSList *tty_devs; - GSList *matched_paths; - FILE *fd; - char tmp[5]; - gchar *vendor_path, *product_path, *path_copy; - gchar *prefix, *subdir_path, *device_path, *tty_path; - unsigned long read_vendor_id, read_product_id; - const char *file; - - l = NULL; - tty_devs = NULL; - matched_paths = NULL; - - if (!(devices_dir = g_dir_open(device_tree, 0, NULL))) - return NULL; - - /* - * Find potential candidates using the vendor ID and product ID - * and store them in matched_paths. - */ - while ((usb_dev = g_dir_read_name(devices_dir))) { - vendor_path = g_strconcat(device_tree, - usb_dev, "/idVendor", NULL); - product_path = g_strconcat(device_tree, - usb_dev, "/idProduct", NULL); - - if (!g_file_test(vendor_path, G_FILE_TEST_EXISTS) || - !g_file_test(product_path, G_FILE_TEST_EXISTS)) - goto skip_device; - - if ((fd = g_fopen(vendor_path, "r")) == NULL) - goto skip_device; - - if (fgets(tmp, sizeof(tmp), fd) == NULL) { - fclose(fd); - goto skip_device; - } - read_vendor_id = strtoul(tmp, NULL, 16); - - fclose(fd); - - if ((fd = g_fopen(product_path, "r")) == NULL) - goto skip_device; - - if (fgets(tmp, sizeof(tmp), fd) == NULL) { - fclose(fd); - goto skip_device; - } - read_product_id = strtoul(tmp, NULL, 16); + GSList *tty_devs = NULL; + struct sp_port **ports; + int i, vid, pid; - fclose(fd); - - if (vendor_id == read_vendor_id && - product_id == read_product_id) { - path_copy = g_strdup(usb_dev); - matched_paths = g_slist_prepend(matched_paths, - path_copy); - } + if (sp_list_ports(&ports) != SP_OK) + return NULL; -skip_device: - g_free(vendor_path); - g_free(product_path); - } - g_dir_close(devices_dir); + 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) + tty_devs = g_slist_prepend(tty_devs, + g_strdup(sp_get_port_name(ports[i]))); - /* For every matched device try to find a ttyUSBX subfolder. */ - for (l = matched_paths; l; l = l->next) { - subdir_path = NULL; + sp_free_port_list(ports); + return tty_devs; +} - device_path = g_strconcat(device_tree, l->data, NULL); +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; - if (!(device_dir = g_dir_open(device_path, 0, NULL))) { - g_free(device_path); - continue; - } + /* Default to 1s. */ + timeout_ms = 1000; - prefix = g_strconcat(l->data, ":", NULL); + if (sp_new_config(&config) < 0) + return timeout_ms; - while ((file = g_dir_read_name(device_dir))) { - if (g_str_has_prefix(file, prefix)) { - subdir_path = g_strconcat(device_path, - "/", file, NULL); - break; - } - } - g_dir_close(device_dir); + bits = baud = 0; + do { + if (sp_get_config(port->data, config) < 0) + break; - g_free(prefix); - g_free(device_path); + /* 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 (subdir_path) { - if (!(device_dir = g_dir_open(subdir_path, 0, NULL))) { - g_free(subdir_path); - continue; - } - g_free(subdir_path); - - while ((file = g_dir_read_name(device_dir))) { - if (g_str_has_prefix(file, "ttyUSB")) { - tty_path = g_strconcat("/dev/", - file, NULL); - sr_dbg("Found USB device %04x:%04x attached to %s.", - vendor_id, product_id, tty_path); - tty_devs = g_slist_prepend(tty_devs, - tty_path); - break; - } - } - g_dir_close(device_dir); - } + if (bits && baud) { + /* Throw in 10ms for misc OS overhead. */ + timeout_ms = 10; + timeout_ms += ((1000.0 / baud) * bits) * num_bytes; } - g_slist_free_full(matched_paths, g_free); - return tty_devs; -#else - (void)vendor_id; - (void)product_id; + sp_free_config(config); - return NULL; -#endif + return timeout_ms; }