]> sigrok.org Git - libsigrok.git/commitdiff
uni-t-ut32x: migrate from USB transfers to serial-over-HID communication
authorGerhard Sittig <redacted>
Fri, 6 Oct 2017 00:52:29 +0000 (01:52 +0100)
committerUwe Hermann <redacted>
Sun, 2 Jun 2019 18:39:02 +0000 (20:39 +0200)
Switch the UT32x driver from running specific USB transfers to generic
serial communication. Preset the bitrate and frame format, but allow for
user specified overrides as well. Default to the WCH CH9325 HID chip,
but allow for overrides or more specific selection so that users can
resolve ambiguities with multiple cables.

The switch from libusb to hidapi removes a limitation that specifically
was reported for the Mac platform. The serial-over-HID variant should
now work as well. See bug #555.

Drop the background transfers. Stick with a local acquisition stop
routine, because a STOP request needs to get sent to the device. Reduce
the receive buffer size such that either blocking or non-blocking calls
will work. The additional flexibility of the buffer handling and packet
processing does not harm.

configure.ac
src/hardware/uni-t-ut32x/api.c
src/hardware/uni-t-ut32x/protocol.c
src/hardware/uni-t-ut32x/protocol.h

index 1d1462839545ce09d8dcc1607b785cd38834e013..9601dbe339798f3e3e3352216e1bc2b0a81d8370 100644 (file)
@@ -293,7 +293,7 @@ SR_DRIVER([Teleinfo], [teleinfo], [serial_comm])
 SR_DRIVER([Testo], [testo], [libusb])
 SR_DRIVER([Tondaj SL-814], [tondaj-sl-814], [serial_comm])
 SR_DRIVER([UNI-T DMM], [uni-t-dmm], [libusb])
-SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [libusb])
+SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [serial_comm])
 SR_DRIVER([Victor DMM], [victor-dmm], [libusb])
 SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm])
 SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb])
