]> sigrok.org Git - libsigrok.git/blame - src/hardware/asix-omega-rtm-cli/api.c
strutil: add common parse logic for SR_CONF_PROBE_NAMES input specs
[libsigrok.git] / src / hardware / asix-omega-rtm-cli / api.c
CommitLineData
13f6da67
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
1cadb5ec
GS
20/*
21 * This sigrok driver implementation uses the vendor's CLI application
22 * for the ASIX OMEGA to operate the device in real time mode. The
23 * external process handles the device detection, USB communication
24 * (FTDI FIFO), FPGA netlist download, and device control. The process'
25 * stdout provides a continuous RLE compressed stream of 16bit samples
26 * taken at 200MHz.
27 *
28 * Known limitations: The samplerate is fixed. Hardware triggers are not
29 * available in this mode. The start of the acquisition takes a few
30 * seconds, but the device's native protocol is unknown and its firmware
31 * is unavailable, so that a native sigrok driver is in some distant
32 * future. Users need to initiate the acquisition in sigrok early so
33 * that the device is capturing when the event of interest happens.
34 *
35 * The vendor application's executable either must be named omegartmcli
36 * and must be found in PATH, or the OMEGARTMCLI environment variable
37 * must contain its location. A scan option could be used when a
38 * suitable SR_CONF key gets identified which communicates executable
39 * locations.
40 *
41 * When multiple devices are connected, then a conn=sn=... specification
42 * can select one of the devices. The serial number should contain six
43 * or eight hex digits (this follows the vendor's approach for the CLI
44 * application).
45 */
46
47/*
48 * Implementor's notes. Examples of program output which gets parsed by
49 * this sigrok driver.
50 *
51 * $ ./omegartmcli.exe -version
52 * omegartmcli.exe Omega Real-Time Mode
53 * Version 2016-12-14
54 * Copyright (c) 1991-2016 ASIX s.r.o.
55 * Email: support@asix.net
56 *
57 * $ ./omegartmcli.exe -bin [-serial SERNO] <NULL>
58 * (five command line words including the terminator)
59 *
60 * The RTM CLI application terminates when its stdin closes, or when
61 * CTRL-C is pressed. The former is more portable across platforms. The
62 * stderr output should get ignored, it's rather noisy here under wine,
63 * communicates non-fatal diagnostics, and may communicate "progress"
64 * which we don't care about.
65 *
66 * Ideally the external process could get started earlier, and gets
67 * re-used across several sigrok acquisition activities. Unfortunately
68 * the driver's open/close actions lack a sigrok session, and cannot
69 * register the receive callback (or needs to duplicate common support
70 * code). When such an approach gets implemented, the external process'
71 * output must get drained even outside of sigrok acquisition phases,
72 * the cost of which is yet to get determined (depends on the input
73 * signals, may be expensive).
74 *
75 * The binary data format is used to reduce the amount of inter process
76 * communication. The format is rather simple: Three 16bit items (LE
77 * format) carry a timestamp (10ns resolution), and two 16bit samples
78 * (taken at 5ns intervals). The timestamp may translate to a repetition
79 * of the last sample a given number of times (RLE compression of idle
80 * phases where inputs don't change). The first timestamp after program
81 * startup is to get ignored. Chunks are sent after at most 32Ki 10ns
82 * ticks, to not overflow the 16bit counter. Which translates to a data
83 * volume of 6 bytes each 328us for idle inputs, higher for changing
84 * input signals.
85 *
86 * Is it useful to implement a set of samplerates in the sigrok driver,
87 * and downsample the data which is provided by the Asix application?
88 * This would not avoid the pressure of receiving the acquisition
89 * process' output, but may result in reduced cost on the sigrok side
90 * when users want to inspect slow signals, or export to "expensive"
91 * file formats.
92 *
93 * This driver implementation may benefit from software trigger support.
94 */
95
13f6da67 96#include <config.h>
13f6da67 97
1cadb5ec
GS
98#include <stdlib.h>
99#include <string.h>
13f6da67 100
1cadb5ec 101#include "protocol.h"
13f6da67 102
1cadb5ec
GS
103static const char *channel_names[] = {
104 "1", "2", "3", "4", "5", "6", "7", "8",
105 "9", "10", "11", "12", "13", "14", "15", "16",
106};
13f6da67 107
1cadb5ec
GS
108static const uint64_t samplerates[] = {
109 SR_MHZ(200),
110};
13f6da67 111
1cadb5ec
GS
112static const uint32_t scanopts[] = {
113 SR_CONF_CONN, /* Accepts serial number specs. */
114};
13f6da67 115
1cadb5ec
GS
116static const uint32_t drvopts[] = {
117 SR_CONF_LOGIC_ANALYZER,
118};
119
120static const uint32_t devopts[] = {
121 SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
122 SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
123 SR_CONF_CONN | SR_CONF_GET,
124 SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_LIST,
125};
13f6da67 126
1cadb5ec 127static GSList *scan(struct sr_dev_driver *di, GSList *options)
13f6da67 128{
1cadb5ec
GS
129 const char *conn, *serno, *exe;
130 GSList *devices;
131 size_t argc, chidx;
132 gchar **argv, *output, *vers_text, *eol;
133 GSpawnFlags flags;
134 GError *error;
135 gboolean ok;
136 char serno_buff[10];
137 struct sr_dev_inst *sdi;
138 struct dev_context *devc;
139
140 /* Extract optional serial number from conn= spec. */
141 conn = NULL;
142 (void)sr_serial_extract_options(options, &conn, NULL);
143 if (!conn || !*conn)
144 conn = NULL;
145 serno = NULL;
146 if (conn) {
147 if (!g_str_has_prefix(conn, "sn=")) {
148 sr_err("conn= must specify a serial number.");
149 return NULL;
150 }
151 serno = conn + strlen("sn=");
152 if (!*serno)
153 serno = NULL;
154 }
155 if (serno)
156 sr_dbg("User specified serial number: %s", serno);
157 if (serno && strlen(serno) == 4) {
158 sr_dbg("Adding 03 prefix to user specified serial number");
159 snprintf(serno_buff, sizeof(serno_buff), "03%s", serno);
160 serno = serno_buff;
161 }
162 if (serno && strlen(serno) != 6 && strlen(serno) != 8) {
163 sr_err("Serial number must be 03xxxx or A603xxxx");
164 serno = NULL;
165 }
13f6da67 166
1cadb5ec 167 devices = NULL;
13f6da67 168
1cadb5ec
GS
169 /*
170 * Check availability of the external executable. Notice that
171 * failure is non-fatal, the scan can take place even when users
172 * don't request and don't expect to use Asix Omega devices.
173 */
174 exe = getenv("OMEGARTMCLI");
175 if (!exe || !*exe)
176 exe = "omegartmcli";
177 sr_dbg("Vendor application executable: %s", exe);
178 argv = g_malloc0(5 * sizeof(argv[0]));
179 argc = 0;
180 argv[argc++] = g_strdup(exe);
181 argv[argc++] = g_strdup("-version");
182 argv[argc++] = NULL;
183 flags = G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL;
184 output = NULL;
185 error = NULL;
186 ok = g_spawn_sync(NULL, argv, NULL, flags, NULL, NULL,
187 &output, NULL, NULL, &error);
188 if (error && error->code != G_SPAWN_ERROR_NOENT)
189 sr_err("Cannot execute RTM CLI process: %s", error->message);
190 if (error) {
191 ok = FALSE;
192 g_error_free(error);
193 }
194 if (!output || !*output)
195 ok = FALSE;
196 if (!ok) {
197 sr_dbg("External RTM CLI execution failed.");
198 g_free(output);
199 g_strfreev(argv);
200 return NULL;
201 }
13f6da67 202
1cadb5ec
GS
203 /*
204 * Get the executable's version from second stdout line. This
205 * only executes when the executable is found, failure to get
206 * the version information is considered fatal.
207 */
208 vers_text = strstr(output, "Version ");
209 if (!vers_text)
210 ok = FALSE;
211 if (ok) {
212 vers_text += strlen("Version ");
213 eol = strchr(vers_text, '\n');
214 if (eol)
215 *eol = '\0';
216 eol = strchr(vers_text, '\r');
217 if (eol)
218 *eol = '\0';
219 if (!vers_text || !*vers_text)
220 ok = FALSE;
221 }
222 if (!ok) {
223 sr_err("Cannot get RTM CLI executable's version.");
224 g_free(output);
225 g_strfreev(argv);
226 return NULL;
227 }
228 sr_info("RTM CLI executable version: %s", vers_text);
229
230 /*
231 * Create a device instance, add it to the result set. Create a
232 * device context. Change the -version command into the command
233 * for acquisition for later use in the driver's lifetime.
234 */
235 sdi = g_malloc0(sizeof(*sdi));
236 devices = g_slist_append(devices, sdi);
237 sdi->status = SR_ST_INITIALIZING;
238 sdi->vendor = g_strdup("ASIX");
239 sdi->model = g_strdup("OMEGA RTM CLI");
240 sdi->version = g_strdup(vers_text);
241 if (serno)
242 sdi->serial_num = g_strdup(serno);
243 if (conn)
244 sdi->connection_id = g_strdup(conn);
245 for (chidx = 0; chidx < ARRAY_SIZE(channel_names); chidx++) {
246 sr_channel_new(sdi, chidx, SR_CHANNEL_LOGIC,
247 TRUE, channel_names[chidx]);
248 }
13f6da67 249
1cadb5ec
GS
250 devc = g_malloc0(sizeof(*devc));
251 sdi->priv = devc;
252 sr_sw_limits_init(&devc->limits);
253 argc = 1;
254 g_free(argv[argc]);
255 argv[argc++] = g_strdup("-bin");
256 if (serno) {
257 argv[argc++] = g_strdup("-serial");
258 argv[argc++] = g_strdup(serno);
259 }
260 argv[argc++] = NULL;
261 devc->child.argv = argv;
262 devc->child.flags = flags | G_SPAWN_CLOEXEC_PIPES;
263 devc->child.fd_stdin_write = -1;
264 devc->child.fd_stdout_read = -1;
13f6da67 265
1cadb5ec 266 return std_scan_complete(di, devices);
13f6da67
GS
267}
268
269static int config_get(uint32_t key, GVariant **data,
270 const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
271{
1cadb5ec 272 struct dev_context *devc;
13f6da67 273
13f6da67
GS
274 (void)cg;
275
1cadb5ec
GS
276 if (!sdi)
277 return SR_ERR_ARG;
278 devc = sdi->priv;
279
13f6da67 280 switch (key) {
1cadb5ec
GS
281 case SR_CONF_CONN:
282 if (!sdi->connection_id)
283 return SR_ERR_NA;
284 *data = g_variant_new_string(sdi->connection_id);
285 break;
286 case SR_CONF_SAMPLERATE:
287 *data = g_variant_new_uint64(samplerates[0]);
288 break;
289 case SR_CONF_LIMIT_MSEC:
290 case SR_CONF_LIMIT_SAMPLES:
291 return sr_sw_limits_config_get(&devc->limits, key, data);
13f6da67
GS
292 default:
293 return SR_ERR_NA;
294 }
295
1cadb5ec 296 return SR_OK;
13f6da67
GS
297}
298
299static int config_set(uint32_t key, GVariant *data,
300 const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
301{
1cadb5ec 302 struct dev_context *devc;
13f6da67 303
13f6da67
GS
304 (void)cg;
305
1cadb5ec
GS
306 if (!sdi)
307 return SR_ERR_ARG;
308 devc = sdi->priv;
309
13f6da67 310 switch (key) {
1cadb5ec
GS
311 case SR_CONF_LIMIT_MSEC:
312 case SR_CONF_LIMIT_SAMPLES:
313 return sr_sw_limits_config_set(&devc->limits, key, data);
13f6da67 314 default:
1cadb5ec 315 return SR_ERR_NA;
13f6da67
GS
316 }
317
1cadb5ec 318 return SR_OK;
13f6da67
GS
319}
320
321static int config_list(uint32_t key, GVariant **data,
322 const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
323{
13f6da67 324
13f6da67 325 switch (key) {
1cadb5ec
GS
326 case SR_CONF_SCAN_OPTIONS:
327 case SR_CONF_DEVICE_OPTIONS:
328 if (cg)
329 return SR_ERR_NA;
330 return STD_CONFIG_LIST(key, data, sdi, cg,
331 scanopts, drvopts, devopts);
332 case SR_CONF_SAMPLERATE:
333 *data = std_gvar_samplerates(ARRAY_AND_SIZE(samplerates));
334 break;
13f6da67
GS
335 default:
336 return SR_ERR_NA;
337 }
338
1cadb5ec 339 return SR_OK;
13f6da67
GS
340}
341
342static int dev_acquisition_start(const struct sr_dev_inst *sdi)
343{
1cadb5ec
GS
344 struct dev_context *devc;
345 int ret;
346 int fd, events;
347 uint64_t remain_count;
348
349 devc = sdi->priv;
350
351 /* Start the external acquisition process. */
352 ret = omega_rtm_cli_open(sdi);
353 if (ret != SR_OK)
354 return ret;
355 fd = devc->child.fd_stdout_read;
356 events = G_IO_IN | G_IO_ERR;
357
358 /*
359 * Start supervising acquisition limits. Arrange for a stricter
360 * "samples count" check than supported by the common approach.
361 */
362 sr_sw_limits_acquisition_start(&devc->limits);
363 ret = sr_sw_limits_get_remain(&devc->limits,
364 &remain_count, NULL, NULL, NULL);
365 if (ret != SR_OK)
366 return ret;
367 if (remain_count) {
368 devc->samples.remain_count = remain_count;
369 devc->samples.check_count = TRUE;
370 }
13f6da67 371
1cadb5ec
GS
372 /* Send the session feed header. */
373 ret = std_session_send_df_header(sdi);
374 if (ret != SR_OK)
375 return ret;
376
377 /* Start processing the external process' output. */
378 ret = sr_session_source_add(sdi->session, fd, events, 10,
379 omega_rtm_cli_receive_data, (void *)sdi); /* Un-const. */
380 if (ret != SR_OK)
381 return ret;
13f6da67
GS
382
383 return SR_OK;
384}
385
386static int dev_acquisition_stop(struct sr_dev_inst *sdi)
387{
1cadb5ec
GS
388 struct dev_context *devc;
389 int ret;
390 int fd;
391
392 devc = sdi->priv;
393
394 /*
395 * Implementor's note: Do run all stop activities even if
396 * some of them may fail. Emit diagnostics messages as errors
397 * are seen, but don't return early.
398 */
399
400 /* Stop processing the external process' output. */
401 fd = devc->child.fd_stdout_read;
402 if (fd >= 0) {
403 ret = sr_session_source_remove(sdi->session, fd);
404 if (ret != SR_OK) {
405 sr_err("Cannot stop reading acquisition data");
406 }
407 }
408
409 ret = std_session_send_df_end(sdi);
410 (void)ret;
13f6da67 411
1cadb5ec
GS
412 ret = omega_rtm_cli_close(sdi);
413 if (ret != SR_OK) {
414 sr_err("Could not terminate acquisition process");
415 }
416 (void)ret;
13f6da67
GS
417
418 return SR_OK;
419}
420
421static struct sr_dev_driver asix_omega_rtm_cli_driver_info = {
422 .name = "asix-omega-rtm-cli",
423 .longname = "ASIX OMEGA RTM CLI",
424 .api_version = 1,
425 .init = std_init,
426 .cleanup = std_cleanup,
427 .scan = scan,
428 .dev_list = std_dev_list,
429 .dev_clear = std_dev_clear,
430 .config_get = config_get,
431 .config_set = config_set,
432 .config_list = config_list,
1cadb5ec
GS
433 .dev_open = std_dummy_dev_open,
434 .dev_close = std_dummy_dev_close,
13f6da67
GS
435 .dev_acquisition_start = dev_acquisition_start,
436 .dev_acquisition_stop = dev_acquisition_stop,
437 .context = NULL,
438};
439SR_REGISTER_DEV_DRIVER(asix_omega_rtm_cli_driver_info);