]> sigrok.org Git - libsigrok.git/blobdiff - hardware/common/serial.c
build: Portability fixes.
[libsigrok.git] / hardware / common / serial.c
index a7376af00dc253f663da44f97d55eb7b655674b7..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)
 {
@@ -167,17 +171,8 @@ SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial)
        return SR_OK;
 }
 
-/**
- * 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.
- *
- * @return The number of bytes written, or a negative error code upon failure.
- */
-SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
-               const void *buf, size_t count)
+static int _serial_write(struct sr_serial_dev_inst *serial,
+               const void *buf, size_t count, int nonblocking)
 {
        ssize_t ret;
        char *error;
@@ -192,7 +187,7 @@ SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
                return SR_ERR;
        }
 
-       if (serial->nonblocking)
+       if (nonblocking)
                ret = sp_nonblocking_write(serial->data, buf, count);
        else
                ret = sp_blocking_write(serial->data, buf, count, 0);
@@ -214,16 +209,44 @@ SR_PRIV int serial_write(struct sr_serial_dev_inst *serial,
 }
 
 /**
- * Read a number of bytes from the specified serial port.
+ * Write a number of bytes to the specified serial port.
  *
  * @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] buf Buffer containing the bytes to write.
+ * @param[in] count Number of bytes to write.
  *
- * @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 written.
  */
-SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
-               size_t count)
+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()
+ */
+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)
+{
+       return _serial_write(serial, buf, count, 1);
+}
+
+static int _serial_read(struct sr_serial_dev_inst *serial, void *buf,
+               size_t count, int nonblocking)
 {
        ssize_t ret;
        char *error;
@@ -238,7 +261,7 @@ SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
                return SR_ERR;
        }
 
-       if (serial->nonblocking)
+       if (nonblocking)
                ret = sp_nonblocking_read(serial->data, buf, count);
        else
                ret = sp_blocking_read(serial->data, buf, count, 0);
@@ -260,6 +283,44 @@ SR_PRIV int serial_read(struct sr_serial_dev_inst *serial, void *buf,
        return ret;
 }
 
+/**
+ * Read a number of bytes from the specified serial port.
+ *
+ * @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(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()
+ */
+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)
+{
+       return _serial_read(serial, buf, count, 1);
+}
+
 /**
  * Set serial parameters for the specified serial port.
  *
@@ -273,7 +334,7 @@ SR_PRIV int serial_read(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,
@@ -338,21 +399,30 @@ SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
 }
 
 /**
- * Set serial parameters for the specified serial port.
+ * Set serial parameters for the specified serial port from parameter string.
  *
  * @param serial Previously initialized serial port structure.
- * @param[in] paramstr A serial communication parameters string, in the form
- * of \<speed\>/\<data bits\>\<parity\>\<stopbits\>\<flow\>, for example
- * "9600/8n1" or "600/7o2" or "460800/8n1/flow=2" where flow is 0 for none,
- * 1 for rts/cts and 2 for xon/xoff.
- *
+ * @param[in] paramstr A serial communication parameters string of the form
+ * "<baudrate>/<bits><parity><stopbits>{/<option>}".\n
+ *  Examples: "9600/8n1", "600/7o2/dtr=1/rts=0" or "460800/8n1/flow=2".\n
+ * \<baudrate\>=integer Baud rate.\n
+ * \<bits\>=5|6|7|8 Number of data bits.\n
+ * \<parity\>=n|e|o None, even, odd.\n
+ * \<stopbits\>=1|2 One or two stop bits.\n
+ * Options:\n
+ * dtr=0|1 Set DTR off resp. on.\n
+ * flow=0|1|2 Flow control. 0 for none, 1 for RTS/CTS, 2 for XON/XOFF.\n
+ * 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.
  */
-#define SERIAL_COMM_SPEC "^(\\d+)/([5678])([neo])([12])(.*)$"
 SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
                const char *paramstr)
 {
+#define SERIAL_COMM_SPEC "^(\\d+)/([5678])([neo])([12])(.*)$"
+
        GRegex *reg;
        GMatchInfo *match;
        int speed, databits, parity, stopbits, flow, rts, dtr, i;
@@ -519,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;
@@ -637,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;
@@ -673,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);
@@ -697,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
+}