]> sigrok.org Git - libsigrok.git/blame - src/hardware/rdtech-dps/protocol.c
rdtech-dps: address simple style issues
[libsigrok.git] / src / hardware / rdtech-dps / protocol.c
CommitLineData
0549416e
JC
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2018 James Churchill <pelrun@gmail.com>
7c0891b0 5 * Copyright (C) 2019 Frank Stettner <frank-stettner@gmx.net>
d7a4dad8 6 * Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@gmx.net>
0549416e
JC
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
5352598f 22#include "config.h"
d7a4dad8 23
02a4f485 24#include <math.h>
d7a4dad8
GS
25#include <string.h>
26
0549416e
JC
27#include "protocol.h"
28
62f3228b 29/* These are the Modbus RTU registers for the DPS family of devices. */
d7a4dad8 30enum rdtech_dps_register {
884ae8c0
GS
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. */
d7a4dad8
GS
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 */
884ae8c0
GS
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. */
d7a4dad8 59};
884ae8c0 60#define PRE_DPS_STRIDE 0x10
d7a4dad8
GS
61
62enum rdtech_dps_protect_state {
63 STATE_NORMAL = 0,
64 STATE_OVP = 1,
65 STATE_OCP = 2,
66 STATE_OPP = 3,
67};
68
69enum rdtech_dps_regulation_mode {
70 MODE_CV = 0,
71 MODE_CC = 1,
72};
73
02a4f485 74/*
62f3228b
GS
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.
02a4f485 78 */
884ae8c0
GS
79enum 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 */
02a4f485
MD
95 REG_RD_PRESET = 19, /* u16 */
96 REG_RD_RANGE = 20, /* u16 */
884ae8c0
GS
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
d7a4dad8
GS
112/* Retries failed modbus read attempts for improved reliability. */
113static int rdtech_dps_read_holding_registers(struct sr_modbus_dev_inst *modbus,
114 int address, int nb_registers, uint16_t *registers)
aff20941 115{
d7a4dad8
GS
116 size_t retries;
117 int ret;
aff20941 118
d7a4dad8 119 retries = 3;
f0f6b926 120 while (retries--) {
aff20941
FS
121 ret = sr_modbus_read_holding_registers(modbus,
122 address, nb_registers, registers);
d7a4dad8
GS
123 if (ret == SR_OK)
124 return ret;
f0f6b926 125 }
aff20941
FS
126
127 return ret;
128}
129
884ae8c0
GS
130/* Set one 16bit register. LE format for DPS devices. */
131static int rdtech_dps_set_reg(const struct sr_dev_inst *sdi,
132 uint16_t address, uint16_t value)
69b05583 133{
7c0891b0
FS
134 struct dev_context *devc;
135 struct sr_modbus_dev_inst *modbus;
69b05583 136 uint16_t registers[1];
7c0891b0 137 int ret;
884ae8c0 138 uint8_t *wrptr;
7c0891b0
FS
139
140 devc = sdi->priv;
141 modbus = sdi->conn;
142
884ae8c0 143 wrptr = (void *)registers;
a7e919a3 144 write_u16be(wrptr, value);
884ae8c0 145
7c0891b0 146 g_mutex_lock(&devc->rw_mutex);
884ae8c0
GS
147 ret = sr_modbus_write_multiple_registers(modbus, address,
148 ARRAY_SIZE(registers), registers);
7c0891b0 149 g_mutex_unlock(&devc->rw_mutex);
d7a4dad8 150
69b05583
JC
151 return ret;
152}
153
884ae8c0
GS
154/* Set one 16bit register. BE format for RD devices. */
155static int rdtech_rd_set_reg(const struct sr_dev_inst *sdi,
d7a4dad8 156 uint16_t address, uint16_t value)
69b05583 157{
7c0891b0
FS
158 struct dev_context *devc;
159 struct sr_modbus_dev_inst *modbus;
69b05583 160 uint16_t registers[1];
7c0891b0 161 int ret;
d7a4dad8 162 uint8_t *wrptr;
7c0891b0
FS
163
164 devc = sdi->priv;
165 modbus = sdi->conn;
166
d7a4dad8 167 wrptr = (void *)registers;
884ae8c0 168 write_u16be(wrptr, value);
d7a4dad8 169
7c0891b0 170 g_mutex_lock(&devc->rw_mutex);
d7a4dad8
GS
171 ret = sr_modbus_write_multiple_registers(modbus, address,
172 ARRAY_SIZE(registers), registers);
7c0891b0 173 g_mutex_unlock(&devc->rw_mutex);
d7a4dad8 174
7c0891b0 175 return ret;
69b05583
JC
176}
177
d7a4dad8 178/* Get DPS model number and firmware version from a connected device. */
69b05583 179SR_PRIV int rdtech_dps_get_model_version(struct sr_modbus_dev_inst *modbus,
884ae8c0
GS
180 enum rdtech_dps_model_type model_type,
181 uint16_t *model, uint16_t *version, uint32_t *serno)
69b05583 182{
884ae8c0 183 uint16_t registers[4];
69b05583 184 int ret;
d7a4dad8
GS
185 const uint8_t *rdptr;
186
7c0891b0 187 /*
884ae8c0
GS
188 * No mutex here because when the routine executes then the
189 * device instance was not created yet (probe phase).
7c0891b0 190 */
884ae8c0
GS
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;
a7e919a3
VA
199 *model = read_u16be_inc(&rdptr);
200 *version = read_u16be_inc(&rdptr);
884ae8c0
GS
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;
02a4f485 212 *model = read_u16be_inc(&rdptr);
884ae8c0
GS
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 */
69b05583
JC
223}
224
02a4f485
MD
225SR_PRIV void rdtech_dps_update_multipliers(const struct sr_dev_inst *sdi)
226{
227 struct dev_context *devc;
1ac04e6d 228 const struct rdtech_dps_range *range;
02a4f485
MD
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 */
240SR_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
d7a4dad8
GS
267/* Send a measured value to the session feed. */
268static 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)
69b05583
JC
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;
d7a4dad8 278 int ret;
69b05583
JC
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;
c9b187a6 285 analog.meaning->mqflags = mqflags;
69b05583 286 analog.meaning->unit = unit;
69b05583
JC
287
288 packet.type = SR_DF_ANALOG;
289 packet.payload = &analog;
d7a4dad8
GS
290 ret = sr_session_send(sdi, &packet);
291
69b05583 292 g_slist_free(analog.meaning->channels);
d7a4dad8
GS
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 */
307SR_PRIV int rdtech_dps_get_state(const struct sr_dev_inst *sdi,
7a78fd56 308 struct rdtech_dps_state *state, enum rdtech_dps_state_context reason)
d7a4dad8
GS
309{
310 struct dev_context *devc;
311 struct sr_modbus_dev_inst *modbus;
7a78fd56 312 gboolean get_config, get_init_state, get_curr_meas;
02a4f485 313 uint16_t registers[14];
d7a4dad8
GS
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;
02a4f485 320 uint16_t range;
d7a4dad8
GS
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
7a78fd56
GS
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
02a4f485
MD
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
884ae8c0
GS
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 ...
884ae8c0
GS
379 */
380 g_mutex_lock(&devc->rw_mutex);
1ac04e6d
GS
381 ret = rdtech_dps_read_holding_registers(modbus,
382 REG_DPS_USET, REG_DPS_ENABLE - REG_DPS_USET + 1,
383 registers);
884ae8c0
GS
384 g_mutex_unlock(&devc->rw_mutex);
385 if (ret != SR_OK)
386 return ret;
d7a4dad8 387
884ae8c0
GS
388 /* Interpret the registers' values. */
389 rdptr = (const void *)registers;
a7e919a3 390 uset_raw = read_u16be_inc(&rdptr);
884ae8c0 391 volt_target = uset_raw / devc->voltage_multiplier;
a7e919a3 392 iset_raw = read_u16be_inc(&rdptr);
884ae8c0 393 curr_limit = iset_raw / devc->current_multiplier;
a7e919a3 394 uout_raw = read_u16be_inc(&rdptr);
884ae8c0 395 curr_voltage = uout_raw / devc->voltage_multiplier;
a7e919a3 396 iout_raw = read_u16be_inc(&rdptr);
884ae8c0 397 curr_current = iout_raw / devc->current_multiplier;
a7e919a3 398 power_raw = read_u16be_inc(&rdptr);
884ae8c0 399 curr_power = power_raw / 100.0f;
a7e919a3
VA
400 (void)read_u16be_inc(&rdptr); /* UIN */
401 reg_val = read_u16be_inc(&rdptr); /* LOCK */
884ae8c0 402 is_lock = reg_val != 0;
a7e919a3 403 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
884ae8c0
GS
404 uses_ovp = reg_val == STATE_OVP;
405 uses_ocp = reg_val == STATE_OCP;
a7e919a3 406 reg_state = read_u16be_inc(&rdptr); /* CV_CC */
884ae8c0 407 is_reg_cc = reg_state == MODE_CC;
a7e919a3 408 out_state = read_u16be_inc(&rdptr); /* ENABLE */
884ae8c0
GS
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;
d7a4dad8 418
884ae8c0
GS
419 /* Interpret the second registers chunk's values. */
420 rdptr = (const void *)registers;
a7e919a3 421 ovpset_raw = read_u16be_inc(&rdptr); /* PRE OVPSET */
884ae8c0 422 ovp_threshold = ovpset_raw * devc->voltage_multiplier;
a7e919a3 423 ocpset_raw = read_u16be_inc(&rdptr); /* PRE OCPSET */
884ae8c0
GS
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,
02a4f485
MD
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);
884ae8c0
GS
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;
02a4f485 463 if (devc->model->n_ranges > 1) {
1ac04e6d 464 (void)read_u16be_inc(&rdptr); /* PRESET */
02a4f485
MD
465 range = read_u16be_inc(&rdptr) ? 1 : 0; /* RANGE */
466 }
884ae8c0
GS
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;
d7a4dad8 482
884ae8c0
GS
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 }
d7a4dad8 492
7a78fd56
GS
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 */
d7a4dad8
GS
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;
02a4f485
MD
527 if (devc->model->n_ranges > 1) {
528 state->range = range;
529 state->mask |= STATE_RANGE;
530 }
d7a4dad8
GS
531
532 return SR_OK;
533}
534
535/* Setup device's parameters. Selectively, from caller specs. */
536SR_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;
884ae8c0
GS
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 }
d7a4dad8
GS
566 }
567 if (state->mask & STATE_VOLTAGE_TARGET) {
568 reg_value = state->voltage_target * devc->voltage_multiplier;
884ae8c0
GS
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 }
d7a4dad8
GS
583 }
584 if (state->mask & STATE_CURRENT_LIMIT) {
585 reg_value = state->current_limit * devc->current_multiplier;
884ae8c0
GS
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 }
d7a4dad8
GS
600 }
601 if (state->mask & STATE_OVP_THRESHOLD) {
602 reg_value = state->ovp_threshold * devc->voltage_multiplier;
884ae8c0
GS
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 }
d7a4dad8
GS
617 }
618 if (state->mask & STATE_OCP_THRESHOLD) {
619 reg_value = state->ocp_threshold * devc->current_multiplier;
884ae8c0
GS
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 }
d7a4dad8
GS
634 }
635 if (state->mask & STATE_LOCK) {
884ae8c0
GS
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 }
d7a4dad8 649 }
02a4f485
MD
650 if (state->mask & STATE_RANGE) {
651 reg_value = state->range;
652 switch (devc->model->model_type) {
653 case MODEL_DPS:
62f3228b 654 /* DPS models don't support current ranges at all. */
02a4f485
MD
655 if (reg_value > 0)
656 return SR_ERR_ARG;
657 break;
658 case MODEL_RD:
62f3228b
GS
659 /*
660 * Need not set the range when the device only
661 * supports a single fixed range.
662 */
02a4f485 663 if (devc->model->n_ranges == 1)
02a4f485
MD
664 return SR_OK;
665 ret = rdtech_rd_set_reg(sdi, REG_RD_RANGE, reg_value);
666 if (ret != SR_OK)
667 return ret;
62f3228b
GS
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 */
02a4f485
MD
674 if (!devc->acquisition_started) {
675 devc->curr_range = reg_value ? 1 : 0;
676 rdtech_dps_update_multipliers(sdi);
677 }
02a4f485
MD
678 break;
679 default:
680 return SR_ERR_ARG;
681 }
682 }
d7a4dad8
GS
683
684 return SR_OK;
69b05583
JC
685}
686
d7a4dad8
GS
687/* Get the current state when acquisition starts. */
688SR_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
4efd5462
FS
694 if (!sdi || !sdi->priv)
695 return SR_ERR_ARG;
696 devc = sdi->priv;
02a4f485 697 devc->acquisition_started = TRUE;
4efd5462 698
7a78fd56 699 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_PRE_ACQ);
d7a4dad8
GS
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;
02a4f485
MD
711 if (state.mask & STATE_RANGE) {
712 devc->curr_range = state.range;
713 rdtech_dps_update_multipliers(sdi);
714 }
d7a4dad8
GS
715
716 return SR_OK;
717}
718
719/* Get measurements, track state changes during acquisition. */
0549416e
JC
720SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
721{
69b05583 722 struct sr_dev_inst *sdi;
0549416e 723 struct dev_context *devc;
d7a4dad8 724 struct rdtech_dps_state state;
7c0891b0 725 int ret;
d7a4dad8 726 struct sr_channel *ch;
1ac04e6d 727 const char *regulation_text, *range_text;
0549416e
JC
728
729 (void)fd;
69b05583 730 (void)revents;
0549416e 731
d7a4dad8
GS
732 sdi = cb_data;
733 if (!sdi)
0549416e 734 return TRUE;
69b05583
JC
735 devc = sdi->priv;
736
d7a4dad8 737 /* Get the device's current state. */
7a78fd56 738 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_IN_ACQ);
d7a4dad8
GS
739 if (ret != SR_OK)
740 return ret;
7c0891b0 741
d7a4dad8
GS
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,
02a4f485 748 devc->model->ranges[devc->curr_range].voltage_digits);
d7a4dad8
GS
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,
02a4f485 752 devc->model->ranges[devc->curr_range].current_digits);
d7a4dad8
GS
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;
69b05583 781 }
02a4f485 782 if (devc->curr_range != state.range) {
1ac04e6d 783 range_text = devc->model->ranges[state.range].range_str;
02a4f485 784 (void)sr_session_send_meta(sdi, SR_CONF_RANGE,
1ac04e6d 785 g_variant_new_string(range_text));
02a4f485
MD
786 devc->curr_range = state.range;
787 rdtech_dps_update_multipliers(sdi);
788 }
69b05583 789
d7a4dad8
GS
790 /* Check optional acquisition limits. */
791 sr_sw_limits_update_samples_read(&devc->limits, 1);
69b05583
JC
792 if (sr_sw_limits_check(&devc->limits)) {
793 sr_dev_acquisition_stop(sdi);
794 return TRUE;
0549416e
JC
795 }
796
797 return TRUE;
798}