]> sigrok.org Git - libsigrok.git/blame - src/hardware/baylibre-acme/protocol.c
baylibre-acme: Check for power-switch presence at probe's initialization.
[libsigrok.git] / src / hardware / baylibre-acme / protocol.c
CommitLineData
dfaee1de
BG
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2015 Bartosz Golaszewski <bgolaszewski@baylibre.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
6ec6c43b 20#include <config.h>
6b80b80d 21#include <string.h>
391e23a9 22#include <stdlib.h>
6b80b80d 23#include <errno.h>
391e23a9 24#include <fcntl.h>
61f2b7f7 25#include <glib/gstdio.h>
dfaee1de 26#include "protocol.h"
740ad48a 27#include "gpio.h"
dfaee1de 28
133016f6
BG
29enum channel_type {
30 ENRG_PWR = 1,
31 ENRG_CURR,
32 ENRG_VOL,
33 TEMP_IN,
34 TEMP_OUT,
35};
36
6b80b80d
BG
37struct channel_group_priv {
38 int hwmon_num;
61f2b7f7 39 int probe_type;
740ad48a 40 int index;
8f196120 41 int has_pws;
6b80b80d
BG
42};
43
44struct channel_priv {
45 int ch_type;
4e88b86c 46 int fd;
7e5a048f 47 float val;
6b80b80d
BG
48 struct channel_group_priv *probe;
49};
50
51static const uint8_t enrg_i2c_addrs[] = {
52 0x40, 0x41, 0x44, 0x45, 0x42, 0x43, 0x46, 0x47,
53};
54
55static const uint8_t temp_i2c_addrs[] = {
56 0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x4f, 0x4b,
57};
58
740ad48a 59static const uint32_t pws_gpios[] = {
391e23a9 60 486, 498, 502, 482, 478, 506, 510, 474,
740ad48a
BG
61};
62
63static const uint32_t pws_info_gpios[] = {
64 487, 499, 503, 483, 479, 507, 511, 475,
65};
66
61f2b7f7
BG
67#define MOHM_TO_UOHM(x) ((x) * 1000)
68#define UOHM_TO_MOHM(x) ((x) / 1000)
69
6b80b80d
BG
70SR_PRIV uint8_t bl_acme_get_enrg_addr(int index)
71{
72 return enrg_i2c_addrs[index];
73}
74
75SR_PRIV uint8_t bl_acme_get_temp_addr(int index)
76{
77 return temp_i2c_addrs[index];
78}
79
80SR_PRIV gboolean bl_acme_is_sane(void)
81{
82 gboolean status;
83
84 /*
85 * We expect sysfs to be present and mounted at /sys, ina226 and
86 * tmp435 sensors detected by the system and their appropriate
87 * drivers loaded and functional.
88 */
89 status = g_file_test("/sys", G_FILE_TEST_IS_DIR);
90 if (!status) {
91 sr_err("/sys/ directory not found - sysfs not mounted?");
92 return FALSE;
93 }
94
95 return TRUE;
96}
97
98static void probe_name_path(unsigned int addr, GString *path)
99{
100 g_string_printf(path,
101 "/sys/class/i2c-adapter/i2c-1/1-00%02x/name", addr);
102}
103
104/*
105 * For given address fill buf with the path to appropriate hwmon entry.
106 */
107static void probe_hwmon_path(unsigned int addr, GString *path)
108{
109 g_string_printf(path,
110 "/sys/class/i2c-adapter/i2c-1/1-00%02x/hwmon", addr);
111}
112
113SR_PRIV gboolean bl_acme_detect_probe(unsigned int addr,
114 int prb_num, const char *prb_name)
115{
116 gboolean ret = FALSE, status;
117 char *buf = NULL;
118 GString *path = g_string_sized_new(64);
119 GError *err = NULL;
120 gsize size;
121
122 probe_name_path(addr, path);
123 status = g_file_get_contents(path->str, &buf, &size, &err);
124 if (!status) {
125 sr_dbg("Name for probe %d can't be read: %s",
126 prb_num, err->message);
127 g_string_free(path, TRUE);
128 return ret;
6b80b80d
BG
129 }
130
391e23a9 131 if (!strncmp(buf, prb_name, strlen(prb_name))) {
6b80b80d
BG
132 /*
133 * Correct driver registered on this address - but is
134 * there an actual probe connected?
135 */
136 probe_hwmon_path(addr, path);
137 status = g_file_test(path->str, G_FILE_TEST_IS_DIR);
138 if (status) {
139 /* We have found an ACME probe. */
140 ret = TRUE;
141 }
142 }
143
144 g_free(buf);
145 g_string_free(path, TRUE);
146
147 return ret;
148}
149
150static int get_hwmon_index(unsigned int addr)
151{
152 int status, hwmon;
153 GString *path = g_string_sized_new(64);
154 GError *err = NULL;
155 GDir *dir;
156
157 probe_hwmon_path(addr, path);
158 dir = g_dir_open(path->str, 0, &err);
391e23a9 159 if (!dir) {
6b80b80d
BG
160 sr_err("Error opening %s: %s", path->str, err->message);
161 g_string_free(path, TRUE);
162 return -1;
163 }
164
165 g_string_free(path, TRUE);
166
167 /*
168 * The directory should contain a single file named hwmonX where X
169 * is the hwmon index.
170 */
171 status = sscanf(g_dir_read_name(dir), "hwmon%d", &hwmon);
172 g_dir_close(dir);
173 if (status != 1) {
174 sr_err("Unable to determine the hwmon entry");
175 return -1;
176 }
177
178 return hwmon;
179}
180
391e23a9 181static void append_channel(struct sr_dev_inst *sdi, struct sr_channel_group *cg,
6b80b80d
BG
182 int index, int type)
183{
184 struct channel_priv *cp;
185 struct dev_context *devc;
186 struct sr_channel *ch;
187 char *name;
188
189 devc = sdi->priv;
190
191 switch (type) {
192 case ENRG_PWR:
193 name = g_strdup_printf("P%d_ENRG_PWR", index);
194 break;
195 case ENRG_CURR:
196 name = g_strdup_printf("P%d_ENRG_CURR", index);
197 break;
198 case ENRG_VOL:
199 name = g_strdup_printf("P%d_ENRG_VOL", index);
200 break;
201 case TEMP_IN:
202 name = g_strdup_printf("P%d_TEMP_IN", index);
203 break;
204 case TEMP_OUT:
205 name = g_strdup_printf("P%d_TEMP_OUT", index);
206 break;
207 default:
391e23a9 208 sr_err("Invalid channel type: %d.", type);
6b80b80d
BG
209 return;
210 }
211
212 cp = g_malloc0(sizeof(struct channel_priv));
213 cp->ch_type = type;
214 cp->probe = cg->priv;
215
5e23fcab 216 ch = sr_channel_new(sdi, devc->num_channels++,
6b80b80d
BG
217 SR_CHANNEL_ANALOG, TRUE, name);
218 g_free(name);
219
220 ch->priv = cp;
221 cg->channels = g_slist_append(cg->channels, ch);
6b80b80d
BG
222}
223
224SR_PRIV gboolean bl_acme_register_probe(struct sr_dev_inst *sdi, int type,
225 unsigned int addr, int prb_num)
226{
227 struct sr_channel_group *cg;
228 struct channel_group_priv *cgp;
229 int hwmon;
230
231 /* Obtain the hwmon index. */
232 hwmon = get_hwmon_index(addr);
233 if (hwmon < 0)
234 return FALSE;
235
236 cg = g_malloc0(sizeof(struct sr_channel_group));
237 cgp = g_malloc0(sizeof(struct channel_group_priv));
238 cgp->hwmon_num = hwmon;
61f2b7f7 239 cgp->probe_type = type;
740ad48a 240 cgp->index = prb_num - 1;
8f196120 241 cgp->has_pws = sr_gpio_getval_export(pws_info_gpios[cgp->index]);
6b80b80d
BG
242 cg->name = g_strdup_printf("Probe_%d", prb_num);
243 cg->priv = cgp;
244
245 if (type == PROBE_ENRG) {
246 append_channel(sdi, cg, prb_num, ENRG_PWR);
247 append_channel(sdi, cg, prb_num, ENRG_CURR);
248 append_channel(sdi, cg, prb_num, ENRG_VOL);
249 } else if (type == PROBE_TEMP) {
250 append_channel(sdi, cg, prb_num, TEMP_IN);
251 append_channel(sdi, cg, prb_num, TEMP_OUT);
252 } else {
391e23a9 253 sr_err("Invalid probe type: %d.", type);
6b80b80d
BG
254 }
255
256 sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
257
258 return TRUE;
259}
260
1fe04eb8
BG
261SR_PRIV int bl_acme_get_probe_type(const struct sr_channel_group *cg)
262{
263 struct channel_group_priv *cgp = cg->priv;
264
265 return cgp->probe_type;
266}
267
268SR_PRIV int bl_acme_probe_has_pws(const struct sr_channel_group *cg)
269{
270 struct channel_group_priv *cgp = cg->priv;
271
8f196120 272 return cgp->has_pws;
1fe04eb8
BG
273}
274
61f2b7f7
BG
275/*
276 * Sets path to the hwmon attribute if this channel group
277 * supports shunt resistance setting. The caller has to supply
278 * a valid GString.
279 */
280static int get_shunt_path(const struct sr_channel_group *cg, GString *path)
281{
282 struct channel_group_priv *cgp;
283 int ret = SR_OK, status;
284
285 cgp = cg->priv;
286
287 if (cgp->probe_type != PROBE_ENRG) {
288 sr_err("Probe doesn't support shunt resistance setting");
289 return SR_ERR_ARG;
290 }
291
292 g_string_append_printf(path,
293 "/sys/class/hwmon/hwmon%d/shunt_resistor",
294 cgp->hwmon_num);
295
296 /*
297 * The shunt_resistor sysfs attribute is available
298 * in the Linux kernel since version 3.20. We have
391e23a9 299 * to notify the user if this attribute is not present.
61f2b7f7
BG
300 */
301 status = g_file_test(path->str, G_FILE_TEST_EXISTS);
302 if (!status) {
391e23a9 303 sr_err("shunt_resistance attribute not present, please update "
61f2b7f7
BG
304 "your kernel to version >=3.20");
305 return SR_ERR_NA;
306 }
307
308 return ret;
309}
310
7c91c22a
BG
311/*
312 * Try setting the update_interval sysfs attribute for each probe according
313 * to samplerate.
314 */
315SR_PRIV void bl_acme_maybe_set_update_interval(const struct sr_dev_inst *sdi,
316 uint64_t samplerate)
317{
318 struct sr_channel_group *cg;
319 struct channel_group_priv *cgp;
320 GString *hwmon;
321 GSList *l;
322 FILE *fd;
323
324 for (l = sdi->channel_groups; l != NULL; l = l->next) {
325 cg = l->data;
326 cgp = cg->priv;
327
328 hwmon = g_string_sized_new(64);
329 g_string_append_printf(hwmon,
330 "/sys/class/hwmon/hwmon%d/update_interval",
331 cgp->hwmon_num);
332
333 if (g_file_test(hwmon->str, G_FILE_TEST_EXISTS)) {
334 fd = g_fopen(hwmon->str, "w");
335 if (!fd) {
336 g_string_free(hwmon, TRUE);
337 continue;
338 }
339
340 g_fprintf(fd, "%" PRIu64 "\n", 1000 / samplerate);
341 fclose(fd);
342 }
343
344 g_string_free(hwmon, TRUE);
345 }
346}
347
61f2b7f7
BG
348SR_PRIV int bl_acme_get_shunt(const struct sr_channel_group *cg,
349 uint64_t *shunt)
350{
351 GString *path = g_string_sized_new(64);
352 gchar *contents;
353 int status, ret = SR_OK;
354 GError *err = NULL;
355
356 status = get_shunt_path(cg, path);
357 if (status != SR_OK) {
358 ret = status;
359 goto out;
360 }
361
362 status = g_file_get_contents(path->str, &contents, NULL, &err);
363 if (!status) {
364 sr_err("Error reading shunt resistance: %s", err->message);
365 ret = SR_ERR_IO;
366 goto out;
367 }
368
369 *shunt = UOHM_TO_MOHM(strtol(contents, NULL, 10));
370
371out:
372 g_string_free(path, TRUE);
373 return ret;
374}
375
391e23a9 376SR_PRIV int bl_acme_set_shunt(const struct sr_channel_group *cg, uint64_t shunt)
61f2b7f7
BG
377{
378 GString *path = g_string_sized_new(64);;
379 int status, ret = SR_OK;
380 FILE *fd;
381
382 status = get_shunt_path(cg, path);
383 if (status != SR_OK) {
384 ret = status;
385 goto out;
386 }
387
388 /*
389 * Can't use g_file_set_contents() here, as it calls open() with
390 * O_EXEC flag in a sysfs directory thus failing with EACCES.
391 */
392 fd = g_fopen(path->str, "w");
393 if (!fd) {
7237e912 394 sr_err("Error opening %s: %s", path->str, g_strerror(errno));
350b8b07
BG
395 ret = SR_ERR_IO;
396 goto out;
61f2b7f7
BG
397 }
398
34527854 399 g_fprintf(fd, "%" PRIu64 "\n", MOHM_TO_UOHM(shunt));
61f2b7f7
BG
400 fclose(fd);
401
402out:
403 g_string_free(path, TRUE);
404 return ret;
405}
406
740ad48a
BG
407SR_PRIV int bl_acme_read_power_state(const struct sr_channel_group *cg,
408 gboolean *off)
409{
410 struct channel_group_priv *cgp;
411 int val;
412
413 cgp = cg->priv;
414
1fe04eb8 415 if (!bl_acme_probe_has_pws(cg)) {
740ad48a
BG
416 sr_err("Probe has no power-switch");
417 return SR_ERR_ARG;
418 }
419
420 val = sr_gpio_getval_export(pws_gpios[cgp->index]);
421 *off = val ? FALSE : TRUE;
422
423 return SR_OK;
424}
425
426SR_PRIV int bl_acme_set_power_off(const struct sr_channel_group *cg,
427 gboolean off)
428{
429 struct channel_group_priv *cgp;
09d217a4 430 int val;
740ad48a
BG
431
432 cgp = cg->priv;
433
1fe04eb8 434 if (!bl_acme_probe_has_pws(cg)) {
740ad48a
BG
435 sr_err("Probe has no power-switch");
436 return SR_ERR_ARG;
437 }
438
09d217a4 439 val = sr_gpio_setval_export(pws_gpios[cgp->index], off ? 0 : 1);
192d37e7
BG
440 if (val < 0) {
441 sr_err("Error setting power-off state: gpio: %d",
442 pws_gpios[cgp->index]);
443 return SR_ERR_IO;
444 }
740ad48a
BG
445
446 return SR_OK;
447}
448
6b80b80d 449static int channel_to_mq(struct sr_channel *ch)
dfaee1de 450{
6b80b80d
BG
451 struct channel_priv *chp;
452
453 chp = ch->priv;
454
455 switch (chp->ch_type) {
456 case ENRG_PWR:
457 return SR_MQ_POWER;
458 case ENRG_CURR:
459 return SR_MQ_CURRENT;
460 case ENRG_VOL:
461 return SR_MQ_VOLTAGE;
391e23a9 462 case TEMP_IN: /* Fallthrough */
6b80b80d
BG
463 case TEMP_OUT:
464 return SR_MQ_TEMPERATURE;
465 default:
466 return -1;
467 }
468}
469
470static int channel_to_unit(struct sr_channel *ch)
471{
472 struct channel_priv *chp;
473
474 chp = ch->priv;
475
476 switch (chp->ch_type) {
477 case ENRG_PWR:
478 return SR_UNIT_WATT;
479 case ENRG_CURR:
480 return SR_UNIT_AMPERE;
481 case ENRG_VOL:
482 return SR_UNIT_VOLT;
391e23a9 483 case TEMP_IN: /* Fallthrough */
6b80b80d
BG
484 case TEMP_OUT:
485 return SR_UNIT_CELSIUS;
486 default:
487 return -1;
488 }
489}
490
491/* We need to scale measurements down from the units used by the drivers. */
492static float adjust_data(int val, int type)
493{
494 switch (type) {
495 case ENRG_PWR:
496 return ((float)val) / 1000000.0;
391e23a9
UH
497 case ENRG_CURR: /* Fallthrough */
498 case ENRG_VOL: /* Fallthrough */
499 case TEMP_IN: /* Fallthrough */
6b80b80d
BG
500 case TEMP_OUT:
501 return ((float)val) / 1000.0;
502 default:
503 return 0.0;
504 }
505}
506
507static float read_sample(struct sr_channel *ch)
508{
509 struct channel_priv *chp;
4e88b86c 510 char buf[16];
6b80b80d
BG
511 ssize_t len;
512 int fd;
513
4e88b86c
DL
514 chp = ch->priv;
515 fd = chp->fd;
516
517 lseek(fd, 0, SEEK_SET);
518
519 len = read(fd, buf, sizeof(buf));
520 if (len < 0) {
6433156c 521 sr_err("Error reading from channel %s (hwmon: %d): %s",
7237e912 522 ch->name, chp->probe->hwmon_num, g_strerror(errno));
4e88b86c
DL
523 ch->enabled = FALSE;
524 return -1.0;
525 }
526
527 return adjust_data(strtol(buf, NULL, 10), chp->ch_type);
528}
529
530SR_PRIV int bl_acme_open_channel(struct sr_channel *ch)
531{
532 struct channel_priv *chp;
533 char path[64], *file;
534 int fd;
535
6b80b80d
BG
536 chp = ch->priv;
537
538 switch (chp->ch_type) {
539 case ENRG_PWR: file = "power1_input"; break;
540 case ENRG_CURR: file = "curr1_input"; break;
541 case ENRG_VOL: file = "in1_input"; break;
542 case TEMP_IN: file = "temp1_input"; break;
543 case TEMP_OUT: file = "temp2_input"; break;
544 default:
391e23a9 545 sr_err("Invalid channel type: %d.", chp->ch_type);
4e88b86c 546 return SR_ERR;
6b80b80d
BG
547 }
548
391e23a9 549 snprintf(path, sizeof(path), "/sys/class/hwmon/hwmon%d/%s",
6b80b80d 550 chp->probe->hwmon_num, file);
4e88b86c 551
6b80b80d
BG
552 fd = open(path, O_RDONLY);
553 if (fd < 0) {
7237e912 554 sr_err("Error opening %s: %s", path, g_strerror(errno));
6b80b80d 555 ch->enabled = FALSE;
4e88b86c 556 return SR_ERR;
6b80b80d
BG
557 }
558
4e88b86c 559 chp->fd = fd;
6b80b80d 560
4e88b86c
DL
561 return 0;
562}
563
564SR_PRIV void bl_acme_close_channel(struct sr_channel *ch)
565{
566 struct channel_priv *chp;
567
568 chp = ch->priv;
569 close(chp->fd);
570 chp->fd = -1;
6b80b80d
BG
571}
572
573SR_PRIV int bl_acme_receive_data(int fd, int revents, void *cb_data)
574{
a0648b1a
DL
575 uint32_t cur_time, elapsed_time;
576 uint64_t nrexpiration;
6b80b80d
BG
577 struct sr_datafeed_packet packet, framep;
578 struct sr_datafeed_analog analog;
579 struct sr_dev_inst *sdi;
580 struct sr_channel *ch;
7e5a048f 581 struct channel_priv *chp;
dfaee1de 582 struct dev_context *devc;
6b80b80d 583 GSList *chl, chonly;
7e5a048f 584 unsigned i;
dfaee1de
BG
585
586 (void)fd;
6b80b80d
BG
587 (void)revents;
588
589 sdi = cb_data;
590 if (!sdi)
591 return TRUE;
dfaee1de 592
6b80b80d
BG
593 devc = sdi->priv;
594 if (!devc)
dfaee1de
BG
595 return TRUE;
596
6b80b80d
BG
597 packet.type = SR_DF_ANALOG;
598 packet.payload = &analog;
d0148a50 599 memset(&analog, 0, sizeof(struct sr_datafeed_analog));
6b80b80d 600
a0648b1a
DL
601 if (read(devc->timer_fd, &nrexpiration, sizeof(nrexpiration)) < 0) {
602 sr_warn("Failed to read timer information");
603 return TRUE;
604 }
605
6b80b80d 606 /*
a0648b1a
DL
607 * We were not able to process the previous timer expiration, we are
608 * overloaded.
6b80b80d 609 */
a0648b1a
DL
610 if (nrexpiration > 1)
611 devc->samples_missed += nrexpiration - 1;
6b80b80d 612
6b80b80d 613 /*
7e5a048f
BG
614 * XXX This is a nasty workaround...
615 *
616 * At high sampling rates and maximum channels we are not able to
617 * acquire samples fast enough, even though frontends still think
618 * that samples arrive on time. This causes shifts in frontend
619 * plots.
620 *
621 * To compensate for the delay we check if any clock events were
622 * missed and - if so - don't really read the next value, but send
623 * the same sample as fast as possible. We do it until we are back
624 * on schedule.
625 *
626 * At high sampling rate this doesn't seem to visibly reduce the
627 * accuracy.
6b80b80d 628 */
7e5a048f
BG
629 for (i = 0; i < nrexpiration; i++) {
630 framep.type = SR_DF_FRAME_BEGIN;
631 sr_session_send(cb_data, &framep);
632
633 /*
634 * Due to different units used in each channel we're sending
635 * samples one-by-one.
636 */
637 for (chl = sdi->channels; chl; chl = chl->next) {
638 ch = chl->data;
639 chp = ch->priv;
6b80b80d 640
7e5a048f
BG
641 if (!ch->enabled)
642 continue;
643 chonly.next = NULL;
644 chonly.data = ch;
645 analog.channels = &chonly;
646 analog.num_samples = 1;
647 analog.mq = channel_to_mq(chl->data);
648 analog.unit = channel_to_unit(ch);
649
650 if (i < 1)
651 chp->val = read_sample(ch);
652
653 analog.data = &chp->val;
654 sr_session_send(cb_data, &packet);
655 }
656
657 framep.type = SR_DF_FRAME_END;
658 sr_session_send(cb_data, &framep);
659 }
6b80b80d
BG
660
661 devc->samples_read++;
662 if (devc->limit_samples > 0 &&
663 devc->samples_read >= devc->limit_samples) {
664 sr_info("Requested number of samples reached.");
665 sdi->driver->dev_acquisition_stop(sdi, cb_data);
666 devc->last_sample_fin = g_get_monotonic_time();
dfaee1de 667 return TRUE;
6b80b80d
BG
668 } else if (devc->limit_msec > 0) {
669 cur_time = g_get_monotonic_time();
670 elapsed_time = cur_time - devc->start_time;
dfaee1de 671
6b80b80d
BG
672 if (elapsed_time >= devc->limit_msec) {
673 sr_info("Sampling time limit reached.");
674 sdi->driver->dev_acquisition_stop(sdi, cb_data);
675 devc->last_sample_fin = g_get_monotonic_time();
676 return TRUE;
677 }
dfaee1de
BG
678 }
679
6b80b80d 680 devc->last_sample_fin = g_get_monotonic_time();
dfaee1de
BG
681 return TRUE;
682}