]> sigrok.org Git - libsigrok.git/blob - src/hardware/rdtech-dps/protocol.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / rdtech-dps / protocol.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 <math.h>
25 #include <string.h>
26
27 #include "protocol.h"
28
29 /* These are the Modbus RTU registers for the DPS family of devices. */
30 enum rdtech_dps_register {
31         REG_DPS_USET       = 0x00, /* Mirror of 0x50 */
32         REG_DPS_ISET       = 0x01, /* Mirror of 0x51 */
33         REG_DPS_UOUT       = 0x02,
34         REG_DPS_IOUT       = 0x03,
35         REG_DPS_POWER      = 0x04,
36         REG_DPS_UIN        = 0x05,
37         REG_DPS_LOCK       = 0x06,
38         REG_DPS_PROTECT    = 0x07,
39         REG_DPS_CV_CC      = 0x08,
40         REG_DPS_ENABLE     = 0x09,
41         REG_DPS_BACKLIGHT  = 0x0A, /* Mirror of 0x55 */
42         REG_DPS_MODEL      = 0x0B,
43         REG_DPS_VERSION    = 0x0C,
44
45         REG_DPS_PRESET     = 0x23, /* Loads a preset into preset 0. */
46
47         /*
48          * Add (preset * 0x10) to each of the following, for preset 1-9.
49          * Preset 0 regs below are the active output settings.
50          */
51         PRE_DPS_USET       = 0x50,
52         PRE_DPS_ISET       = 0x51,
53         PRE_DPS_OVPSET     = 0x52,
54         PRE_DPS_OCPSET     = 0x53,
55         PRE_DPS_OPPSET     = 0x54,
56         PRE_DPS_BACKLIGHT  = 0x55,
57         PRE_DPS_DISABLE    = 0x56, /* Disable output if 0 is copied here from a preset (1 is no change). */
58         PRE_DPS_BOOT       = 0x57, /* Enable output at boot if 1. */
59 };
60 #define PRE_DPS_STRIDE 0x10
61
62 enum rdtech_dps_protect_state {
63         STATE_NORMAL = 0,
64         STATE_OVP    = 1,
65         STATE_OCP    = 2,
66         STATE_OPP    = 3,
67 };
68
69 enum rdtech_dps_regulation_mode {
70         MODE_CV      = 0,
71         MODE_CC      = 1,
72 };
73
74 /*
75  * These are the Modbus RTU registers for the RD family of devices.
76  * Some registers are device specific, like REG_RD_RANGE of RD6012P
77  * which could be battery related in other devices.
78  */
79 enum rdtech_rd_register {
80         REG_RD_MODEL = 0, /* u16 */
81         REG_RD_SERIAL = 1, /* u32 */
82         REG_RD_FIRMWARE = 3, /* u16 */
83         REG_RD_TEMP_INT = 4, /* 2x u16 */
84         REG_RD_TEMP_INT_F = 6, /* 2x u16 */
85         REG_RD_VOLT_TGT = 8, /* u16 */
86         REG_RD_CURR_LIM = 9, /* u16 */
87         REG_RD_VOLTAGE = 10, /* u16 */
88         REG_RD_CURRENT = 11, /* u16 */
89         REG_RD_ENERGY = 12, /* u16 */
90         REG_RD_POWER = 13, /* u16 */
91         REG_RD_VOLT_IN = 14, /* u16 */
92         REG_RD_PROTECT = 16, /* u16 */
93         REG_RD_REGULATION = 17, /* u16 */
94         REG_RD_ENABLE = 18, /* u16 */
95         REG_RD_PRESET = 19, /* u16 */
96         REG_RD_RANGE = 20, /* u16 */
97         /*
98          * Battery at 32 == 0x20 pp:
99          * Mode, voltage, temperature, capacity, energy.
100          */
101         /*
102          * Date/time at 48 == 0x30 pp:
103          * Year, month, day, hour, minute, second.
104          */
105         /* Backlight at 72 == 0x48. */
106         REG_RD_OVP_THR = 82, /* 0x52 */
107         REG_RD_OCP_THR = 83, /* 0x53 */
108         /* One "live" slot and 9 "memory" positions. */
109         REG_RD_START_MEM = 84, /* 0x54 */
110 };
111
112 /* Retries failed modbus read attempts for improved reliability. */
113 static int rdtech_dps_read_holding_registers(struct sr_modbus_dev_inst *modbus,
114         int address, int nb_registers, uint16_t *registers)
115 {
116         size_t retries;
117         int ret;
118
119         retries = 3;
120         while (retries--) {
121                 ret = sr_modbus_read_holding_registers(modbus,
122                         address, nb_registers, registers);
123                 if (ret == SR_OK)
124                         return ret;
125         }
126
127         return ret;
128 }
129
130 /* Set one 16bit register. LE format for DPS devices. */
131 static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi,
132         uint16_t address, uint16_t value)
133 {
134         struct dev_context *devc;
135         struct sr_modbus_dev_inst *modbus;
136         uint16_t registers[1];
137         int ret;
138         uint8_t *wrptr;
139
140         devc = sdi->priv;
141         modbus = sdi->conn;
142
143         wrptr = (void *)registers;
144         write_u16be(wrptr, value);
145
146         g_mutex_lock(&devc->rw_mutex);
147         ret = sr_modbus_write_multiple_registers(modbus, address,
148                 ARRAY_SIZE(registers), registers);
149         g_mutex_unlock(&devc->rw_mutex);
150
151         return ret;
152 }
153
154 /* Set one 16bit register. BE format for RD devices. */
155 static int rdtech_rd_set_reg(const struct sr_dev_inst *sdi,
156         uint16_t address, uint16_t value)
157 {
158         struct dev_context *devc;
159         struct sr_modbus_dev_inst *modbus;
160         uint16_t registers[1];
161         int ret;
162         uint8_t *wrptr;
163
164         devc = sdi->priv;
165         modbus = sdi->conn;
166
167         wrptr = (void *)registers;
168         write_u16be(wrptr, value);
169
170         g_mutex_lock(&devc->rw_mutex);
171         ret = sr_modbus_write_multiple_registers(modbus, address,
172                 ARRAY_SIZE(registers), registers);
173         g_mutex_unlock(&devc->rw_mutex);
174
175         return ret;
176 }
177
178 /* Get DPS model number and firmware version from a connected device. */
179 SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
180         enum rdtech_dps_model_type model_type,
181         uint16_t *model, uint16_t *version, uint32_t *serno)
182 {
183         uint16_t registers[4];
184         int ret;
185         const uint8_t *rdptr;
186
187         /*
188          * No mutex here because when the routine executes then the
189          * device instance was not created yet (probe phase).
190          */
191         switch (model_type) {
192         case MODEL_DPS:
193                 /* Get the MODEL and VERSION registers. */
194                 ret = rdtech_dps_read_holding_registers(modbus,
195                         REG_DPS_MODEL, 2, registers);
196                 if (ret != SR_OK)
197                         return ret;
198                 rdptr = (void *)registers;
199                 *model = read_u16be_inc(&rdptr);
200                 *version = read_u16be_inc(&rdptr);
201                 *serno = 0;
202                 sr_info("RDTech DPS/DPH model: %u version: %u",
203                         *model, *version);
204                 return SR_OK;
205         case MODEL_RD:
206                 /* Get the MODEL, SERIAL, and FIRMWARE registers. */
207                 ret = rdtech_dps_read_holding_registers(modbus,
208                         REG_RD_MODEL, 4, registers);
209                 if (ret != SR_OK)
210                         return ret;
211                 rdptr = (void *)registers;
212                 *model = read_u16be_inc(&rdptr);
213                 *serno = read_u32be_inc(&rdptr);
214                 *version = read_u16be_inc(&rdptr);
215                 sr_info("RDTech RD model: %u version: %u, serno %u",
216                         *model, *version, *serno);
217                 return SR_OK;
218         default:
219                 sr_err("Unexpected RDTech PSU device type. Programming error?");
220                 return SR_ERR_ARG;
221         }
222         /* UNREACH */
223 }
224
225 SR_PRIV void rdtech_dps_update_multipliers(const struct sr_dev_inst *sdi)
226 {
227         struct dev_context *devc;
228         const struct rdtech_dps_range *range;
229
230         devc = sdi->priv;
231         range = &devc->model->ranges[devc->curr_range];
232         devc->current_multiplier = pow(10.0, range->current_digits);
233         devc->voltage_multiplier = pow(10.0, range->voltage_digits);
234 }
235
236 /*
237  * Determine range of connected device. Don't do anything once
238  * acquisition has started (since the range will then be tracked).
239  */
240 SR_PRIV int rdtech_dps_update_range(const struct sr_dev_inst *sdi)
241 {
242         struct dev_context *devc;
243         uint16_t range;
244         int ret;
245
246         devc = sdi->priv;
247
248         /*
249          * Only update range if there are multiple ranges and data
250          * acquisition hasn't started.
251          */
252         if (devc->model->n_ranges <= 1 || devc->acquisition_started)
253                 return SR_OK;
254         if (devc->model->model_type != MODEL_RD)
255                 return SR_ERR;
256
257         ret = rdtech_dps_read_holding_registers(sdi->conn,
258                 REG_RD_RANGE, 1, &range);
259         if (ret != SR_OK)
260                 return ret;
261         range = range ? 1 : 0;
262         devc->curr_range = range;
263         rdtech_dps_update_multipliers(sdi);
264
265         return SR_OK;
266 }
267
268 /* Send a measured value to the session feed. */
269 static int send_value(const struct sr_dev_inst *sdi,
270         struct sr_channel *ch, float value,
271         enum sr_mq mq, enum sr_mqflag mqflags,
272         enum sr_unit unit, int digits)
273 {
274         struct sr_datafeed_packet packet;
275         struct sr_datafeed_analog analog;
276         struct sr_analog_encoding encoding;
277         struct sr_analog_meaning meaning;
278         struct sr_analog_spec spec;
279         int ret;
280
281         sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
282         analog.meaning->channels = g_slist_append(NULL, ch);
283         analog.num_samples = 1;
284         analog.data = &value;
285         analog.meaning->mq = mq;
286         analog.meaning->mqflags = mqflags;
287         analog.meaning->unit = unit;
288
289         packet.type = SR_DF_ANALOG;
290         packet.payload = &analog;
291         ret = sr_session_send(sdi, &packet);
292
293         g_slist_free(analog.meaning->channels);
294
295         return ret;
296 }
297
298 /*
299  * Get the device's current state. Exhaustively, relentlessly.
300  * Concentrate all details of communication in the physical transport,
301  * register layout interpretation, and potential model dependency in
302  * this central spot, to simplify maintenance.
303  *
304  * TODO Optionally limit the transfer volume depending on caller's spec
305  * which detail level is desired? Is 10 registers each 16bits an issue
306  * when the UART bitrate is only 9600bps?
307  */
308 SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
309         struct rdtech_dps_state *state, enum rdtech_dps_state_context reason)
310 {
311         struct dev_context *devc;
312         struct sr_modbus_dev_inst *modbus;
313         gboolean get_config, get_init_state, get_curr_meas;
314         uint16_t registers[14];
315         int ret;
316         const uint8_t *rdptr;
317         uint16_t uset_raw, iset_raw, uout_raw, iout_raw, power_raw;
318         uint16_t reg_val, reg_state, out_state, ovpset_raw, ocpset_raw;
319         gboolean is_lock, is_out_enabled, is_reg_cc;
320         gboolean uses_ovp, uses_ocp;
321         gboolean have_range;
322         uint16_t range;
323         float volt_target, curr_limit;
324         float ovp_threshold, ocp_threshold;
325         float curr_voltage, curr_current, curr_power;
326
327         if (!sdi || !sdi->priv || !sdi->conn)
328                 return SR_ERR_ARG;
329         devc = sdi->priv;
330         modbus = sdi->conn;
331         if (!state)
332                 return SR_ERR_ARG;
333
334         /* Determine the requested level of response detail. */
335         get_config = FALSE;
336         get_init_state = FALSE;
337         get_curr_meas = FALSE;
338         switch (reason) {
339         case ST_CTX_CONFIG:
340                 get_config = TRUE;
341                 get_init_state = TRUE;
342                 get_curr_meas = TRUE;
343                 break;
344         case ST_CTX_PRE_ACQ:
345                 get_init_state = TRUE;
346                 get_curr_meas = TRUE;
347                 break;
348         case ST_CTX_IN_ACQ:
349                 get_curr_meas = TRUE;
350                 break;
351         default:
352                 /* EMPTY */
353                 break;
354         }
355         /*
356          * TODO Make use of this information to reduce the transfer
357          * volume, especially on low bitrate serial connections. Though
358          * the device firmware's samplerate is probably more limiting
359          * than communication bandwidth is.
360          */
361         (void)get_config;
362         (void)get_init_state;
363         (void)get_curr_meas;
364
365         have_range = devc->model->n_ranges > 1;
366         if (!have_range)
367                 range = 0;
368
369         switch (devc->model->model_type) {
370         case MODEL_DPS:
371                 /*
372                  * Transfer a chunk of registers in a single call. It's
373                  * unfortunate that the model dependency and the sparse
374                  * register map force us to open code addresses, sizes,
375                  * and the sequence of the registers and how to interpret
376                  * their bit fields. But then this is not too unusual for
377                  * a hardware specific device driver ...
378                  */
379                 g_mutex_lock(&devc->rw_mutex);
380                 ret = rdtech_dps_read_holding_registers(modbus,
381                         REG_DPS_USET, REG_DPS_ENABLE - REG_DPS_USET + 1,
382                         registers);
383                 g_mutex_unlock(&devc->rw_mutex);
384                 if (ret != SR_OK)
385                         return ret;
386
387                 /* Interpret the registers' values. */
388                 rdptr = (const void *)registers;
389                 uset_raw = read_u16be_inc(&rdptr);
390                 volt_target = uset_raw / devc->voltage_multiplier;
391                 iset_raw = read_u16be_inc(&rdptr);
392                 curr_limit = iset_raw / devc->current_multiplier;
393                 uout_raw = read_u16be_inc(&rdptr);
394                 curr_voltage = uout_raw / devc->voltage_multiplier;
395                 iout_raw = read_u16be_inc(&rdptr);
396                 curr_current = iout_raw / devc->current_multiplier;
397                 power_raw = read_u16be_inc(&rdptr);
398                 curr_power = power_raw / 100.0f;
399                 (void)read_u16be_inc(&rdptr); /* UIN */
400                 reg_val = read_u16be_inc(&rdptr); /* LOCK */
401                 is_lock = reg_val != 0;
402                 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
403                 uses_ovp = reg_val == STATE_OVP;
404                 uses_ocp = reg_val == STATE_OCP;
405                 reg_state = read_u16be_inc(&rdptr); /* CV_CC */
406                 is_reg_cc = reg_state == MODE_CC;
407                 out_state = read_u16be_inc(&rdptr); /* ENABLE */
408                 is_out_enabled = out_state != 0;
409
410                 /* Transfer another chunk of registers in a single call. */
411                 g_mutex_lock(&devc->rw_mutex);
412                 ret = rdtech_dps_read_holding_registers(modbus,
413                         PRE_DPS_OVPSET, 2, registers);
414                 g_mutex_unlock(&devc->rw_mutex);
415                 if (ret != SR_OK)
416                         return ret;
417
418                 /* Interpret the second registers chunk's values. */
419                 rdptr = (const void *)registers;
420                 ovpset_raw = read_u16be_inc(&rdptr); /* PRE OVPSET */
421                 ovp_threshold = ovpset_raw * devc->voltage_multiplier;
422                 ocpset_raw = read_u16be_inc(&rdptr); /* PRE OCPSET */
423                 ocp_threshold = ocpset_raw * devc->current_multiplier;
424
425                 break;
426
427         case MODEL_RD:
428                 /* Retrieve a set of adjacent registers. */
429                 g_mutex_lock(&devc->rw_mutex);
430                 ret = rdtech_dps_read_holding_registers(modbus,
431                         REG_RD_VOLT_TGT,
432                         devc->model->n_ranges > 1
433                                 ? REG_RD_RANGE - REG_RD_VOLT_TGT + 1
434                                 : REG_RD_ENABLE - REG_RD_VOLT_TGT + 1,
435                         registers);
436                 g_mutex_unlock(&devc->rw_mutex);
437                 if (ret != SR_OK)
438                         return ret;
439
440                 /* Interpret the registers' raw content. */
441                 rdptr = (const void *)registers;
442                 uset_raw = read_u16be_inc(&rdptr); /* USET */
443                 volt_target = uset_raw / devc->voltage_multiplier;
444                 iset_raw = read_u16be_inc(&rdptr); /* ISET */
445                 curr_limit = iset_raw / devc->current_multiplier;
446                 uout_raw = read_u16be_inc(&rdptr); /* UOUT */
447                 curr_voltage = uout_raw / devc->voltage_multiplier;
448                 iout_raw = read_u16be_inc(&rdptr); /* IOUT */
449                 curr_current = iout_raw / devc->current_multiplier;
450                 (void)read_u16be_inc(&rdptr); /* ENERGY */
451                 power_raw = read_u16be_inc(&rdptr); /* POWER */
452                 curr_power = power_raw / 100.0f;
453                 (void)read_u16be_inc(&rdptr); /* VOLT_IN */
454                 (void)read_u16be_inc(&rdptr);
455                 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
456                 uses_ovp = reg_val == STATE_OVP;
457                 uses_ocp = reg_val == STATE_OCP;
458                 reg_state = read_u16be_inc(&rdptr); /* REGULATION */
459                 is_reg_cc = reg_state == MODE_CC;
460                 out_state = read_u16be_inc(&rdptr); /* ENABLE */
461                 is_out_enabled = out_state != 0;
462                 if (have_range) {
463                         (void)read_u16be_inc(&rdptr); /* PRESET */
464                         range = read_u16be_inc(&rdptr) ? 1 : 0; /* RANGE */
465                 }
466
467                 /* Retrieve a set of adjacent registers. */
468                 g_mutex_lock(&devc->rw_mutex);
469                 ret = rdtech_dps_read_holding_registers(modbus,
470                         REG_RD_OVP_THR, 2, registers);
471                 g_mutex_unlock(&devc->rw_mutex);
472                 if (ret != SR_OK)
473                         return ret;
474
475                 /* Interpret the registers' raw content. */
476                 rdptr = (const void *)registers;
477                 ovpset_raw = read_u16be_inc(&rdptr); /* OVP THR */
478                 ovp_threshold = ovpset_raw / devc->voltage_multiplier;
479                 ocpset_raw = read_u16be_inc(&rdptr); /* OCP THR */
480                 ocp_threshold = ocpset_raw / devc->current_multiplier;
481
482                 /* Details which we cannot query from the device. */
483                 is_lock = FALSE;
484
485                 break;
486
487         default:
488                 /* ShouldNotHappen(TM). Probe should have failed. */
489                 return SR_ERR_ARG;
490         }
491
492         /*
493          * Store gathered details in the high level container.
494          *
495          * TODO Make use of the caller's context. The register access
496          * code path above need not have gathered every detail in every
497          * invocation.
498          */
499         memset(state, 0, sizeof(*state));
500         state->lock = is_lock;
501         state->mask |= STATE_LOCK;
502         state->output_enabled = is_out_enabled;
503         state->mask |= STATE_OUTPUT_ENABLED;
504         state->regulation_cc = is_reg_cc;
505         state->mask |= STATE_REGULATION_CC;
506         state->protect_ovp = uses_ovp;
507         state->mask |= STATE_PROTECT_OVP;
508         state->protect_ocp = uses_ocp;
509         state->mask |= STATE_PROTECT_OCP;
510         state->protect_enabled = TRUE;
511         state->mask |= STATE_PROTECT_ENABLED;
512         state->voltage_target = volt_target;
513         state->mask |= STATE_VOLTAGE_TARGET;
514         state->current_limit = curr_limit;
515         state->mask |= STATE_CURRENT_LIMIT;
516         state->ovp_threshold = ovp_threshold;
517         state->mask |= STATE_OVP_THRESHOLD;
518         state->ocp_threshold = ocp_threshold;
519         state->mask |= STATE_OCP_THRESHOLD;
520         state->voltage = curr_voltage;
521         state->mask |= STATE_VOLTAGE;
522         state->current = curr_current;
523         state->mask |= STATE_CURRENT;
524         state->power = curr_power;
525         state->mask |= STATE_POWER;
526         if (have_range) {
527                 state->range = range;
528                 state->mask |= STATE_RANGE;
529         }
530
531         return SR_OK;
532 }
533
534 /* Setup device's parameters. Selectively, from caller specs. */
535 SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
536         struct rdtech_dps_state *state)
537 {
538         struct dev_context *devc;
539         uint16_t reg_value;
540         int ret;
541
542         if (!sdi || !sdi->priv || !sdi->conn)
543                 return SR_ERR_ARG;
544         devc = sdi->priv;
545         if (!state)
546                 return SR_ERR_ARG;
547
548         /* Only a subset of known values is settable. */
549         if (state->mask & STATE_OUTPUT_ENABLED) {
550                 reg_value = state->output_enabled ? 1 : 0;
551                 switch (devc->model->model_type) {
552                 case MODEL_DPS:
553                         ret = rdtech_dps_set_reg(sdi, REG_DPS_ENABLE, reg_value);
554                         if (ret != SR_OK)
555                                 return ret;
556                         break;
557                 case MODEL_RD:
558                         ret = rdtech_rd_set_reg(sdi, REG_RD_ENABLE, reg_value);
559                         if (ret != SR_OK)
560                                 return ret;
561                         break;
562                 default:
563                         return SR_ERR_ARG;
564                 }
565         }
566         if (state->mask & STATE_VOLTAGE_TARGET) {
567                 reg_value = state->voltage_target * devc->voltage_multiplier;
568                 switch (devc->model->model_type) {
569                 case MODEL_DPS:
570                         ret = rdtech_dps_set_reg(sdi, REG_DPS_USET, reg_value);
571                         if (ret != SR_OK)
572                                 return ret;
573                         break;
574                 case MODEL_RD:
575                         ret = rdtech_rd_set_reg(sdi, REG_RD_VOLT_TGT, reg_value);
576                         if (ret != SR_OK)
577                                 return ret;
578                         break;
579                 default:
580                         return SR_ERR_ARG;
581                 }
582         }
583         if (state->mask & STATE_CURRENT_LIMIT) {
584                 reg_value = state->current_limit * devc->current_multiplier;
585                 switch (devc->model->model_type) {
586                 case MODEL_DPS:
587                         ret = rdtech_dps_set_reg(sdi, REG_DPS_ISET, reg_value);
588                         if (ret != SR_OK)
589                                 return ret;
590                         break;
591                 case MODEL_RD:
592                         ret = rdtech_rd_set_reg(sdi, REG_RD_CURR_LIM, reg_value);
593                         if (ret != SR_OK)
594                                 return ret;
595                         break;
596                 default:
597                         return SR_ERR_ARG;
598                 }
599         }
600         if (state->mask & STATE_OVP_THRESHOLD) {
601                 reg_value = state->ovp_threshold * devc->voltage_multiplier;
602                 switch (devc->model->model_type) {
603                 case MODEL_DPS:
604                         ret = rdtech_dps_set_reg(sdi, PRE_DPS_OVPSET, reg_value);
605                         if (ret != SR_OK)
606                                 return ret;
607                         break;
608                 case MODEL_RD:
609                         ret = rdtech_rd_set_reg(sdi, REG_RD_OVP_THR, reg_value);
610                         if (ret != SR_OK)
611                                 return ret;
612                         break;
613                 default:
614                         return SR_ERR_ARG;
615                 }
616         }
617         if (state->mask & STATE_OCP_THRESHOLD) {
618                 reg_value = state->ocp_threshold * devc->current_multiplier;
619                 switch (devc->model->model_type) {
620                 case MODEL_DPS:
621                         ret = rdtech_dps_set_reg(sdi, PRE_DPS_OCPSET, reg_value);
622                         if (ret != SR_OK)
623                                 return ret;
624                         break;
625                 case MODEL_RD:
626                         ret = rdtech_rd_set_reg(sdi, REG_RD_OCP_THR, reg_value);
627                         if (ret != SR_OK)
628                                 return ret;
629                         break;
630                 default:
631                         return SR_ERR_ARG;
632                 }
633         }
634         if (state->mask & STATE_LOCK) {
635                 switch (devc->model->model_type) {
636                 case MODEL_DPS:
637                         reg_value = state->lock ? 1 : 0;
638                         ret = rdtech_dps_set_reg(sdi, REG_DPS_LOCK, reg_value);
639                         if (ret != SR_OK)
640                                 return ret;
641                         break;
642                 case MODEL_RD:
643                         /* Do nothing, _and_ silently succeed. */
644                         break;
645                 default:
646                         return SR_ERR_ARG;
647                 }
648         }
649         if (state->mask & STATE_RANGE) {
650                 reg_value = state->range;
651                 switch (devc->model->model_type) {
652                 case MODEL_DPS:
653                         /* DPS models don't support current ranges at all. */
654                         if (reg_value > 0)
655                                 return SR_ERR_ARG;
656                         break;
657                 case MODEL_RD:
658                         /*
659                          * Reject unsupported range indices.
660                          * Need not set the range when the device only
661                          * supports a single fixed range.
662                          */
663                         if (reg_value >= devc->model->n_ranges)
664                                 return SR_ERR_NA;
665                         if (devc->model->n_ranges <= 1)
666                                 return SR_OK;
667                         ret = rdtech_rd_set_reg(sdi, REG_RD_RANGE, reg_value);
668                         if (ret != SR_OK)
669                                 return ret;
670                         /*
671                          * Immediately update internal state outside of
672                          * an acquisition. Assume that in-acquisition
673                          * activity will update internal state. This is
674                          * essential for meta package emission.
675                          */
676                         if (!devc->acquisition_started) {
677                                 devc->curr_range = reg_value;
678                                 rdtech_dps_update_multipliers(sdi);
679                         }
680                         break;
681                 default:
682                         return SR_ERR_ARG;
683                 }
684         }
685
686         return SR_OK;
687 }
688
689 /* Get the current state when acquisition starts. */
690 SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi)
691 {
692         struct dev_context *devc;
693         struct rdtech_dps_state state;
694         int ret;
695
696         if (!sdi || !sdi->priv)
697                 return SR_ERR_ARG;
698         devc = sdi->priv;
699
700         ret = rdtech_dps_get_state(sdi, &state, ST_CTX_PRE_ACQ);
701         if (ret != SR_OK)
702                 return ret;
703
704         if (state.mask & STATE_PROTECT_OVP)
705                 devc->curr_ovp_state = state.protect_ovp;
706         if (state.mask & STATE_PROTECT_OCP)
707                 devc->curr_ocp_state = state.protect_ocp;
708         if (state.mask & STATE_REGULATION_CC)
709                 devc->curr_cc_state = state.regulation_cc;
710         if (state.mask & STATE_OUTPUT_ENABLED)
711                 devc->curr_out_state = state.output_enabled;
712         if (state.mask & STATE_RANGE) {
713                 devc->curr_range = state.range;
714                 rdtech_dps_update_multipliers(sdi);
715         }
716
717         return SR_OK;
718 }
719
720 /* Get measurements, track state changes during acquisition. */
721 SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
722 {
723         struct sr_dev_inst *sdi;
724         struct dev_context *devc;
725         struct rdtech_dps_state state;
726         int ret;
727         struct sr_channel *ch;
728         const char *regulation_text, *range_text;
729
730         (void)fd;
731         (void)revents;
732
733         sdi = cb_data;
734         if (!sdi)
735                 return TRUE;
736         devc = sdi->priv;
737
738         /* Get the device's current state. */
739         ret = rdtech_dps_get_state(sdi, &state, ST_CTX_IN_ACQ);
740         if (ret != SR_OK)
741                 return ret;
742
743
744         /* Submit measurement data to the session feed. */
745         std_session_send_df_frame_begin(sdi);
746         ch = g_slist_nth_data(sdi->channels, 0);
747         send_value(sdi, ch, state.voltage,
748                 SR_MQ_VOLTAGE, SR_MQFLAG_DC, SR_UNIT_VOLT,
749                 devc->model->ranges[devc->curr_range].voltage_digits);
750         ch = g_slist_nth_data(sdi->channels, 1);
751         send_value(sdi, ch, state.current,
752                 SR_MQ_CURRENT, SR_MQFLAG_DC, SR_UNIT_AMPERE,
753                 devc->model->ranges[devc->curr_range].current_digits);
754         ch = g_slist_nth_data(sdi->channels, 2);
755         send_value(sdi, ch, state.power,
756                 SR_MQ_POWER, 0, SR_UNIT_WATT, 2);
757         std_session_send_df_frame_end(sdi);
758
759         /* Check for state changes. */
760         if (devc->curr_ovp_state != state.protect_ovp) {
761                 (void)sr_session_send_meta(sdi,
762                         SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE,
763                         g_variant_new_boolean(state.protect_ovp));
764                 devc->curr_ovp_state = state.protect_ovp;
765         }
766         if (devc->curr_ocp_state != state.protect_ocp) {
767                 (void)sr_session_send_meta(sdi,
768                         SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE,
769                         g_variant_new_boolean(state.protect_ocp));
770                 devc->curr_ocp_state = state.protect_ocp;
771         }
772         if (devc->curr_cc_state != state.regulation_cc) {
773                 regulation_text = state.regulation_cc ? "CC" : "CV";
774                 (void)sr_session_send_meta(sdi, SR_CONF_REGULATION,
775                         g_variant_new_string(regulation_text));
776                 devc->curr_cc_state = state.regulation_cc;
777         }
778         if (devc->curr_out_state != state.output_enabled) {
779                 (void)sr_session_send_meta(sdi, SR_CONF_ENABLED,
780                         g_variant_new_boolean(state.output_enabled));
781                 devc->curr_out_state = state.output_enabled;
782         }
783         if (devc->curr_range != state.range) {
784                 range_text = devc->model->ranges[state.range].range_str;
785                 (void)sr_session_send_meta(sdi, SR_CONF_RANGE,
786                         g_variant_new_string(range_text));
787                 devc->curr_range = state.range;
788                 rdtech_dps_update_multipliers(sdi);
789         }
790
791         /* Check optional acquisition limits. */
792         sr_sw_limits_update_samples_read(&devc->limits, 1);
793         if (sr_sw_limits_check(&devc->limits)) {
794                 sr_dev_acquisition_stop(sdi);
795                 return TRUE;
796         }
797
798         return TRUE;
799 }