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

Generated on Sun Jul 29 14:09:38 2007 for libacpi by  doxygen 1.5.2