00001
00002
00003
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
00047
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
00059 if((tmpkey = strstr(tmpbuf, key))) {
00060
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
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
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
00112
00113 int
00114 check_acpi_support(void){
00115 int version = get_acpi_version();
00116
00117
00118 if(version == NOT_SUPPORTED || version < 20020214)
00119 return NOT_SUPPORTED;
00120 return SUCCESS;
00121 }
00122
00123
00124
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
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
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
00194
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
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
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
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
00245
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
00278
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
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
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
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
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
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
00362 if(globals->thermal_count == 1)
00363 globals->temperature = info->temperature;
00364 }
00365 if(buf) free(buf);
00366
00367
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
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
00384
00385 return SUCCESS;
00386 }
00387
00388
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
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
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
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
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
00458
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
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
00481
00482 free(buf);
00483
00484 return SUCCESS;
00485 }
00486
00487
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
00507
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
00521 batt_charge_state(num, info);
00522
00523 free(buf);
00524 return SUCCESS;
00525 }
00526
00527
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
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
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
00571
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 }