Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ libsigrok_la_SOURCES += \
src/serial_hid.h \
src/serial_hid_bu86x.c \
src/serial_hid_ch9325.c \
src/serial_hid_ch9329.c \
src/serial_hid_cp2110.c \
src/serial_hid_victor.c \
src/serial_libsp.c \
Expand Down
21 changes: 15 additions & 6 deletions src/hardware/uni-t-ut181a/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,12 @@ static int ut181a_query_initial_state(struct sr_dev_inst *sdi, int timeout_ms)
deadline += timeout_ms * 1000;
while (1) {
ret = ut181a_waitfor_response(sdi, 100);
if (ret < 0)
return ret;
if (devc->info.meas_head.mode)
if (ret == SR_OK && devc->info.meas_head.mode)
break;
if (g_get_monotonic_time() >= deadline)
if (g_get_monotonic_time() >= deadline) {
(void)ut181a_send_cmd_monitor(serial, FALSE);
return SR_ERR_DATA;
}
}
(void)ut181a_send_cmd_monitor(serial, FALSE);
ret = ut181a_configure_waitfor(devc, TRUE, 0, 0,
Expand Down Expand Up @@ -409,13 +409,22 @@ static int config_set(uint32_t key, GVariant *data,
ret = ut181a_send_cmd_setmode(sdi->conn, mode);
if (ret < 0)
return ret;
ret = ut181a_waitfor_response(sdi->conn, 100);
ret = ut181a_configure_waitfor(devc, TRUE, 0, 0,
FALSE, FALSE, FALSE, FALSE);
if (ret < 0)
return ret;
ret = ut181a_waitfor_response(sdi, 100);
if (ret < 0)
return ret;
if (devc->info.rsp_head.rsp_type != RSP_TYPE_REPLY_CODE)
return SR_ERR_DATA;
if (!devc->info.reply_code.ok)
if (!devc->info.reply_code.ok) {
sr_dbg("Meter NAKed mode 0x%04x (reply code 0x%04x). "
"Mode switches may be restricted to the "
"function currently selected by the rotary knob.",
mode, devc->info.reply_code.code);
return SR_ERR_DATA;
}
break;
case SR_CONF_RANGE:
range = g_variant_get_string(data, NULL);
Expand Down
2 changes: 1 addition & 1 deletion src/hardware/uni-t-ut181a/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ SR_PRIV int ut181a_set_range_from_text(const struct sr_dev_inst *sdi, const char
/* Handle the simple case of "auto" caller spec. */
if (strcmp(text, range_auto) == 0) {
range = 0;
return ut181a_send_cmd_setmode(sdi->conn, range);
return ut181a_send_cmd_setrange(sdi->conn, range);
}

/* Lookup the list of ranges which depend on the meter's current mode. */
Expand Down
2 changes: 2 additions & 0 deletions src/libsigrok-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1628,6 +1628,7 @@ struct sr_serial_dev_inst {
SER_HID_CHIP_SIL_CP2110, /**!< SiLabs CP2110 */
SER_HID_CHIP_VICTOR_DMM, /**!< Victor 70/86 DMM cable */
SER_HID_CHIP_WCH_CH9325, /**!< WCH CH9325 */
SER_HID_CHIP_WCH_CH9329, /**!< WCH CH9329, custom-HID mode (UNI-T meter cable, 1a86:e429) */
SER_HID_CHIP_LAST, /**!< sentinel */
} hid_chip;
struct ser_hid_chip_functions *hid_chip_funcs;
Expand Down Expand Up @@ -2151,6 +2152,7 @@ struct ser_hid_chip_functions {
};
extern SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_bu86x;
extern SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_ch9325;
extern SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_ch9329;
extern SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_cp2110;
extern SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_victor;
SR_PRIV const char *ser_hid_chip_find_name_vid_pid(uint16_t vid, uint16_t pid);
Expand Down
1 change: 1 addition & 0 deletions src/serial_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ static struct ser_hid_chip_functions **chips[SER_HID_CHIP_LAST] = {
[SER_HID_CHIP_SIL_CP2110] = &ser_hid_chip_funcs_cp2110,
[SER_HID_CHIP_VICTOR_DMM] = &ser_hid_chip_funcs_victor,
[SER_HID_CHIP_WCH_CH9325] = &ser_hid_chip_funcs_ch9325,
[SER_HID_CHIP_WCH_CH9329] = &ser_hid_chip_funcs_ch9329,
};

static struct ser_hid_chip_functions *get_hid_chip_funcs(enum ser_hid_chip_t chip)
Expand Down
158 changes: 158 additions & 0 deletions src/serial_hid_ch9329.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2017-2019 Gerhard Sittig <gerhard.sittig@gmx.net>
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <config.h>
#include <glib.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
#include "serial_hid.h"
#include <string.h>

#define LOG_PREFIX "serial-ch9329"

#ifdef HAVE_SERIAL_COMM
#ifdef HAVE_LIBHIDAPI

/**
* @file
*
* Support serial-over-HID for the WCH CH9329 found in some UNI-T meter
* USB cables (USB VID:PID 1a86:e429), running in the chip's custom-HID
* mode (chip work mode 3, serial mode 2 per the CH9329 datasheet): a
* plain bidirectional UART tunnel over a vendor-defined HID interface
* (usage page 0xffa0), no keyboard/mouse emulation involved.
*/

#define CH9329_REPORT_SIZE 64
#define CH9329_MAX_BYTES_PER_REQUEST (CH9329_REPORT_SIZE - 1)

static const struct vid_pid_item vid_pid_items_ch9329[] = {
{ 0x1a86, 0xe429, }, /* WCH CH9329 in UNI-T meter USB cable (custom-HID mode) */
ALL_ZERO
};

static int ch9329_set_params(struct sr_serial_dev_inst *serial,
int baudrate, int bits, int parity, int stopbits,
int flowcontrol, int rts, int dtr)
{
/*
* The CH9329's UART line coding is persistent chip configuration,
* not a per-session USB transaction: it is programmed through a
* dedicated parameter block that is gated behind the chip's SET
* pin and only takes effect after a power-on/reset (see the
* @file comment above). There is nothing to negotiate here over
* the plain HID tunnel this driver uses, and the vendor software
* itself never issues a rate change on this path either. The
* meter and the cable both run fixed at the chip's 9600 8N1
* default, so this is intentionally a no-op.
*/
(void)serial;
(void)baudrate;
(void)bits;
(void)parity;
(void)stopbits;
(void)flowcontrol;
(void)rts;
(void)dtr;

return SR_OK;
}

static int ch9329_read_bytes(struct sr_serial_dev_inst *serial,
uint8_t *data, int space, unsigned int timeout)
{
uint8_t buffer[CH9329_REPORT_SIZE];
int rc;
int count;

/*
* Check for available input data from the serial port.
* Packet layout (no report-ID placeholder needed for reads):
* @0, length 1, number of bytes (0..63, not masked)
* @1, length N, data bytes (up to 63 bytes)
*/
rc = ser_hid_hidapi_get_data(serial, 2, buffer, sizeof(buffer), timeout);
if (rc < 0)
return SR_ERR;
if (rc == 0)
return 0;
sr_dbg("DBG: %s() got report len %d, count %d.", __func__, rc, buffer[0]);

count = buffer[0];
if (count > CH9329_MAX_BYTES_PER_REQUEST)
return SR_ERR;
if (count > space)
return SR_ERR;

memcpy(data, &buffer[1], count);
return count;
}

static int ch9329_write_bytes(struct sr_serial_dev_inst *serial,
const uint8_t *data, int size)
{
uint8_t buffer[1 + CH9329_REPORT_SIZE];
int rc;

sr_dbg("DBG: %s() shall send UART TX data, len %d.", __func__, size);

if (size < 1)
return 0;
if (size > CH9329_MAX_BYTES_PER_REQUEST) {
size = CH9329_MAX_BYTES_PER_REQUEST;
sr_dbg("DBG: %s() capping size to %d.", __func__, size);
}

/*
* Packet layout to send serial data to the USB HID chip. This
* device has no report IDs, but hid_write() still requires the
* mandatory report-ID placeholder byte ahead of the actual report
* content (length = report size + 1):
* (@-1, length 1, report-ID placeholder, always 0)
* @0, length 1, number of bytes (0..63, not masked)
* @1, length N, data bytes (up to 63 bytes)
*/
memset(buffer, 0, sizeof(buffer));
buffer[1] = size;
memcpy(&buffer[2], data, size);
rc = ser_hid_hidapi_set_data(serial, 2, buffer, sizeof(buffer), 0);
if (rc < 0)
return rc;
if (rc == 0)
return 0;
return size;
}

static struct ser_hid_chip_functions chip_ch9329 = {
.chipname = "ch9329",
.chipdesc = "WCH CH9329, custom-HID mode (UNI-T meter cable, 1a86:e429)",
.vid_pid_items = vid_pid_items_ch9329,
.max_bytes_per_request = CH9329_MAX_BYTES_PER_REQUEST,
.set_params = ch9329_set_params,
.read_bytes = ch9329_read_bytes,
.write_bytes = ch9329_write_bytes,
};
SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_ch9329 = &chip_ch9329;

#else

SR_PRIV struct ser_hid_chip_functions *ser_hid_chip_funcs_ch9329 = NULL;

#endif
#endif