libacpi.c

Go to the documentation of this file.
00001 /*
00002  * (C)opyright 2007 Nico Golde <nico@ngolde.de>
00003  * See LICENSE file for license details
00004  */
00005 
00006 #include <stdio.h>
00007 #include <stdlib.h>
00008 #include <unistd.h>
00009 #include <string.h>
00010 #include <dirent.h>
00011 #include <ctype.h>
00012 #include <stddef.h>
00013 
00014 #include "libacpi.h"
00015 #include "list.h"
00016 
00017 static int read_acpi_battinfo(const int num);
00018 static int read_acpi_battalarm(const int num);
00019 static int read_acpi_battstate(const int num);
00020 static void read_acpi_thermalzones(global_t *globals);
00021 
00022 typedef struct {
00023         char * value;
00024         size_t offset;
00025 } acpi_value_t;
00026 
00027 static acpi_value_t
00028 battinfo_values[] = {
00029         { "last full capacity:", offsetof(battery_t, last_full_cap) },
00030         { "design voltage:", offsetof(battery_t, design_voltage) },
00031         { "design capacity warning:", offsetof(battery_t, design_warn) },
00032         { "design capacity low:", offsetof(battery_t, design_low) },
00033         { "capacity granularity 1:", offsetof(battery_t, design_level1) },
00034         { "capacity granularity 2:", offsetof(battery_t, design_level2) },
00035         { NULL, 0 }
00036 };
00037 
00038 static acpi_value_t
00039 battstate_values[] = {
00040         { "present rate:", offsetof(battery_t, present_rate) },
00041         { "remaining capacity:", offsetof(battery_t, remaining_cap) },
00042         { "present voltage:", offsetof(battery_t, present_voltage) },
00043         { NULL, 0 }
00044 };
00045 
00046 /* given a buffer for example from a file, search for key
00047  * and return a pointer to the value of it. On error return NULL*/
00048 static char *
00049 scan_acpi_value(const char *buf, const char *key){
00050         char *ptr = NULL;
00051         char *tmpbuf = NULL;
00052         char *tmpkey = NULL;
00053         char *tmpval = NULL;
00054 
00055         if((tmpbuf = strdup(buf)) == NULL)
00056                 return NULL;
00057 
00058         /* jump to the key in buffer */
00059         if((tmpkey = strstr(tmpbuf, key))) {
00060                 /* jump behind the key, whitespaces and tabs */
00061                 for(tmpkey += strlen(key); *tmpkey && (*tmpkey == ' ' || *tmpkey == '\t'); tmpkey++);
00062                 for(tmpval = tmpkey; *tmpval && *tmpval != ' ' &&
00063                                 *tmpval != '\t' && *tmpval != '\n' &&
00064                                 *tmpval != '\r'; tmpval++);
00065                 if(tmpval)
00066                         *tmpval = '\0';
00067 
00068                 if((ptr = strdup(tmpkey)) == NULL)
00069                         return NULL;
00070         }
00071         free(tmpbuf);
00072         return ptr;
00073 }
00074 
00075 /* reads a file into a buffer and returns a pointer to it, or NULL on error */
00076 static char *
00077 get_acpi_content(const char *file){
00078         FILE *input = NULL;
00079         char *buf = NULL;
00080         int read = 0;
00081 
00082         if((buf = malloc(MAX_BUF + 1)) == NULL)
00083                 return NULL;
00084         if((input = fopen(file, "r")) == NULL)
00085                 return NULL;
00086         read = fread(buf, MAX_BUF, 1, input);
00087         buf[read - 1] = '\0';
00088         fclose(input);
00089         return buf;
00090 }
00091 
00092 /* returns the acpi version or NOT_SUPPORTED(negative value) on failure */
00093 static int
00094 get_acpi_version(void){
00095         char *tmp = get_acpi_content(PROC_ACPI "info");
00096         char *version = NULL;
00097         
00098         if(!tmp) {
00099                 tmp = get_acpi_content("/sys/module/acpi/parameters/acpica_version");
00100                 if (tmp) return strtol(tmp, NULL, 10);
00101                 else return NOT_SUPPORTED;
00102         }
00103         if((version = scan_acpi_value(tmp, "version:")) == NULL){
00104                 free(tmp);
00105                 return NOT_SUPPORTED;
00106         }
00107         free(tmp);
00108         return strtol(version, NULL, 10);
00109 }
00110 
00111 /* check if acpi is supported on the system, return 0 on success
00112  * and -1 if not */
00113 int
00114 check_acpi_support(void){
00115         int version = get_acpi_version();
00116 
00117         /* we don't support 2.4 kernel versions TODO */
00118         if(version == NOT_SUPPORTED || version < 20020214)
00119                 return NOT_SUPPORTED;
00120         return SUCCESS;
00121 }
00122 
00123 /* reads existent battery directories and starts to fill the battery
00124  * structure. Returns 0 on success, negative values on error */
00125 int
00126 init_acpi_batt(global_t *globals){
00127         char *names[MAX_ITEMS];
00128         battery_t *binfo;
00129         list_t *lst = NULL;
00130         node_t *node = NULL;
00131         int i = 0;
00132 
00133         globals->batt_count = 0;
00134         if((lst = dir_list(PROC_ACPI "battery")) == NULL || !lst->top)
00135                 return NOT_SUPPORTED;
00136         for(node = lst->top; node; node=node->next){
00137                 if((names[globals->batt_count] = strdup(node->name)) == NULL){
00138                         delete_list(lst);
00139                         return ALLOC_ERR;
00140                 }
00141                 globals->batt_count++;
00142         }
00143 
00144         if(globals->batt_count > MAX_ITEMS) return ITEM_EXCEED;
00145 
00146         /* A quick insertion sort, to sort battery names */
00147         {
00148                 char *tmp1, *tmp2;
00149                 int x,y;
00150                 for (x = 1; x < globals->batt_count; x++) {
00151                         tmp1 = names[x];
00152                         y = x - 1;
00153                         while ((y >= 0) && ((strcmp (tmp1, names[y])) < 0)) {
00154                                 tmp2 = names[y + 1];
00155                                 names[y + 1] = names[y];
00156                                 names[y] = tmp2;
00157                         }
00158                 }
00159         }
00160 
00161         for (i=0; i < globals->batt_count && i < MAX_ITEMS; i++){
00162                 binfo = &batteries[i];
00163                 snprintf(binfo->name, MAX_NAME, "%s", names[i]);
00164                 snprintf(binfo->state_file, MAX_NAME, PROC_ACPI "battery/%s/state", names[i]);
00165                 snprintf(binfo->info_file, MAX_NAME, PROC_ACPI "battery/%s/info", names[i]);
00166                 snprintf(binfo->alarm_file, MAX_NAME, PROC_ACPI "battery/%s/alarm", names[i]);
00167                 read_acpi_battinfo(i);
00168                 read_acpi_battalarm(i);
00169         }
00170         delete_list(lst);
00171         return SUCCESS;
00172 }
00173 
00174 /* reads the acpi state and writes it into the globals structure, void */
00175 void
00176 read_acpi_acstate(global_t *globals){
00177         adapter_t *ac = &globals->adapt;
00178         char *buf = NULL;
00179         char *tmp = NULL;
00180 
00181         if(ac->state_file && (buf = get_acpi_content(ac->state_file)) == NULL){
00182                 ac->ac_state = P_ERR;
00183                 return;
00184         }
00185         if((tmp = scan_acpi_value(buf, "state:")) && !strncmp(tmp, "on-line", 7))
00186                 ac->ac_state = P_AC;
00187         else if(tmp && !strncmp(tmp, "off-line", 8))
00188                 ac->ac_state = P_BATT;
00189         else ac->ac_state = P_ERR;
00190         free(buf);
00191 }
00192 
00193 /* reads the name of the ac-adapter directory and fills the adapter_t
00194  * structure with the name and the state-file. Return 0 on success, negative values on errors */
00195 int
00196 init_acpi_acadapt(global_t *globals){
00197         list_t *lst = NULL;
00198         adapter_t *ac = &globals->adapt;
00199 
00200         if((lst = dir_list(PROC_ACPI "ac_adapter")) == NULL || !lst->top)
00201                 return NOT_SUPPORTED;
00202 
00203         if((!lst->top->name || ((ac->name = strdup(lst->top->name)) == NULL))){
00204                 delete_list(lst);
00205                 return ALLOC_ERR;
00206         }
00207         snprintf(ac->state_file, MAX_NAME, PROC_ACPI "ac_adapter/%s/state", ac->name);
00208         delete_list(lst);
00209         read_acpi_acstate(globals);
00210         return SUCCESS;
00211 }
00212 
00213 /* read acpi information for fan num, returns 0 on success and negative values on errors */
00214 int
00215 read_acpi_fan(const int num){
00216         char *buf = NULL;
00217         char *tmp = NULL;
00218         fan_t *info = &fans[num];
00219 
00220         if(num > MAX_ITEMS) return ITEM_EXCEED;
00221 
00222         /* scan state file */
00223         if((buf = get_acpi_content(info->state_file)) == NULL)
00224                 info->fan_state = F_ERR;
00225 
00226         if(!buf || (tmp = scan_acpi_value(buf, "status:")) == NULL){
00227                 info->fan_state = F_ERR;
00228                 return NOT_SUPPORTED;
00229         }
00230         if (tmp[0] == 'o' && tmp[1] == 'n') info->fan_state = F_ON;
00231         else if(tmp[0] == 'o' && tmp[1] == 'f') info->fan_state = F_OFF;
00232         else info->fan_state = F_ERR;
00233         return SUCCESS;
00234 }
00235 
00236 /* read all fans, fill the fan structures */
00237 static void
00238 read_acpi_fans(global_t *globals){
00239         unsigned int i;
00240         for(i = 0; i < globals->fan_count; i++)
00241                 read_acpi_fan(i);
00242 }
00243 
00244 /* reads the names of the fan directories, fills fan_t,
00245  * return 0 on success, negative values on errors */
00246 int
00247 init_acpi_fan(global_t *globals){
00248         char *names[MAX_ITEMS];
00249         list_t *lst = NULL;
00250         node_t *node = NULL;
00251         unsigned int i = 0;
00252         fan_t *finfo = NULL;
00253         globals->fan_count = 0;
00254 
00255         if((lst = dir_list(PROC_ACPI "fan")) == NULL || !lst->top)
00256                 return NOT_SUPPORTED;
00257         for(node = lst->top; node; node = node->next){
00258                 if((names[globals->fan_count] = strdup(node->name)) == NULL){
00259                         delete_list(lst);
00260                         return ALLOC_ERR;
00261                 }
00262                 globals->fan_count++;
00263         }
00264 
00265         if(globals->fan_count > MAX_ITEMS) return ITEM_EXCEED;
00266 
00267         for (; i < globals->fan_count && i < MAX_ITEMS; i++){
00268                 finfo = &fans[i];
00269                 snprintf(finfo->name, MAX_NAME, "%s", names[i]);
00270                 snprintf(finfo->state_file, MAX_NAME, PROC_ACPI "fan/%s/state", names[i]);
00271         }
00272         delete_list(lst);
00273         read_acpi_fans(globals);
00274         return SUCCESS;
00275 }
00276 
00277 /* reads the name of the thermal-zone directory and fills the adapter_t
00278  * structure with the name and the state-file. Return 0 on success, negative values on errors */
00279 int
00280 init_acpi_thermal(global_t *globals){
00281         char *names[MAX_ITEMS];
00282         list_t *lst = NULL;
00283         node_t *node = NULL;
00284         thermal_t *tinfo = NULL;
00285         unsigned int i = 0;
00286         globals->thermal_count = 0;
00287 
00288         if((lst = dir_list(PROC_ACPI "thermal_zone")) == NULL)
00289                 return NOT_SUPPORTED;
00290         for(node = lst->top; node; node = node->next){
00291                 if((names[globals->thermal_count] = strdup(node->name)) == NULL){
00292                         delete_list(lst);
00293                         return ALLOC_ERR;
00294                 }
00295                 globals->thermal_count++;
00296         }
00297 
00298         if(globals->thermal_count > MAX_ITEMS) return ITEM_EXCEED;
00299 
00300         for (; i < globals->thermal_count && i < MAX_ITEMS; i++){
00301                 tinfo = &thermals[i];
00302                 snprintf(tinfo->name, MAX_NAME, "%s", names[i]);
00303                 snprintf(tinfo->state_file, MAX_NAME, PROC_ACPI "thermal_zone/%s/state", names[i]);
00304                 snprintf(tinfo->temp_file, MAX_NAME, PROC_ACPI "thermal_zone/%s/temperature", names[i]);
00305                 snprintf(tinfo->cooling_file, MAX_NAME, PROC_ACPI "thermal_zone/%s/cooling_mode", names[i]);
00306                 snprintf(tinfo->freq_file, MAX_NAME, PROC_ACPI "thermal_zone/%s/polling_frequency", names[i]);
00307                 snprintf(tinfo->trips_file, MAX_NAME, PROC_ACPI "thermal_zone/%s/trip_points", names[i]);
00308         }
00309         delete_list(lst);
00310         read_acpi_thermalzones(globals);
00311         return SUCCESS;
00312 }
00313 
00314 /* checks the string state and sets the thermal state, returns void */
00315 static void
00316 thermal_state(const char *state, thermal_t *info){
00317         if(state[0] == 'o')
00318                 info->therm_state = T_OK;
00319         else if(!strncmp (state, "crit", 4))
00320                 info->therm_state = T_CRIT;
00321         else if (!strncmp (state, "hot", 3))
00322                 info->therm_state = T_HOT; else if (!strncmp (state, "pas", 3))
00323                 info->therm_state = T_PASS;
00324         else
00325                 info->therm_state = T_ACT;
00326 }
00327 
00328 /* checks the string tmp and sets the cooling mode */
00329 static void
00330 fill_cooling_mode(const char *tmp, thermal_t *info){
00331         if(tmp[0] == 'a')
00332                 info->therm_mode = CO_ACT;
00333         else if(tmp[0]  == 'p')
00334                 info->therm_mode = CO_PASS;
00335         else info->therm_mode = CO_CRIT;
00336 }
00337 
00338 /* reads values for thermal_zone num, return 0 on success, negative values on error */
00339 int
00340 read_acpi_zone(const int num, global_t *globals){
00341         char *buf = NULL;
00342         char *tmp = NULL;
00343         thermal_t *info = &thermals[num];
00344 
00345         if(num > MAX_ITEMS) return ITEM_EXCEED;
00346 
00347         /* scan state file */
00348         if((buf = get_acpi_content(info->state_file)) == NULL)
00349                 info->therm_state = T_ERR;
00350 
00351         if(buf && (tmp = scan_acpi_value(buf, "state:")))
00352                         thermal_state(tmp, info);
00353         if(buf) free(buf);
00354 
00355         /* scan temperature file */
00356         if((buf = get_acpi_content(info->temp_file)) == NULL)
00357                 info->temperature = NOT_SUPPORTED;
00358 
00359         if(buf && (tmp = scan_acpi_value(buf, "temperature:"))){
00360                 info->temperature = strtol(tmp, NULL, 10);
00361                 /* if we just have one big thermal zone, this will be the global temperature */
00362                 if(globals->thermal_count == 1)
00363                         globals->temperature = info->temperature;
00364         }
00365         if(buf) free(buf);
00366 
00367         /* scan cooling mode file */
00368         if((buf = get_acpi_content(info->cooling_file)) == NULL)
00369                 info->therm_mode = CO_ERR;
00370         if(buf && (tmp = scan_acpi_value(buf, "cooling mode:")))
00371                 fill_cooling_mode(tmp, info);
00372         else info->therm_mode = CO_ERR;
00373         if(buf) free(buf);
00374 
00375         /* scan polling_frequencies file */
00376         if((buf = get_acpi_content(info->freq_file)) == NULL)
00377                 info->frequency = DISABLED;
00378         if(buf && (tmp = scan_acpi_value(buf, "polling frequency:")))
00379                 info->frequency = strtol(tmp, NULL, 10);
00380         else info->frequency = DISABLED;
00381         if(buf) free(buf);
00382 
00383         /* TODO: IMPLEMENT TRIP POINTS FILE */
00384 
00385         return SUCCESS;
00386 }
00387 
00388 /* read all thermal zones, fill the thermal structures */
00389 static void
00390 read_acpi_thermalzones(global_t *globals){
00391         unsigned int i;
00392         for(i = 0; i < globals->thermal_count; i++)
00393                 read_acpi_zone(i, globals);
00394 }
00395 
00396 /* fill battery_state for given battery, return 0 on success or negative values on error */
00397 static void
00398 batt_charge_state(const int num, battery_t *info){
00399         unsigned int high = info->last_full_cap / 2;
00400         unsigned int med = high / 2;
00401 
00402         if(info->remaining_cap > high)
00403                 info->batt_state = B_HIGH;
00404         else if(info->remaining_cap <= high && info->remaining_cap > med)
00405                 info->batt_state = B_MED;
00406         else if(info->remaining_cap <= med && info->remaining_cap > info->design_warn)
00407                 info->batt_state = B_LOW;
00408         else if(info->remaining_cap <= info->design_warn && info->remaining_cap > info->design_low)
00409                 info->batt_state = B_CRIT;
00410         else info->batt_state = B_HARD_CRIT;
00411 }
00412 
00413 /* fill charge_state of a given battery num, return 0 on success or negative values on error */
00414 static void
00415 fill_charge_state(const int num, const char *state, battery_t *info){
00416         if(state[0] == 'u')
00417                 info->charge_state = C_ERR;
00418         else if(!strncmp (state, "disch", 5))
00419                 info->charge_state = C_DISCHARGE;
00420         else if (!strncmp (state, "charge", 6))
00421                 info->charge_state = C_CHARGED;
00422         else if (!strncmp (state, "chargi", 6))
00423                 info->charge_state = C_CHARGE;
00424         else
00425                 info->charge_state = C_NOINFO;
00426 }
00427 
00428 /* read alarm capacity, return 0 on success, negative values on error */
00429 static int
00430 read_acpi_battalarm(const int num){
00431         char *buf = NULL;
00432         char *tmp = NULL;
00433         battery_t *info = &batteries[num];
00434 
00435         if((buf = get_acpi_content(info->alarm_file)) == NULL)
00436                 return NOT_SUPPORTED;
00437 
00438         if((tmp = scan_acpi_value(buf, "alarm:")) && tmp[0] != 'u')
00439                 info->alarm = strtol(tmp, NULL, 10);
00440         else
00441                 info->alarm = NOT_SUPPORTED;
00442         free(buf);
00443         return SUCCESS;
00444 }
00445 
00446 /* reads static values for a battery (info file), returns SUCCESS */
00447 static int
00448 read_acpi_battinfo(const int num){
00449         char *buf = NULL;
00450         char *tmp = NULL;
00451         battery_t *info = &batteries[num];
00452         unsigned int i = 0;
00453 
00454         if((buf = get_acpi_content(info->info_file)) == NULL)
00455                 return NOT_SUPPORTED;
00456 
00457         /* you have to read the present value always since a battery can be taken away while
00458          * refreshing the data */
00459         if((tmp = scan_acpi_value(buf, "present:")) && !strncmp(tmp, "yes", 3)) info->present = 1;
00460         else {
00461                 info->present = 0;
00462                 free(buf);
00463                 return NOT_PRESENT;
00464         }
00465 
00466         if((tmp = scan_acpi_value(buf, "design capacity:")) && tmp[0] != 'u'){
00467                 info->design_cap = strtol(tmp, NULL, 10);
00468                 /* workaround ACPI's broken way of reporting no battery */
00469                 if(info->design_cap == 655350) info->design_cap = NOT_SUPPORTED;
00470         }
00471         else info->design_cap = NOT_SUPPORTED;
00472 
00473         for (;battinfo_values[i].value; i++) {
00474                 if ((tmp = scan_acpi_value(buf, battinfo_values[i].value)) && tmp[0] != 'u')
00475                         *((int *)(((char *)info) + battinfo_values[i].offset)) = strtol(tmp, NULL, 10);
00476                 else
00477                         *((int *)(((char *)info) + battinfo_values[i].offset)) = NOT_SUPPORTED;
00478         }
00479 
00480         /* TODO remove debug */
00481         /* printf("%s\n", buf); */
00482         free(buf);
00483 
00484         return SUCCESS;
00485 }
00486 
00487 /* read information for battery num, return 0 on success or negative values on error */
00488 static int
00489 read_acpi_battstate(const int num){
00490         char *buf = NULL;
00491         char *tmp = NULL;
00492         battery_t *info = &batteries[num];
00493         unsigned int i = 0;
00494 
00495 
00496         if((buf = get_acpi_content(info->state_file)) == NULL)
00497                 return NOT_SUPPORTED;
00498         
00499         if((tmp = scan_acpi_value(buf, "present:")) && !strncmp(tmp, "yes", 3)) info->present = 1;
00500         else {
00501                 info->present = 0;
00502                 free(buf);
00503                 return NOT_PRESENT;
00504         }
00505 
00506         /* TODO REMOVE DEBUG */
00507         /* printf("%s\n\n", buf); */
00508 
00509         if((tmp = scan_acpi_value(buf, "charging state:")) && tmp[0] != 'u')
00510                 fill_charge_state(num, tmp, info);
00511         else info->charge_state = C_NOINFO;
00512 
00513         for (;battstate_values[i].value; i++) {
00514                 if ((tmp = scan_acpi_value(buf, battstate_values[i].value)) && tmp[0] != 'u')
00515                         *((int *)(((char *)info) + battstate_values[i].offset)) = strtol(tmp, NULL, 10);
00516                 else
00517                         *((int *)(((char *)info) + battstate_values[i].offset)) = NOT_SUPPORTED;
00518         }
00519 
00520         /* get information from the info file */
00521         batt_charge_state(num, info);
00522         
00523         free(buf);
00524         return SUCCESS;
00525 }
00526 
00527 /* calculate percentage of battery capacity num */
00528 static void
00529 calc_remain_perc(const int num){
00530         float lfcap;
00531         battery_t *info = &batteries[num];
00532         int perc;
00533 
00534         if(info->remaining_cap < 0){
00535                 info->percentage = NOT_SUPPORTED;
00536                 return;
00537         }
00538         else{
00539                 lfcap = info->last_full_cap;
00540                 if(lfcap <= 0) lfcap = 1;
00541                 perc = (int) ((info->remaining_cap / lfcap) * 100.0);
00542         }
00543         info->percentage = perc > 100 ? 100 : perc;
00544 }
00545 
00546 /* calculate remaining charge time for battery num */
00547 static void
00548 calc_remain_chargetime(const int num){
00549         battery_t *info = &batteries[num];
00550 
00551         if(info->present_rate < 0 || info->charge_state != C_CHARGE){
00552                 info->charge_time = 0;
00553                 return;
00554         }
00555         info->charge_time = (int) ((((float)info->last_full_cap - (float)info->remaining_cap) / info->present_rate) * 60.0);
00556 }
00557 
00558 /* calculate remaining time for battery num */
00559 static void
00560 calc_remain_time(const int num){
00561         battery_t *info = &batteries[num];
00562 
00563         if(info->present_rate < 0 || info->charge_state != C_DISCHARGE){
00564                 info->remaining_time = 0;
00565                 return;
00566         }
00567         info->remaining_time = (int) (((float)info->remaining_cap / (float)info->present_rate) * 60.0);
00568 }
00569 
00570 /* read/refresh information about a given battery num
00571  * returns 0 on SUCCESS, negative values on errors */
00572 int
00573 read_acpi_batt(const int num){
00574         if(num > MAX_ITEMS) return ITEM_EXCEED;
00575         read_acpi_battstate(num);
00576         read_acpi_battalarm(num);
00577         calc_remain_perc(num);
00578         calc_remain_chargetime(num);
00579         calc_remain_time(num);
00580         return SUCCESS;
00581 }

Generated on Thu Jun 14 18:39:48 2007 for libacpi by  doxygen 1.5.2