index 47211fa41ff0aee622efbfd08f30b7173f286daa..199fe473c069af6e86519029ff2e2b03dfddabff 100644 (file)
@@ -23,6 +23,7 @@
 
 static const uint32_t scanopts[] = {
        SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
 };
 
 static const uint32_t drvopts[] = {
@@ -55,108 +56,61 @@ static const char *data_sources[] = {
 
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       struct drv_context *drvc;
-       struct dev_context *devc;
-       struct sr_dev_inst *sdi;
+       const char *conn, *serialcomm;
        struct sr_config *src;
-       GSList *usb_devices, *devices, *l;
-       unsigned int i;
-       const char *conn;
-
-       drvc = di->context;
+       GSList *l, *devices;
+       struct sr_serial_dev_inst *serial;
+       int rc;
+       struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+       size_t i;
 
-       conn = "1a86.e008";
+       conn = "hid/ch9325";
+       serialcomm = "2400/8n1";
        for (l = options; l; l = l->next) {
                src = l->data;
                switch (src->key) {
                case SR_CONF_CONN:
                        conn = g_variant_get_string(src->data, NULL);
                        break;
+               case SR_CONF_SERIALCOMM:
+                       serialcomm = g_variant_get_string(src->data, NULL);
+                       break;
                }
        }
-       if (!conn)
-               return NULL;
 
        devices = NULL;
-       if ((usb_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn))) {
-               /* We have a list of sr_usb_dev_inst matching the connection
-                * string. Wrap them in sr_dev_inst and we're done. */
-               for (l = usb_devices; l; l = l->next) {
-                       sdi = g_malloc0(sizeof(struct sr_dev_inst));
-                       sdi->status = SR_ST_INACTIVE;
-                       sdi->vendor = g_strdup("UNI-T");
-                       sdi->model = g_strdup("UT32x");
-                       sdi->inst_type = SR_INST_USB;
-                       sdi->conn = l->data;
-                       for (i = 0; i < ARRAY_SIZE(channel_names); i++)
-                               sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE,
-                                               channel_names[i]);
-                       devc = g_malloc0(sizeof(struct dev_context));
-                       sdi->priv = devc;
-                       sr_sw_limits_init(&devc->limits);
-                       devc->data_source = DEFAULT_DATA_SOURCE;
-                       devices = g_slist_append(devices, sdi);
-               }
-               g_slist_free(usb_devices);
-       } else
-               g_slist_free_full(usb_devices, g_free);
-
-       return std_scan_complete(di, devices);
-}
-
-static int dev_open(struct sr_dev_inst *sdi)
-{
-       struct sr_dev_driver *di = sdi->driver;
-       struct drv_context *drvc = di->context;
-       struct sr_usb_dev_inst *usb;
-       int ret;
-
-       usb = sdi->conn;
-
-       if (sr_usb_open(drvc->sr_ctx->libusb_ctx, usb) != SR_OK)
-               return SR_ERR;
-
-/*
- * The libusb 1.0.9 Darwin backend is broken: it can report a kernel
- * driver being active, but detaching it always returns an error.
- */
-#if !defined(__APPLE__)
-       if (libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) {
-               if ((ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE)) < 0) {
-                       sr_err("failed to detach kernel driver: %s",
-                                       libusb_error_name(ret));
-                       return SR_ERR;
-               }
+       serial = sr_serial_dev_inst_new(conn, serialcomm);
+       rc = serial_open(serial, SERIAL_RDWR);
+       serial_flush(serial);
+       /* Cannot query/identify the device. Successful open shall suffice. */
+       serial_close(serial);
+       if (rc != SR_OK) {
+               sr_serial_dev_inst_free(serial);
+               return devices;
        }
-#endif
 
-       if ((ret = libusb_set_configuration(usb->devhdl, USB_CONFIGURATION))) {
-               sr_err("Failed to set configuration: %s.", libusb_error_name(ret));
-               return SR_ERR;
+       sdi = g_malloc0(sizeof(*sdi));
+       sdi->status = SR_ST_INACTIVE;
+       sdi->vendor = g_strdup("UNI-T");
+       sdi->model = g_strdup("UT32x");
+       sdi->inst_type = SR_INST_SERIAL;
+       sdi->conn = serial;
+       devc = g_malloc0(sizeof(*devc));
+       sdi->priv = devc;
+       sr_sw_limits_init(&devc->limits);
+       devc->data_source = DEFAULT_DATA_SOURCE;
+       for (i = 0; i < ARRAY_SIZE(channel_names); i++) {
+               sr_channel_new(sdi, i, SR_CHANNEL_ANALOG, TRUE,
+                               channel_names[i]);
        }
+       devices = g_slist_append(devices, sdi);
 
-       if ((ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE))) {
-               sr_err("Failed to claim interface: %s.", libusb_error_name(ret));
-               return SR_ERR;
-       }
+       serial_close(serial);
+       if (!devices)
+               sr_serial_dev_inst_free(serial);
 
-       return SR_OK;
-}
-
-static int dev_close(struct sr_dev_inst *sdi)
-{
-       struct sr_usb_dev_inst *usb;
-
-       usb = sdi->conn;
-
-       if (!usb->devhdl)
-               return SR_ERR_BUG;
-
-       libusb_release_interface(usb->devhdl, USB_INTERFACE);
-       libusb_close(usb->devhdl);
-       usb->devhdl = NULL;
-
-       return SR_OK;
+       return std_scan_complete(di, devices);
 }
 
 static int config_get(uint32_t key, GVariant **data,
@@ -226,66 +180,32 @@ static int config_list(uint32_t key, GVariant **data,
 
 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
-       struct sr_dev_driver *di = sdi->driver;
-       struct drv_context *drvc;
        struct dev_context *devc;
-       struct sr_usb_dev_inst *usb;
-       int len, ret;
-       unsigned char cmd[2];
+       struct sr_serial_dev_inst *serial;
+       uint8_t cmd;
 
-       drvc = di->context;
        devc = sdi->priv;
-       usb = sdi->conn;
+       serial = sdi->conn;
 
        sr_sw_limits_acquisition_start(&devc->limits);
        devc->packet_len = 0;
-
-       /* Configure serial port parameters on USB-UART interface
-        * chip inside the device (just baudrate 2400 actually). */
-       cmd[0] = 0x09;
-       cmd[1] = 0x60;
-       ret = libusb_control_transfer(usb->devhdl, 0x21, 0x09, 0x0300, 0x00,
-                       cmd, 2, 5);
-       if (ret != 2) {
-               sr_dbg("Failed to configure CH9325: %s", libusb_error_name(ret));
-               return SR_ERR;
-       }
-
        std_session_send_df_header(sdi);
 
-       if (!(devc->xfer = libusb_alloc_transfer(0)))
-               return SR_ERR;
-
-       /* Length of payload to follow. */
-       cmd[0] = 0x01;
        if (devc->data_source == DATA_SOURCE_LIVE)
-               cmd[1] = CMD_GET_LIVE;
+               cmd = CMD_GET_LIVE;
        else
-               cmd[1] = CMD_GET_STORED;
-
-       ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, cmd, 2, &len, 5);
-       if (ret != 0 || len != 2) {
-               sr_dbg("Failed to start acquisition: %s", libusb_error_name(ret));
-               libusb_free_transfer(devc->xfer);
-               return SR_ERR;
-       }
-
-       libusb_fill_bulk_transfer(devc->xfer, usb->devhdl, EP_IN, devc->buf,
-                       8, uni_t_ut32x_receive_transfer, (void *)sdi, 15);
-       if (libusb_submit_transfer(devc->xfer) != 0) {
-               libusb_free_transfer(devc->xfer);
-               return SR_ERR;
-       }
+               cmd = CMD_GET_STORED;
+       serial_write_blocking(serial, &cmd, sizeof(cmd), 0);
 
-       usb_source_add(sdi->session, drvc->sr_ctx, 10,
-                       uni_t_ut32x_handle_events, (void *)sdi);
+       serial_source_add(sdi->session, serial, G_IO_IN, 10,
+                       ut32x_handle_events, (void *)sdi);
 
        return SR_OK;
 }
 
 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       /* Signal USB transfer handler to clean up and stop. */
