]> sigrok.org Git - libsigrok.git/blob - src/hardware/dcttech-usbrelay/api.c
2f35d436d68aa7790716156449aa832f33cf9e2f
[libsigrok.git] / src / hardware / dcttech-usbrelay / api.c
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>
21
22 #include <hidapi.h>
23 #include <string.h>
24
25 #include "protocol.h"
26
27 static const uint32_t scanopts[] = {
28         SR_CONF_CONN,
29 };
30
31 static const uint32_t drvopts[] = {
32         SR_CONF_MULTIPLEXER,
33 };
34
35 static const uint32_t devopts[] = {
36         SR_CONF_CONN | SR_CONF_GET,
37         SR_CONF_ENABLED | SR_CONF_SET, /* Enable/disable all relays at once. */
38 };
39
40 static const uint32_t devopts_cg[] = {
41         SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
42 };
43
44 static struct sr_dev_driver dcttech_usbrelay_driver_info;
45
46 static struct sr_dev_inst *probe_device(struct hid_device_info *dev,
47         size_t relay_count)
48 {
49         hid_device *hid;
50         int ret;
51         char serno[SERNO_LENGTH + 1];
52         uint8_t curr_state;
53         uint8_t report[1 + REPORT_BYTECOUNT];
54         GString *txt;
55         size_t snr_pos;
56         char c;
57         struct sr_dev_inst *sdi;
58         struct dev_context *devc;
59         struct channel_group_context *cgc;
60         size_t idx, nr;
61         struct sr_channel_group *cg;
62
63         /* Open device, need to communicate to identify. */
64         hid = hid_open_path(dev->path);
65         if (!hid) {
66                 sr_err("Cannot open %s: %ls.", dev->path, hid_error(NULL));
67                 return NULL;
68         }
69
70         /* Get an HID report. */
71         hid_set_nonblocking(hid, 0);
72         memset(&report, 0, sizeof(report));
73         report[0] = REPORT_NUMBER;
74         ret = hid_get_feature_report(hid, report, sizeof(report));
75         hid_close(hid);
76         if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
77                 txt = sr_hexdump_new(report, sizeof(report));
78                 sr_spew("raw report, rc %d, bytes %s", ret, txt->str);
79                 sr_hexdump_free(txt);
80         }
81         if (ret < 0) {
82                 sr_err("Cannot read %s: %ls.", dev->path, hid_error(NULL));
83                 return NULL;
84         }
85         if (ret != sizeof(report)) {
86                 sr_err("Unexpected HID report length: %s.", dev->path);
87                 return NULL;
88         }
89
90         /*
91          * Serial number must be all printable characters. Relay state
92          * is for information only, gets re-retrieved before configure
93          * API calls (get/set).
94          */
95         memset(serno, 0, sizeof(serno));
96         for (snr_pos = 0; snr_pos < SERNO_LENGTH; snr_pos++) {
97                 c = report[1 + snr_pos];
98                 serno[snr_pos] = c;
99                 if (c < 0x20 || c > 0x7e) {
100                         sr_dbg("non-printable serno");
101                         return NULL;
102                 }
103         }
104         curr_state = report[1 + STATE_INDEX];
105         sr_spew("report data, serno[%s], relays 0x%02x.", serno, curr_state);
106
107         /* Create a device instance. */
108         sdi = g_malloc0(sizeof(*sdi));
109         sdi->vendor = g_strdup_printf("%ls", dev->manufacturer_string);
110         sdi->model = g_strdup_printf("%ls", dev->product_string);
111         sdi->serial_num = g_strdup(serno);
112         sdi->connection_id = g_strdup(dev->path);
113         sdi->driver = &dcttech_usbrelay_driver_info;
114         sdi->inst_type = SR_INST_USB;
115
116         /* Create channels (groups). */
117         devc = g_malloc0(sizeof(*devc));
118         sdi->priv = devc;
119         devc->hid_path = g_strdup(dev->path);
120         devc->relay_count = relay_count;
121         devc->relay_mask = (1U << relay_count) - 1;
122         for (idx = 0; idx < devc->relay_count; idx++) {
123                 nr = idx + 1;
124                 cg = g_malloc0(sizeof(*cg));
125                 cg->name = g_strdup_printf("R%zu", nr);
126                 cgc = g_malloc0(sizeof(*cgc));
127                 cg->priv = cgc;
128                 cgc->number = nr;
129                 sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
130         }
131
132         return sdi;
133 }
134
135 static GSList *scan(struct sr_dev_driver *di, GSList *options)
136 {
137         const char *conn;
138         GSList *devices;
139         struct drv_context *drvc;
140         struct hid_device_info *devs, *curdev;
141         int ret;
142         wchar_t *ws;
143         char nonws[32];
144         char *s, *endp;
145         unsigned long relay_count;
146         struct sr_dev_inst *sdi;
147
148         /* Get optional conn= spec when provided. */
149         conn = NULL;
150         (void)sr_serial_extract_options(options, &conn, NULL);
151         if (conn && !*conn)
152                 conn = NULL;
153         /*
154          * TODO Accept different types of conn= specs? Either paths that
155          * hidapi(3) can open. Or bus.addr specs that we can check for
156          * during USB enumeration. Derive want_path, want_bus, want_addr
157          * here from the optional conn= spec.
158          */
159
160         devices = NULL;
161         drvc = di->context;
162         drvc->instances = NULL;
163
164         /*
165          * The firmware is V-USB based. The USB VID:PID identification
166          * is shared across several projects. Need to inspect the vendor
167          * and product _strings_ to actually identify the device.
168          *
169          * The USB serial number need not be present nor reliable. The
170          * HID report contains a five character string which may serve
171          * as an identification for boards (is said to differ between
172          * boards). The last byte encodes the current relays state.
173          */
174         devs = hid_enumerate(VENDOR_ID, PRODUCT_ID);
175         for (curdev = devs; curdev; curdev = curdev->next) {
176                 if (!curdev->vendor_id || !curdev->product_id)
177                         continue;
178                 if (!curdev->manufacturer_string || !curdev->product_string)
179                         continue;
180                 if (!*curdev->manufacturer_string || !*curdev->product_string)
181                         continue;
182                 if (conn && strcmp(curdev->path, conn) != 0) {
183                         sr_dbg("skipping %s, conn= mismatch", curdev->path);
184                         continue;
185                 }
186                 sr_dbg("checking %04hx:%04hx, vendor %ls, product %ls.",
187                         curdev->vendor_id, curdev->product_id,
188                         curdev->manufacturer_string, curdev->product_string);
189
190                 /* Check USB details retrieved by enumeration. */
191                 ws = curdev->manufacturer_string;
192                 if (!ws || !wcslen(ws))
193                         continue;
194                 snprintf(nonws, sizeof(nonws), "%ls", ws);
195                 if (strcmp(nonws, VENDOR_STRING) != 0)
196                         continue;
197                 ws = curdev->product_string;
198                 if (!ws || !wcslen(ws))
199                         continue;
200                 snprintf(nonws, sizeof(nonws), "%ls", ws);
201                 s = nonws;
202                 if (!g_str_has_prefix(s, PRODUCT_STRING_PREFIX))
203                         continue;
204                 s += strlen(PRODUCT_STRING_PREFIX);
205                 ret = sr_atoul_base(s, &relay_count, &endp, 10);
206                 if (ret != SR_OK || !endp || *endp)
207                         continue;
208                 sr_info("Found: HID path %s, relay count %lu.",
209                         curdev->path, relay_count);
210
211                 /* Identify device by communicating to it. */
212                 sdi = probe_device(curdev, relay_count);
213                 if (!sdi) {
214                         sr_warn("Failed to communicate to %s.", curdev->path);
215                         continue;
216                 }
217                 devices = g_slist_append(devices, sdi);
218         }
219         hid_free_enumeration(devs);
220
221         return devices;
222 }
223
224 static int dev_open(struct sr_dev_inst *sdi)
225 {
226         struct dev_context *devc;
227
228         devc = sdi->priv;
229
230         if (devc->hid_dev) {
231                 hid_close(devc->hid_dev);
232                 devc->hid_dev = NULL;
233         }
234
235         devc->hid_dev = hid_open_path(devc->hid_path);
236         if (!devc->hid_dev)
237                 return SR_ERR_IO;
238
239         (void)dcttech_usbrelay_update_state(sdi);
240
241         return SR_OK;
242 }
243
244 static int dev_close(struct sr_dev_inst *sdi)
245 {
246         struct dev_context *devc;
247
248         devc = sdi->priv;
249
250         if (devc->hid_dev) {
251                 hid_close(devc->hid_dev);
252                 devc->hid_dev = NULL;
253         }
254
255         return SR_OK;
256 }
257
258 static int config_get(uint32_t key, GVariant **data,
259         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
260 {
261         gboolean on;
262         int ret;
263
264         if (!cg) {
265                 switch (key) {
266                 case SR_CONF_CONN:
267                         if (!sdi->connection_id)
268                                 return SR_ERR_NA;
269                         *data = g_variant_new_string(sdi->connection_id);
270                         return SR_OK;
271                 default:
272                         return SR_ERR_NA;
273                 }
274         }
275
276         switch (key) {
277         case SR_CONF_ENABLED:
278                 ret = dcttech_usbrelay_query_cg(sdi, cg, &on);
279                 if (ret != SR_OK)
280                         return ret;
281                 *data = g_variant_new_boolean(on);
282                 return SR_OK;
283         default:
284                 return SR_ERR_NA;
285         }
286 }
287
288 static int config_set(uint32_t key, GVariant *data,
289         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
290 {
291         gboolean on;
292
293         if (!cg) {
294                 switch (key) {
295                 case SR_CONF_ENABLED:
296                         /* Enable/disable all channels at the same time. */
297                         on = g_variant_get_boolean(data);
298                         return dcttech_usbrelay_switch_cg(sdi, cg, on);
299                 default:
300                         return SR_ERR_NA;
301                 }
302         } else {
303                 switch (key) {
304                 case SR_CONF_ENABLED:
305                         on = g_variant_get_boolean(data);
306                         return dcttech_usbrelay_switch_cg(sdi, cg, on);
307                 default:
308                         return SR_ERR_NA;
309                 }
310         }
311
312         return SR_OK;
313 }
314
315 static int config_list(uint32_t key, GVariant **data,
316         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
317 {
318
319         if (!cg) {
320                 switch (key) {
321                 case SR_CONF_SCAN_OPTIONS:
322                 case SR_CONF_DEVICE_OPTIONS:
323                         return STD_CONFIG_LIST(key, data, sdi, cg,
324                                 scanopts, drvopts, devopts);
325                 default:
326                         return SR_ERR_NA;
327                 }
328         }
329
330         switch (key) {
331         case SR_CONF_DEVICE_OPTIONS:
332                 *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
333                 return SR_OK;
334         default:
335                 return SR_ERR_NA;
336         }
337 }
338
339 static struct sr_dev_driver dcttech_usbrelay_driver_info = {
340         .name = "dcttech-usbrelay",
341         .longname = "dcttech usbrelay",
342         .api_version = 1,
343         .init = std_init,
344         .cleanup = std_cleanup,
345         .scan = scan,
346         .dev_list = std_dev_list,
347         .dev_clear = std_dev_clear,
348         .config_get = config_get,
349         .config_set = config_set,
350         .config_list = config_list,
351         .dev_open = dev_open,
352         .dev_close = dev_close,
353         .dev_acquisition_start = std_dummy_dev_acquisition_start,
354         .dev_acquisition_stop = std_dummy_dev_acquisition_stop,
355         .context = NULL,
356 };
357 SR_REGISTER_DEV_DRIVER(dcttech_usbrelay_driver_info);