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