]> sigrok.org Git - libsigrok.git/blame - src/hardware/rdtech-dps/protocol.c
rdtech-dps: touch up comments
[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;
02a4f485 120 do {
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;
02a4f485 125 } while (--retries);
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;
228 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 */
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);
02a4f485
MD
381 ret = rdtech_dps_read_holding_registers(modbus, REG_DPS_USET,
382 REG_DPS_ENABLE - REG_DPS_USET + 1, registers);
884ae8c0
GS
383 g_mutex_unlock(&devc->rw_mutex);
384 if (ret != SR_OK)
385 return ret;
d7a4dad8 386
884ae8c0
GS
387 /* Interpret the registers' values. */
388 rdptr = (const void *)registers;
a7e919a3 389 uset_raw = read_u16be_inc(&rdptr);
884ae8c0 390 volt_target = uset_raw / devc->voltage_multiplier;
a7e919a3 391 iset_raw = read_u16be_inc(&rdptr);
884ae8c0 392 curr_limit = iset_raw / devc->current_multiplier;
a7e919a3 393 uout_raw = read_u16be_inc(&rdptr);
884ae8c0 394 curr_voltage = uout_raw / devc->voltage_multiplier;
a7e919a3 395 iout_raw = read_u16be_inc(&rdptr);
884ae8c0 396 curr_current = iout_raw / devc->current_multiplier;
a7e919a3 397 power_raw = read_u16be_inc(&rdptr);
884ae8c0 398 curr_power = power_raw / 100.0f;
a7e919a3
VA
399 (void)read_u16be_inc(&rdptr); /* UIN */
400 reg_val = read_u16be_inc(&rdptr); /* LOCK */
884ae8c0 401 is_lock = reg_val != 0;
a7e919a3 402 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
884ae8c0
GS
403 uses_ovp = reg_val == STATE_OVP;
404 uses_ocp = reg_val == STATE_OCP;
a7e919a3 405 reg_state = read_u16be_inc(&rdptr); /* CV_CC */
884ae8c0 406 is_reg_cc = reg_state == MODE_CC;
a7e919a3 407 out_state = read_u16be_inc(&rdptr); /* ENABLE */
884ae8c0
GS
408 is_out_enabled = out_state != 0;
409
410 /* Transfer another chunk of registers in a single call. */
411 g_mutex_lock(&devc->rw_mutex);
412 ret = rdtech_dps_read_holding_registers(modbus,
413 PRE_DPS_OVPSET, 2, registers);
414 g_mutex_unlock(&devc->rw_mutex);
415 if (ret != SR_OK)
416 return ret;
d7a4dad8 417
884ae8c0
GS
418 /* Interpret the second registers chunk's values. */
419 rdptr = (const void *)registers;
a7e919a3 420 ovpset_raw = read_u16be_inc(&rdptr); /* PRE OVPSET */
884ae8c0 421 ovp_threshold = ovpset_raw * devc->voltage_multiplier;
a7e919a3 422 ocpset_raw = read_u16be_inc(&rdptr); /* PRE OCPSET */
884ae8c0
GS
423 ocp_threshold = ocpset_raw * devc->current_multiplier;
424
425 break;
426
427 case MODEL_RD:
428 /* Retrieve a set of adjacent registers. */
429 g_mutex_lock(&devc->rw_mutex);
430 ret = rdtech_dps_read_holding_registers(modbus,
02a4f485
MD
431 REG_RD_VOLT_TGT,
432 devc->model->n_ranges > 1
433 ? REG_RD_RANGE - REG_RD_VOLT_TGT + 1
434 : REG_RD_ENABLE - REG_RD_VOLT_TGT + 1,
435 registers);
884ae8c0
GS
436 g_mutex_unlock(&devc->rw_mutex);
437 if (ret != SR_OK)
438 return ret;
439
440 /* Interpret the registers' raw content. */
441 rdptr = (const void *)registers;
442 uset_raw = read_u16be_inc(&rdptr); /* USET */
443 volt_target = uset_raw / devc->voltage_multiplier;
444 iset_raw = read_u16be_inc(&rdptr); /* ISET */
445 curr_limit = iset_raw / devc->current_multiplier;
446 uout_raw = read_u16be_inc(&rdptr); /* UOUT */
447 curr_voltage = uout_raw / devc->voltage_multiplier;
448 iout_raw = read_u16be_inc(&rdptr); /* IOUT */
449 curr_current = iout_raw / devc->current_multiplier;
450 (void)read_u16be_inc(&rdptr); /* ENERGY */
451 power_raw = read_u16be_inc(&rdptr); /* POWER */
452 curr_power = power_raw / 100.0f;
453 (void)read_u16be_inc(&rdptr); /* VOLT_IN */
454 (void)read_u16be_inc(&rdptr);
455 reg_val = read_u16be_inc(&rdptr); /* PROTECT */
456 uses_ovp = reg_val == STATE_OVP;
457 uses_ocp = reg_val == STATE_OCP;
458 reg_state = read_u16be_inc(&rdptr); /* REGULATION */
459 is_reg_cc = reg_state == MODE_CC;
460 out_state = read_u16be_inc(&rdptr); /* ENABLE */
461 is_out_enabled = out_state != 0;
02a4f485
MD
462 if (devc->model->n_ranges > 1) {
463 rdptr += sizeof (uint16_t); /* PRESET */
464 range = read_u16be_inc(&rdptr) ? 1 : 0; /* RANGE */
465 }
884ae8c0
GS
466
467 /* Retrieve a set of adjacent registers. */
468 g_mutex_lock(&devc->rw_mutex);
469 ret = rdtech_dps_read_holding_registers(modbus,
470 REG_RD_OVP_THR, 2, registers);
471 g_mutex_unlock(&devc->rw_mutex);
472 if (ret != SR_OK)
473 return ret;
474
475 /* Interpret the registers' raw content. */
476 rdptr = (const void *)registers;
477 ovpset_raw = read_u16be_inc(&rdptr); /* OVP THR */
478 ovp_threshold = ovpset_raw / devc->voltage_multiplier;
479 ocpset_raw = read_u16be_inc(&rdptr); /* OCP THR */
480 ocp_threshold = ocpset_raw / devc->current_multiplier;
d7a4dad8 481
884ae8c0
GS
482 /* Details which we cannot query from the device. */
483 is_lock = FALSE;
484
485 break;
486
487 default:
488 /* ShouldNotHappen(TM). Probe should have failed. */
489 return SR_ERR_ARG;
490 }
d7a4dad8 491
7a78fd56
GS
492 /*
493 * Store gathered details in the high level container.
494 *
495 * TODO Make use of the caller's context. The register access
496 * code path above need not have gathered every detail in every
497 * invocation.
498 */
d7a4dad8
GS
499 memset(state, 0, sizeof(*state));
500 state->lock = is_lock;
501 state->mask |= STATE_LOCK;
502 state->output_enabled = is_out_enabled;
503 state->mask |= STATE_OUTPUT_ENABLED;
504 state->regulation_cc = is_reg_cc;
505 state->mask |= STATE_REGULATION_CC;
506 state->protect_ovp = uses_ovp;
507 state->mask |= STATE_PROTECT_OVP;
508 state->protect_ocp = uses_ocp;
509 state->mask |= STATE_PROTECT_OCP;
510 state->protect_enabled = TRUE;
511 state->mask |= STATE_PROTECT_ENABLED;
512 state->voltage_target = volt_target;
513 state->mask |= STATE_VOLTAGE_TARGET;
514 state->current_limit = curr_limit;
515 state->mask |= STATE_CURRENT_LIMIT;
516 state->ovp_threshold = ovp_threshold;
517 state->mask |= STATE_OVP_THRESHOLD;
518 state->ocp_threshold = ocp_threshold;
519 state->mask |= STATE_OCP_THRESHOLD;
520 state->voltage = curr_voltage;
521 state->mask |= STATE_VOLTAGE;
522 state->current = curr_current;
523 state->mask |= STATE_CURRENT;
524 state->power = curr_power;
525 state->mask |= STATE_POWER;
02a4f485
MD
526 if (devc->model->n_ranges > 1) {
527 state->range = range;
528 state->mask |= STATE_RANGE;
529 }
d7a4dad8
GS
530
531 return SR_OK;
532}
533
534/* Setup device's parameters. Selectively, from caller specs. */
535SR_PRIV int rdtech_dps_set_state(const struct sr_dev_inst *sdi,
536 struct rdtech_dps_state *state)
537{
538 struct dev_context *devc;
539 uint16_t reg_value;
540 int ret;
541
542 if (!sdi || !sdi->priv || !sdi->conn)
543 return SR_ERR_ARG;
544 devc = sdi->priv;
545 if (!state)
546 return SR_ERR_ARG;
547
548 /* Only a subset of known values is settable. */
549 if (state->mask & STATE_OUTPUT_ENABLED) {
550 reg_value = state->output_enabled ? 1 : 0;
884ae8c0
GS
551 switch (devc->model->model_type) {
552 case MODEL_DPS:
553 ret = rdtech_dps_set_reg(sdi, REG_DPS_ENABLE, reg_value);
554 if (ret != SR_OK)
555 return ret;
556 break;
557 case MODEL_RD:
558 ret = rdtech_rd_set_reg(sdi, REG_RD_ENABLE, reg_value);
559 if (ret != SR_OK)
560 return ret;
561 break;
562 default:
563 return SR_ERR_ARG;
564 }
d7a4dad8
GS
565 }
566 if (state->mask & STATE_VOLTAGE_TARGET) {
567 reg_value = state->voltage_target * devc->voltage_multiplier;
884ae8c0
GS
568 switch (devc->model->model_type) {
569 case MODEL_DPS:
570 ret = rdtech_dps_set_reg(sdi, REG_DPS_USET, reg_value);
571 if (ret != SR_OK)
572 return ret;
573 break;
574 case MODEL_RD:
575 ret = rdtech_rd_set_reg(sdi, REG_RD_VOLT_TGT, reg_value);
576 if (ret != SR_OK)
577 return ret;
578 break;
579 default:
580 return SR_ERR_ARG;
581 }
d7a4dad8
GS
582 }
583 if (state->mask & STATE_CURRENT_LIMIT) {
584 reg_value = state->current_limit * devc->current_multiplier;
884ae8c0
GS
585 switch (devc->model->model_type) {
586 case MODEL_DPS:
587 ret = rdtech_dps_set_reg(sdi, REG_DPS_ISET, reg_value);
588 if (ret != SR_OK)
589 return ret;
590 break;
591 case MODEL_RD:
592 ret = rdtech_rd_set_reg(sdi, REG_RD_CURR_LIM, reg_value);
593 if (ret != SR_OK)
594 return ret;
595 break;
596 default:
597 return SR_ERR_ARG;
598 }
d7a4dad8
GS
599 }
600 if (state->mask & STATE_OVP_THRESHOLD) {
601 reg_value = state->ovp_threshold * devc->voltage_multiplier;
884ae8c0
GS
602 switch (devc->model->model_type) {
603 case MODEL_DPS:
604 ret = rdtech_dps_set_reg(sdi, PRE_DPS_OVPSET, reg_value);
605 if (ret != SR_OK)
606 return ret;
607 break;
608 case MODEL_RD:
609 ret = rdtech_rd_set_reg(sdi, REG_RD_OVP_THR, reg_value);
610 if (ret != SR_OK)
611 return ret;
612 break;
613 default:
614 return SR_ERR_ARG;
615 }
d7a4dad8
GS
616 }
617 if (state->mask & STATE_OCP_THRESHOLD) {
618 reg_value = state->ocp_threshold * devc->current_multiplier;
884ae8c0
GS
619 switch (devc->model->model_type) {
620 case MODEL_DPS:
621 ret = rdtech_dps_set_reg(sdi, PRE_DPS_OCPSET, reg_value);
622 if (ret != SR_OK)
623 return ret;
624 break;
625 case MODEL_RD:
626 ret = rdtech_rd_set_reg(sdi, REG_RD_OCP_THR, reg_value);
627 if (ret != SR_OK)
628 return ret;
629 break;
630 default:
631 return SR_ERR_ARG;
632 }
d7a4dad8
GS
633 }
634 if (state->mask & STATE_LOCK) {
884ae8c0
GS
635 switch (devc->model->model_type) {
636 case MODEL_DPS:
637 reg_value = state->lock ? 1 : 0;
638 ret = rdtech_dps_set_reg(sdi, REG_DPS_LOCK, reg_value);
639 if (ret != SR_OK)
640 return ret;
641 break;
642 case MODEL_RD:
643 /* Do nothing, _and_ silently succeed. */
644 break;
645 default:
646 return SR_ERR_ARG;
647 }
d7a4dad8 648 }
02a4f485
MD
649 if (state->mask & STATE_RANGE) {
650 reg_value = state->range;
651 switch (devc->model->model_type) {
652 case MODEL_DPS:
62f3228b 653 /* DPS models don't support current ranges at all. */
02a4f485
MD
654 if (reg_value > 0)
655 return SR_ERR_ARG;
656 break;
657 case MODEL_RD:
62f3228b
GS
658 /*
659 * Need not set the range when the device only
660 * supports a single fixed range.
661 */
02a4f485 662 if (devc->model->n_ranges == 1)
02a4f485
MD
663 return SR_OK;
664 ret = rdtech_rd_set_reg(sdi, REG_RD_RANGE, reg_value);
665 if (ret != SR_OK)
666 return ret;
62f3228b
GS
667 /*
668 * Immediately update internal state outside of
669 * an acquisition. Assume that in-acquisition
670 * activity will update internal state. This is
671 * essential for meta package emission.
672 */
02a4f485
MD
673 if (!devc->acquisition_started) {
674 devc->curr_range = reg_value ? 1 : 0;
675 rdtech_dps_update_multipliers(sdi);
676 }
02a4f485
MD
677 break;
678 default:
679 return SR_ERR_ARG;
680 }
681 }
d7a4dad8
GS
682
683 return SR_OK;
69b05583
JC
684}
685
d7a4dad8
GS
686/* Get the current state when acquisition starts. */
687SR_PRIV int rdtech_dps_seed_receive(const struct sr_dev_inst *sdi)
688{
689 struct dev_context *devc;
690 struct rdtech_dps_state state;
691 int ret;
692
4efd5462
FS
693 if (!sdi || !sdi->priv)
694 return SR_ERR_ARG;
695 devc = sdi->priv;
02a4f485 696 devc->acquisition_started = TRUE;
4efd5462 697
7a78fd56 698 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_PRE_ACQ);
d7a4dad8
GS
699 if (ret != SR_OK)
700 return ret;
701
702 if (state.mask & STATE_PROTECT_OVP)
703 devc->curr_ovp_state = state.protect_ovp;
704 if (state.mask & STATE_PROTECT_OCP)
705 devc->curr_ocp_state = state.protect_ocp;
706 if (state.mask & STATE_REGULATION_CC)
707 devc->curr_cc_state = state.regulation_cc;
708 if (state.mask & STATE_OUTPUT_ENABLED)
709 devc->curr_out_state = state.output_enabled;
02a4f485
MD
710 if (state.mask & STATE_RANGE) {
711 devc->curr_range = state.range;
712 rdtech_dps_update_multipliers(sdi);
713 }
d7a4dad8
GS
714
715 return SR_OK;
716}
717
718/* Get measurements, track state changes during acquisition. */
0549416e
JC
719SR_PRIV int rdtech_dps_receive_data(int fd, int revents, void *cb_data)
720{
69b05583 721 struct sr_dev_inst *sdi;
0549416e 722 struct dev_context *devc;
d7a4dad8 723 struct rdtech_dps_state state;
7c0891b0 724 int ret;
d7a4dad8
GS
725 struct sr_channel *ch;
726 const char *regulation_text;
0549416e
JC
727
728 (void)fd;
69b05583 729 (void)revents;
0549416e 730
d7a4dad8
GS
731 sdi = cb_data;
732 if (!sdi)
0549416e 733 return TRUE;
69b05583
JC
734 devc = sdi->priv;
735
d7a4dad8 736 /* Get the device's current state. */
7a78fd56 737 ret = rdtech_dps_get_state(sdi, &state, ST_CTX_IN_ACQ);
d7a4dad8
GS
738 if (ret != SR_OK)
739 return ret;
7c0891b0 740
d7a4dad8
GS
741
742 /* Submit measurement data to the session feed. */
743 std_session_send_df_frame_begin(sdi);
744 ch = g_slist_nth_data(sdi->channels, 0);
745 send_value(sdi, ch, state.voltage,
746 SR_MQ_VOLTAGE, SR_MQFLAG_DC, SR_UNIT_VOLT,
02a4f485 747 devc->model->ranges[devc->curr_range].voltage_digits);
d7a4dad8
GS
748 ch = g_slist_nth_data(sdi->channels, 1);
749 send_value(sdi, ch, state.current,
750 SR_MQ_CURRENT, SR_MQFLAG_DC, SR_UNIT_AMPERE,
02a4f485 751 devc->model->ranges[devc->curr_range].current_digits);
d7a4dad8
GS
752 ch = g_slist_nth_data(sdi->channels, 2);
753 send_value(sdi, ch, state.power,
754 SR_MQ_POWER, 0, SR_UNIT_WATT, 2);
755 std_session_send_df_frame_end(sdi);
756
757 /* Check for state changes. */
758 if (devc->curr_ovp_state != state.protect_ovp) {
759 (void)sr_session_send_meta(sdi,
760 SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE,
761 g_variant_new_boolean(state.protect_ovp));
762 devc->curr_ovp_state = state.protect_ovp;
763 }
764 if (devc->curr_ocp_state != state.protect_ocp) {
765 (void)sr_session_send_meta(sdi,
766 SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE,
767 g_variant_new_boolean(state.protect_ocp));
768 devc->curr_ocp_state = state.protect_ocp;
769 }
770 if (devc->curr_cc_state != state.regulation_cc) {
771 regulation_text = state.regulation_cc ? "CC" : "CV";
772 (void)sr_session_send_meta(sdi, SR_CONF_REGULATION,
773 g_variant_new_string(regulation_text));
774 devc->curr_cc_state = state.regulation_cc;
775 }
776 if (devc->curr_out_state != state.output_enabled) {
777 (void)sr_session_send_meta(sdi, SR_CONF_ENABLED,
778 g_variant_new_boolean(state.output_enabled));
779 devc->curr_out_state = state.output_enabled;
69b05583 780 }
02a4f485
MD
781 if (devc->curr_range != state.range) {
782 (void)sr_session_send_meta(sdi, SR_CONF_RANGE,
783 g_variant_new_string(
784 devc->model->ranges[state.range].range_str));
785 devc->curr_range = state.range;
786 rdtech_dps_update_multipliers(sdi);
787 }
69b05583 788
d7a4dad8
GS
789 /* Check optional acquisition limits. */
790 sr_sw_limits_update_samples_read(&devc->limits, 1);
69b05583
JC
791 if (sr_sw_limits_check(&devc->limits)) {
792 sr_dev_acquisition_stop(sdi);
793 return TRUE;
0549416e
JC
794 }
795
796 return TRUE;
797}