]> sigrok.org Git - libsigrok.git/blob - src/hardware/mooshimeter-dmm/api.c
output/csv: use intermediate time_t var, silence compiler warning
[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, 0);
115         if (ret < 0)
116                 goto err;
117
118         ret = sr_bt_connect_ble(desc);
119         if (ret < 0)
120                 goto err;
121         sr_bt_disconnect(desc);
122
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
135 err:
136         sr_bt_desc_free(desc);
137         return NULL;
138 }
139
140 static 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
159 static 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
169 static 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
179 static 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
188 static 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
198 static 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
208 static 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
218 static 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
225 static 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
256 static 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
263 static 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
294 static 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
301 static 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
336 static 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
343 static 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
378 static 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
385 static 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
420 static 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
427 static 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
458 static 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
465 static 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
496 static 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
503 static 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
538 static 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
545 static 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
580 static 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
587 static 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  */
644 static 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
806 err_free_parameters:
807         g_strfreev(parameters);
808 err_free_channel_config:
809         g_strfreev(channel_config);
810         return ret;
811 }
812
813 static 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
868 static 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
886 static 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
920 static 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
944 static 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
987 static 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
1012 static 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
1026 static 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 };
1044 SR_REGISTER_DEV_DRIVER(mooshimeter_dmm_driver_info);