]> sigrok.org Git - libsigrok.git/blob - src/hardware/korad-kaxxxxp/protocol.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / korad-kaxxxxp / protocol.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2015 Hannu Vuolasaho <vuokkosetae@gmail.com>
5  * Copyright (C) 2018-2019 Frank Stettner <frank-stettner@gmx.net>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include "protocol.h"
23
24 #define DEVICE_PROCESSING_TIME_MS 80
25 #define EXTRA_PROCESSING_TIME_MS  450
26
27 SR_PRIV int korad_kaxxxxp_send_cmd(struct sr_serial_dev_inst *serial,
28         const char *cmd)
29 {
30         int ret;
31
32         sr_dbg("Sending '%s'.", cmd);
33         if ((ret = serial_write_blocking(serial, cmd, strlen(cmd), 0)) < 0) {
34                 sr_err("Error sending command: %d.", ret);
35                 return ret;
36         }
37
38         return ret;
39 }
40
41 /**
42  * Read a variable length non-terminated string (caller specified maximum size).
43  *
44  * @param[in] serial The serial port to read from.
45  * @param[in] count The maximum amount of data to read.
46  * @param[out] buf The buffer to read data into. Must be larger than @a count.
47  *
48  * @return The amount of received data, or negative in case of error.
49  *     See @ref SR_ERR and other error codes.
50  *
51  * @internal
52  *
53  * The protocol has no concept of request/response termination. The only
54  * terminating conditions are either the caller's expected maxmimum byte
55  * count, or a period of time without receive data. It's essential to
56  * accept a longer initial period of time before the first receive data
57  * is seen. The supported devices can be very slow to respond.
58  *
59  * The protocol is text based. That's why the 'count' parameter specifies
60  * the expected number of text characters, and does not include the NUL
61  * termination which is not part of the wire protocol but gets added by
62  * the receive routine. The caller provided buffer is expected to have
63  * enough space for the text data and the NUL termination.
64  *
65  * Implementation detail: It's assumed that once receive data was seen,
66  * remaining response data will follow at wire speed. No further delays
67  * are expected beyond bitrate expectations. All normal commands in the
68  * acquisition phase are of fixed length which is known to the caller.
69  * Identification during device scan needs to deal with variable length
70  * data. Quick termination after reception is important there, as is the
71  * larger initial timeout period before receive data is seen.
72  */
73 SR_PRIV int korad_kaxxxxp_read_chars(struct sr_serial_dev_inst *serial,
74         size_t count, char *buf)
75 {
76         int timeout_first, timeout_later, timeout;
77         size_t retries_first, retries_later, retries;
78         size_t received;
79         int ret;
80
81         /* Clear the buffer early, to simplify the receive code path. */
82         memset(buf, 0, count + 1);
83
84         /*
85          * This calculation is aiming for backwards compatibility with
86          * an earlier implementation. An initial timeout is used which
87          * depends on the expected response byte count, and a maximum
88          * iteration count is used for read attempts.
89          *
90          * TODO Consider an absolute initial timeout instead, to reduce
91          * accumulated rounding errors for serial timeout results. The
92          * iteration with a short period is still required for variable
93          * length responses, because otherwise the serial communication
94          * layer would spend the total amount of time waiting for the
95          * remaining bytes, while the device probe code path by design
96          * passes a larger acceptable count than the typical and legal
97          * response would occupy.
98          *
99          * After initial receive data was seen, a shorter timeout is
100          * used which corresponds to a few bytes at wire speed. Idle
101          * periods without receive data longer than this threshold are
102          * taken as the end of the response. This is not compatible to
103          * the previous implementation, but was found to work as well.
104          * And severely reduces the time spent scanning for devices.
105          */
106         timeout_first = serial_timeout(serial, count);
107         retries_first = 100;
108         timeout_later = serial_timeout(serial, 3);
109         retries_later = 1;
110
111         sr_spew("want %zu bytes, timeout/retry: init %d/%zu, later %d/%zu.",
112                 count, timeout_first, retries_first,
113                 timeout_later, retries_later);
114
115         /*
116          * Run a sequence of read attempts. Try with the larger timeout
117          * and a high retry count until the first receive data became
118          * available. Then continue with a short timeout and small retry
119          * count.
120          *
121          * Failed read is fatal, immediately terminates the read sequence.
122          * A timeout in the initial phase just keeps repeating. A timeout
123          * after receive data was seen regularly terminates the sequence.
124          * Successful reads of non-empty responses keep extending the
125          * read sequence until no more receive data is available.
126          */
127         received = 0;
128         timeout = timeout_first;
129         retries = retries_first;
130         while (received < count && retries--) {
131                 ret = serial_read_blocking(serial,
132                         &buf[received], count - received, timeout);
133                 if (ret < 0) {
134                         sr_err("Error %d reading %zu bytes from device.",
135                                ret, count);
136                         return ret;
137                 }
138                 if (ret == 0 && !received)
139                         continue;
140                 if (ret == 0 && received) {
141                         sr_spew("receive timed out, want %zu, received %zu.",
142                                 count, received);
143                         break;
144                 }
145                 received += ret;
146                 timeout = timeout_later;
147                 retries = retries_later;
148         }
149         /* TODO Escape non-printables? Seen those with status queries. */
150         sr_dbg("got %zu bytes, received: '%s'.", received, buf);
151
152         return received;
153 }
154
155 static void give_device_time_to_process(struct dev_context *devc)
156 {
157         int64_t sleeping_time;
158
159         if (!devc->next_req_time)
160                 return;
161
162         sleeping_time = devc->next_req_time - g_get_monotonic_time();
163         if (sleeping_time > 0) {
164                 g_usleep(sleeping_time);
165                 sr_spew("Sleeping for processing %" PRIi64 " usec", sleeping_time);
166         }
167 }
168
169 static int64_t next_req_time(struct dev_context *devc,
170         gboolean is_set, int target)
171 {
172         gboolean is_slow_device, is_long_command;
173         int64_t processing_time_us;
174
175         is_slow_device = devc->model->quirks & KORAD_QUIRK_SLOW_PROCESSING;
176         is_long_command = is_set;
177         is_long_command |= target == KAXXXXP_STATUS;
178
179         processing_time_us = DEVICE_PROCESSING_TIME_MS;
180         if (is_slow_device && is_long_command)
181                 processing_time_us += EXTRA_PROCESSING_TIME_MS;
182         processing_time_us *= 1000;
183
184         return g_get_monotonic_time() + processing_time_us;
185 }
186
187 SR_PRIV int korad_kaxxxxp_set_value(struct sr_serial_dev_inst *serial,
188         int target, struct dev_context *devc)
189 {
190         char msg[20];
191         int ret;
192
193         g_mutex_lock(&devc->rw_mutex);
194         give_device_time_to_process(devc);
195
196         msg[0] = '\0';
197         ret = SR_OK;
198         switch (target) {
199         case KAXXXXP_CURRENT:
200         case KAXXXXP_VOLTAGE:
201         case KAXXXXP_STATUS:
202                 sr_err("Can't set measured value %d.", target);
203                 ret = SR_ERR;
204                 break;
205         case KAXXXXP_CURRENT_LIMIT:
206                 sr_snprintf_ascii(msg, sizeof(msg),
207                         "ISET1:%05.3f", devc->set_current_limit);
208                 break;
209         case KAXXXXP_VOLTAGE_TARGET:
210                 sr_snprintf_ascii(msg, sizeof(msg),
211                         "VSET1:%05.2f", devc->set_voltage_target);
212                 break;
213         case KAXXXXP_OUTPUT:
214                 sr_snprintf_ascii(msg, sizeof(msg),
215                         "OUT%1d", (devc->set_output_enabled) ? 1 : 0);
216                 /* Set value back to recognize changes */
217                 devc->output_enabled = devc->set_output_enabled;
218                 break;
219         case KAXXXXP_BEEP:
220                 sr_snprintf_ascii(msg, sizeof(msg),
221                         "BEEP%1d", (devc->set_beep_enabled) ? 1 : 0);
222                 break;
223         case KAXXXXP_OCP:
224                 sr_snprintf_ascii(msg, sizeof(msg),
225                         "OCP%1d", (devc->set_ocp_enabled) ? 1 : 0);
226                 /* Set value back to recognize changes */
227                 devc->ocp_enabled = devc->set_ocp_enabled;
228                 break;
229         case KAXXXXP_OVP:
230                 sr_snprintf_ascii(msg, sizeof(msg),
231                         "OVP%1d", (devc->set_ovp_enabled) ? 1 : 0);
232                 /* Set value back to recognize changes */
233                 devc->ovp_enabled = devc->set_ovp_enabled;
234                 break;
235         case KAXXXXP_SAVE:
236                 if (devc->program < 1 || devc->program > 5) {
237                         sr_err("Program %d is not in the supported 1-5 range.",
238                                devc->program);
239                         ret = SR_ERR;
240                         break;
241                 }
242                 sr_snprintf_ascii(msg, sizeof(msg),
243                         "SAV%1d", devc->program);
244                 break;
245         case KAXXXXP_RECALL:
246                 if (devc->program < 1 || devc->program > 5) {
247                         sr_err("Program %d is not in the supported 1-5 range.",
248                                devc->program);
249                         ret = SR_ERR;
250                         break;
251                 }
252                 sr_snprintf_ascii(msg, sizeof(msg),
253                         "RCL%1d", devc->program);
254                 break;
255         default:
256                 sr_err("Don't know how to set target %d.", target);
257                 ret = SR_ERR;
258                 break;
259         }
260
261         if (ret == SR_OK && msg[0]) {
262                 ret = korad_kaxxxxp_send_cmd(serial, msg);
263                 devc->next_req_time = next_req_time(devc, TRUE, target);
264         }
265
266         g_mutex_unlock(&devc->rw_mutex);
267
268         return ret;
269 }
270
271 SR_PRIV int korad_kaxxxxp_get_value(struct sr_serial_dev_inst *serial,
272         int target, struct dev_context *devc)
273 {
274         int ret, count;
275         char reply[6];
276         float *value;
277         char status_byte;
278         gboolean needs_ovp_quirk;
279         gboolean prev_status;
280
281         g_mutex_lock(&devc->rw_mutex);
282         give_device_time_to_process(devc);
283
284         value = NULL;
285         count = 5;
286
287         switch (target) {
288         case KAXXXXP_CURRENT:
289                 /* Read current from device. */
290                 ret = korad_kaxxxxp_send_cmd(serial, "IOUT1?");
291                 value = &(devc->current);
292                 break;
293         case KAXXXXP_CURRENT_LIMIT:
294                 /* Read set current from device. */
295                 ret = korad_kaxxxxp_send_cmd(serial, "ISET1?");
296                 value = &(devc->current_limit);
297                 break;
298         case KAXXXXP_VOLTAGE:
299                 /* Read voltage from device. */
300                 ret = korad_kaxxxxp_send_cmd(serial, "VOUT1?");
301                 value = &(devc->voltage);
302                 break;
303         case KAXXXXP_VOLTAGE_TARGET:
304                 /* Read set voltage from device. */
305                 ret = korad_kaxxxxp_send_cmd(serial, "VSET1?");
306                 value = &(devc->voltage_target);
307                 break;
308         case KAXXXXP_STATUS:
309         case KAXXXXP_OUTPUT:
310         case KAXXXXP_OCP:
311         case KAXXXXP_OVP:
312                 /* Read status from device. */
313                 ret = korad_kaxxxxp_send_cmd(serial, "STATUS?");
314                 count = 1;
315                 break;
316         default:
317                 sr_err("Don't know how to query %d.", target);
318                 ret = SR_ERR;
319         }
320         if (ret < 0) {
321                 g_mutex_unlock(&devc->rw_mutex);
322                 return ret;
323         }
324
325         devc->next_req_time = next_req_time(devc, FALSE, target);
326
327         if ((ret = korad_kaxxxxp_read_chars(serial, count, reply)) < 0) {
328                 g_mutex_unlock(&devc->rw_mutex);
329                 return ret;
330         }
331
332         if (value) {
333                 sr_atof_ascii((const char *)&reply, value);
334                 sr_dbg("value: %f", *value);
335         } else {
336                 /* We have status reply. */
337                 status_byte = reply[0];
338
339                 /* Constant current channel one. */
340                 prev_status = devc->cc_mode[0];
341                 devc->cc_mode[0] = !(status_byte & (1 << 0));
342                 devc->cc_mode_1_changed = devc->cc_mode[0] != prev_status;
343                 /* Constant current channel two. */
344                 prev_status = devc->cc_mode[1];
345                 devc->cc_mode[1] = !(status_byte & (1 << 1));
346                 devc->cc_mode_2_changed = devc->cc_mode[1] != prev_status;
347
348                 /*
349                  * Tracking:
350                  * status_byte & ((1 << 2) | (1 << 3))
351                  * 00 independent 01 series 11 parallel
352                  */
353                 devc->beep_enabled = status_byte & (1 << 4);
354
355                 /* OCP enabled. */
356                 prev_status = devc->ocp_enabled;
357                 devc->ocp_enabled = status_byte & (1 << 5);
358                 devc->ocp_enabled_changed = devc->ocp_enabled != prev_status;
359
360                 /* Output status. */
361                 prev_status = devc->output_enabled;
362                 devc->output_enabled = status_byte & (1 << 6);
363                 devc->output_enabled_changed = devc->output_enabled != prev_status;
364
365                 /* OVP enabled, special handling for Velleman LABPS3005 quirk. */
366                 needs_ovp_quirk = devc->model->quirks & KORAD_QUIRK_LABPS_OVP_EN;
367                 if (!needs_ovp_quirk || devc->output_enabled) {
368                         prev_status = devc->ovp_enabled;
369                         devc->ovp_enabled = status_byte & (1 << 7);
370                         devc->ovp_enabled_changed = devc->ovp_enabled != prev_status;
371                 }
372
373                 sr_dbg("Status: 0x%02x", status_byte);
374                 sr_spew("Status: CH1: constant %s CH2: constant %s. "
375                         "Tracking would be %s and %s. Output is %s. "
376                         "OCP is %s, OVP is %s. Device is %s.",
377                         (status_byte & (1 << 0)) ? "voltage" : "current",
378                         (status_byte & (1 << 1)) ? "voltage" : "current",
379                         (status_byte & (1 << 2)) ? "parallel" : "series",
380                         (status_byte & (1 << 3)) ? "tracking" : "independent",
381                         (status_byte & (1 << 6)) ? "enabled" : "disabled",
382                         (status_byte & (1 << 5)) ? "enabled" : "disabled",
383                         (status_byte & (1 << 7)) ? "enabled" : "disabled",
384                         (status_byte & (1 << 4)) ? "beeping" : "silent");
385         }
386
387         /* Read the sixth byte from ISET? BUG workaround. */
388         if (target == KAXXXXP_CURRENT_LIMIT)
389                 serial_read_blocking(serial, &status_byte, 1, 10);
390
391         g_mutex_unlock(&devc->rw_mutex);
392
393         return ret;
394 }
395
396 SR_PRIV int korad_kaxxxxp_get_all_values(struct sr_serial_dev_inst *serial,
397         struct dev_context *devc)
398 {
399         int ret, target;
400
401         for (target = KAXXXXP_CURRENT;
402                         target <= KAXXXXP_STATUS; target++) {
403                 if ((ret = korad_kaxxxxp_get_value(serial, target, devc)) < 0)
404                         return ret;
405         }
406
407         return ret;
408 }
409
410 static void next_measurement(struct dev_context *devc)
411 {
412         switch (devc->acquisition_target) {
413         case KAXXXXP_CURRENT:
414                 devc->acquisition_target = KAXXXXP_VOLTAGE;
415                 break;
416         case KAXXXXP_VOLTAGE:
417                 devc->acquisition_target = KAXXXXP_STATUS;
418                 break;
419         case KAXXXXP_STATUS:
420                 devc->acquisition_target = KAXXXXP_CURRENT;
421                 break;
422         default:
423                 devc->acquisition_target = KAXXXXP_CURRENT;
424                 sr_err("Invalid target for next acquisition.");
425         }
426 }
427
428 SR_PRIV int korad_kaxxxxp_receive_data(int fd, int revents, void *cb_data)
429 {
430         struct sr_dev_inst *sdi;
431         struct dev_context *devc;
432         struct sr_serial_dev_inst *serial;
433         struct sr_datafeed_packet packet;
434         struct sr_datafeed_analog analog;
435         struct sr_analog_encoding encoding;
436         struct sr_analog_meaning meaning;
437         struct sr_analog_spec spec;
438         GSList *l;
439
440         (void)fd;
441         (void)revents;
442
443         if (!(sdi = cb_data))
444                 return TRUE;
445
446         if (!(devc = sdi->priv))
447                 return TRUE;
448
449         serial = sdi->conn;
450
451         /* Get the value. */
452         korad_kaxxxxp_get_value(serial, devc->acquisition_target, devc);
453
454         /* Note: digits/spec_digits will be overridden later. */
455         sr_analog_init(&analog, &encoding, &meaning, &spec, 0);
456
457         /* Send the value forward. */
458         packet.type = SR_DF_ANALOG;
459         packet.payload = &analog;
460         analog.num_samples = 1;
461         l = g_slist_copy(sdi->channels);
462         if (devc->acquisition_target == KAXXXXP_CURRENT) {
463                 l = g_slist_remove_link(l, g_slist_nth(l, 0));
464                 analog.meaning->channels = l;
465                 analog.meaning->mq = SR_MQ_CURRENT;
466                 analog.meaning->unit = SR_UNIT_AMPERE;
467                 analog.meaning->mqflags = SR_MQFLAG_DC;
468                 analog.encoding->digits = 3;
469                 analog.spec->spec_digits = 3;
470                 analog.data = &devc->current;
471                 sr_session_send(sdi, &packet);
472         } else if (devc->acquisition_target == KAXXXXP_VOLTAGE) {
473                 l = g_slist_remove_link(l, g_slist_nth(l, 1));
474                 analog.meaning->channels = l;
475                 analog.meaning->mq = SR_MQ_VOLTAGE;
476                 analog.meaning->unit = SR_UNIT_VOLT;
477                 analog.meaning->mqflags = SR_MQFLAG_DC;
478                 analog.encoding->digits = 2;
479                 analog.spec->spec_digits = 2;
480                 analog.data = &devc->voltage;
481                 sr_session_send(sdi, &packet);
482                 sr_sw_limits_update_samples_read(&devc->limits, 1);
483         } else if (devc->acquisition_target == KAXXXXP_STATUS) {
484                 if (devc->cc_mode_1_changed) {
485                         sr_session_send_meta(sdi, SR_CONF_REGULATION,
486                                 g_variant_new_string((devc->cc_mode[0]) ? "CC" : "CV"));
487                         devc->cc_mode_1_changed = FALSE;
488                 }
489                 if (devc->cc_mode_2_changed) {
490                         sr_session_send_meta(sdi, SR_CONF_REGULATION,
491                                 g_variant_new_string((devc->cc_mode[1]) ? "CC" : "CV"));
492                         devc->cc_mode_2_changed = FALSE;
493                 }
494                 if (devc->output_enabled_changed) {
495                         sr_session_send_meta(sdi, SR_CONF_ENABLED,
496                                 g_variant_new_boolean(devc->output_enabled));
497                         devc->output_enabled_changed = FALSE;
498                 }
499                 if (devc->ocp_enabled_changed) {
500                         sr_session_send_meta(sdi, SR_CONF_OVER_CURRENT_PROTECTION_ENABLED,
501                                 g_variant_new_boolean(devc->ocp_enabled));
502                         devc->ocp_enabled_changed = FALSE;
503                 }
504                 if (devc->ovp_enabled_changed) {
505                         sr_session_send_meta(sdi, SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED,
506                                 g_variant_new_boolean(devc->ovp_enabled));
507                         devc->ovp_enabled_changed = FALSE;
508                 }
509         }
510         next_measurement(devc);
511
512         if (sr_sw_limits_check(&devc->limits))
513                 sr_dev_acquisition_stop(sdi);
514
515         return TRUE;
516 }