]> sigrok.org Git - libsigrok.git/blobdiff - hardware/common/serial.c
build: Portability fixes.
[libsigrok.git] / hardware / common / serial.c
index a267d6fb88367fa246c3c739a697b218e110a0dc..9a9a0d94fd3b3058e84ba74202db681de8397885 100644 (file)
@@ -22,6 +22,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <glib.h>
+#include <glib/gstdio.h>
 #include <libserialport.h>
 #include "libsigrok.h"
 #include "libsigrok-internal.h"
  * 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;
@@ -682,8 +708,9 @@ typedef HANDLE event_handle;
 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)
+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)
 {
        enum sp_event mask = 0;
        unsigned int i;
@@ -718,20 +745,21 @@ 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],
-                               timeout, cb, cb_data) != SR_OK)
+               if (sr_session_source_add_pollfd(session, &serial->pollfds[i],
+                                       timeout, cb, cb_data) != SR_OK)
                        return SR_ERR;
        }
 
        return SR_OK;
 }
 
-SR_PRIV int serial_source_remove(struct sr_serial_dev_inst *serial)
+SR_PRIV int serial_source_remove(struct sr_session *session,
+               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_session_source_remove_pollfd(session, &serial->pollfds[i]) != SR_OK)
                        return SR_ERR;
 
        g_free(serial->pollfds);
@@ -742,3 +770,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
+}