X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=hardware%2Fcommon%2Fserial.c;h=62bbdaf9595c052fe13d466d97a998608d9e9079;hb=5a7e62211c4714ea2aac35baac10cf448373d4f5;hp=a267d6fb88367fa246c3c739a697b218e110a0dc;hpb=48d3238e66bac0958fbec316db837aa3a8a1075a;p=libsigrok.git diff --git a/hardware/common/serial.c b/hardware/common/serial.c index a267d6fb..62bbdaf9 100644 --- a/hardware/common/serial.c +++ b/hardware/common/serial.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "libsigrok.h" #include "libsigrok-internal.h" @@ -32,13 +33,14 @@ * Open the specified serial port. * * @param serial Previously initialized serial port structure. - * @param flags Flags to use when opening the serial port. Possible flags + * @param[in] flags Flags to use when opening the serial port. Possible flags * include SERIAL_RDWR, SERIAL_RDONLY, SERIAL_NONBLOCK. * * If the serial structure contains a serialcomm string, it will be * passed to serial_set_paramstr() after the port is opened. * - * @return SR_OK on success, SR_ERR on failure. + * @retval SR_OK Success. + * @retval SR_ERR Failure. */ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) { @@ -87,7 +89,8 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) * * @param serial Previously initialized serial port structure. * - * @return SR_OK on success, SR_ERR on failure. + * @retval SR_OK Success. + * @retval SR_ERR Failure. */ SR_PRIV int serial_close(struct sr_serial_dev_inst *serial) { @@ -131,7 +134,8 @@ SR_PRIV int serial_close(struct sr_serial_dev_inst *serial) * * @param serial Previously initialized serial port structure. * - * @return SR_OK on success, SR_ERR on failure. + * @retval SR_OK Success. + * @retval SR_ERR Failure. */ SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial) { @@ -208,10 +212,12 @@ static int _serial_write(struct sr_serial_dev_inst *serial, * Write a number of bytes to the specified serial port. * * @param serial Previously initialized serial port structure. - * @param buf Buffer containing the bytes to write. - * @param count Number of bytes to write. + * @param[in] buf Buffer containing the bytes to write. + * @param[in] count Number of bytes to write. * - * @return The number of bytes written, or a negative error code upon failure. + * @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) @@ -219,12 +225,20 @@ SR_PRIV int serial_write(struct sr_serial_dev_inst *serial, return _serial_write(serial, buf, count, serial->nonblocking); } +/** + * Write a number of bytes to the specified serial port, blocking until finished. + * @copydetails serial_write() + */ SR_PRIV int serial_write_blocking(struct sr_serial_dev_inst *serial, const void *buf, size_t count) { return _serial_write(serial, buf, count, 0); } +/** + * Write a number of bytes to the specified serial port, return immediately. + * @copydetails serial_write() +*/ SR_PRIV int serial_write_nonblocking(struct sr_serial_dev_inst *serial, const void *buf, size_t count) { @@ -274,9 +288,11 @@ static int _serial_read(struct sr_serial_dev_inst *serial, void *buf, * * @param serial Previously initialized serial port structure. * @param buf Buffer where to store the bytes that are read. - * @param count The number of bytes to read. + * @param[in] count The number of bytes to read. * - * @return The number of bytes read, or a negative error code upon failure. + * @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) @@ -284,12 +300,21 @@ SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf, return _serial_read(serial, buf, count, serial->nonblocking); } +/** + * Read a number of bytes from the specified serial port, block until finished. + * @copydetails serial_read() + */ SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf, size_t count) { return _serial_read(serial, buf, count, 0); } +/** + * Try to read up to @a count bytes from the specified serial port, return + * immediately with what's available. + * @copydetails serial_read() + */ SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf, size_t count) { @@ -309,7 +334,7 @@ SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf * @param[in] rts Status of RTS line (0 or 1; required by some interfaces). * @param[in] dtr Status of DTR line (0 or 1; required by some interfaces). * - * @retval SR_OK Success + * @retval SR_OK Success. * @retval SR_ERR Failure. */ SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate, @@ -564,12 +589,13 @@ SR_PRIV int serial_readline(struct sr_serial_dev_inst *serial, char **buf, * critical, but it helps fine tune the serial port polling * delay. * - * @retval SR_OK Valid packet was found within the given timeout + * @retval SR_OK Valid packet was found within the given timeout. * @retval SR_ERR Failure. */ SR_PRIV int serial_stream_detect(struct sr_serial_dev_inst *serial, uint8_t *buf, size_t *buflen, - size_t packet_size, packet_valid_t is_valid, + size_t packet_size, + packet_valid_callback is_valid, uint64_t timeout_ms, int baudrate) { uint64_t start, time, byte_delay_us; @@ -683,7 +709,7 @@ typedef int event_handle; #endif SR_PRIV int serial_source_add(struct sr_serial_dev_inst *serial, int events, - int timeout, sr_receive_data_callback_t cb, void *cb_data) + int timeout, sr_receive_data_callback cb, void *cb_data) { enum sp_event mask = 0; unsigned int i; @@ -718,7 +744,7 @@ SR_PRIV int serial_source_add(struct sr_serial_dev_inst *serial, int events, if (mask & SP_EVENT_ERROR) serial->pollfds[i].events |= G_IO_ERR; - if (sr_session_source_add_pollfd(&serial->pollfds[i], + if (sr_source_add_pollfd(&serial->pollfds[i], timeout, cb, cb_data) != SR_OK) return SR_ERR; } @@ -731,7 +757,7 @@ SR_PRIV int serial_source_remove(struct sr_serial_dev_inst *serial) unsigned int i; for (i = 0; i < serial->event_set->count; i++) - if (sr_session_source_remove_pollfd(&serial->pollfds[i]) != SR_OK) + if (sr_source_remove_pollfd(&serial->pollfds[i]) != SR_OK) return SR_ERR; g_free(serial->pollfds); @@ -742,3 +768,142 @@ SR_PRIV int serial_source_remove(struct sr_serial_dev_inst *serial) return SR_OK; } + +/** + * Find USB serial devices via the USB vendor ID and product ID. + * + * @param[in] vendor_id Vendor ID of the USB device. + * @param[in] product_id Product ID of the USB device. + * + * @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. + */ +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); + + 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); + } + +skip_device: + g_free(vendor_path); + g_free(product_path); + } + g_dir_close(devices_dir); + + /* For every matched device try to find a ttyUSBX subfolder. */ + for (l = matched_paths; l; l = l->next) { + subdir_path = NULL; + + device_path = g_strconcat(device_tree, l->data, NULL); + + if (!(device_dir = g_dir_open(device_path, 0, NULL))) { + g_free(device_path); + continue; + } + + prefix = g_strconcat(l->data, ":", NULL); + + 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); + + g_free(prefix); + g_free(device_path); + + 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); + } + } + g_slist_free_full(matched_paths, g_free); + + return tty_devs; +#else + (void)vendor_id; + (void)product_id; + + return NULL; +#endif +}