+       /* Have the reception routine stop the acquisition. */
        sdi->status = SR_ST_STOPPING;
 
        return SR_OK;
@@ -303,8 +223,8 @@ static struct sr_dev_driver uni_t_ut32x_driver_info = {
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
-       .dev_open = dev_open,
-       .dev_close = dev_close,
+       .dev_open = std_serial_dev_open,
+       .dev_close = std_serial_dev_close,
        .dev_acquisition_start = dev_acquisition_start,
        .dev_acquisition_stop = dev_acquisition_stop,
        .context = NULL,
index 4da246626012cc45b5dd620ef6818a2e9246c4be..bd5a5cb0338352eb663c1ec2c72063dd27e5cd9c 100644 (file)
@@ -212,88 +212,65 @@ static int process_buffer(struct sr_dev_inst *sdi)
        return 0;
 }
 
-SR_PRIV void LIBUSB_CALL uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer)
+/* Gets invoked when RX data is available. */
+static int ut32x_receive_data(struct sr_dev_inst *sdi)
 {
        struct dev_context *devc;
-       struct sr_dev_inst *sdi;
-       int hid_payload_len, ret;
+       struct sr_serial_dev_inst *serial;
+       size_t len;
 
-       sdi = transfer->user_data;
        devc = sdi->priv;
-       if (transfer->actual_length == 8) {
-               /* CH9325 encodes length in low nibble of first byte, with
-                * bytes 1-7 being the (padded) payload. */
-               hid_payload_len = transfer->buffer[0] & 0x0f;
-               memcpy(devc->packet + devc->packet_len, transfer->buffer + 1,
-                               hid_payload_len);
-               devc->packet_len += hid_payload_len;
-               /*
-                * Discard receive data when the buffer is exhausted. This shall
-                * allow to (re-)synchronize to the data stream when we find it
-                * in an arbitrary state. Check the receive buffer for packets.
-                */
-               if (devc->packet_len == sizeof(devc->packet)) {
-                       process_packet(sdi, &devc->packet[0], devc->packet_len);
-                       devc->packet_len = 0;
-               }
-               process_buffer(sdi);
+       serial = sdi->conn;
+
+       /*
+        * Discard receive data when the buffer is exhausted. This shall
+        * allow to (re-)synchronize to the data stream when we find it
+        * in an arbitrary state. Drain more data from the serial port,
+        * and check the receive buffer for packets.
+        */
+       if (devc->packet_len == sizeof(devc->packet)) {
+               process_packet(sdi, &devc->packet[0], devc->packet_len);
+               devc->packet_len = 0;
        }
+       len = sizeof(devc->packet) - devc->packet_len;
+       len = serial_read_nonblocking(serial,
+                       &devc->packet[devc->packet_len], len);
+       if (!len)
+               return 0;
 
-       /* Get the next transfer (unless we're shutting down). */
-       if (sdi->status != SR_ST_STOPPING) {
-               if ((ret = libusb_submit_transfer(devc->xfer)) != 0) {
-                       sr_dbg("Failed to resubmit transfer: %s", libusb_error_name(ret));
-                       sdi->status = SR_ST_STOPPING;
-                       libusb_free_transfer(devc->xfer);
-               }
-       } else
-               libusb_free_transfer(devc->xfer);
+       devc->packet_len += len;
+       process_buffer(sdi);
 
+       return 0;
 }
 
-SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data)
+/* Gets periodically invoked by the glib main loop. */
+SR_PRIV int ut32x_handle_events(int fd, int revents, void *cb_data)
 {
-       struct drv_context *drvc;
-       struct dev_context *devc;
-       struct sr_dev_driver *di;
        struct sr_dev_inst *sdi;
-       struct sr_usb_dev_inst *usb;
-       struct timeval tv;
-       int len, ret;
-       unsigned char cmd[2];
+       struct sr_serial_dev_inst *serial;
+       uint8_t cmd;
 
        (void)fd;
-       (void)revents;
 
-       if (!(sdi = cb_data))
+       sdi = cb_data;
+       if (!sdi)
                return TRUE;
-
-       di = sdi->driver;
-       drvc = di->context;
-
-       if (!(devc = sdi->priv))
+       serial = sdi->conn;
+       if (!serial)
                return TRUE;
 
-       memset(&tv, 0, sizeof(struct timeval));
-       libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv,
-                       NULL);
+       if (revents & G_IO_IN)
+               ut32x_receive_data(sdi);
 
        if (sdi->status == SR_ST_STOPPING) {
-               usb_source_remove(sdi->session, drvc->sr_ctx);
+               serial_source_remove(sdi->session, serial);
                std_session_send_df_end(sdi);
-
-               /* Tell the device to stop sending USB packets. */
-               usb = sdi->conn;
-               cmd[0] = 0x01;
-               cmd[1] = CMD_STOP;
-               ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, cmd, 2, &len, 5);
-               if (ret != 0 || len != 2) {
-                       /* Warning only, doesn't matter. */
-                       sr_dbg("Failed to send stop command: %s", libusb_error_name(ret));
-               }
-
                sdi->status = SR_ST_ACTIVE;
