ACPI support for battery_applet.



Please forgive me if this is -not- the right list for such things.  Best
I can tell there's no list specificaly for the gnome-applets package so here
goes.

Most every patch I've seen to put ACPI support into battery_applet only assumed
that the user would have one battery, so I put together one that should
accomodate as many batteries as possible.  If APM is there it falls back
to ACPI rather than have to pick APM or ACPI at compile time.  It's working
for me, though I admit the CPU usage seems a bit high on it right now.

Here's the patch.  I've only been able to test it against 2.4.7 w/ the
original Kernel ACPI drivers and 2.4.16 with the 20011205 drivers from Intel.

Thank you,

Justin Buist

*** read-battery.c.orig	Mon Dec 10 19:47:26 2001
--- read-battery.c	Mon Dec 10 19:42:46 2001
***************
*** 35,40 ****
--- 35,337 ----
  #include <gnome.h>
  #include "read-battery.h"
  
+ #ifdef __linux__
+ /* I don't see the info/status file ever containin more than this much
+  * data.  I should perhaps make this more dynamic.  It works for now though
+  */
+ #define PROC_MAX_ACPI_FILESIZE 512
+ /* Not explicitly declared in the kernel sources but remains consistent
+  * for both the info and status file.  Could possibly change as the ACPI
+  * support matures.
+  */
+ #define ACPI_FIELD_NAME_LEN 25
+ 
+ gboolean
+ reset_proc_stream (FILE ** fstream)
+ {
+   int dup_fd;
+ 
+   dup_fd = dup (fileno (*fstream));
+   if (dup_fd == -1)
+     {
+       g_error (_("Unable to dup a file descriptor in /proc: %s\n"),
+ 	       g_strerror (errno));
+       return (FALSE);
+     }
+ 
+   fclose (*fstream);
+   *fstream = fdopen (dup_fd, "r");
+   fseek (*fstream, 0, SEEK_SET); 
+ 
+   return (TRUE);
+ }
+ 
+ int
+ count_acpi_batteries (DIR * battery_dir)
+ {
+   int batteries = 0;
+   struct dirent *battery_entry = readdir (battery_dir);
+   while (battery_entry != NULL)
+     {
+       if (strcmp (battery_entry->d_name, ".") == 0 ||
+ 	  strcmp (battery_entry->d_name, "..") == 0)
+ 	{
+ 	  battery_entry = readdir (battery_dir);
+ 	  continue;
+ 	}
+       batteries++;
+       battery_entry = readdir (battery_dir);
+     }
+   rewinddir (battery_dir);
+ 
+   return (batteries);
+ }
+ 
+ void
+ acpi_open_data_files (FILE *** f_info_list, FILE *** f_status_list,
+ 		      DIR * battery_dir, int batteries)
+ {
+   struct dirent *battery_entry;
+   int curr_bat;
+   char tmp_buf[128];
+ 
+   *f_info_list = malloc (sizeof (FILE *) * batteries);
+   *f_status_list = malloc (sizeof (FILE *) * batteries);
+ 
+   for (curr_bat = 0; curr_bat < batteries; curr_bat++)
+     {
+       do
+ 	{
+ 	  battery_entry = readdir (battery_dir);
+ 	}
+       while (strcmp (battery_entry->d_name, ".") == 0 ||
+ 	     strcmp (battery_entry->d_name, "..") == 0);
+ 
+       /* Open the data files -- only done once.  Subsequent reads
+        * will dup() the fd and seek back to the beginning of the file
+        * to refresh the data stream
+        */
+       strcpy (tmp_buf, "/proc/acpi/battery/");
+       strcat (tmp_buf, battery_entry->d_name);
+       strcat (tmp_buf, "/info");
+       (*f_info_list)[curr_bat] = fopen (tmp_buf, "r");
+ 
+       strcpy (tmp_buf, "/proc/acpi/battery/");
+       strcat (tmp_buf, battery_entry->d_name);
+       strcat (tmp_buf, "/status");
+       (*f_status_list)[curr_bat] = fopen (tmp_buf, "r");
+     }
+ }
+ 
+ void
+ acpi_data_sanity_check (acpi_info * i)
+ {
+   if (i->rem_cap > i->full_cap)
+     {
+       /* This occured on Linux 2.4.7 on a Dell Inspiron 4000, eventually
+        * the system -did- correct itself though.
+        */
+       i->rem_cap = i->full_cap;
+     }
+ 
+ }
+ 
+ gboolean
+ read_acpi (int *percentage,
+ 	   int *ac_online, int *hours_remaining, int *minutes_remaining)
+ {
+   static acpi_info i;
+ 
+   /* Arrays holding references to our last stream to the info
+    * and status files for each battery
+    */
+   static FILE **f_info_list = NULL;
+   static FILE **f_status_list = NULL;
+ 
+   /* References to the info or status file that we're currently working
+    * with.
+    */
+   FILE *f_curr;
+ 
+   static int total_batteries = 0;
+   int curr_battery;
+ 
+ 
+   gboolean ret_val = TRUE;
+ 
+   char *ptr; /* temporary pointer used in char* manipulations */
+   char tmp_buf[128];
+   char *buffer = (char *) malloc (PROC_MAX_ACPI_FILESIZE);
+   char my_err[128] = "";
+ 
+   if (total_batteries == 0)
+     {
+       DIR *battery_dir = opendir ("/proc/acpi/battery");
+       if (battery_dir == NULL)
+ 	{
+ 	  /* apparently we have no ACPI battery support, return FALSE
+ 	   * signifying the ACPI subsystem failed, user will be presented
+ 	   * with dialog box now telling them that APM and ACPI have
+ 	   * both failed.
+ 	   */
+ 	  ret_val = FALSE;
+ 	  strcpy(my_err, "No acpi support found.");
+ 	  goto errored;
+ 	}
+ 
+       /* find out how many batteries we have */
+       total_batteries = count_acpi_batteries (battery_dir);
+       /* open up the info and status file for each battery */
+       acpi_open_data_files (&f_info_list, &f_status_list, battery_dir,
+ 			    total_batteries);
+       closedir (battery_dir);
+       battery_dir = NULL;
+     }
+ 
+   /* reset full capacity and remaining capacity values */
+   i.full_cap = 0;
+   i.rem_cap = 0;
+   /* Assume we're plugged in to AC, unless one of the batteries says
+    * otherwise.
+    */
+   i.ac_online_status = 1;
+ 
+   for (curr_battery = 0; curr_battery < total_batteries; curr_battery++)
+     {
+       /* Reset the FILE*'s that we're going to use in this loop */
+       reset_proc_stream(&(f_info_list[curr_battery]));
+       reset_proc_stream(&(f_status_list[curr_battery]));
+ 
+       /* Read data from the info file */
+       f_curr = f_info_list[curr_battery];
+       if (f_curr == FALSE)
+ 	{
+ 	  strcpy(my_err, "reset info failed");
+ 	  goto errored;
+ 	}
+ 
+       if (read (fileno (f_curr), buffer, PROC_MAX_ACPI_FILESIZE - 1) == -1)
+ 	{
+ 	  perror("read(): ");
+ 	  fprintf(stderr, "fd == %d\n", fileno (f_curr));
+ 	}
+ 
+       ptr = (char *) strstr (buffer, "Present:");
+       if (ptr == NULL)
+ 	{
+ 	  strcpy(my_err, "Present string not found");
+ 	  goto errored;
+ 	}
+ 
+       ptr += ACPI_FIELD_NAME_LEN;
+       if (*ptr == 'n') /* yes or no are only options */
+ 	{
+ 	  /* battery not present in socket, skip to next entry */
+ 	  continue;
+ 	}
+ 
+       /* battery present, find maximum charge it can hold */
+       ptr = (char *) strstr (ptr, "Design Capacity:");
+       if (ptr == NULL)
+ 	{
+ 	  strcpy(my_err, "Design Capacity string not found");
+ 	  goto errored;
+ 	}
+ 
+       ptr += ACPI_FIELD_NAME_LEN;
+       sscanf (ptr, "%s", tmp_buf);
+       i.full_cap += atoi (tmp_buf);
+ 
+       /* Read data from the status file */
+       f_curr = f_status_list[curr_battery];
+       if (f_curr == FALSE)
+ 	{
+ 	  strcpy(my_err, "reset status failed");
+ 	  goto errored;
+ 	}
+ 
+       read (fileno (f_curr), buffer, PROC_MAX_ACPI_FILESIZE - 1);
+ 
+       /* Find out if we're plugged into the AC outlet or not */
+       ptr = (char *) strstr (buffer, "State:");
+       if (ptr == NULL)
+ 	{
+ 	  strcpy(my_err, "State string not found");
+ 	  goto errored;
+ 	}
+ 
+       ptr += ACPI_FIELD_NAME_LEN;
+       if (*ptr == 'd')
+ 	{			/* discharging  or charging */
+ 	  /* battery is discharging, we are not on AC power */
+ 	  i.ac_online_status = 0;
+ 
+ 	  /* determine current draw on battery */
+ 	  ptr = (char *) strstr (ptr, "Present Rate:");
+ 	  if (ptr == NULL)
+ 	    {
+ 	      strcpy(my_err, "Present Rate string not found");
+ 	      goto errored;
+ 	    }
+ 
+ 	  ptr += ACPI_FIELD_NAME_LEN;
+ 	  sscanf (ptr, "%s", tmp_buf);
+ 	  i.present_rate = atoi (tmp_buf);
+ 
+ 	  if (i.present_rate == 0)
+ 	    {
+ 	      /* ACPI isn't reading the values right yet it seems */
+ 	      strcpy(my_err, "Unknown present rate");
+ 	      goto errored;
+ 	    }
+ 	}
+ 
+       /* Check power remaining on this battery */
+       ptr = (char *) strstr (ptr, "Remaining Capacity:");
+       if (ptr == NULL)
+ 	{
+ 	  strcpy(my_err, "Remaining Capacity not fnd.");
+ 	  goto errored;
+ 	}
+ 
+       ptr += ACPI_FIELD_NAME_LEN;
+       sscanf (ptr, "%s", tmp_buf);
+       i.rem_cap += atoi (tmp_buf);
+       if (i.rem_cap == 0)
+ 	{
+ 	  /* ACPI isn't reading the values right yet it seems */
+ 	  strcpy(my_err, "Unknown remaining capacity");
+ 	  goto errored;
+ 	}
+ 
+     }
+ 
+   /* All data from batteries accounted for -- do calculations. */
+   acpi_data_sanity_check (&i);
+ 
+   if (i.ac_online_status == 0)
+     {
+       /* Calculate time remaining until battery is dead */
+       double real_min_left = (double) i.rem_cap / i.present_rate * 60;
+ 
+       *hours_remaining = (int) floor (real_min_left / 60);
+       *minutes_remaining = (int) real_min_left % 60;
+     }
+ 
+   *percentage =
+     (int) floor ((double) i.rem_cap / (double) i.full_cap * (double) 100);
+ 
+   /* :KLUDGE: Easy way for me to figure out why the function failed */
+   goto success;
+ errored:
+   printf("read_acpi() failed: %s\n", my_err);
+ success:
+   *ac_online = i.ac_online_status;
+   free (buffer);
+   return ret_val;
+ }
+ #endif
+ 
  gboolean
  battery_read_charge (int * percentage,
  		     int * ac_online,
***************
*** 43,48 ****
--- 340,347 ----
  {
  #ifdef __linux__
    static FILE * apm_file = NULL;
+   static gboolean use_acpi = FALSE;
+   static gboolean gave_warning = FALSE;
  
    char buffer[256], units[10];
    apm_info i;
***************
*** 53,58 ****
--- 352,363 ----
    *hours_remaining = -1;
    *minutes_remaining = 1;
  
+   if (use_acpi == TRUE)
+     {
+       return (read_acpi (percentage, ac_online, hours_remaining,
+ 			 minutes_remaining));
+     }
+ 
    /*
     * (1) Open /proc/apm
     */
***************
*** 62,72 ****
  
        if (apm_file == NULL)
  	{
!           static gboolean gave_warning = FALSE;
  	  if (!gave_warning)
  	    {
! 	      g_warning (_("Cannot open /proc/apm!  Make sure that you built APM "
! 		          "support into your kernel.\n"));
  	      gave_warning = TRUE;
  	    }
  	  *ac_online = 1;
--- 367,384 ----
  
        if (apm_file == NULL)
  	{
! 	  /* try ACPI method because APM failed to open */
! 	  use_acpi = read_acpi (percentage, ac_online, hours_remaining,
! 				minutes_remaining);
! 	  if (use_acpi == TRUE)
! 	    return TRUE;
! 
  	  if (!gave_warning)
  	    {
! 	      g_warning (_
! 			 ("Cannot open /proc/apm or /proc/acpi/battery!  "
! 			  "Make sure that you built APM or ACPI battery "
! 			  "support into your kernel.\n"));
  	      gave_warning = TRUE;
  	    }
  	  *ac_online = 1;
***************
*** 94,114 ****
         * filesystem.
         *
         */
! 
!       int dup_fd;
! 
!       dup_fd = dup( fileno(apm_file) );
!       if (dup_fd == -1)
! 	{
! 	  g_error(_("Could not dup() APM file descriptor: %s\n"),
! 		  g_strerror(errno));
! 	  abort();
! 	}
! 
!       fclose(apm_file);
! 
!       apm_file = fdopen(dup_fd, "r");
!       fseek(apm_file, 0, SEEK_SET);
      }
  
    /*
--- 406,412 ----
         * filesystem.
         *
         */
!       reset_proc_stream(&apm_file);
      }
  
    /*
*** read-battery.h.orig	Mon Dec 10 19:47:37 2001
--- read-battery.h	Mon Dec 10 19:42:46 2001
***************
*** 14,19 ****
--- 14,51 ----
    int        battery_time;
    int        using_minutes;
  } apm_info;
+ 
+ typedef struct acpi_info {
+   int full_cap;
+   int rem_cap;
+   int present_rate;
+   int ac_online_status;
+ } acpi_info;
+ 
+ #include <math.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <dirent.h>
+ #include <fcntl.h>
+ 
+ /* Prototypes for Linux ACPI specific functions */
+ gboolean
+ reset_proc_stream (FILE ** fstream);
+ 
+ int
+ count_acpi_batteries (DIR * battery_dir);
+ 
+ void
+ acpi_open_data_files (FILE *** f_info_list, FILE *** f_status_list, 
+ 		      DIR * battery_dir, int batteries);
+ 
+ void
+ acpi_data_sanity_check (acpi_info * i);
+ 
+ gboolean
+ read_acpi (int *percentage, int *ac_online, int *hours_remaining, 
+ 	   int *minutes_remaining);
+ 
  #elif __FreeBSD__
  #include <fcntl.h>
  #include <machine/apm_bios.h>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]