]> sigrok.org Git - libsigrok.git/blob - src/hardware/rdtech-dps/api.c
79dac48b2b4a3e28f904a606c86b336145a0ad66
[libsigrok.git] / src / hardware / rdtech-dps / api.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2018 James Churchill <pelrun@gmail.com>
5  * Copyright (C) 2019 Frank Stettner <frank-stettner@gmx.net>
6  * Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@gmx.net>
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "config.h"
23
24 #include <string.h>
25
26 #include "protocol.h"
27
28 static const uint32_t scanopts[] = {
29         SR_CONF_CONN,
30         SR_CONF_SERIALCOMM,
31         SR_CONF_MODBUSADDR,
32 };
33
34 static const uint32_t drvopts[] = {
35         SR_CONF_POWER_SUPPLY,
36 };
37
38 static const uint32_t devopts[] = {
39         SR_CONF_CONTINUOUS,
40         SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
41         SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
42         SR_CONF_VOLTAGE | SR_CONF_GET,
43         SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
44         SR_CONF_CURRENT | SR_CONF_GET,
45         SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
46         SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
47         SR_CONF_REGULATION | SR_CONF_GET,
48         SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
49         SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
50         SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
51         SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
52 };
53
54 static const uint32_t devopts_w_range[] = {
55         SR_CONF_CONTINUOUS,
56         SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
57         SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
58         SR_CONF_VOLTAGE | SR_CONF_GET,
59         SR_CONF_VOLTAGE_TARGET | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
60         SR_CONF_CURRENT | SR_CONF_GET,
61         SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
62         SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
63         SR_CONF_REGULATION | SR_CONF_GET,
64         SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE | SR_CONF_GET,
65         SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
66         SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE | SR_CONF_GET,
67         SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD | SR_CONF_GET | SR_CONF_SET,
68         SR_CONF_RANGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
69 };
70
71 /* range name, max current/voltage/power, current/voltage digits */
72 static struct rdtech_dps_range ranges_dps3005[] = {
73         {  "5A",  5, 30,  160, 3, 2 }
74 };
75
76 static struct rdtech_dps_range ranges_dps5005[] = {
77         {  "5A",  5, 50,  250, 3, 2 }
78 };
79
80 static struct rdtech_dps_range ranges_dps5015[] = {
81         { "15A", 15, 50,  750, 2, 2 }
82 };
83
84 static struct rdtech_dps_range ranges_dps5020[] = {
85         { "20A", 20, 50, 1000, 2, 2 }
86 };
87
88 static struct rdtech_dps_range ranges_dps8005[] = {
89         {  "5A",  5, 80,  408, 3, 2 }
90 };
91
92 static struct rdtech_dps_range ranges_rd6006[] = {
93         {  "6A",  6, 60,  360, 3, 2 }
94 };
95
96 static struct rdtech_dps_range ranges_rd6006p[] = {
97         {  "6A",  6, 60,  360, 4, 3 }
98 };
99
100 static struct rdtech_dps_range ranges_rd6012[] = {
101         { "12A", 12, 60,  720, 2, 2 }
102 };
103
104 /*
105  * Current digits for RD6012P is 4 for the 6A range (RTU reg  20 = 0) and
106  * 3 for the 12A range (RTU reg 20 = 1)
107  */
108 static struct rdtech_dps_range ranges_rd6012p[] = {
109         {  "6A",  6, 60,  360, 4, 3 },
110         { "12A", 12, 60,  720, 3, 3 }
111 };
112
113 static struct rdtech_dps_range ranges_rd6018[] = {
114         { "18A", 18, 60, 1080, 2, 2 }
115 };
116
117 static struct rdtech_dps_range ranges_rd6024[] = {
118         { "24A", 24, 60, 1440, 2, 2 }
119 };
120
121 /* model ID, model name, range */
122 static const struct rdtech_dps_model supported_models[] = {
123         { MODEL_DPS, 3005, "DPS3005", ARRAY_AND_SIZE(ranges_dps3005) },
124         { MODEL_DPS, 5005, "DPS5005", ARRAY_AND_SIZE(ranges_dps5005) },
125         { MODEL_DPS, 5205, "DPH5005", ARRAY_AND_SIZE(ranges_dps5005) },
126         { MODEL_DPS, 5015, "DPS5015", ARRAY_AND_SIZE(ranges_dps5015) },
127         { MODEL_DPS, 5020, "DPS5020", ARRAY_AND_SIZE(ranges_dps5020) },
128         { MODEL_DPS, 8005, "DPS8005", ARRAY_AND_SIZE(ranges_dps8005) },
129         /*
130          * Specs for models RD60nn taken from the 2020.12.2 instruction manual,
131          * specs for RD6006P from the 2021.2.26 (english) manual,
132          * specs for RD6012P from the 2021.10.26 (english) manual,
133          * and specs for RD6024P from the 2021.1.7 (english) manual.
134          */
135         { MODEL_RD, 60061, "RD6006" , ARRAY_AND_SIZE(ranges_rd6006)  },
136         { MODEL_RD, 60062, "RD6006" , ARRAY_AND_SIZE(ranges_rd6006)  },
137         { MODEL_RD, 60065, "RD6006P", ARRAY_AND_SIZE(ranges_rd6006p) },
138         { MODEL_RD, 60121, "RD6012" , ARRAY_AND_SIZE(ranges_rd6012)  },
139         { MODEL_RD, 60125, "RD6012P", ARRAY_AND_SIZE(ranges_rd6012p) },
140         { MODEL_RD, 60181, "RD6018" , ARRAY_AND_SIZE(ranges_rd6018)  },
141         { MODEL_RD, 60241, "RD6024" , ARRAY_AND_SIZE(ranges_rd6024)  },
142 };
143
144 static struct sr_dev_driver rdtech_dps_driver_info;
145 static struct sr_dev_driver rdtech_rd_driver_info;
146
147 static struct sr_dev_inst *probe_device(struct sr_modbus_dev_inst *modbus,
148         enum rdtech_dps_model_type model_type)
149 {
150         static const char *type_prefix[] = {
151                 [MODEL_DPS] = "DPS",
152                 [MODEL_RD]  = "RD",
153         };
154
155         uint16_t id, version;
156         uint32_t serno;
157         int ret;
158         const struct rdtech_dps_model *model, *supported;
159         size_t i;
160         struct sr_dev_inst *sdi;
161         struct dev_context *devc;
162
163         ret = rdtech_dps_get_model_version(modbus,
164                 model_type, &id, &version, &serno);
165         sr_dbg("probe: ret %d, type %s, model %u, vers %u, snr %u.",
166                 ret, type_prefix[model_type], id, version, serno);
167         if (ret != SR_OK)
168                 return NULL;
169         model = NULL;
170         for (i = 0; i < ARRAY_SIZE(supported_models); i++) {
171                 supported = &supported_models[i];
172                 if (model_type != supported->model_type)
173                         continue;
174                 if (id != supported->id)
175                         continue;
176                 model = supported;
177                 break;
178         }
179         if (!model) {
180                 sr_err("Unknown model: %s%u.", type_prefix[model_type], id);
181                 return NULL;
182         }
183
184         sdi = g_malloc0(sizeof(*sdi));
185         sdi->status = SR_ST_INACTIVE;
186         sdi->vendor = g_strdup("RDTech");
187         switch (model_type) {
188         case MODEL_DPS:
189                 sdi->model = g_strdup(model->name);
190                 sdi->version = g_strdup_printf("v%u", version);
191                 sdi->driver = &rdtech_dps_driver_info;
192                 break;
193         case MODEL_RD:
194                 sdi->model = g_strdup(model->name);
195                 sdi->version = g_strdup_printf("v%u.%u",
196                         version / 100, version % 100);
197                 if (serno)
198                         sdi->serial_num = g_strdup_printf("%u", serno);
199                 sdi->driver = &rdtech_rd_driver_info;
200                 break;
201         default:
202                 sr_err("Programming error, unhandled DPS/DPH/RD device type.");
203                 g_free(sdi->vendor);
204                 g_free(sdi);
205                 return NULL;
206         }
207         sdi->conn = modbus;
208         sdi->inst_type = SR_INST_MODBUS;
209
210         sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
211         sr_channel_new(sdi, 1, SR_CHANNEL_ANALOG, TRUE, "I");
212         sr_channel_new(sdi, 2, SR_CHANNEL_ANALOG, TRUE, "P");
213
214         devc = g_malloc0(sizeof(*devc));
215         sr_sw_limits_init(&devc->limits);
216         devc->model = model;
217         sdi->priv = devc;
218         ret = rdtech_dps_update_range(sdi);
219         if (ret != SR_OK)
220                 return NULL;
221
222         return sdi;
223 }
224
225 static struct sr_dev_inst *probe_device_dps(struct sr_modbus_dev_inst *modbus)
226 {
227         return probe_device(modbus, MODEL_DPS);
228 }
229
230 static struct sr_dev_inst *probe_device_rd(struct sr_modbus_dev_inst *modbus)
231 {
232         return probe_device(modbus, MODEL_RD);
233 }
234
235 static int config_compare(gconstpointer a, gconstpointer b)
236 {
237         const struct sr_config *ac = a, *bc = b;
238         return ac->key != bc->key;
239 }
240
241 static GSList *scan(struct sr_dev_driver *di, GSList *options,
242         enum rdtech_dps_model_type model_type)
243 {
244         static const char *default_serialcomm_dps = "9600/8n1";
245         static const char *default_serialcomm_rd = "115200/8n1";
246
247         struct sr_config default_serialcomm = {
248                 .key = SR_CONF_SERIALCOMM,
249                 .data = NULL,
250         };
251         struct sr_config default_modbusaddr = {
252                 .key = SR_CONF_MODBUSADDR,
253                 .data = g_variant_new_uint64(1),
254         };
255         GSList *opts, *devices;
256         const char *serialcomm;
257         struct sr_dev_inst *(*probe_func)(struct sr_modbus_dev_inst *modbus);
258
259         /* TODO See why di->context isn't available yet at this time. */
260         serialcomm = NULL;
261         probe_func = NULL;
262         if (di->context == &rdtech_dps_driver_info || model_type == MODEL_DPS) {
263                 serialcomm = default_serialcomm_dps;
264                 probe_func = probe_device_dps;
265         }
266         if (di->context == &rdtech_rd_driver_info || model_type == MODEL_RD) {
267                 serialcomm = default_serialcomm_rd;
268                 probe_func = probe_device_rd;
269         }
270         if (!probe_func)
271                 return NULL;
272         if (serialcomm && *serialcomm)
273                 default_serialcomm.data = g_variant_new_string(serialcomm);
274
275         opts = options;
276         if (!g_slist_find_custom(options, &default_serialcomm, config_compare))
277                 opts = g_slist_prepend(opts, &default_serialcomm);
278         if (!g_slist_find_custom(options, &default_modbusaddr, config_compare))
279                 opts = g_slist_prepend(opts, &default_modbusaddr);
280
281         devices = sr_modbus_scan(di->context, opts, probe_func);
282
283         while (opts != options)
284                 opts = g_slist_delete_link(opts, opts);
285         g_variant_unref(default_serialcomm.data);
286         g_variant_unref(default_modbusaddr.data);
287
288         return devices;
289 }
290
291 static GSList *scan_dps(struct sr_dev_driver *di, GSList *options)
292 {
293         return scan(di, options, MODEL_DPS);
294 }
295
296 static GSList *scan_rd(struct sr_dev_driver *di, GSList *options)
297 {
298         return scan(di, options, MODEL_RD);
299 }
300
301 static int dev_open(struct sr_dev_inst *sdi)
302 {
303         struct sr_modbus_dev_inst *modbus;
304         struct rdtech_dps_state state;
305         int ret;
306
307         modbus = sdi->conn;
308         if (sr_modbus_open(modbus) < 0)
309                 return SR_ERR;
310
311         memset(&state, 0, sizeof(state));
312         state.lock = TRUE;
313         state.mask |= STATE_LOCK;
314         ret = rdtech_dps_set_state(sdi, &state);
315         if (ret != SR_OK)
316                 return ret;
317
318         return SR_OK;
319 }
320
321 static int dev_close(struct sr_dev_inst *sdi)
322 {
323         struct sr_modbus_dev_inst *modbus;
324         struct rdtech_dps_state state;
325
326         modbus = sdi->conn;
327         if (!modbus)
328                 return SR_ERR_BUG;
329
330         memset(&state, 0, sizeof(state));
331         state.lock = FALSE;
332         state.mask |= STATE_LOCK;
333         (void)rdtech_dps_set_state(sdi, &state);
334
335         return sr_modbus_close(modbus);
336 }
337
338 static int config_get(uint32_t key, GVariant **data,
339         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
340 {
341         struct dev_context *devc;
342         struct rdtech_dps_state state;
343         int ret;
344         const char *cc_text;
345
346         (void)cg;
347
348         devc = sdi->priv;
349
350         ret = SR_OK;
351         switch (key) {
352         case SR_CONF_LIMIT_SAMPLES:
353         case SR_CONF_LIMIT_MSEC:
354                 ret = sr_sw_limits_config_get(&devc->limits, key, data);
355                 break;
356         case SR_CONF_ENABLED:
357                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
358                 if (ret != SR_OK)
359                         return ret;
360                 if (!(state.mask & STATE_OUTPUT_ENABLED))
361                         return SR_ERR_DATA;
362                 *data = g_variant_new_boolean(state.output_enabled);
363                 break;
364         case SR_CONF_REGULATION:
365                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
366                 if (ret != SR_OK)
367                         return ret;
368                 if (!(state.mask & STATE_REGULATION_CC))
369                         return SR_ERR_DATA;
370                 cc_text = state.regulation_cc ? "CC" : "CV";
371                 *data = g_variant_new_string(cc_text);
372                 break;
373         case SR_CONF_VOLTAGE:
374                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
375                 if (ret != SR_OK)
376                         return ret;
377                 if (!(state.mask & STATE_VOLTAGE))
378                         return SR_ERR_DATA;
379                 *data = g_variant_new_double(state.voltage);
380                 break;
381         case SR_CONF_VOLTAGE_TARGET:
382                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
383                 if (ret != SR_OK)
384                         return ret;
385                 if (!(state.mask & STATE_VOLTAGE_TARGET))
386                         return SR_ERR_DATA;
387                 *data = g_variant_new_double(state.voltage_target);
388                 break;
389         case SR_CONF_CURRENT:
390                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
391                 if (ret != SR_OK)
392                         return ret;
393                 if (!(state.mask & STATE_CURRENT))
394                         return SR_ERR_DATA;
395                 *data = g_variant_new_double(state.current);
396                 break;
397         case SR_CONF_CURRENT_LIMIT:
398                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
399                 if (ret != SR_OK)
400                         return ret;
401                 if (!(state.mask & STATE_CURRENT_LIMIT))
402                         return SR_ERR_DATA;
403                 *data = g_variant_new_double(state.current_limit);
404                 break;
405         case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
406                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
407                 if (ret != SR_OK)
408                         return ret;
409                 if (!(state.mask & STATE_PROTECT_ENABLED))
410                         return SR_ERR_DATA;
411                 *data = g_variant_new_boolean(state.protect_enabled);
412                 break;
413         case SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE:
414                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
415                 if (ret != SR_OK)
416                         return ret;
417                 if (!(state.mask & STATE_PROTECT_OVP))
418                         return SR_ERR_DATA;
419                 *data = g_variant_new_boolean(state.protect_ovp);
420                 break;
421         case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
422                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
423                 if (ret != SR_OK)
424                         return ret;
425                 if (!(state.mask & STATE_OUTPUT_ENABLED))
426                         return SR_ERR_DATA;
427                 *data = g_variant_new_double(state.ovp_threshold);
428                 break;
429         case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
430                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
431                 if (ret != SR_OK)
432                         return ret;
433                 if (!(state.mask & STATE_PROTECT_ENABLED))
434                         return SR_ERR_DATA;
435                 *data = g_variant_new_boolean(state.protect_enabled);
436                 break;
437         case SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE:
438                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
439                 if (ret != SR_OK)
440                         return ret;
441                 if (!(state.mask & STATE_PROTECT_OCP))
442                         return SR_ERR_DATA;
443                 *data = g_variant_new_boolean(state.protect_ocp);
444                 break;
445         case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
446                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
447                 if (ret != SR_OK)
448                         return ret;
449                 if (!(state.mask & STATE_OCP_THRESHOLD))
450                         return SR_ERR_DATA;
451                 *data = g_variant_new_double(state.ocp_threshold);
452                 break;
453         case SR_CONF_RANGE:
454                 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_CONFIG);
455                 if (ret != SR_OK)
456                         return ret;
457                 if (!(state.mask & STATE_RANGE))
458                         return SR_ERR_DATA;
459                 *data = g_variant_new_string(
460                         devc->model->ranges[state.range].range_str);
461                 break;
462         default:
463                 return SR_ERR_NA;
464         }
465
466         return ret;
467 }
468
469 static int config_set(uint32_t key, GVariant *data,
470         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
471 {
472         struct dev_context *devc;
473         struct rdtech_dps_state state;
474         const char *range_str;
475         size_t i;
476
477         (void)cg;
478
479         devc = sdi->priv;
480         memset(&state, 0, sizeof(state));
481
482         switch (key) {
483         case SR_CONF_LIMIT_SAMPLES:
484         case SR_CONF_LIMIT_MSEC:
485                 return sr_sw_limits_config_set(&devc->limits, key, data);
486         case SR_CONF_ENABLED:
487                 state.output_enabled = g_variant_get_boolean(data);
488                 state.mask |= STATE_OUTPUT_ENABLED;
489                 return rdtech_dps_set_state(sdi, &state);
490         case SR_CONF_VOLTAGE_TARGET:
491                 state.voltage_target = g_variant_get_double(data);
492                 state.mask |= STATE_VOLTAGE_TARGET;
493                 return rdtech_dps_set_state(sdi, &state);
494         case SR_CONF_CURRENT_LIMIT:
495                 state.current_limit = g_variant_get_double(data);
496                 state.mask |= STATE_CURRENT_LIMIT;
497                 return rdtech_dps_set_state(sdi, &state);
498         case SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD:
499                 state.ovp_threshold = g_variant_get_double(data);
500                 state.mask |= STATE_OVP_THRESHOLD;
501                 return rdtech_dps_set_state(sdi, &state);
502         case SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD:
503                 state.ocp_threshold = g_variant_get_double(data);
504                 state.mask |= STATE_OCP_THRESHOLD;
505                 return rdtech_dps_set_state(sdi, &state);
506         case SR_CONF_RANGE:
507                 range_str = g_variant_get_string(data, NULL);
508                 for (i = 0; i < devc->model->n_ranges; ++i) {
509                         if (g_strcmp0(devc->model->ranges[i].range_str, range_str)
510                                         == 0) {
511                                 state.range = i;
512                                 state.mask |= STATE_RANGE;
513                                 return rdtech_dps_set_state(sdi, &state);
514                         }
515                 }
516                 return SR_ERR_NA;
517         default:
518                 return SR_ERR_NA;
519         }
520
521         return SR_OK;
522 }
523
524 static int config_list(uint32_t key, GVariant **data,
525         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
526 {
527         struct dev_context *devc;
528         struct rdtech_dps_range *range;
529         GVariantBuilder gvb;
530         size_t i;
531
532         devc = (sdi) ? sdi->priv : NULL;
533
534         switch (key) {
535         case SR_CONF_SCAN_OPTIONS:
536         case SR_CONF_DEVICE_OPTIONS:
537                 if (devc && (devc->model->n_ranges > 1))
538                         return STD_CONFIG_LIST(key, data, sdi, cg, scanopts,
539                                 drvopts, devopts_w_range);
540                 else
541                         return STD_CONFIG_LIST(key, data, sdi, cg, scanopts,
542                                 drvopts, devopts);
543         case SR_CONF_VOLTAGE_TARGET:
544                 rdtech_dps_update_range(sdi);
545                 range = &devc->model->ranges[devc->curr_range];
546                 *data = std_gvar_min_max_step(0.0, range->max_voltage,
547                         1 / devc->voltage_multiplier);
548                 break;
549         case SR_CONF_CURRENT_LIMIT:
550                 rdtech_dps_update_range(sdi);
551                 range = &devc->model->ranges[devc->curr_range];
552                 *data = std_gvar_min_max_step(0.0, range->max_current,
553                         1 / devc->current_multiplier);
554                 break;
555         case SR_CONF_RANGE:
556                 g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
557                 for (i = 0; i < devc->model->n_ranges; ++i)
558                         g_variant_builder_add(&gvb, "s",
559                                 devc->model->ranges[i].range_str);
560                 *data = g_variant_builder_end(&gvb);
561                 break;
562         default:
563                 return SR_ERR_NA;
564         }
565
566         return SR_OK;
567 }
568
569 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
570 {
571         struct dev_context *devc;
572         struct sr_modbus_dev_inst *modbus;
573         int ret;
574
575         modbus = sdi->conn;
576         devc = sdi->priv;
577
578         /* Seed internal state from current data. */
579         ret = rdtech_dps_seed_receive(sdi);
580         if (ret != SR_OK)
581                 return ret;
582
583         /* Register the periodic data reception callback. */
584         ret = sr_modbus_source_add(sdi->session, modbus, G_IO_IN, 10,
585                         rdtech_dps_receive_data, (void *)sdi);
586         if (ret != SR_OK)
587                 return ret;
588
589         sr_sw_limits_acquisition_start(&devc->limits);
590         std_session_send_df_header(sdi);
591
592         return SR_OK;
593 }
594
595 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
596 {
597         struct sr_modbus_dev_inst *modbus;
598
599         std_session_send_df_end(sdi);
600
601         modbus = sdi->conn;
602         sr_modbus_source_remove(sdi->session, modbus);
603
604         return SR_OK;
605 }
606
607 static struct sr_dev_driver rdtech_dps_driver_info = {
608         .name = "rdtech-dps",
609         .longname = "RDTech DPS/DPH series power supply",
610         .api_version = 1,
611         .init = std_init,
612         .cleanup = std_cleanup,
613         .scan = scan_dps,
614         .dev_list = std_dev_list,
615         .dev_clear = std_dev_clear,
616         .config_get = config_get,
617         .config_set = config_set,
618         .config_list = config_list,
619         .dev_open = dev_open,
620         .dev_close = dev_close,
621         .dev_acquisition_start = dev_acquisition_start,
622         .dev_acquisition_stop = dev_acquisition_stop,
623         .context = NULL,
624 };
625 SR_REGISTER_DEV_DRIVER(rdtech_dps_driver_info);
626
627 static struct sr_dev_driver rdtech_rd_driver_info = {
628         .name = "rdtech-rd",
629         .longname = "RDTech RD series power supply",
630         .api_version = 1,
631         .init = std_init,
632         .cleanup = std_cleanup,
633         .scan = scan_rd,
634         .dev_list = std_dev_list,
635         .dev_clear = std_dev_clear,
636         .config_get = config_get,
637         .config_set = config_set,
638         .config_list = config_list,
639         .dev_open = dev_open,
640         .dev_close = dev_close,
641         .dev_acquisition_start = dev_acquisition_start,
642         .dev_acquisition_stop = dev_acquisition_stop,
643         .context = NULL,
644 };
645 SR_REGISTER_DEV_DRIVER(rdtech_rd_driver_info);