-               return TRUE;
+
+               /* Tell the device to stop sending data. */
+               cmd = CMD_STOP;
+               serial_write_blocking(serial, &cmd, sizeof(cmd), 0);
        }
 
        return TRUE;
index eb10a65b8b267a464d7f5d61329637d9fb782718..83a9da19bea383a7cf4ecdda4dc13f9943de8c41 100644 (file)
 #define LOG_PREFIX "uni-t-ut32x"
 
 #define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE
-#define USB_CONN "1a86.e008"
-#define USB_INTERFACE 0
-#define USB_CONFIGURATION 1
-
-#define EP_IN (0x80 | 2)
-#define EP_OUT 2
 
 #define PACKET_SIZE    19
 
@@ -51,14 +45,10 @@ enum ut32x_cmd_code {
 struct dev_context {
        struct sr_sw_limits limits;
        enum ut32x_data_source data_source;
-       unsigned char buf[8];
-       struct libusb_transfer *xfer;
-
-       unsigned char packet[32];
+       uint8_t packet[PACKET_SIZE];
        size_t packet_len;
 };
 
-SR_PRIV int uni_t_ut32x_handle_events(int fd, int revents, void *cb_data);
-SR_PRIV void LIBUSB_CALL uni_t_ut32x_receive_transfer(struct libusb_transfer *transfer);
+SR_PRIV int ut32x_handle_events(int fd, int revents, void *cb_data);
 
 #endif