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