summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile13
-rw-r--r--README2
-rwxr-xr-xcsv2plot67
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control13
-rw-r--r--debian/copyright31
-rw-r--r--debian/docs1
-rw-r--r--debian/files1
-rwxr-xr-xdebian/rules12
-rw-r--r--debian/source/format1
-rwxr-xr-xsmartpower.c457
12 files changed, 604 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..169c84c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+OUT = smartpower
+SRC = smartpower.c
+
+$(OUT): $(SRC)
+ gcc $< -Wall -O2 -o $@
+
+clean:
+ -rm -f $(OUT)
+
+install: $(OUT)
+ cp $(OUT) $(DESTDIR)/usr/bin
+
+$(OUT): Makefile
diff --git a/README b/README
new file mode 100644
index 0000000..d5381bb
--- /dev/null
+++ b/README
@@ -0,0 +1,2 @@
+This work is motivated by the need of having command line tool that can stream
+data from ODROID Smart Power power supply.
diff --git a/csv2plot b/csv2plot
new file mode 100755
index 0000000..fbb250f
--- /dev/null
+++ b/csv2plot
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# rudimental script to plot two csv data sets
+#
+# Copyright (c) 2015, Aliaksei Katovich <aliaksei.katovich at gmail.com>
+#
+# Licensed under the GNU General Public License version 2 (GPLv2).
+
+color1="#000070"
+color2="#007000"
+
+bg="grey80"
+fg="black"
+
+_init()
+{
+ echo "set obj 1 rectangle behind from screen 0,0 to screen 1,1"
+ echo "set obj 1 fillstyle solid 1.0 fillcolor rgb '$bg'"
+ echo "set term wxt font 'Arial Bold,9'"
+
+ echo "set title '$name1 vs $name2' tc rgb '$fg'"
+ echo "set key left Left"
+ echo "set key tc rgb '$fg'"
+ echo "set xtic auto"
+ echo "set xlabel 'Time in seconds' tc rgb '$fg' offset 0,-4"
+ echo "set xtics rotate by 90 offset 0,-4"
+
+ echo "set ylabel '$name1 (mA)' tc rgb '$color1'"
+ echo "set ytic auto nomirror tc lt 1"
+
+ echo "set y2label '$name2 (mA)' tc rgb '$color2'"
+ echo "set y2tic auto nomirror tc lt 2"
+
+ echo "set border 1 lt rgb '$fg'"
+ echo "set grid lt 0 lw 1 lc rgb '$fg'"
+
+ echo "set ytics tc rgb '$fg'"
+ echo "set y2tics tc rgb '$fg'"
+ echo "set style data lines"
+
+ echo "set datafile separator ','"
+
+ echo "f(x) = mean_y"
+ echo "fit f(x) '$file1' u 1:3 via mean_y"
+ echo "f(x) = mean_y2"
+ echo "fit f(x) '$file2' u 1:3 via mean_y2"
+
+ echo -n "plot '$file1' u 1:3 "
+ echo -n "title sprintf('$name1 mean %.06f mA', mean_y) "
+ echo -n "lt 1 lc rgb '$color1' axes x1y1,"
+ echo -n "'$file2' u 1:3 "
+ echo -n "title sprintf('$name2 mean %.06f mA', mean_y2) "
+ echo "lt 2 lc rgb '$color2' axes x1y2"
+}
+
+file1=$1
+file2=$2
+
+if [ -z "$file2" ]; then
+ echo "Usage: $(basename $0) <data1.csv> <data2.csv>"
+ exit 1
+fi
+
+name1=$(basename $file1)
+name2=$(basename $file2)
+
+_init | gnuplot -noraise -p
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..a8841e2
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+smartpower (0.1-1) unstable; urgency=low
+
+ * Initial release
+
+ -- Donghoon Shin <dhs.shin@samsung.com> Mon, 30 May 2016 11:24:58 +0900
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..e05c9f0
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,13 @@
+Source: smartpower
+Section: devel
+Priority: optional
+Maintainer: Donghoon Shin <dhs.shin@samsung.com>
+Build-Depends: debhelper (>= 8.0.0)
+Standards-Version: 3.9.4
+Homepage: https://github.com/polarcat/smartpower.git
+
+Package: smartpower
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: smartpower
+ This work is motivated by the need of having command line tool that can stream data from ODROID Smart Power power supply.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..610786c
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,31 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: smartpower
+Source: https://github.com/polarcat/smartpower.git
+
+Upstream Authors: Aliaksei Katovich
+
+Files: *
+Copyright:
+ Copyright (C) 2013 Aliaksei Katovich
+License: GPL-3.0+
+
+Files: debian/*
+Copyright: 2016 Donghoon Shin <dhs.shin@samsung.com>
+License: GPL-3.0+
+
+License: GPL-3.0+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..e845566
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1 @@
+README
diff --git a/debian/files b/debian/files
new file mode 100644
index 0000000..b6a272c
--- /dev/null
+++ b/debian/files
@@ -0,0 +1 @@
+smartpower_0.1-1_amd64.deb devel optional
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..6c65b43
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,12 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+%:
+ dh $@
+
+override_dh_auto_install:
+ mkdir -p debian/smartpower/usr/bin
+ dh_auto_install
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/smartpower.c b/smartpower.c
new file mode 100755
index 0000000..b603126
--- /dev/null
+++ b/smartpower.c
@@ -0,0 +1,457 @@
+/*
+ * @file smartpower.c: ODROID Smart Power data logger
+ *
+ * @author: Aliaksei Katovich <aliaksei.katovich@gmail.com>
+ *
+ * Copyright (C) 2013 Aliaksei Katovich
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/hidraw.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define err(fmt, args...) { \
+ fprintf(stderr, "(ee) "fmt, ##args); \
+ fprintf(stderr, "(ii) %s:%d: %s, %d\n", __func__, __LINE__, \
+ strerror(errno), errno); \
+}
+
+#define msg(fmt, args...) fprintf(stderr, "(==) "fmt, ##args)
+
+#define MAX_BUF 65
+#define MAX_SLEEP 100 /* us */
+
+#define FLG_DATA 0x37
+#define FLG_STARTSTOP 0x80
+#define FLG_STATUS 0x81
+#define FLG_ONOFF 0x82
+#define FLG_VERSION 0x83
+
+const char *bus_str(int bus)
+{
+ switch (bus) {
+ case BUS_USB:
+ return "USB";
+ break;
+ case BUS_HIL:
+ return "HIL";
+ break;
+ case BUS_BLUETOOTH:
+ return "Bluetooth";
+ break;
+ case BUS_VIRTUAL:
+ return "Virtual";
+ break;
+ default:
+ return "Other";
+ break;
+ }
+}
+
+static int smartp_open(const char *dev)
+{
+ int fd;
+
+ fd = open(dev, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ err("%s: unable to open device\n", dev);
+
+ if (errno == 13) {
+ msg("become root or escalate priviledge\n");
+ exit(errno);
+ }
+
+ return -errno;
+ }
+
+ return fd;
+}
+
+static int csv;
+static char sep;
+static int ts_style;
+static double ts_start;
+
+static void smartp_printd(const unsigned char *data)
+{
+ struct timeval tv;
+ double ts;
+ unsigned long long ts_sec, ts_usec;
+
+ if (ts_start == 0.0) {
+ gettimeofday(&tv, NULL);
+ ts_start = (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0;
+ if (ts_style == 0)
+ printf("0.0%c%s\n", sep, data);
+ else
+ printf("[%5u.%06u] %s\n", 0, 0, data);
+ return;
+ }
+
+ gettimeofday(&tv, NULL);
+ ts = (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0 - ts_start;
+ if (ts_style == 0) {
+ printf("%f%c%s\n", ts, sep, data);
+ } else {
+ ts_sec = ts;
+ ts_usec = (ts - (unsigned long long) ts) * 1000000.0;
+ printf("[%5u.%06u] %s\n", (unsigned int) ts_sec,
+ (unsigned int) ts_usec, data);
+ }
+}
+
+static void smartp_csv(unsigned char *data, size_t len, unsigned int start)
+{
+ int i, space = 0;
+ unsigned char *ptr;
+
+ for (i = 0, ptr = &data[start]; *ptr != '\0'; ptr++) {
+ if (*ptr == ' ' && space == 0) {
+ data[i] = ',';
+ space++;
+ i++;
+ } else if (*ptr == '.' || (*ptr >= 0x30 && *ptr <= 0x39)) {
+ data[i] = *ptr;
+ space = 0;
+ i++;
+ }
+ }
+ data[i] = '\0';
+}
+
+#define SMARTP_READ_MAX 100000
+
+static int smartp_read(int fd, unsigned char *buf, size_t len)
+{
+ int rc = -1;
+ int i;
+
+ memset(buf, 0, len);
+
+ for (i = 0; i < SMARTP_READ_MAX; i++) {
+ rc = read(fd, buf, len);
+ if (rc >= 0)
+ break;
+ }
+
+ if (rc < 0) {
+ err("failed to read %lu bytes\n", (unsigned long)len);
+ return rc;
+ }
+
+ switch (buf[0]) {
+ case 0x37:
+ if (!csv) {
+ smartp_printd(&buf[2]);
+ } else {
+ smartp_csv(buf, len, 2);
+ smartp_printd(buf);
+ }
+ break;
+ case 0x81:
+ printf("Power %s, record %s\n",
+ buf[2] == 0 ? "off" : "on",
+ buf[1] == 0 ? "off" : "on");
+ break;
+ case 0x83:
+ printf("Version: %s\n", buf);
+ break;
+ default:
+ printf("????:");
+ for (i = 0; i < rc; i++)
+ printf("%hhx ", buf[i]);
+ printf("\n");
+ }
+
+ return rc;
+}
+
+static int smartp_send(int fd, unsigned char *buf, size_t len)
+{
+ int rc;
+
+ rc = write(fd, buf, len);
+ if (rc < 0) {
+ err("cmd=%02x\n", buf[1]);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int smartp_toggle_record(int fd)
+{
+ int rc;
+ unsigned char cmd[2] = { 0x00, FLG_STARTSTOP, };
+ unsigned char buf[3];
+
+ rc = smartp_send(fd, cmd, sizeof(cmd));
+ if (rc < 0)
+ return rc;
+
+ /* update status */
+ cmd[1] = FLG_STATUS;
+ rc = smartp_send(fd, cmd, sizeof(cmd));
+ if (rc < 0)
+ return rc;
+
+ return smartp_read(fd, buf, sizeof(buf));
+}
+
+static int smartp_toggle_power(int fd)
+{
+ int rc;
+ unsigned char cmd[2] = { 0x00, FLG_ONOFF, };
+ unsigned char buf[3];
+
+ rc = smartp_send(fd, cmd, sizeof(cmd));
+ if (rc < 0)
+ return rc;
+
+ /* update status */
+ cmd[1] = FLG_STATUS;
+ rc = smartp_send(fd, cmd, sizeof(cmd));
+ if (rc < 0)
+ return rc;
+
+ usleep(100000); /* wait status update; time gained experimentally */
+ return smartp_read(fd, buf, sizeof(buf));
+}
+
+#define MAX_VERSION 17
+
+static int smartp_version(int fd)
+{
+ int rc;
+ unsigned char buf[MAX_VERSION];
+ unsigned char cmd[2] = { 0x00, FLG_VERSION, };
+
+ rc = smartp_send(fd, cmd, sizeof(cmd));
+ if (rc < 0)
+ return rc;
+
+ memset(buf, 0, sizeof(buf));
+ return smartp_read(fd, buf, sizeof(buf));
+}
+
+#define MAX_DATA 34
+
+static int smartp_getdata(int fd)
+{
+ int rc;
+ unsigned char buf[MAX_DATA];
+ unsigned char cmd[2] = { 0x00, FLG_DATA, };
+
+ rc = smartp_send(fd, cmd, sizeof(cmd));
+ if (rc < 0)
+ return rc;
+
+ memset(buf, 0, sizeof(buf));
+ return smartp_read(fd, buf, sizeof(buf));
+}
+
+#define SMARTP_VENDOR 0x04d8
+#define SMARTP_PRODUCT 0x003f
+
+#define HIDRAW_CLASS "/sys/class/hidraw"
+
+static int smartp_probe(void)
+{
+ int i = 0;
+ int rc;
+ int fd = -1;
+ struct hidraw_devinfo info;
+ char name[80];
+ DIR *dir;
+ struct dirent *dentry;
+
+ dir = opendir(HIDRAW_CLASS);
+ if (!dir) {
+ err(HIDRAW_CLASS": failed to open directory\n");
+ msg("try to enable CONFIG_HIDRAW in kernel config\n");
+ return -errno;
+ }
+
+ while ((dentry = readdir(dir))) {
+ if (dentry->d_name[0] == '.')
+ continue;
+ i++;
+ snprintf(name, sizeof(name), "/dev/%s", dentry->d_name);
+ fd = smartp_open(name);
+ if (fd < 0)
+ continue;
+ rc = ioctl(fd, HIDIOCGRAWINFO, &info);
+ if (rc < 0)
+ continue;
+ if (info.vendor == SMARTP_VENDOR && info.product == SMARTP_PRODUCT) {
+ msg("Detected smartp at %s\n", name);
+ break;
+ }
+ }
+ closedir(dir);
+
+ if (i == 0)
+ msg("smart power device is not connected\n");
+
+ return fd;
+}
+
+static int smartp_verbose(int fd, const char *dev)
+{
+ int rc;
+ unsigned char buf[256];
+ struct hidraw_devinfo info;
+
+ memset(&info, 0x0, sizeof(info));
+ memset(buf, 0x0, sizeof(buf));
+
+ /* Get Raw Name */
+ rc = ioctl(fd, HIDIOCGRAWNAME(256), buf);
+ if (rc < 0) {
+ err("%s: failed to get raw name\n", dev);
+ return -errno;
+ }
+ printf("Raw name: %s\n", buf);
+
+ /* Get Physical Location */
+ rc = ioctl(fd, HIDIOCGRAWPHYS(256), buf);
+ if (rc < 0) {
+ err("%s: failed to get physical location\n", dev);
+ return -errno;
+ }
+ printf("Raw phys: %s\n", buf);
+
+ /* Get Raw Info */
+ rc = ioctl(fd, HIDIOCGRAWINFO, &info);
+ if (rc < 0) {
+ err("%s: failed to get raw info\n", dev);
+ return -errno;
+ }
+ printf("Raw info: bustype %d (%s), vendor 0x%04hx, product 0x%04hx\n",
+ info.bustype, bus_str(info.bustype), info.vendor, info.product);
+ return 0;
+}
+
+static int signal_caught;
+
+void signal_handler(int signum)
+{
+ signal_caught = signum;
+}
+
+static void help(const char *name)
+{
+ printf("Usage: %s [options]\n", name);
+ printf("Options:\n");
+ printf(" -h, --help print this message\n");
+ printf(" -p, --power toggle power supply on/off\n");
+ printf(" -r, --record toggle power consumption recording\n");
+ printf(" -v, --verbose print hidraw details\n");
+ printf(" -k, --kernel dmesg like time stamps\n");
+ printf(" -c, --csv produce csv output (default raw)\n");
+ printf(" -s, --samples <n> take n samples and exit\n");
+ printf(" -d, --dev <dev> path to hidraw device node\n");
+}
+
+static int opt(const char *arg, const char *args, const char *argl)
+{
+ return (strcmp(arg, args) == 0 || strcmp(arg, argl) == 0);
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+ int i, samples = 0;
+ const char *dev = NULL;
+ const char *arg;
+ int power = 0, record = 0, verbose = 0;
+
+ for (i = 0; i < argc; i++) {
+ arg = argv[i];
+ if (opt(arg, "-d", "--dev")) {
+ i++;
+ dev = argv[i];
+ continue;
+ }
+ if (opt(arg, "-s", "--samples")) {
+ i++;
+ samples = atoi(argv[i]);
+ continue;
+ }
+ if (opt(arg, "-p", "--power")) {
+ power = 1;
+ continue;
+ }
+ if (opt(arg, "-r", "--record")) {
+ record = 1;
+ continue;
+ }
+ if (opt(arg, "-v", "--verbose")) {
+ verbose = 1;
+ continue;
+ }
+ if (opt(arg, "-k", "--kernel")) {
+ ts_style = 1;
+ csv = 0;
+ sep = ' ';
+ continue;
+ }
+ if (opt(arg, "-c", "--csv")) {
+ csv = 1;
+ sep = ',';
+ ts_style = 0;
+ continue;
+ }
+ if (opt(arg, "-h", "--help")) {
+ help(argv[0]);
+ return 0;
+ }
+ }
+
+ if (dev)
+ fd = smartp_open(dev);
+ else
+ fd = smartp_probe();
+
+ if (fd < 0)
+ return fd;
+
+ if (verbose == 1) {
+ smartp_verbose(fd, dev);
+ smartp_version(fd);
+ }
+
+ if (record == 1)
+ return smartp_toggle_record(fd);
+
+ if (power == 1)
+ return smartp_toggle_power(fd);
+
+ signal(SIGINT, signal_handler);
+ signal(SIGTERM, signal_handler);
+
+ if (samples == 0) {
+ while (!signal_caught)
+ smartp_getdata(fd);
+ } else {
+ for (i = 0; i < samples && !signal_caught; i++)
+ smartp_getdata(fd);
+ }
+
+ close(fd);
+ return 0;
+}