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