]> sigrok.org Git - libsigrok.git/blob - src/hardware/rdtech-dps/protocol.c
f2b492f579ad361d3033702a034fd594d11d238f
[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         devc->curr_range = range ? 1 : 0;
262         rdtech_dps_update_multipliers(sdi);
263
264         return SR_OK;
265 }
266
267 /* Send a measured value to the session feed. */
268 static int send_value(const struct sr_dev_inst *sdi,
269         struct sr_channel *ch, float value,
270         enum sr_mq mq, enum sr_mqflag mqflags,
271         enum sr_unit unit, int digits)
272 {
273         struct sr_datafeed_packet packet;
274         struct sr_datafeed_analog analog;
275         struct sr_analog_encoding encoding;
276         struct sr_analog_meaning meaning;
277         struct sr_analog_spec spec;
278         int ret;
279
280         sr_analog_init(&analog, &encoding, &meaning, &spec, digits);
281         analog.meaning->channels = g_slist_append(NULL, ch);
282         analog.num_samples = 1;
283         analog.data = &value;
284         analog.meaning->mq = mq;
285         analog.meaning->mqflags = mqflags;
286         analog.meaning->unit = unit;
287
288         packet.type = SR_DF_ANALOG;
289         packet.payload = &analog;
290         ret = sr_session_send(sdi, &packet);
291
292         g_slist_free(analog.meaning->channels);
293
294         return ret;
295 }
296
297 /*
298  * Get the device's current state. Exhaustively, relentlessly.
299  * Concentrate all details of communication in the physical transport,
300  * register layout interpretation, and potential model dependency in
301  * this central spot, to simplify maintenance.
302  *
303  * TODO Optionally limit the transfer volume depending on caller's spec
304  * which detail level is desired? Is 10 registers each 16bits an issue
305  * when the UART bitrate is only 9600bps?
306  */
307 SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
308         struct rdtech_dps_state *state, enum rdtech_dps_state_context reason)
309 {
310         struct dev_context *devc;
311         struct sr_modbus_dev_inst *modbus;
312         gboolean get_config, get_init_state, get_curr_meas;
313         uint16_t registers[14];
314         int ret;
315         const uint8_t *rdptr;
316         uint16_t uset_raw, iset_raw, uout_raw, iout_raw, power_raw;
317         uint16_t reg_val, reg_state, out_state, ovpset_raw, ocpset_raw;
318         gboolean is_lock, is_out_enabled, is_reg_cc;
319         gboolean uses_ovp, uses_ocp;
320         uint16_t range;
321         float volt_target, curr_limit;
322         float ovp_threshold, ocp_threshold;
323         float curr_voltage, curr_current, curr_power;
324
325         if (!sdi || !sdi->priv || !sdi->conn)
326                 return SR_ERR_ARG;
327         devc = sdi->priv;
328         modbus = sdi->conn;
329         if (!state)
330                 return SR_ERR_ARG;
331
332         /* Determine the requested level of response detail. */
333         get_config = FALSE;
334         get_init_state = FALSE;
335         get_curr_meas = FALSE;
336         switch (reason) {
337         case ST_CTX_CONFIG:
338                 get_config = TRUE;
339                 get_init_state = TRUE;
340                 get_curr_meas = TRUE;
341                 break;
342         case ST_CTX_PRE_ACQ:
343                 get_init_state = TRUE;
344                 get_curr_meas = TRUE;
345                 break;
346         case ST_CTX_IN_ACQ:
347                 get_curr_meas = TRUE;
348                 break;
349         default:
350                 /* EMPTY */
351                 break;
352         }
353         /*
354          * TODO Make use of this information to reduce the transfer
355          * volume, especially on low bitrate serial connections. Though
356          * the device firmware's samplerate is probably more limiting
357          * than communication bandwidth is.
358          */
359         (void)get_config;
360         (void)get_init_state;
361         (void)get_curr_meas;
362
363         /*
364          * The model RD6012P has two voltage/current ranges. We set a
365          * default value here such that the compiler doesn't generate
366          * an uninitialized variable warning.
367          */
368         range = 0;
369
370         switch (devc->model->model_type) {
371         case MODEL_DPS:
372                 /*
373                  * Transfer a chunk of registers in a single call. It's
374                  * unfortunate that the model dependency and the sparse
375                  * register map force us to open code addresses, sizes,
376                  * and the sequence of the registers and how to interpret
377                  * their bit fields. But then this is not too unusual for
378                  * a hardware specific device driver ...
379                  */
380                 g_mutex_lock(&devc->rw_mutex);
381                 ret = rdtech_dps_read_holding_registers(modbus,
382                         REG_DPS_USET, REG_DPS_ENABLE - REG_DPS_USET + 1,
383                         registers);
384                 g_mutex_unlock(&devc->rw_mutex);
385                 if (ret != SR_OK)
386                         return ret;
387
388                 /* Interpret the registers' values. */
389                 rdptr = (const void *)registers;
390                 uset_raw = read_u16be_inc(&rdptr);
391                 volt_target = uset_raw / devc->voltage_multiplier;
392                 iset_raw = read_u16be_inc(&rdptr);
393                 curr_limit = iset_raw / devc->current_multiplier;
394                 uout_raw = read_u16be_inc(&rdptr);
395                 curr_voltage = uout_raw / devc->voltage_multiplier;
396                 iout_raw = read_u16be_inc(&rdptr);
397                 curr_current = iout_raw / devc->current_multiplier;
398                 power_raw = read_u16be_inc(&rdptr);
399                 curr_power = power_raw / 100.0f;
400                 (void)read_u16be_inc(&rdptr); /* UIN */
401                 reg_val = read_u16be_inc(&rdptr); /* LOCK */
402                 is_lock = reg_val != 0;
403                 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
404                 uses_ovp = reg_val == STATE_OVP;
405                 uses_ocp = reg_val == STATE_OCP;
406                 reg_state = read_u16be_inc(&rdptr); /* CV_CC */
407                 is_reg_cc = reg_state == MODE_CC;
408                 out_state = read_u16be_inc(&rdptr); /* ENABLE */
409                 is_out_enabled = out_state != 0;
410
411                 /* Transfer another chunk of registers in a single call. */
412                 g_mutex_lock(&devc->rw_mutex);
413                 ret = rdtech_dps_read_holding_registers(modbus,
414                         PRE_DPS_OVPSET, 2, registers);
415                 g_mutex_unlock(&devc->rw_mutex);
416                 if (ret != SR_OK)
417                         return ret;
418
419                 /* Interpret the second registers chunk's values. */
420                 rdptr = (const void *)registers;
421                 ovpset_raw = read_u16be_inc(&rdptr); /* PRE OVPSET */
422                 ovp_threshold = ovpset_raw * devc->voltage_multiplier;
423                 ocpset_raw = read_u16be_inc(&rdptr); /* PRE OCPSET */
424                 ocp_threshold = ocpset_raw * devc->current_multiplier;
425
426                 break;
427
428         case MODEL_RD:
429                 /* Retrieve a set of adjacent registers. */
430                 g_mutex_lock(&devc->rw_mutex);
431                 ret = rdtech_dps_read_holding_registers(modbus,
432                         REG_RD_VOLT_TGT,
433                         devc->model->n_ranges > 1
434                                 ? REG_RD_RANGE - REG_RD_VOLT_TGT + 1
435                                 : REG_RD_ENABLE - REG_RD_VOLT_TGT + 1,
436                         registers);
437                 g_mutex_unlock(&devc->rw_mutex);
438                 if (ret != SR_OK)
439                         return ret;
440
441                 /* Interpret the registers' raw content. */
442                 rdptr = (const void *)registers;
443                 uset_raw = read_u16be_inc(&rdptr); /* USET */
444                 volt_target = uset_raw / devc->voltage_multiplier;
445                 iset_raw = read_u16be_inc(&rdptr); /* ISET */
446                 curr_limit = iset_raw / devc->current_multiplier;
447                 uout_raw = read_u16be_inc(&rdptr); /* UOUT */
448                 curr_voltage = uout_raw / devc->voltage_multiplier;
449                 iout_raw = read_u16be_inc(&rdptr); /* IOUT */
450                 curr_current = iout_raw / devc->current_multiplier;
451                 (void)read_u16be_inc(&rdptr); /* ENERGY */
452                 power_raw = read_u16be_inc(&rdptr); /* POWER */
453                 curr_power = power_raw / 100.0f;
454                 (void)read_u16be_inc(&rdptr); /* VOLT_IN */
455                 (void)read_u16be_inc(&rdptr);
456                 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
457                 uses_ovp = reg_val == STATE_OVP;
458                 uses_ocp = reg_val == STATE_OCP;
459                 reg_state = read_u16be_inc(&rdptr); /* REGULATION */
460                 is_reg_cc = reg_state == MODE_CC;
461                 out_state = read_u16be_inc(&rdptr); /* ENABLE */
462                 is_out_enabled = out_state != 0;
463                 if (devc->model->n_ranges > 1) {
464                         (void)read_u16be_inc(&rdptr); /* PRESET */
465                         range = read_u16be_inc(&rdptr) ? 1 : 0; /* RANGE */
466                 }
467
468                 /* Retrieve a set of adjacent registers. */
469                 g_mutex_lock(&devc->rw_mutex);
470                 ret = rdtech_dps_read_holding_registers(modbus,
471                         REG_RD_OVP_THR, 2, registers);
472                 g_mutex_unlock(&devc->rw_mutex);
473                 if (ret != SR_OK)
474                         return ret;
475
476                 /* Interpret the registers' raw content. */
477                 rdptr = (const void *)registers;
478                 ovpset_raw = read_u16be_inc(&rdptr); /* OVP THR */
479                 ovp_threshold = ovpset_raw / devc->voltage_multiplier;
480                 ocpset_raw = read_u16be_inc(&rdptr); /* OCP THR */
481                 ocp_threshold = ocpset_raw / devc->current_multiplier;
482
483                 /* Details which we cannot query from the device. */
484                 is_lock = FALSE;
485
486                 break;
487
488         default:
489                 /* ShouldNotHappen(TM). Probe should have failed. */
490                 return SR_ERR_ARG;
491         }
492
493         /*
494          * Store gathered details in the high level container.
495          *
496          * TODO Make use of the caller's context. The register access
497          * code path above need not have gathered every detail in every
498          * invocation.
499          */
500         memset(state, 0, sizeof(*state));
501         state->lock = is_lock;
502         state->mask |= STATE_LOCK;
503         state->output_enabled = is_out_enabled;
504         state->mask |= STATE_OUTPUT_ENABLED;
505         state->regulation_cc = is_reg_cc;
506         state->mask |= STATE_REGULATION_CC;
507         state->protect_ovp = uses_ovp;
508         state->mask |= STATE_PROTECT_OVP;
509         state->protect_ocp = uses_ocp;
510         state->mask |= STATE_PROTECT_OCP;
511         state->protect_enabled = TRUE;
512         state->mask |= STATE_PROTECT_ENABLED;
513         state->voltage_target = volt_target;
514         state->mask |= STATE_VOLTAGE_TARGET;
515         state->current_limit = curr_limit;
516         state->mask |= STATE_CURRENT_LIMIT;
517         state->ovp_threshold = ovp_threshold;
518         state->mask |= STATE_OVP_THRESHOLD;
519         state->ocp_threshold = ocp_threshold;
520         state->mask |= STATE_OCP_THRESHOLD;
521         state->voltage = curr_voltage;
522         state->mask |= STATE_VOLTAGE;
523         state->current = curr_current;
524         state->mask |= STATE_CURRENT;
525         state->power = curr_power;
526         state->mask |= STATE_POWER;
527         if (devc->model->n_ranges > 1) {
528                 state->range = range;
529                 state->mask |= STATE_RANGE;
530         }
531
532         return SR_OK;
533 }
534
535 /* Setup device's parameters. Selectively, from caller specs. */
536 SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
537         struct rdtech_dps_state *state)
538 {
539         struct dev_context *devc;
540         uint16_t reg_value;
541         int ret;
542
543         if (!sdi || !sdi->priv || !sdi->conn)
544                 return SR_ERR_ARG;
545         devc = sdi->priv;
546         if (!state)
547                 return SR_ERR_ARG;
548
549         /* Only a subset of known values is settable. */
550         if (state->mask & STATE_OUTPUT_ENABLED) {
551                 reg_value = state->output_enabled ? 1 : 0;
552                 switch (devc->model->model_type) {
553                 case MODEL_DPS:
554                         ret = rdtech_dps_set_reg(sdi, REG_DPS_ENABLE, reg_value);
555                         if (ret != SR_OK)
556                                 return ret;
557                         break;
558                 case MODEL_RD:
559                         ret = rdtech_rd_set_reg(sdi, REG_RD_ENABLE, reg_value);
560                         if (ret != SR_OK)
561                                 return ret;
562                         break;
563                 default:
564                         return SR_ERR_ARG;
565                 }
566         }
567         if (state->mask & STATE_VOLTAGE_TARGET) {
568                 reg_value = state->voltage_target * devc->voltage_multiplier;
569                 switch (devc->model->model_type) {
570                 case MODEL_DPS:
571                         ret = rdtech_dps_set_reg(sdi, REG_DPS_USET, reg_value);
572                         if (ret != SR_OK)
573                                 return ret;
574                         break;
575                 case MODEL_RD:
576                         ret = rdtech_rd_set_reg(sdi, REG_RD_VOLT_TGT, reg_value);
577                         if (ret != SR_OK)
578                                 return ret;
579                         break;
580                 default:
581                         return SR_ERR_ARG;
582                 }
583         }
584         if (state->mask & STATE_CURRENT_LIMIT) {
585                 reg_value = state->current_limit * devc->current_multiplier;
586                 switch (devc->model->model_type) {
587                 case MODEL_DPS:
588                         ret = rdtech_dps_set_reg(sdi, REG_DPS_ISET, reg_value);
589                         if (ret != SR_OK)
590                                 return ret;
591                         break;
592                 case MODEL_RD:
593                         ret = rdtech_rd_set_reg(sdi, REG_RD_CURR_LIM, reg_value);
594                         if (ret != SR_OK)
595                                 return ret;
596                         break;
597                 default:
598                         return SR_ERR_ARG;
599                 }
600         }
601         if (state->mask & STATE_OVP_THRESHOLD) {
602                 reg_value = state->ovp_threshold * devc->voltage_multiplier;
603                 switch (devc->model->model_type) {
604                 case MODEL_DPS:
605                         ret = rdtech_dps_set_reg(sdi, PRE_DPS_OVPSET, reg_value);
606                         if (ret != SR_OK)
607                                 return ret;
608                         break;
609                 case MODEL_RD:
610                         ret = rdtech_rd_set_reg(sdi, REG_RD_OVP_THR, reg_value);
611                         if (ret != SR_OK)
612                                 return ret;
613                         break;
614                 default:
615                         return SR_ERR_ARG;
616                 }
617         }
618         if (state->mask & STATE_OCP_THRESHOLD) {
619                 reg_value = state->ocp_threshold * devc->current_multiplier;
620                 switch (devc->model->model_type) {
621                 case MODEL_DPS:
622                         ret = rdtech_dps_set_reg(sdi, PRE_DPS_OCPSET, reg_value);
623                         if (ret != SR_OK)
624                                 return ret;
625                         break;
626                 case MODEL_RD:
627                         ret = rdtech_rd_set_reg(sdi, REG_RD_OCP_THR, reg_value);
628                         if (ret != SR_OK)
629                                 return ret;
630                         break;
631                 default:
632                         return SR_ERR_ARG;
633                 }
634         }
635         if (state->mask & STATE_LOCK) {
636                 switch (devc->model->model_type) {
637                 case MODEL_DPS:
638                         reg_value = state->lock ? 1 : 0;
639                         ret = rdtech_dps_set_reg(sdi, REG_DPS_LOCK, reg_value);
640                         if (ret != SR_OK)
641                                 return ret;
642                         break;
643                 case MODEL_RD:
644                         /* Do nothing, _and_ silently succeed. */
645                         break;
646                 default:
647                         return SR_ERR_ARG;
648                 }
649         }
650         if (state->mask & STATE_RANGE) {
651                 reg_value = state->range;
652                 switch (devc->model->model_type) {
653                 case MODEL_DPS:
654                         /* DPS models don't support current ranges at all. */
655                         if (reg_value > 0)
656                                 return SR_ERR_ARG;
657                         break;
658                 case MODEL_RD:
659                         /*
660                          * Need not set the range when the device only
661                          * supports a single fixed range.
662                          */
663                         if (devc->model->n_ranges == 1)
664                                 return SR_OK;
665                         ret = rdtech_rd_set_reg(sdi, REG_RD_RANGE, reg_value);
666                         if (ret != SR_OK)
667                                 return ret;
668                         /*
669                          * Immediately update internal state outside of
670                          * an acquisition. Assume that in-acquisition
671                          * activity will update internal state. This is
672                          * essential for meta package emission.
673                          */
674                         if (!devc->acquisition_started) {
675                                 devc->curr_range = reg_value ? 1 : 0;
676                                 rdtech_dps_update_multipliers(sdi);
677                         }
678                         break;
679                 default:
680                         return SR_ERR_ARG;
681                 }
682         }
683
684         return SR_OK;
685 }
686
687 /* Get the current state when acquisition starts. */
688 SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi)
689 {
690         struct dev_context *devc;
691         struct rdtech_dps_state state;
692         int ret;
693
694         if (!sdi || !sdi->priv)
695                 return SR_ERR_ARG;
696         devc = sdi->priv;
697         devc->acquisition_started = TRUE;
698
699         ret = rdtech_dps_get_state(sdi, &state, ST_CTX_PRE_ACQ);
700         if (ret != SR_OK)
701                 return ret;
702
703         if (state.mask & STATE_PROTECT_OVP)
704                 devc->curr_ovp_state = state.protect_ovp;
705         if (state.mask & STATE_PROTECT_OCP)
706                 devc->curr_ocp_state = state.protect_ocp;
707         if (state.mask & STATE_REGULATION_CC)
708                 devc->curr_cc_state = state.regulation_cc;
709         if (state.mask & STATE_OUTPUT_ENABLED)
710                 devc->curr_out_state = state.output_enabled;
711         if (state.mask & STATE_RANGE) {
712                 devc->curr_range = state.range;
713                 rdtech_dps_update_multipliers(sdi);
714         }
715
716         return SR_OK;
717 }
718
719 /* Get measurements, track state changes during acquisition. */
720 SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
721 {
722         struct sr_dev_inst *sdi;
723         struct dev_context *devc;
724         struct rdtech_dps_state state;
725         int ret;
726         struct sr_channel *ch;
727         const char *regulation_text, *range_text;
728
729         (void)fd;
730         (void)revents;
731
732         sdi = cb_data;
733         if (!sdi)
734                 return TRUE;
735         devc = sdi->priv;
736
737         /* Get the device's current state. */
738         ret = rdtech_dps_get_state(sdi, &state, ST_CTX_IN_ACQ);
739         if (ret != SR_OK)
740                 return ret;
741
742
743         /* Submit measurement data to the session feed. */
744         std_session_send_df_frame_begin(sdi);
745         ch = g_slist_nth_data(sdi->channels, 0);
746         send_value(sdi, ch, state.voltage,
747                 SR_MQ_VOLTAGE, SR_MQFLAG_DC, SR_UNIT_VOLT,
748                 devc->model->ranges[devc->curr_range].voltage_digits);
749         ch = g_slist_nth_data(sdi->channels, 1);
750         send_value(sdi, ch, state.current,
751                 SR_MQ_CURRENT, SR_MQFLAG_DC, SR_UNIT_AMPERE,
752                 devc->model->ranges[devc->curr_range].current_digits);
753         ch = g_slist_nth_data(sdi->channels, 2);
754         send_value(sdi, ch, state.power,
755                 SR_MQ_POWER, 0, SR_UNIT_WATT, 2);
756         std_session_send_df_frame_end(sdi);
757
758         /* Check for state changes. */
759         if (devc->curr_ovp_state != state.protect_ovp) {
760                 (void)sr_session_send_meta(sdi,
761                         SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE,
762                         g_variant_new_boolean(state.protect_ovp));
763                 devc->curr_ovp_state = state.protect_ovp;
764         }
765         if (devc->curr_ocp_state != state.protect_ocp) {
766                 (void)sr_session_send_meta(sdi,
767                         SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE,
768                         g_variant_new_boolean(state.protect_ocp));
769                 devc->curr_ocp_state = state.protect_ocp;
770         }
771         if (devc->curr_cc_state != state.regulation_cc) {
772                 regulation_text = state.regulation_cc ? "CC" : "CV";
773                 (void)sr_session_send_meta(sdi, SR_CONF_REGULATION,
774                         g_variant_new_string(regulation_text));
775                 devc->curr_cc_state = state.regulation_cc;
776         }
777         if (devc->curr_out_state != state.output_enabled) {
778                 (void)sr_session_send_meta(sdi, SR_CONF_ENABLED,
779                         g_variant_new_boolean(state.output_enabled));
780                 devc->curr_out_state = state.output_enabled;
781         }
782         if (devc->curr_range != state.range) {
783                 range_text = devc->model->ranges[state.range].range_str;
784                 (void)sr_session_send_meta(sdi, SR_CONF_RANGE,
785                         g_variant_new_string(range_text));
786                 devc->curr_range = state.range;
787                 rdtech_dps_update_multipliers(sdi);
788         }
789
790         /* Check optional acquisition limits. */
791         sr_sw_limits_update_samples_read(&devc->limits, 1);
792         if (sr_sw_limits_check(&devc->limits)) {
793                 sr_dev_acquisition_stop(sdi);
794                 return TRUE;
795         }
796
797         return TRUE;
798 }