]> sigrok.org Git - libsigrok.git/blame - src/hardware/dcttech-usbrelay/api.c
dcttech-usbrelay: support conn=vid.pid specs in addition to paths
[libsigrok.git] / src / hardware / dcttech-usbrelay / api.c
CommitLineData
64d54a71
GS
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@gmx.net>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <config.h>
321f85fb 21
0498ef4e 22#include <ctype.h>
321f85fb
GS
23#include <hidapi.h>
24#include <string.h>
25
64d54a71
GS
26#include "protocol.h"
27
321f85fb
GS
28static const uint32_t scanopts[] = {
29 SR_CONF_CONN,
30};
31
32static const uint32_t drvopts[] = {
33 SR_CONF_MULTIPLEXER,
34};
35
36static const uint32_t devopts[] = {
37 SR_CONF_CONN | SR_CONF_GET,
38 SR_CONF_ENABLED | SR_CONF_SET, /* Enable/disable all relays at once. */
39};
40
41static const uint32_t devopts_cg[] = {
42 SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
43};
44
64d54a71
GS
45static struct sr_dev_driver dcttech_usbrelay_driver_info;
46
0498ef4e 47static struct sr_dev_inst *probe_device_common(const char *path,
8e5dbaf6 48 uint16_t vid, uint16_t pid,
0498ef4e 49 const wchar_t *vendor, const wchar_t *product)
321f85fb 50{
0498ef4e
GS
51 char nonws[16], *s, *endp;
52 unsigned long relay_count;
321f85fb
GS
53 hid_device *hid;
54 int ret;
55 char serno[SERNO_LENGTH + 1];
56 uint8_t curr_state;
57 uint8_t report[1 + REPORT_BYTECOUNT];
58 GString *txt;
59 size_t snr_pos;
60 char c;
61 struct sr_dev_inst *sdi;
62 struct dev_context *devc;
63 struct channel_group_context *cgc;
64 size_t idx, nr;
65 struct sr_channel_group *cg;
66
0498ef4e
GS
67 /*
68 * Get relay count from product string. Weak condition,
69 * accept any trailing number regardless of preceeding text.
70 */
71 snprintf(nonws, sizeof(nonws), "%ls", product);
72 s = nonws;
73 s += strlen(s);
74 while (s > nonws && isdigit((int)s[-1]))
75 s--;
76 ret = sr_atoul_base(s, &relay_count, &endp, 10);
77 if (ret != SR_OK || !endp || *endp)
78 return NULL;
79 if (!relay_count)
80 return NULL;
81 sr_info("Relay count %lu from product string %s.", relay_count, nonws);
82
321f85fb 83 /* Open device, need to communicate to identify. */
8e5dbaf6
GS
84 if (vid && pid)
85 hid = hid_open(vid, pid, NULL);
86 else
87 hid = hid_open_path(path);
996331ce 88 if (!hid) {
0498ef4e 89 sr_err("Cannot open %s.", path);
321f85fb 90 return NULL;
996331ce 91 }
321f85fb
GS
92
93 /* Get an HID report. */
94 hid_set_nonblocking(hid, 0);
95 memset(&report, 0, sizeof(report));
96 report[0] = REPORT_NUMBER;
97 ret = hid_get_feature_report(hid, report, sizeof(report));
98 hid_close(hid);
99 if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
100 txt = sr_hexdump_new(report, sizeof(report));
0498ef4e 101 sr_spew("Got report bytes: %s, rc %d.", txt->str, ret);
321f85fb
GS
102 sr_hexdump_free(txt);
103 }
996331ce 104 if (ret < 0) {
0498ef4e 105 sr_err("Cannot read %s: %ls.", path, hid_error(NULL));
996331ce
GS
106 return NULL;
107 }
108 if (ret != sizeof(report)) {
0498ef4e 109 sr_err("Unexpected HID report length %d from %s.", ret, path);
321f85fb 110 return NULL;
996331ce 111 }
321f85fb
GS
112
113 /*
114 * Serial number must be all printable characters. Relay state
115 * is for information only, gets re-retrieved before configure
116 * API calls (get/set).
117 */
118 memset(serno, 0, sizeof(serno));
119 for (snr_pos = 0; snr_pos < SERNO_LENGTH; snr_pos++) {
120 c = report[1 + snr_pos];
121 serno[snr_pos] = c;
996331ce 122 if (c < 0x20 || c > 0x7e) {
0498ef4e 123 sr_warn("Skipping %s, non-printable serial.", path);
321f85fb 124 return NULL;
996331ce 125 }
321f85fb
GS
126 }
127 curr_state = report[1 + STATE_INDEX];
0498ef4e
GS
128 sr_info("HID report data: serial number %s, relay state 0x%02x.",
129 serno, curr_state);
321f85fb 130
996331ce 131 /* Create a device instance. */
321f85fb 132 sdi = g_malloc0(sizeof(*sdi));
0498ef4e
GS
133 sdi->vendor = g_strdup_printf("%ls", vendor);
134 sdi->model = g_strdup_printf("%ls", product);
e333a40c 135 sdi->serial_num = g_strdup(serno);
0498ef4e 136 sdi->connection_id = g_strdup(path);
996331ce
GS
137 sdi->driver = &dcttech_usbrelay_driver_info;
138 sdi->inst_type = SR_INST_USB;
139
140 /* Create channels (groups). */
321f85fb
GS
141 devc = g_malloc0(sizeof(*devc));
142 sdi->priv = devc;
0498ef4e 143 devc->hid_path = g_strdup(path);
8e5dbaf6
GS
144 devc->usb_vid = vid;
145 devc->usb_pid = pid;
321f85fb
GS
146 devc->relay_count = relay_count;
147 devc->relay_mask = (1U << relay_count) - 1;
148 for (idx = 0; idx < devc->relay_count; idx++) {
149 nr = idx + 1;
150 cg = g_malloc0(sizeof(*cg));
151 cg->name = g_strdup_printf("R%zu", nr);
152 cgc = g_malloc0(sizeof(*cgc));
153 cg->priv = cgc;
154 cgc->number = nr;
155 sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
156 }
157
158 return sdi;
159}
160
0498ef4e
GS
161static struct sr_dev_inst *probe_device_enum(struct hid_device_info *dev)
162{
8e5dbaf6 163 return probe_device_common(dev->path, 0, 0,
0498ef4e
GS
164 dev->manufacturer_string, dev->product_string);
165}
166
8e5dbaf6 167static struct sr_dev_inst *probe_device_conn(const char *path)
0498ef4e 168{
8e5dbaf6
GS
169 char vid_pid[12];
170 uint16_t vid, pid;
171 const char *s;
172 char *endp;
173 unsigned long num;
0498ef4e
GS
174 hid_device *dev;
175 gboolean ok;
176 int ret;
177 wchar_t vendor[32], product[32];
178
179 /*
8e5dbaf6
GS
180 * The hidapi(3) library's API strives for maximum portability,
181 * thus won't provide ways of getting a path from alternative
182 * presentations like VID:PID pairs, bus.addr specs, etc. The
183 * typical V-USB setup neither provides reliable serial numbers
184 * (that USB enumeration would cover). So this driver's support
185 * for conn= specs beyond Unix style path names is limited, too.
186 * This implementation tries "VID.PID" then assumes "path". The
187 * inability to even get the path for a successfully opened HID
188 * results in redundancy across the places which open devices.
0498ef4e
GS
189 */
190
8e5dbaf6
GS
191 /* Check for "<vid>.<pid>" specs. */
192 vid = pid = 0;
193 s = path;
194 ret = sr_atoul_base(s, &num, &endp, 16);
195 if (ret == SR_OK && endp && endp == s + 4 && *endp == '.' && num) {
196 vid = num;
197 s = ++endp;
198 }
199 ret = sr_atoul_base(s, &num, &endp, 16);
200 if (ret == SR_OK && endp && endp == s + 4 && *endp == '\0' && num) {
201 pid = num;
202 s = ++endp;
203 }
204 if (vid && pid) {
205 snprintf(vid_pid, sizeof(vid_pid), "%04x.%04x", vid, pid);
206 path = vid_pid;
207 sr_dbg("Using VID.PID %s.", path);
208 }
209
210 /* Open the device, get vendor and product strings. */
211 if (vid && pid)
212 dev = hid_open(vid, pid, NULL);
213 else
214 dev = hid_open_path(path);
0498ef4e
GS
215 if (!dev) {
216 sr_err("Cannot open %s.", path);
217 return NULL;
218 }
0498ef4e
GS
219 ok = TRUE;
220 ret = hid_get_manufacturer_string(dev, vendor, ARRAY_SIZE(vendor));
221 if (ret != 0)
222 ok = FALSE;
223 if (!wcslen(vendor))
224 ok = FALSE;
225 ret = hid_get_product_string(dev, product, ARRAY_SIZE(product));
226 if (ret != 0)
227 ok = FALSE;
228 if (!wcslen(product))
229 ok = FALSE;
230 hid_close(dev);
231 if (!ok)
232 return NULL;
233
8e5dbaf6 234 return probe_device_common(path, vid, pid, vendor, product);
0498ef4e
GS
235}
236
64d54a71
GS
237static GSList *scan(struct sr_dev_driver *di, GSList *options)
238{
321f85fb 239 const char *conn;
64d54a71 240 GSList *devices;
321f85fb
GS
241 struct drv_context *drvc;
242 struct hid_device_info *devs, *curdev;
321f85fb
GS
243 wchar_t *ws;
244 char nonws[32];
321f85fb
GS
245 struct sr_dev_inst *sdi;
246
247 /* Get optional conn= spec when provided. */
248 conn = NULL;
249 (void)sr_serial_extract_options(options, &conn, NULL);
250 if (conn && !*conn)
251 conn = NULL;
64d54a71
GS
252
253 devices = NULL;
254 drvc = di->context;
255 drvc->instances = NULL;
256
321f85fb
GS
257 /*
258 * The firmware is V-USB based. The USB VID:PID identification
259 * is shared across several projects. Need to inspect the vendor
0498ef4e
GS
260 * and product _strings_ to actually identify the device. The
261 * USB serial number need not be present nor reliable. The HID
262 * report content will carry the board's serial number.
321f85fb 263 *
0498ef4e
GS
264 * When conn= was specified, then have HIDAPI open _this_ device
265 * and skip the enumeration. Which allows users to specify paths
266 * that need not match the enumeration's details.
321f85fb 267 */
0498ef4e
GS
268 if (conn) {
269 sr_info("Checking HID path %s.", conn);
8e5dbaf6 270 sdi = probe_device_conn(conn);
0498ef4e
GS
271 if (!sdi)
272 sr_warn("Failed to communicate to %s.", conn);
273 else
274 devices = g_slist_append(devices, sdi);
275 }
321f85fb
GS
276 devs = hid_enumerate(VENDOR_ID, PRODUCT_ID);
277 for (curdev = devs; curdev; curdev = curdev->next) {
0498ef4e
GS
278 if (conn)
279 break;
321f85fb
GS
280 if (!curdev->vendor_id || !curdev->product_id)
281 continue;
282 if (!curdev->manufacturer_string || !curdev->product_string)
283 continue;
284 if (!*curdev->manufacturer_string || !*curdev->product_string)
285 continue;
0498ef4e 286 sr_dbg("Checking %04hx:%04hx, vendor %ls, product %ls.",
321f85fb
GS
287 curdev->vendor_id, curdev->product_id,
288 curdev->manufacturer_string, curdev->product_string);
289
290 /* Check USB details retrieved by enumeration. */
291 ws = curdev->manufacturer_string;
292 if (!ws || !wcslen(ws))
293 continue;
294 snprintf(nonws, sizeof(nonws), "%ls", ws);
295 if (strcmp(nonws, VENDOR_STRING) != 0)
296 continue;
297 ws = curdev->product_string;
298 if (!ws || !wcslen(ws))
299 continue;
300 snprintf(nonws, sizeof(nonws), "%ls", ws);
0498ef4e 301 if (!g_str_has_prefix(nonws, PRODUCT_STRING_PREFIX))
321f85fb 302 continue;
321f85fb
GS
303
304 /* Identify device by communicating to it. */
0498ef4e
GS
305 sr_info("Checking HID path %s.", curdev->path);
306 sdi = probe_device_enum(curdev);
321f85fb
GS
307 if (!sdi) {
308 sr_warn("Failed to communicate to %s.", curdev->path);
309 continue;
310 }
321f85fb
GS
311 devices = g_slist_append(devices, sdi);
312 }
313 hid_free_enumeration(devs);
64d54a71
GS
314
315 return devices;
316}
317
318static int dev_open(struct sr_dev_inst *sdi)
319{
321f85fb
GS
320 struct dev_context *devc;
321
322 devc = sdi->priv;
323
324 if (devc->hid_dev) {
325 hid_close(devc->hid_dev);
326 devc->hid_dev = NULL;
327 }
64d54a71 328
8e5dbaf6
GS
329 if (devc->usb_vid && devc->usb_pid)
330 devc->hid_dev = hid_open(devc->usb_vid, devc->usb_pid, NULL);
331 else
332 devc->hid_dev = hid_open_path(devc->hid_path);
321f85fb
GS
333 if (!devc->hid_dev)
334 return SR_ERR_IO;
335
336 (void)dcttech_usbrelay_update_state(sdi);
64d54a71
GS
337
338 return SR_OK;
339}
340
341static int dev_close(struct sr_dev_inst *sdi)
342{
321f85fb
GS
343 struct dev_context *devc;
344
345 devc = sdi->priv;
64d54a71 346
321f85fb
GS
347 if (devc->hid_dev) {
348 hid_close(devc->hid_dev);
349 devc->hid_dev = NULL;
350 }
64d54a71
GS
351
352 return SR_OK;
353}
354
355static int config_get(uint32_t key, GVariant **data,
356 const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
357{
321f85fb 358 gboolean on;
64d54a71
GS
359 int ret;
360
321f85fb
GS
361 if (!cg) {
362 switch (key) {
363 case SR_CONF_CONN:
e333a40c 364 if (!sdi->connection_id)
321f85fb 365 return SR_ERR_NA;
e333a40c 366 *data = g_variant_new_string(sdi->connection_id);
321f85fb
GS
367 return SR_OK;
368 default:
369 return SR_ERR_NA;
370 }
371 }
64d54a71 372
64d54a71 373 switch (key) {
321f85fb
GS
374 case SR_CONF_ENABLED:
375 ret = dcttech_usbrelay_query_cg(sdi, cg, &on);
376 if (ret != SR_OK)
377 return ret;
378 *data = g_variant_new_boolean(on);
379 return SR_OK;
64d54a71
GS
380 default:
381 return SR_ERR_NA;
382 }
64d54a71
GS
383}
384
385static int config_set(uint32_t key, GVariant *data,
386 const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
387{
321f85fb
GS
388 gboolean on;
389
390 if (!cg) {
391 switch (key) {
392 case SR_CONF_ENABLED:
393 /* Enable/disable all channels at the same time. */
394 on = g_variant_get_boolean(data);
395 return dcttech_usbrelay_switch_cg(sdi, cg, on);
396 default:
397 return SR_ERR_NA;
398 }
399 } else {
400 switch (key) {
401 case SR_CONF_ENABLED:
402 on = g_variant_get_boolean(data);
403 return dcttech_usbrelay_switch_cg(sdi, cg, on);
404 default:
405 return SR_ERR_NA;
406 }
64d54a71
GS
407 }
408
321f85fb 409 return SR_OK;
64d54a71
GS
410}
411
412static int config_list(uint32_t key, GVariant **data,
413 const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
414{
64d54a71 415
321f85fb
GS
416 if (!cg) {
417 switch (key) {
418 case SR_CONF_SCAN_OPTIONS:
419 case SR_CONF_DEVICE_OPTIONS:
420 return STD_CONFIG_LIST(key, data, sdi, cg,
421 scanopts, drvopts, devopts);
422 default:
423 return SR_ERR_NA;
424 }
425 }
64d54a71 426
64d54a71 427 switch (key) {
321f85fb
GS
428 case SR_CONF_DEVICE_OPTIONS:
429 *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
430 return SR_OK;
64d54a71
GS
431 default:
432 return SR_ERR_NA;
433 }
64d54a71
GS
434}
435
436static struct sr_dev_driver dcttech_usbrelay_driver_info = {
437 .name = "dcttech-usbrelay",
438 .longname = "dcttech usbrelay",
439 .api_version = 1,
440 .init = std_init,
441 .cleanup = std_cleanup,
442 .scan = scan,
443 .dev_list = std_dev_list,
444 .dev_clear = std_dev_clear,
445 .config_get = config_get,
446 .config_set = config_set,
447 .config_list = config_list,
448 .dev_open = dev_open,
449 .dev_close = dev_close,
321f85fb
GS
450 .dev_acquisition_start = std_dummy_dev_acquisition_start,
451 .dev_acquisition_stop = std_dummy_dev_acquisition_stop,
64d54a71
GS
452 .context = NULL,
453};
454SR_REGISTER_DEV_DRIVER(dcttech_usbrelay_driver_info);