]> sigrok.org Git - libsigrok.git/blame - src/hardware/mooshimeter-dmm/api.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / mooshimeter-dmm / api.c
CommitLineData
ebcd1aba
DH
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2019 Derek Hageman <hageman@inthat.cloud>
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#include "protocol.h"
22
23static struct sr_dev_driver mooshimeter_dmm_driver_info;
24
25static const uint32_t scanopts[] = {
26 SR_CONF_CONN,
27};
28
29static const uint32_t drvopts[] = {
30 SR_CONF_MULTIMETER,
31};
32
33static const uint32_t devopts[] = {
34 SR_CONF_CONTINUOUS,
35 SR_CONF_LIMIT_SAMPLES | SR_CONF_SET | SR_CONF_LIST,
36 SR_CONF_LIMIT_MSEC | SR_CONF_SET | SR_CONF_LIST,
37 SR_CONF_AVG_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
38 SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
39 SR_CONF_CHANNEL_CONFIG | SR_CONF_SET,
40};
41
42static void init_dev(struct sr_dev_inst *sdi)
43{
44 struct dev_context *devc;
45 struct sr_channel *chan;
46
47 devc = g_new0(struct dev_context, 1);
48 sdi->priv = devc;
49 sdi->status = SR_ST_INITIALIZING;
50 sdi->vendor = g_strdup("Mooshim Engineering");
51 sdi->model = g_strdup("Mooshimeter");
52
53 sr_sw_limits_init(&devc->limits);
54
55 chan = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "CH1");
56 devc->channel_meaning[0].mq = SR_MQ_CURRENT;
57 devc->channel_meaning[0].unit = SR_UNIT_AMPERE;
58 devc->channel_meaning[0].mqflags = SR_MQFLAG_DC;
59 devc->channel_meaning[0].channels = g_slist_prepend(NULL, chan);
60
61 chan = sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "CH2");
62 devc->channel_meaning[1].mq = SR_MQ_VOLTAGE;
63 devc->channel_meaning[1].unit = SR_UNIT_VOLT;
64 devc->channel_meaning[1].mqflags = SR_MQFLAG_DC;
65 devc->channel_meaning[1].channels = g_slist_prepend(NULL, chan);
66
67 chan = sr_channel_new(sdi, 2, SR_CHANNEL_ANALOG, FALSE, "P");
68 devc->channel_meaning[2].mq = SR_MQ_POWER;
69 devc->channel_meaning[2].unit = SR_UNIT_WATT;
70 devc->channel_meaning[2].mqflags = SR_MQFLAG_RMS;
71 devc->channel_meaning[2].channels = g_slist_prepend(NULL, chan);
72}
73
74static GSList *scan(struct sr_dev_driver *di, GSList *options)
75{
76 struct sr_bt_desc *desc;
77 const char *conn;
78 struct sr_config *src;
79 GSList *l;
80 int ret;
81
82 conn = NULL;
83 for (l = options; l; l = l->next) {
84 src = l->data;
85 switch (src->key) {
86 case SR_CONF_CONN:
87 conn = g_variant_get_string(src->data, NULL);
88 break;
89 }
90 }
91
92 if (!conn)
93 return NULL;
94
95 desc = sr_bt_desc_new();
96 if (!desc)
97 return NULL;
98
99 ret = sr_bt_config_addr_remote(desc, conn);
100 if (ret < 0)
101 goto err;
102
103 /*
104 * These handles where queried with btgatt-client, since the
105 * documentation specifies them in terms of UUIDs.
106 *
107 * service - start: 0x0010, end: 0xffff, type: primary, uuid: 1bc5ffa0-0200-62ab-e411-f254e005dbd4
108 * charac - start: 0x0011, value: 0x0012, props: 0x08, ext_props: 0x0000, uuid: 1bc5ffa1-0200-62ab-e411-f254e005dbd4
109 * descr - handle: 0x0013, uuid: 00002901-0000-1000-8000-00805f9b34fb
110 * charac - start: 0x0014, value: 0x0015, props: 0x10, ext_props: 0x0000, uuid: 1bc5ffa2-0200-62ab-e411-f254e005dbd4
111 * descr - handle: 0x0016, uuid: 00002902-0000-1000-8000-00805f9b34fb
112 * descr - handle: 0x0017, uuid: 00002901-0000-1000-8000-00805f9b34fb
113 */
25d7c995 114 ret = sr_bt_config_notify(desc, 0x0015, 0x0012, 0x0016, 0x0001, 0);
ebcd1aba
DH
115 if (ret < 0)
116 goto err;
117
59d916fe
UH
118 ret = sr_bt_connect_ble(desc);
119 if (ret < 0)
120 goto err;
121 sr_bt_disconnect(desc);
122
ebcd1aba
DH
123 struct sr_dev_inst *sdi = g_malloc0(sizeof(struct sr_dev_inst));
124 struct dev_context *devc = g_malloc0(sizeof(struct dev_context));
125
126 sdi->priv = devc;
127 sdi->inst_type = SR_INST_USER;
128 sdi->connection_id = g_strdup(conn);
129 sdi->conn = desc;
130
131 init_dev(sdi);
132
133 return std_scan_complete(di, g_slist_prepend(NULL, sdi));
134
135err:
136 sr_bt_desc_free(desc);
137 return NULL;
138}
139
140static int dev_clear(const struct sr_dev_driver *di)
141{
142 struct drv_context *drvc = di->context;
143 struct sr_dev_inst *sdi;
144 GSList *l;
145
146 if (drvc) {
147 for (l = drvc->instances; l; l = l->next) {
148 sdi = l->data;
149 struct sr_bt_desc *desc = sdi->conn;
150 if (desc)
151 sr_bt_desc_free(desc);
152 sdi->conn = NULL;
153 }
154 }
155
156 return std_dev_clear(di);
157}
158
159static int set_channel1_mean(const struct sr_dev_inst *sdi)
160{
161 struct dev_context *devc = sdi->priv;
162 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_RMS;
163 devc->channel_meaning[0].mqflags |= SR_MQFLAG_DC;
164
165 return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS",
166 "CH1:ANALYSIS:MEAN");
167}
168
169static int set_channel1_rms(const struct sr_dev_inst *sdi)
170{
171 struct dev_context *devc = sdi->priv;
172 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DC;
173 devc->channel_meaning[0].mqflags |= SR_MQFLAG_RMS;
174
175 return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS",
176 "CH1:ANALYSIS:RMS");
177}
178
179static int set_channel1_buffer(const struct sr_dev_inst *sdi)
180{
181 struct dev_context *devc = sdi->priv;
182 devc->channel_meaning[0].mqflags &= ~(SR_MQFLAG_DC | SR_MQFLAG_RMS);
183
184 return mooshimeter_dmm_set_chooser(sdi, "CH1:ANALYSIS",
185 "CH1:ANALYSIS:BUFFER");
186}
187
188static int set_channel2_mean(const struct sr_dev_inst *sdi)
189{
190 struct dev_context *devc = sdi->priv;
191 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_RMS;
192 devc->channel_meaning[1].mqflags |= SR_MQFLAG_DC;
193
194 return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS",
195 "CH2:ANALYSIS:MEAN");
196}
197
198static int set_channel2_rms(const struct sr_dev_inst *sdi)
199{
200 struct dev_context *devc = sdi->priv;
201 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DC;
202 devc->channel_meaning[1].mqflags |= SR_MQFLAG_RMS;
203
204 return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS",
205 "CH2:ANALYSIS:RMS");
206}
207
208static int set_channel2_buffer(const struct sr_dev_inst *sdi)
209{
210 struct dev_context *devc = sdi->priv;
211
212 devc->channel_meaning[1].mqflags &= ~(SR_MQFLAG_DC | SR_MQFLAG_RMS);
213
214 return mooshimeter_dmm_set_chooser(sdi, "CH2:ANALYSIS",
215 "CH2:ANALYSIS:BUFFER");
216}
217
218static void autorange_channel1_current(const struct sr_dev_inst *sdi,
219 float value)
220{
221 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
222 "CH1:MAPPING:CURRENT", value);
223}
224
225static int configure_channel1_current(const struct sr_dev_inst *sdi,
226 float range)
227{
228 struct dev_context *devc = sdi->priv;
229 int ret;
230
231 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
232 "CH1:MAPPING:CURRENT");
233 if (ret != SR_OK)
234 return ret;
235
236 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
237 "CH1:MAPPING:CURRENT", range);
238 if (ret != SR_OK)
239 return ret;
240
241 if (range <= 0) {
242 devc->channel_autorange[0] = autorange_channel1_current;
243 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
244 } else {
245 devc->channel_autorange[0] = NULL;
246 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
247 }
248
249 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE;
250 devc->channel_meaning[0].mq = SR_MQ_CURRENT;
251 devc->channel_meaning[0].unit = SR_UNIT_AMPERE;
252
253 return SR_OK;
254}
255
256static void autorange_channel1_temperature(const struct sr_dev_inst *sdi,
257 float value)
258{
259 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
260 "CH1:MAPPING:TEMP", value);
261}
262
263static int configure_channel1_temperature(const struct sr_dev_inst *sdi,
264 float range)
265{
266 struct dev_context *devc = sdi->priv;
267 int ret;
268
269 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
270 "CH1:MAPPING:TEMP");
271 if (ret != SR_OK)
272 return ret;
273
274 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
275 "CH1:MAPPING:TEMP", range);
276 if (ret != SR_OK)
277 return ret;
278
279 if (range <= 0) {
280 devc->channel_autorange[0] = autorange_channel1_temperature;
281 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
282 } else {
283 devc->channel_autorange[0] = NULL;
284 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
285 }
286
287 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE;
288 devc->channel_meaning[0].mq = SR_MQ_TEMPERATURE;
289 devc->channel_meaning[0].unit = SR_UNIT_KELVIN;
290
291 return SR_OK;
292}
293
294static void autorange_channel1_auxv(const struct sr_dev_inst *sdi,
295 float value)
296{
297 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
298 "SHARED:AUX_V", value);
299}
300
301static int configure_channel1_auxv(const struct sr_dev_inst *sdi,
302 float range)
303{
304 struct dev_context *devc = sdi->priv;
305 int ret;
306
307 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:AUX_V");
308 if (ret != SR_OK)
309 return ret;
310
311 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
312 "CH1:MAPPING:SHARED");
313 if (ret != SR_OK)
314 return ret;
315
316 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
317 "SHARED:AUX_V", range);
318 if (ret != SR_OK)
319 return ret;
320
321 if (range <= 0) {
322 devc->channel_autorange[0] = autorange_channel1_auxv;
323 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
324 } else {
325 devc->channel_autorange[0] = NULL;
326 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
327 }
328
329 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE;
330 devc->channel_meaning[0].mq = SR_MQ_VOLTAGE;
331 devc->channel_meaning[0].unit = SR_UNIT_VOLT;
332
333 return SR_OK;
334}
335
336static void autorange_channel1_resistance(const struct sr_dev_inst *sdi,
337 float value)
338{
339 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
340 "SHARED:RESISTANCE", value);
341}
342
343static int configure_channel1_resistance(const struct sr_dev_inst *sdi,
344 float range)
345{
346 struct dev_context *devc = sdi->priv;
347 int ret;
348
349 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:RESISTANCE");
350 if (ret != SR_OK)
351 return ret;
352
353 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
354 "CH1:MAPPING:SHARED");
355 if (ret != SR_OK)
356 return ret;
357
358 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
359 "SHARED:RESISTANCE", range);
360 if (ret != SR_OK)
361 return ret;
362
363 if (range <= 0) {
364 devc->channel_autorange[0] = autorange_channel1_resistance;
365 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
366 } else {
367 devc->channel_autorange[0] = NULL;
368 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
369 }
370
371 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_DIODE;
372 devc->channel_meaning[0].mq = SR_MQ_RESISTANCE;
373 devc->channel_meaning[0].unit = SR_UNIT_OHM;
374
375 return SR_OK;
376}
377
378static void autorange_channel1_diode(const struct sr_dev_inst *sdi,
379 float value)
380{
381 mooshimeter_dmm_set_autorange(sdi, "CH1:RANGE_I",
382 "SHARED:DIODE", value);
383}
384
385static int configure_channel1_diode(const struct sr_dev_inst *sdi,
386 float range)
387{
388 struct dev_context *devc = sdi->priv;
389 int ret;
390
391 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:DIODE");
392 if (ret != SR_OK)
393 return ret;
394
395 ret = mooshimeter_dmm_set_chooser(sdi, "CH1:MAPPING",
396 "CH1:MAPPING:SHARED");
397 if (ret != SR_OK)
398 return ret;
399
400 ret = mooshimeter_dmm_set_larger_number(sdi, "CH1:RANGE_I",
401 "SHARED:DIODE", range);
402 if (ret != SR_OK)
403 return ret;
404
405 if (range <= 0) {
406 devc->channel_autorange[0] = autorange_channel1_diode;
407 devc->channel_meaning[0].mqflags |= SR_MQFLAG_AUTORANGE;
408 } else {
409 devc->channel_autorange[0] = NULL;
410 devc->channel_meaning[0].mqflags &= ~SR_MQFLAG_AUTORANGE;
411 }
412
413 devc->channel_meaning[0].mqflags |= SR_MQFLAG_DIODE;
414 devc->channel_meaning[0].mq = SR_MQ_VOLTAGE;
415 devc->channel_meaning[0].unit = SR_UNIT_VOLT;
416
417 return SR_OK;
418}
419
420static void autorange_channel2_voltage(const struct sr_dev_inst *sdi,
421 float value)
422{
423 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
424 "CH2:MAPPING:VOLTAGE", value);
425}
426
427static int configure_channel2_voltage(const struct sr_dev_inst *sdi,
428 float range)
429{
430 struct dev_context *devc = sdi->priv;
431 int ret;
432
433 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
434 "CH2:MAPPING:VOLTAGE");
435 if (ret != SR_OK)
436 return ret;
437
438 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
439 "CH2:MAPPING:VOLTAGE", range);
440 if (ret != SR_OK)
441 return ret;
442
443 if (range <= 0) {
444 devc->channel_autorange[1] = autorange_channel2_voltage;
445 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
446 } else {
447 devc->channel_autorange[1] = NULL;
448 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
449 }
450
451 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE;
452 devc->channel_meaning[1].mq = SR_MQ_VOLTAGE;
453 devc->channel_meaning[1].unit = SR_UNIT_VOLT;
454
455 return SR_OK;
456}
457
458static void autorange_channel2_temperature(const struct sr_dev_inst *sdi,
459 float value)
460{
461 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
462 "CH2:MAPPING:TEMP", value);
463}
464
465static int configure_channel2_temperature(const struct sr_dev_inst *sdi,
466 float range)
467{
468 struct dev_context *devc = sdi->priv;
469 int ret;
470
471 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
472 "CH2:MAPPING:TEMP");
473 if (ret != SR_OK)
474 return ret;
475
476 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
477 "CH2:MAPPING:TEMP", range);
478 if (ret != SR_OK)
479 return ret;
480
481 if (range <= 0) {
482 devc->channel_autorange[1] = autorange_channel2_temperature;
483 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
484 } else {
485 devc->channel_autorange[1] = NULL;
486 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
487 }
488
489 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE;
490 devc->channel_meaning[1].mq = SR_MQ_TEMPERATURE;
491 devc->channel_meaning[1].unit = SR_UNIT_CELSIUS;
492
493 return SR_OK;
494}
495
496static void autorange_channel2_auxv(const struct sr_dev_inst *sdi,
497 float value)
498{
499 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
500 "SHARED:AUX_V", value);
501}
502
503static int configure_channel2_auxv(const struct sr_dev_inst *sdi,
504 float range)
505{
506 struct dev_context *devc = sdi->priv;
507 int ret;
508
509 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:AUX_V");
510 if (ret != SR_OK)
511 return ret;
512
513 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
514 "CH2:MAPPING:SHARED");
515 if (ret != SR_OK)
516 return ret;
517
518 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
519 "SHARED:AUX_V", range);
520 if (ret != SR_OK)
521 return ret;
522
523 if (range <= 0) {
524 devc->channel_autorange[1] = autorange_channel2_auxv;
525 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
526 } else {
527 devc->channel_autorange[1] = NULL;
528 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
529 }
530
531 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE;
532 devc->channel_meaning[1].mq = SR_MQ_VOLTAGE;
533 devc->channel_meaning[1].unit = SR_UNIT_VOLT;
534
535 return SR_OK;
536}
537
538static void autorange_channel2_resistance(const struct sr_dev_inst *sdi,
539 float value)
540{
541 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
542 "SHARED:RESISTANCE", value);
543}
544
545static int configure_channel2_resistance(const struct sr_dev_inst *sdi,
546 float range)
547{
548 struct dev_context *devc = sdi->priv;
549 int ret;
550
551 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:RESISTANCE");
552 if (ret != SR_OK)
553 return ret;
554
555 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
556 "CH2:MAPPING:SHARED");
557 if (ret != SR_OK)
558 return ret;
559
560 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
561 "SHARED:RESISTANCE", range);
562 if (ret != SR_OK)
563 return ret;
564
565 if (range <= 0) {
566 devc->channel_autorange[1] = autorange_channel2_resistance;
567 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
568 } else {
569 devc->channel_autorange[1] = NULL;
570 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
571 }
572
573 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_DIODE;
574 devc->channel_meaning[1].mq = SR_MQ_RESISTANCE;
575 devc->channel_meaning[1].unit = SR_UNIT_OHM;
576
577 return SR_OK;
578}
579
580static void autorange_channel2_diode(const struct sr_dev_inst *sdi,
581 float value)
582{
583 mooshimeter_dmm_set_autorange(sdi, "CH2:RANGE_I",
584 "SHARED:DIODE", value);
585}
586
587static int configure_channel2_diode(const struct sr_dev_inst *sdi,
588 float range)
589{
590 struct dev_context *devc = sdi->priv;
591 int ret;
592
593 ret = mooshimeter_dmm_set_chooser(sdi, "SHARED", "SHARED:DIODE");
594 if (ret != SR_OK)
595 return ret;
596
597 ret = mooshimeter_dmm_set_chooser(sdi, "CH2:MAPPING",
598 "CH2:MAPPING:SHARED");
599 if (ret != SR_OK)
600 return ret;
601
602 ret = mooshimeter_dmm_set_larger_number(sdi, "CH2:RANGE_I",
603 "SHARED:DIODE", range);
604 if (ret != SR_OK)
605 return ret;
606
607 if (range <= 0) {
608 devc->channel_autorange[1] = autorange_channel2_diode;
609 devc->channel_meaning[1].mqflags |= SR_MQFLAG_AUTORANGE;
610 } else {
611 devc->channel_autorange[1] = NULL;
612 devc->channel_meaning[1].mqflags &= ~SR_MQFLAG_AUTORANGE;
613 }
614
615 devc->channel_meaning[1].mqflags |= SR_MQFLAG_DIODE;
616 devc->channel_meaning[1].mq = SR_MQ_VOLTAGE;
617 devc->channel_meaning[1].unit = SR_UNIT_VOLT;
618
619 return SR_OK;
620}
621
622/*
623 * Full string: CH1,CH2
624 * Each channel: MODE[:RANGE[:ANALYSIS]]
625 * Channel 1 mode:
626 * Current, A
627 * Temperature, T, K
628 * Resistance, Ohm, W
629 * Diode, D
630 * Aux, LV
631 * Channel 2 mode:
632 * Voltage, V
633 * Temperature, T, K
634 * Resistance, Ohm, W
635 * Diode, D
636 * Aux, LV
637 * Range is the upper bound of the range (e.g. 60 for 0-60 V or 600 for 0-600),
638 * zero or absent for autoranging
639 * Analysis:
640 * Mean, DC
641 * RMS, AC
642 * Buffer, Samples
643 */
644static int apply_channel_config(const struct sr_dev_inst *sdi,
645 const char *config)
646{
647 gchar **channel_config;
648 gchar **parameters;
649 const gchar *param;
650 int ret = SR_ERR;
651 float range;
652 gboolean shared_in_use = FALSE;
653
654 channel_config = g_strsplit_set(config, ",/", -1);
655 if (!channel_config[0])
656 goto err_free_channel_config;
657
658 parameters = g_strsplit_set(channel_config[0], ":;", -1);
659 if (parameters[0] && parameters[0][0]) {
660 range = 0;
661 if (parameters[1])
662 range = g_ascii_strtod(parameters[1], NULL);
663
664 param = parameters[0];
665 if (!g_ascii_strncasecmp(param, "Resistance", 10) ||
666 !g_ascii_strncasecmp(param, "Ohm", 3) ||
667 !g_ascii_strncasecmp(param, "W", 1) ||
668 !g_ascii_strncasecmp(param, "R", 1)) {
669 ret = configure_channel1_resistance(sdi, range);
670 if (ret != SR_OK)
671 goto err_free_parameters;
672 shared_in_use = TRUE;
673 } else if (!g_ascii_strncasecmp(param, "Diode", 5) ||
674 !g_ascii_strncasecmp(param, "D", 1)) {
675 ret = configure_channel1_diode(sdi, range);
676 if (ret != SR_OK)
677 goto err_free_parameters;
678 shared_in_use = TRUE;
679 } else if (!g_ascii_strncasecmp(param, "Aux", 3) ||
680 !g_ascii_strncasecmp(param, "LV", 2)) {
681 ret = configure_channel1_auxv(sdi, range);
682 if (ret != SR_OK)
683 goto err_free_parameters;
684 shared_in_use = TRUE;
685 } else if (!g_ascii_strncasecmp(param, "T", 1) ||
686 !g_ascii_strncasecmp(param, "K", 1)) {
687 ret = configure_channel1_temperature(sdi, range);
688 if (ret != SR_OK)
689 goto err_free_parameters;
690 } else if (!g_ascii_strncasecmp(param, "Current", 7) ||
691 !g_ascii_strncasecmp(param, "A", 1) ||
692 *parameters[0]) {
693 ret = configure_channel1_current(sdi, range);
694 if (ret != SR_OK)
695 goto err_free_parameters;
696 } else {
697 sr_info("Unrecognized mode for CH1: %s.", param);
698 ret = configure_channel1_current(sdi, range);
699 if (ret != SR_OK)
700 goto err_free_parameters;
701 }
702
703 if (parameters[1] && parameters[2]) {
704 param = parameters[2];
705 if (!g_ascii_strcasecmp(param, "RMS") ||
706 !g_ascii_strcasecmp(param, "AC")) {
707 ret = set_channel1_rms(sdi);
708 if (ret != SR_OK)
709 goto err_free_parameters;
710 } else if (!g_ascii_strcasecmp(param, "Buffer") ||
711 !g_ascii_strcasecmp(param, "Samples")) {
712 ret = set_channel1_buffer(sdi);
713 if (ret != SR_OK)
714 goto err_free_parameters;
715 } else {
716 ret = set_channel1_mean(sdi);
717 if (ret != SR_OK)
718 goto err_free_parameters;
719 }
720 }
721 }
722 g_strfreev(parameters);
723
724 if (!channel_config[1]) {
725 g_strfreev(channel_config);
726 return SR_OK;
727 }
728
729 parameters = g_strsplit_set(channel_config[1], ":;", -1);
730 if (parameters[0] && parameters[0][0]) {
731 range = 0;
732 if (parameters[1])
733 range = g_ascii_strtod(parameters[1], NULL);
734
735 param = parameters[0];
736 if (!g_ascii_strncasecmp(param, "Resistance", 10) ||
737 !g_ascii_strncasecmp(param, "Ohm", 3) ||
738 !g_ascii_strncasecmp(param, "W", 1) ||
739 !g_ascii_strncasecmp(param, "R", 1)) {
740 if (shared_in_use) {
741 ret = SR_ERR;
742 goto err_free_parameters;
743 }
744 ret = configure_channel2_resistance(sdi, range);
745 if (ret != SR_OK)
746 goto err_free_parameters;
747 } else if (!g_ascii_strncasecmp(param, "Diode", 5) ||
748 !g_ascii_strncasecmp(param, "D", 1)) {
749 if (shared_in_use) {
750 ret = SR_ERR;
751 goto err_free_parameters;
752 }
753 ret = configure_channel2_diode(sdi, range);
754 if (ret != SR_OK)
755 goto err_free_parameters;
756 } else if (!g_ascii_strncasecmp(param, "Aux", 3) ||
757 !g_ascii_strncasecmp(param, "LV", 2)) {
758 if (shared_in_use) {
759 ret = SR_ERR;
760 goto err_free_parameters;
761 }
762 ret = configure_channel2_auxv(sdi, range);
763 if (ret != SR_OK)
764 goto err_free_parameters;
765 } else if (!g_ascii_strncasecmp(param, "T", 1) ||
766 !g_ascii_strncasecmp(param, "K", 1)) {
767 ret = configure_channel2_temperature(sdi, range);
768 if (ret != SR_OK)
769 goto err_free_parameters;
770 } else if (!g_ascii_strncasecmp(param, "V", 1) ||
771 !param[0]) {
772 ret = configure_channel2_voltage(sdi, range);
773 if (ret != SR_OK)
774 goto err_free_parameters;
775 } else {
776 sr_info("Unrecognized mode for CH2: %s.", param);
777 ret = configure_channel2_voltage(sdi, range);
778 if (ret != SR_OK)
779 goto err_free_parameters;
780 }
781
782 if (parameters[1] && parameters[2]) {
783 param = parameters[2];
784 if (!g_ascii_strcasecmp(param, "RMS") ||
785 !g_ascii_strcasecmp(param, "AC")) {
786 ret = set_channel2_rms(sdi);
787 if (ret != SR_OK)
788 goto err_free_parameters;
789 } else if (!g_ascii_strcasecmp(param, "Buffer") ||
790 !g_ascii_strcasecmp(param, "Samples")) {
791 ret = set_channel2_buffer(sdi);
792 if (ret != SR_OK)
793 goto err_free_parameters;
794 } else {
795 ret = set_channel2_mean(sdi);
796 if (ret != SR_OK)
797 goto err_free_parameters;
798 }
799 }
800 }
801 g_strfreev(parameters);
802
803 g_strfreev(channel_config);
804 return SR_OK;
805
806err_free_parameters:
807 g_strfreev(parameters);
808err_free_channel_config:
809 g_strfreev(channel_config);
810 return ret;
811}
812
813static int dev_open(struct sr_dev_inst *sdi)
814{
815 int ret;
816
817 ret = mooshimeter_dmm_open(sdi);
818 if (ret != SR_OK)
819 return ret;
820
821 sdi->status = SR_ST_INACTIVE;
822
823 ret = mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER",
824 "SAMPLING:TRIGGER:OFF");
825 if (ret != SR_OK)
826 return ret;
827
828 ret = mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:RATE",
829 "SAMPLING:RATE", 125);
830 if (ret != SR_OK)
831 return ret;
832
833 ret = mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:DEPTH",
834 "SAMPLING:DEPTH", 64);
835 if (ret != SR_OK)
836 return ret;
837
838 /* Looks like these sometimes get set to 8, somehow? */
839 ret = mooshimeter_dmm_set_integer(sdi, "CH1:BUF_BPS", 24);
840 if (ret != SR_OK)
841 return ret;
842
843 ret = mooshimeter_dmm_set_integer(sdi, "CH2:BUF_BPS", 24);
844 if (ret != SR_OK)
845 return ret;
846
847 ret = configure_channel1_current(sdi, 0);
848 if (ret != SR_OK)
849 return ret;
850
851 ret = set_channel1_mean(sdi);
852 if (ret != SR_OK)
853 return ret;
854
855 ret = configure_channel2_voltage(sdi, 0);
856 if (ret != SR_OK)
857 return ret;
858
859 ret = set_channel2_mean(sdi);
860 if (ret != SR_OK)
861 return ret;
862
863 sdi->status = SR_ST_ACTIVE;
864
865 return SR_OK;
866}
867
868static int dev_close(struct sr_dev_inst *sdi)
869{
870 struct dev_context *devc = sdi->priv;
871
872 sdi->status = SR_ST_INACTIVE;
873
874 g_slist_free(devc->channel_meaning[0].channels);
875 devc->channel_meaning[0].channels = NULL;
876
877 g_slist_free(devc->channel_meaning[1].channels);
878 devc->channel_meaning[1].channels = NULL;
879
880 g_slist_free(devc->channel_meaning[2].channels);
881 devc->channel_meaning[2].channels = NULL;
882
883 return mooshimeter_dmm_close(sdi);
884}
885
886static int config_get(uint32_t key, GVariant **data,
887 const struct sr_dev_inst *sdi,
888 const struct sr_channel_group *cg)
889{
890 struct dev_context *devc = sdi->priv;
891 int ret;
892 float value;
893
894 (void)cg;
895
896 switch (key) {
897 case SR_CONF_SAMPLERATE:
898 ret = mooshimeter_dmm_get_chosen_number(sdi, "SAMPLING:RATE",
899 "SAMPLING:RATE", &value);
900 if (ret != SR_OK)
901 return ret;
902 *data = g_variant_new_uint64((guint64)value);
903 return SR_OK;
904 case SR_CONF_AVG_SAMPLES:
905 ret = mooshimeter_dmm_get_chosen_number(sdi, "SAMPLING:DEPTH",
906 "SAMPLING:DEPTH", &value);
907 if (ret != SR_OK)
908 return ret;
909 *data = g_variant_new_uint64((guint64)value);
910 return SR_OK;
911 case SR_CONF_CHANNEL_CONFIG:
912 return SR_ERR_NA;
913 default:
914 break;
915 }
916
917 return sr_sw_limits_config_get(&devc->limits, key, data);
918}
919
920static int config_set(uint32_t key, GVariant *data,
921 const struct sr_dev_inst *sdi,
922 const struct sr_channel_group *cg)
923{
924 struct dev_context *devc = sdi->priv;
925
926 (void)cg;
927
928 switch (key) {
929 case SR_CONF_SAMPLERATE:
930 return mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:RATE",
931 "SAMPLING:RATE", g_variant_get_uint64(data));
932 case SR_CONF_AVG_SAMPLES:
933 return mooshimeter_dmm_set_larger_number(sdi, "SAMPLING:DEPTH",
934 "SAMPLING:DEPTH", g_variant_get_uint64(data));
935 case SR_CONF_CHANNEL_CONFIG:
936 return apply_channel_config(sdi, g_variant_get_string(data, NULL));
937 default:
938 break;
939 }
940
941 return sr_sw_limits_config_set(&devc->limits, key, data);
942}
943
944static int config_list(uint32_t key, GVariant **data,
945 const struct sr_dev_inst *sdi,
946 const struct sr_channel_group *cg)
947{
948 int ret;
949 float *values;
950 size_t count;
951 uint64_t *integers;
952
953 switch (key) {
954 case SR_CONF_SAMPLERATE:
955 ret = mooshimeter_dmm_get_available_number_choices(sdi,
956 "SAMPLING:RATE", &values, &count);
957 if (ret != SR_OK)
958 return ret;
959 integers = g_malloc(sizeof(uint64_t) * count);
960 for (size_t i = 0; i < count; i++)
961 integers[i] = (uint64_t)values[i];
962 g_free(values);
963 *data = std_gvar_samplerates(integers, count);
964 g_free(integers);
965 return SR_OK;
966 case SR_CONF_AVG_SAMPLES:
967 ret = mooshimeter_dmm_get_available_number_choices(sdi,
968 "SAMPLING:DEPTH", &values, &count);
969 if (ret != SR_OK)
970 return ret;
971 integers = g_malloc(sizeof(uint64_t) * count);
972 for (size_t i = 0; i < count; i++)
973 integers[i] = (uint64_t)values[i];
974 g_free(values);
975 *data = std_gvar_array_u64(integers, count);
976 g_free(integers);
977 return SR_OK;
978 case SR_CONF_CHANNEL_CONFIG:
979 return SR_ERR_NA;
980 default:
981 break;
982 }
983
984 return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
985}
986
987static int dev_acquisition_start(const struct sr_dev_inst *sdi)
988{
989 struct dev_context *devc = sdi->priv;
990 int ret;
991
992 ret = mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER",
993 "SAMPLING:TRIGGER:CONTINUOUS");
994 if (ret)
995 return ret;
996
997 sr_sw_limits_acquisition_start(&devc->limits);
998 std_session_send_df_header(sdi);
999
1000 sr_session_source_add(sdi->session, -1, 0, 10000,
1001 mooshimeter_dmm_heartbeat, (void *)sdi);
1002
1003 /* The Bluetooth socket isn't exposed, so just poll for data. */
1004 sr_session_source_add(sdi->session, -2, 0, 50,
1005 mooshimeter_dmm_poll, (void *)sdi);
1006
1007 devc->enable_value_stream = TRUE;
1008
1009 return SR_OK;
1010}
1011
1012static int dev_acquisition_stop(struct sr_dev_inst *sdi)
1013{
1014 struct dev_context *devc = sdi->priv;
1015
1016 sr_session_source_remove(sdi->session, -1);
1017 sr_session_source_remove(sdi->session, -2);
1018 devc->enable_value_stream = FALSE;
1019
1020 mooshimeter_dmm_set_chooser(sdi, "SAMPLING:TRIGGER",
1021 "SAMPLING:TRIGGER:OFF");
1022
1023 return SR_OK;
1024}
1025
1026static struct sr_dev_driver mooshimeter_dmm_driver_info = {
1027 .name = "mooshimeter-dmm",
1028 .longname = "Mooshimeter DMM",
1029 .api_version = 1,
1030 .init = std_init,
1031 .cleanup = std_cleanup,
1032 .scan = scan,
1033 .dev_list = std_dev_list,
1034 .dev_clear = dev_clear,
1035 .config_get = config_get,
1036 .config_set = config_set,
1037 .config_list = config_list,
1038 .dev_open = dev_open,
1039 .dev_close = dev_close,
1040 .dev_acquisition_start = dev_acquisition_start,
1041 .dev_acquisition_stop = dev_acquisition_stop,
1042 .context = NULL,
1043};
1044SR_REGISTER_DEV_DRIVER(mooshimeter_dmm_driver_info);