Hi,
I fixed the sysfs-patch, at least for my system (lenny i386 with
standard kernel on a samsung x20), but it should be so generic that it
works on any system.
There were several problems and I'll describe shortly my way to fix them:
- The AC adapter is not always listed in /sys/class/power_supply/AC/.
Now it checks in any directory in /sys/class/power_supply if the file
"type" exists and contains "Mains". I hope that AC adapters always
have the type "Mains", otherwise the other possibilities should also
be considered. Only one AC adapter (are there systems with more than
one?) is used.
- In 2.6.24 the charge status is in
/sys/class/power_supply/$battery/charge_full, charge_full_design and
charge_now. It seems like it was energy_* before. now both
possibilities are taken care of.
- The monitor showed random values for the remaining capacity -
probably because the struct that saves acpi status was not initialised
(because of charge_* vs energy_*) and only allocated with malloc.
using calloc should give more deterministic values.
I also changed some code that didn't make sense to me.
I noticed that after unplugging the power cord the monitor show the
new status quite quick, but it takes about 30 seconds until the
displayed time left makes sense. I don't know if this is normal or
connected to my patch.
Cheers,
- Daniel
diff -ru xfce4-battery-plugin-0.5.0.orig/panel-plugin/libacpi.c xfce4-battery-plugin-0.5.0-fixed/panel-plugin/libacpi.c
--- xfce4-battery-plugin-0.5.0.orig/panel-plugin/libacpi.c 2008-04-06 02:56:10.502679698 +0200
+++ xfce4-battery-plugin-0.5.0-fixed/panel-plugin/libacpi.c 2008-04-06 03:15:11.306678469 +0200
@@ -54,6 +54,10 @@
static char batteries[MAXBATT][128];
static char battinfo[MAXBATT][128];
+/* path to AC adapter because not all AC adapter are listed
+in /sys/class/power_supply/AC/ this obviously only supports one AC adapter. */
+static char sysfsacdir[128];
+
#ifndef __linux__
#if HAVE_SYSCTL
static int
@@ -181,9 +185,76 @@
#endif
#endif
+int check_acpi_sysfs(void)
+{
+ DIR *sysfs;
+ struct dirent *batt;
+ char *name;
+ FILE *typefile;
+ char typepath[128];
+ char tmptype[8];
+
+ sysfs = opendir("/sys/class/power_supply");
+ if (sysfs == 0)
+ {
+ #ifdef DEBUG
+ printf("DBG:No acpi support for sysfs. Trying procfs...\n");
+ #endif
+ return 2;
+ }
+
+ while ((batt = readdir(sysfs)))
+ {
+ name = batt->d_name;
+ if (!strncmp(".", name, 1)) continue;
+ /* check whether /sys/class/power_supply/$name/type exists and
+ contains "Battery" or "Mains" */
+ sprintf(typepath, "/sys/class/power_supply/%s/type",name);
+ if(!(typefile = fopen(typepath, "r"))) continue;
+ fgets(tmptype, 8, typefile);
+ fclose(typefile);
+ if(strncmp("Battery", tmptype, 7)==0)
+ {
+ sprintf(batteries[batt_count], "/sys/class/power_supply/%s", name);
+ #ifdef DEBUG
+ printf("DBG:battery number %d at:\n",batt_count);
+ printf("DBG:sysfs dir->%s\n",batteries[batt_count]);
+ printf("DBG:------------------------\n");
+ #endif
+ batt_count++;
+ }
+ /* I guess that the type of the AC adapter is always "Mains" (?) */
+ else if(strncmp("Mains", tmptype, 5)==0){
+ sprintf(sysfsacdir, "/sys/class/power_supply/%s", name);
+ #ifdef DEBUG
+ printf("DBG:sysfs AC dir->%s\n",sysfsacdir);
+ printf("DBG:------------------------\n");
+ #endif
+ }
+ }
+ closedir(sysfs);
+ if ( batt_count == 0 )
+ {
+#ifdef DEBUG
+ printf("DBG:No acpi support for sysfs. Trying procfs...\n");
+#endif
+ acpi_sysfs = 0;
+ return 2;
+ }
+ else
+ {
+ acpi_sysfs = 1;
+ return 0;
+ }
+}
+
/* see if we have ACPI support */
int check_acpi(void)
{
+#ifdef __linux__
+ if ( check_acpi_sysfs() == 0 )
+ return 0;
+#endif
DIR *battdir;
struct dirent *batt;
char *name;
@@ -262,9 +333,82 @@
#endif
}
+int read_sysfs_int(char* filename)
+{
+ FILE* f;
+ f = fopen(filename,"r");
+ if ( !f )
+ {
+#ifdef DEBUG
+ printf("DBG:Could not open %s\n",filename);
+#endif
+ return 0;
+ }
+ int out;
+ fscanf(f,"%d",&out);
+ fclose(f);
+ return out;
+}
+
+char* read_sysfs_string(char* filename)
+{
+ FILE* f;
+ f = fopen(filename,"r");
+ if ( !f )
+ {
+#ifdef DEBUG
+ printf("DBG:Could not open %s\n",filename);
+#endif
+ return NULL;
+ }
+ fscanf(f,"%s",buf2);
+ fclose(f);
+ return buf2;
+}
+
+int read_acad_state_sysfs(void)
+{
+ DIR *sysfs;
+ struct dirent *propety;
+ char *name;
+ char onlinefilepath[128];
+
+ sysfs = opendir(sysfsacdir);
+ if (sysfs == 0)
+ {
+ #ifdef DEBUG
+ printf("DBG:Can't open %s",sysfsacdir);
+ #endif
+ return 0;
+ }
+ closedir(sysfs);
+
+ if (!acadstate) acadstate=(ACADstate *)malloc(sizeof(ACADstate));
+ /* this code doesn't make much sense.. why look at the whole directory?!
+ while ((propety = readdir(sysfs)))
+ {
+ name = propety->d_name;
+ if (!strncmp(".", name, 1) || !strncmp("..", name, 2)) continue;
+
+ if (strcmp(name,"online") == 0)
+ {
+ acadstate->state = ( read_sysfs_int("/sys/class/power_supply/AC/online") == 1 ) ;
+ }
+ }
+ */
+ sprintf(onlinefilepath, "%s/online", sysfsacdir);
+ /* if onlinefilepath doesn't exist read_sysfs_int() will return 0
+ so acadstate->state will be 0, that should be ok */
+ acadstate->state = ( read_sysfs_int(onlinefilepath) == 1 );
+
+ return acadstate->state;
+}
+
int read_acad_state(void)
{
#ifdef __linux__
+ if (acpi_sysfs)
+ return read_acad_state_sysfs();
FILE *acpi;
char *ptr;
char stat;
@@ -354,20 +498,83 @@
#endif
}
+int read_acpi_info_sysfs(int battery)
+{
+ DIR *sysfs;
+ struct dirent *propety;
+ char *name;
+
+ sysfs = opendir(batteries[battery]);
+ if (sysfs == 0)
+ {
+ #ifdef DEBUG
+ printf("DBG:Can't open %s!\n", batteries[battery]);
+ #endif
+ return 0;
+ }
+ /* malloc.. might explain the random battery level values on 2.6.24
+ systems (energe_full is called charge_full so the value isn't initialised
+ and some random data from the heap is displayed..)
+ if (!acpiinfo) acpiinfo=(ACPIinfo *)malloc(sizeof(ACPIinfo));
+ */
+ if (!acpiinfo) acpiinfo=(ACPIinfo *)calloc(1, sizeof(ACPIinfo));
+
+ while ((propety = readdir(sysfs)))
+ {
+ name = propety->d_name;
+ if (!strncmp(".", name, 1) || !strncmp("..", name, 2)) continue;
+ /* at least on my system this is called charge_full */
+ if ((strcmp(name,"energy_full") == 0) || (strcmp(name,"charge_full") == 0))
+ {
+ sprintf(buf,"%s/%s",batteries[battery], name);
+ acpiinfo->last_full_capacity = read_sysfs_int(buf);
+ }
+ if ((strcmp(name,"energy_full_design") == 0) || (strcmp(name,"charge_full_design") == 0))
+ {
+ sprintf(buf,"%s/%s",batteries[battery], name);
+ acpiinfo->design_capacity = read_sysfs_int(buf);
+ }
+ if (strcmp(name,"technology") == 0)
+ {
+ char *tmp;
+ sprintf(buf,"%s/%s",batteries[battery], name);
+ tmp = read_sysfs_string(buf);
+ if (tmp != NULL)
+ {
+ if (strcmp(tmp,"Li-ion") == 0)
+ acpiinfo->battery_technology = 1;
+ else
+ acpiinfo->battery_technology = 0;
+ }
+ }
+ if (strcmp(name,"present") == 0)
+ {
+ sprintf(buf,"%s/%s",batteries[battery], name);
+ acpiinfo->present = read_sysfs_int(buf);
+ }
+ }
+ closedir(sysfs);
+ return acpiinfo->present;
+}
+
int read_acpi_info(int battery)
{
#ifdef __linux__
- FILE *acpi;
- char *ptr;
- char stat;
- int temp;
-
if (battery > MAXBATT) {
#ifdef DEBUG
printf("DBG: error, battery > MAXBATT (%d)\n",MAXBATT);
#endif
return 0;
}
+
+ if (acpi_sysfs)
+ return read_acpi_info_sysfs(battery);
+
+ FILE *acpi;
+ char *ptr;
+ char stat;
+ int temp;
+
if (!(acpi = fopen (battinfo[battery], "r"))) {
#ifdef DEBUG
printf("DBG:cannot open %s for read!\n",battinfo[battery]);
@@ -514,9 +721,80 @@
}
+int read_acpi_state_sysfs(int battery)
+{
+ DIR *sysfs;
+ struct dirent *propety;
+ char *name;
+
+ sysfs = opendir(batteries[battery]);
+ if (sysfs == 0)
+ {
+ #ifdef DEBUG
+ printf("DBG:Can't open %s!\n", batteries[battery]);
+ #endif
+ return 0;
+ }
+
+ /* again it might be better to use calloc
+ if (!acpistate) acpistate=(ACPIstate *)malloc(sizeof(ACPIstate));
+ */
+ if (!acpistate) acpistate=(ACPIstate *)calloc(1, sizeof(ACPIstate));
+
+ while ((propety = readdir(sysfs)))
+ {
+ name = propety->d_name;
+ if (!strncmp(".", name, 1) || !strncmp("..", name, 2)) continue;
+
+ if (strcmp(name,"status") == 0)
+ {
+ char *tmp;
+ sprintf(buf,"%s/%s",batteries[battery], name);
+ tmp = read_sysfs_string(buf);
+ if ( tmp != NULL )
+ {
+ if (strcmp(tmp,"Charging") == 0)
+ acpistate->state = CHARGING;
+ else if (strcmp(tmp,"Discharging") == 0)
+ acpistate->state = DISCHARGING;
+ else if (strcmp(tmp,"Full") == 0)
+ acpistate->state = POWER;
+ else
+ acpistate->state = UNKNOW;
+ }
+ }
+ /* on my system this is called charge_now */
+ if ((strcmp(name,"energy_now") == 0) || (strcmp(name,"charge_now") == 0))
+ {
+ sprintf(buf,"%s/%s",batteries[battery], name);
+ acpistate->rcapacity = read_sysfs_int(buf);
+ acpistate->percentage = (((float) acpistate->rcapacity)/acpiinfo->last_full_capacity) * 100;
+ }
+ if (strcmp(name,"current_now") == 0)
+ {
+ sprintf(buf,"%s/%s",batteries[battery], name);
+ acpistate->prate = read_sysfs_int(buf);
+ if ( acpistate->prate < 0 )
+ acpistate->prate = 0;
+ if ( acpistate->prate > 0 )
+ acpistate->rtime = (((float) acpistate->rcapacity) / acpistate->prate) * 60;
+ }
+ if (strcmp(name,"voltage_now") == 0)
+ {
+ sprintf(buf,"%s/%s",batteries[battery], name);
+ acpistate->pvoltage = read_sysfs_int(buf);
+ }
+ }
+ closedir(sysfs);
+ return acpiinfo->present;
+}
+
int read_acpi_state(int battery)
{
#ifdef __linux__
+ if (acpi_sysfs)
+ return read_acpi_state_sysfs(battery);
+
FILE *acpi;
char *ptr;
char stat;
diff -ru xfce4-battery-plugin-0.5.0.orig/panel-plugin/libacpi.h xfce4-battery-plugin-0.5.0-fixed/panel-plugin/libacpi.h
--- xfce4-battery-plugin-0.5.0.orig/panel-plugin/libacpi.h 2007-01-17 18:56:51.000000000 +0100
+++ xfce4-battery-plugin-0.5.0-fixed/panel-plugin/libacpi.h 2008-04-05 22:13:55.522679792 +0200
@@ -80,6 +80,8 @@
int batt_count;
/* temp buffer */
char buf[512];
+char buf2[512];
+int acpi_sysfs;
#else
extern int batt_count;
extern ACPIstate *acpistate;