/*
* A Simple Battery Status Display via OSD
*
- * (c) 2007--2010 Martin Mares <mj@ucw.cz>
+ * (c) 2007--2012 Martin Mares <mj@ucw.cz>
*/
+#undef DEBUG
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <getopt.h>
+#include <fcntl.h>
#include "osd.h"
static int check_every;
static int warn_threshold = 600;
-static int total_full, total_capa, discharge_rate;
+static int total_now, total_full, discharge_rate;
static int charge_time, discharge_time;
static int ac_online;
static unsigned int present_mask, charge_mask, discharge_mask;
static unsigned int last_charge_mask, last_discharge_mask;
static int last_ac_online = -1;
-static char *parse_line(char *line)
+#define MAX_BATTS 4
+#define BATT_NAME_LEN 32
+static char batt_names[MAX_BATTS][BATT_NAME_LEN];
+
+static char sys_dir[256];
+#define BUFSIZE 256
+
+static int sys_read(char *buf, char *attribute)
{
- char *t = strchr(line, '\n');
- if (t)
- *t = 0;
- char *val = strchr(line, ':');
- if (!val)
- return NULL;
- *val++ = 0;
- while (*val == ' ' || *val == '\t')
- *val++ = 0;
- return val;
+ char name[256];
+ snprintf(name, sizeof(name), "%s/%s", sys_dir, attribute);
+
+ int fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return 0;
+
+ int n = read(fd, buf, BUFSIZE);
+ close(fd);
+ if (n < 0)
+ return 0;
+
+ buf[BUFSIZE-1] = 0;
+ char *nl = strchr(buf, '\n');
+ if (nl)
+ *nl = 0;
+ DBG("\t%s=%s\n", attribute, buf);
+ return 1;
}
-static void scan_ac(void)
+static int sys_read_int(char *attribute, int default_value)
{
- ac_online = 0;
+ char buf[BUFSIZE];
+ if (!sys_read(buf, attribute) || !buf[0])
+ return default_value;
+ else
+ return atoi(buf);
+}
- const char dir[] = "/proc/acpi/ac_adapter";
- DIR *d = opendir(dir);
- if (!d)
- return;
+static void parse_ac(void)
+{
+ ac_online = sys_read_int("online", 0);
+}
- struct dirent *e;
- while (e = readdir(d))
+static int get_batt_id(char *batt_name)
+{
+ for (int i=0; i<MAX_BATTS; i++)
{
- if (e->d_name[0] == '.')
- continue;
- char n[sizeof(dir) + 1 + strlen(e->d_name) + 6];
- sprintf(n, "%s/%s/state", dir, e->d_name);
- FILE *f = fopen(n, "r");
- if (!f)
- continue;
- char line[1024];
- while (fgets(line, sizeof(line)-1, f))
+ if (!strcmp(batt_names[i], batt_name))
+ return i;
+ if (!batt_names[i][0])
{
- char *val = parse_line(line);
- if (!val)
- continue;
- if (!strcmp(line, "state") && !strcmp(val, "on-line"))
- ac_online = 1;
+ snprintf(batt_names[i], BATT_NAME_LEN, "%s", batt_name);
+ return i;
}
- fclose(f);
}
- closedir(d);
+ return MAX_BATTS;
}
-static void scan_batt(void)
+static void parse_batt(char *batt_name)
{
+ int batt_id = get_batt_id(batt_name);
+ DBG("\t-> id %d\n", batt_id);
+
+ if (!sys_read_int("present", 1))
+ return;
+
+ int charging = sys_read_int("charging", 0);
+ int charge_full = sys_read_int("charge_full", 0);
+ int charge_now = sys_read_int("charge_now", 0);
+ int current_now = sys_read_int("current_now", 0);
+
+ present_mask |= 1 << batt_id;
+ total_now += charge_now;
+ total_full += charge_full;
+ if (charging && current_now > 0)
+ {
+ charge_mask |= 1 << batt_id;
+ int ch = (long long)(charge_full - charge_now)*3600 / current_now;
+ if (ch > charge_time)
+ charge_time = ch;
+ }
+ else if (current_now > 0)
+ {
+ discharge_mask |= 1 << batt_id;
+ discharge_rate += current_now;
+ }
+}
+
+static void scan(void)
+{
+ ac_online = 0;
charge_time = discharge_time = 0;
- total_full = total_capa = 0;
+ total_now = total_full = 0;
discharge_rate = 0;
present_mask = charge_mask = discharge_mask = 0;
- const char dir[] = "/proc/acpi/battery";
+ const char dir[] = "/sys/class/power_supply";
DIR *d = opendir(dir);
if (!d)
return;
while (e = readdir(d))
{
if (e->d_name[0] == '.')
- continue;
- char n[sizeof(dir) + 1 + strlen(e->d_name) + 6];
- const char * const names[] = { "state", "info" };
- int present = 0;
- int rate = 0;
- int charging = 0;
- int remains = 0;
- int last_full = 0;
- int batt_id = -1;
- for (int i=0; i<2; i++)
- {
- sprintf(n, "%s/%s/%s", dir, e->d_name, names[i]);
- FILE *f = fopen(n, "r");
- if (!f)
- continue;
- char line[1024];
- while (fgets(line, sizeof(line)-1, f))
- {
- char *val = parse_line(line);
- if (!val)
- continue;
- // printf("<%s> <%s>\n", line, val);
- if (!strcmp(line, "present"))
- present = !strcmp(val, "yes");
- else if (!strcmp(line, "charging state"))
- charging = !strcmp(val, "charging");
- else if (!strcmp(line, "present rate"))
- rate = atol(val);
- else if (!strcmp(line, "remaining capacity"))
- remains = atol(val);
- else if (!strcmp(line, "last full capacity"))
- last_full = atol(val);
- else if (!strcmp(line, "serial number"))
- batt_id = atol(val);
- }
- fclose(f);
- }
- if (present)
- {
- if (batt_id < 0 || batt_id > 31)
- batt_id = 0;
- present_mask |= 1 << batt_id;
- total_full += remains;
- total_capa += last_full;
- if (charging)
- {
- charge_mask |= 1 << batt_id;
- int ch = (last_full - remains)*3600 / rate;
- if (ch > charge_time)
- charge_time = ch;
- }
- else if (rate > 0)
- {
- discharge_mask |= 1 << batt_id;
- discharge_rate += rate;
- }
- }
+ continue;
+ snprintf(sys_dir, sizeof(sys_dir), "%s/%s", dir, e->d_name);
+ DBG("%s\n", sys_dir);
+
+ char type[BUFSIZE];
+ if (!sys_read(type, "type"))
+ continue;
+
+ if (!strcmp(type, "Mains"))
+ parse_ac();
+ else if (!strcmp(type, "Battery"))
+ parse_batt(e->d_name);
}
if (discharge_rate)
- discharge_time = total_full*3600 / discharge_rate;
+ discharge_time = (long long) total_now*3600 / discharge_rate;
else
discharge_time = 1000000;
closedir(d);
-}
-
-static void scan(void)
-{
- scan_ac();
- scan_batt();
+ DBG("=> Capacity: now=%d full=%d\n", total_now, total_full);
+ DBG("=> Charge: mask=%d time=%d\n", charge_mask, charge_time);
+ DBG("=> Discharge: mask=%d rate=%d time=%d\n", discharge_mask, discharge_rate, discharge_time);
}
static char *batt_mask(char *p, unsigned int mask)
{
char status[256];
char *p = status;
- if (total_capa)
- p += sprintf(p, "%d%%", 100*total_full/total_capa);
+ if (total_full)
+ p += sprintf(p, "%d%%", (int)((long long)100*total_now/total_full));
else
p += sprintf(p, "??%%");
if (discharge_mask && discharge_time < 1000000)