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