diff options
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | README | 2 | ||||
-rwxr-xr-x | csv2plot | 67 | ||||
-rw-r--r-- | debian/changelog | 5 | ||||
-rw-r--r-- | debian/compat | 1 | ||||
-rw-r--r-- | debian/control | 13 | ||||
-rw-r--r-- | debian/copyright | 31 | ||||
-rw-r--r-- | debian/docs | 1 | ||||
-rw-r--r-- | debian/files | 1 | ||||
-rwxr-xr-x | debian/rules | 12 | ||||
-rw-r--r-- | debian/source/format | 1 | ||||
-rwxr-xr-x | smartpower.c | 457 |
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 @@ -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; +} |