meta-digi/meta-digi-dey/recipes-digi/dey-examples/files/battery_test/battery_test.c

370 lines
10 KiB
C

/*
* battery_test.c
*
* Copyright (C) 2010 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* Description: Battery characterization test application.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "bat_test.h"
#include <err.h>
#include <errno.h>
#include <pwd.h>
static int debug = 0;
static const char * log_file = "batd.log";
static char * lock_file = "batd.lock";
#define BAT_SYSFS_BUFF_SIZE 64
#define SYSFS_MC13892_BAT_PATH "/sys/class/power_supply/mc13892_bat/"
#define SYSFS_MC13892_MODULE_PATH "/sys/module/pmic_battery/parameters/"
#define log( fmt, arg...) \
do { \
char msg[256]; \
sprintf(msg,"[%s:%d]"fmt , __FUNCTION__,__LINE__, ## arg); \
if(debug) \
printf("%s",msg); \
else \
file_log_message(msg); \
} while (0)
void file_log_message( char *message ) {
FILE * lfd;
lfd=fopen(log_file,"a");
if(!lfd)
return;
fprintf(lfd,"%s\n",message);
fclose(lfd);
}
void signal_handler(int sig) {
switch(sig) {
case SIGHUP:
log("hangup signal catched\n");
break;
case SIGTERM:
log("terminate signal catched\n");
exit(EXIT_SUCCESS);
break;
default:
log("signal %d catched\n",sig);
break;
}
}
static void bat_read_sysfs ( const char * sysfs_path , char * attr_name , char * attr_value )
{
FILE * afd;
int bread = 0;
char path[256] = "";
strcpy(path,sysfs_path);
strcat(path,attr_name);
afd = fopen(path,"r");
if(!afd) {
strcpy(attr_value,"");
return;
}
bread = fread(attr_value,sizeof(char),BAT_SYSFS_BUFF_SIZE,afd);
if (bread < 1 )
strcpy(attr_value,"");
fclose(afd);
return;
}
static void daemonize( const char *lockfile )
{
pid_t pid, sid, parent;
int lfp = -1;
/* already a daemon */
if ( getppid() == 1 )
return;
/* Create the lock file as the current user */
if ( lockfile && lockfile[0] ) {
lfp = open(lockfile,O_RDWR|O_CREAT,0640);
if ( lfp < 0 ) {
printf("unable to create lock file %s, code=%d (%s)",
lockfile, errno, strerror(errno) );
exit(EXIT_FAILURE);
}
}
/* Trap signals that we expect to receive */
signal(SIGCHLD,signal_handler);
signal(SIGUSR1,signal_handler);
signal(SIGALRM,signal_handler);
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
printf( "unable to fork daemon, code=%d (%s)",
errno, strerror(errno) );
exit(EXIT_FAILURE);
}
/* If we got a good PID, then we can exit the parent process. */
if (pid > 0) {
/* Wait for confirmation from the child via SIGTERM or SIGCHLD, or
for two seconds to elapse (SIGALRM). pause() should not return. */
alarm(2);
pause();
exit(EXIT_FAILURE);
}
/* At this point we are executing as the child process */
parent = getppid();
/* Cancel certain signals */
signal(SIGCHLD,SIG_DFL); /* A child process dies */
signal(SIGTSTP,SIG_IGN); /* Various TTY signals */
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */
signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */
/* Change the file mode mask */
umask(0);
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
log("unable to create a new session, code %d (%s)",
errno, strerror(errno) );
exit(EXIT_FAILURE);
}
/* Change the current working directory. This prevents the current
directory from being locked; hence not being able to remove it. */
if ((chdir("/")) < 0) {
log("unable to change directory to %s, code %d (%s)",
"/", errno, strerror(errno) );
exit(EXIT_FAILURE);
}
/* Redirect standard files to /dev/null */
if (freopen("/dev/null", "r", stdin) == NULL)
err(1, "reopen stdin");
if (freopen("/dev/null", "w", stdout) == NULL)
err(1, "reopen stdout");
if (freopen("/dev/null", "w", stderr) == NULL)
err(1, "reopen stderr");
/* Tell the parent process that we are A-okay */
kill( parent, SIGUSR1 );
}
/**
* Prints online help
*
* @fd file to print on
*/
void printUsage(FILE* fd){
fprintf(fd,"Usage: battery_test -c -r -n -e -a -i -d -g [-v] [-h]\n"
" Battery characterization test application\n\n"
" -c Battery capacity in mAh\n"
" -r Battery hour rating (usually 20 hour rating)\n"
" -n Battery Peukert number\n"
" -e Battery charge efficiency factor\n"
" -a Maximum voltage by design in mV\n"
" -i Minimum voltage by design in mV\n"
" -d Battery discharge characterization parameters.\n"
" A comma separated list of 20 voltage values (in mV) representing"
" 5 points capacity percentage increments.\n"
" -g Battery charge characterization parameters.\n"
" A comma separated list of 20 voltage values (in mV) representing"
" 5 points capacity percentage increments.\n"
" -v Debug mode\n"
" -h This help\n\n");
}
static int bat_status_to_state( char * bat_status ) {
int state = -1;
if ( !strcmp(bat_status,"Charging") )
state = POWER_SUPPLY_STATUS_CHARGING;
else if( !strcmp(bat_status,"Discharging") )
state = POWER_SUPPLY_STATUS_DISCHARGING;
else if( !strcmp(bat_status,"Not charging") )
state = POWER_SUPPLY_STATUS_NOT_CHARGING;
else if( !strcmp(bat_status,"Full") )
state = POWER_SUPPLY_STATUS_FULL;
else if( !strcmp(bat_status,"Unknown") )
state = POWER_SUPPLY_STATUS_UNKNOWN;
else {
return state;
}
return state;
}
static int bat_average_read( char * var )
{
unsigned int reads[8];
char tmp[BAT_SYSFS_BUFF_SIZE]="";
int i;
unsigned int val = 0;
for( i= 0 ; i < 8 ; i++ ) {
bat_read_sysfs(SYSFS_MC13892_BAT_PATH,var,tmp);
reads[i] = atoi(tmp);
}
for( i= 0 ; i < 8 ; i++ ) {
val = val + reads[i];
}
return(val/8);
}
static int bat_read_status( char * bat_status , char * bat_current , char * bat_voltage , char * bat_charging_current )
{
int len = 0;
int bat_charging_current_idx = 0;
sprintf(bat_current,"%d",bat_average_read("current_now"));
sprintf(bat_voltage,"%d",bat_average_read("voltage_now"));
bat_read_sysfs(SYSFS_MC13892_BAT_PATH,"status",bat_status);
// Null terminate status
len = strcspn(bat_status,"\n");
bat_status[len] = '\0',
bat_read_sysfs(SYSFS_MC13892_MODULE_PATH,"main_charger_current",bat_charging_current);
bat_charging_current_idx = atoi(bat_charging_current);
strcpy(bat_charging_current,"");
snprintf(bat_charging_current,BAT_SYSFS_BUFF_SIZE,"%d",bat_main_charging_current(bat_charging_current_idx));
if( !strcmp(bat_status,"") || !strcmp(bat_current,"") || !strcmp(bat_voltage,"") || !strcmp(bat_charging_current,"") ) {
log("Error reading sysfs attributes\n");
return(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
static int bat_get_percentage( char * bat_status , char * bat_voltage )
{
int bat_percentage = 0;
bat_percentage = bat_get_capacity(bat_status_to_state(bat_status), atoi(bat_voltage)/1000);
return bat_percentage;
}
int main (int argc, char** argv){
int opt;
int retval = 0;
int bat_rating = 0;
int bat_capacity = 0;
float bat_peukert = 0;
float bat_charge_efficiency = 0;
char * end;
int bat_percentage = 0;
char bat_max_voltage[BAT_SYSFS_BUFF_SIZE] = "";
char bat_min_voltage[BAT_SYSFS_BUFF_SIZE] = "";
char bat_status[BAT_SYSFS_BUFF_SIZE] = "";
char bat_current[BAT_SYSFS_BUFF_SIZE] = "";
char bat_voltage[BAT_SYSFS_BUFF_SIZE] = "";
char bat_charging_current[BAT_SYSFS_BUFF_SIZE] = "";
char bat_charge_parameters[256] = "";
char bat_discharge_parameters[256] = "";
while ( (opt=getopt(argc,argv,"c:r:n:e:d:g:a:i:vh"))!=-1 ){
switch(opt){
case '?':
printUsage(stderr);
return 1;
case 'h':
printUsage(stdout);
return 0;
case 'c':
bat_capacity=atoi(optarg);
break;
case 'r':
bat_rating=atoi(optarg);
break;
case 'n':
bat_peukert=strtof(optarg,&end);
break;
case 'e':
bat_charge_efficiency=strtof(optarg,&end);
break;
case 'd':
strcpy(bat_discharge_parameters,optarg);
break;
case 'g':
strcpy(bat_charge_parameters,optarg);
break;
case 'a':
strcpy(bat_max_voltage,optarg);
break;
case 'i':
strcpy(bat_min_voltage,optarg);
break;
case 'v':
debug=1;
break;
}
}
log("Battery monitoring daemon started.\n");
if( !bat_rating || !bat_capacity || !bat_peukert || !bat_charge_efficiency || !strcmp(bat_charge_parameters,"") || !strcmp(bat_discharge_parameters,"") ) {
printf("Rating, capacity, peukert number, charge efficiency factors and charge/discharge characterization parameters for the specific battery are needed.\n");
printUsage(stderr);
exit(EXIT_FAILURE);
}
if(!debug)
daemonize(lock_file);
bat_parse_parameters(bat_charge_parameters,0 /* charge/discharge switch */);
bat_parse_parameters(bat_discharge_parameters,1 /* charge/discharge switch */);
if(debug)
bat_dump_tables();
while(1) {
bat_read_status( bat_status , bat_current , bat_voltage , bat_charging_current );
bat_percentage = bat_get_percentage( bat_status , bat_voltage );
if ( !strncmp(bat_status,"Char",4) )
log("Battery %s: Capacity %d%% Time to full %fh.\n",
bat_status,bat_percentage,bat_time_to_full(atoi(bat_charging_current),
bat_capacity,bat_charge_efficiency,bat_percentage) );
else if( !strncmp(bat_status,"Disc\n",4) || !strncmp(bat_status,"Not ",4) )
log("Battery %s: Capacity %d%% Time to empty %fh.\n",
bat_status,bat_percentage,bat_time_to_empty(bat_percentage,atoi(bat_current)/1000,
bat_capacity,bat_peukert,bat_rating) );
else if( !strncmp(bat_status,"Full",4) )
log("Battery %s: Capacity 100%%\n",bat_status);
else if( !strncmp(bat_status,"Unkn",4) )
log("Battery %s\n",bat_status);
else {
log("Battery status ERROR %s\n",bat_status);
continue;
}
sleep(60);
}
log("Battery monitoring daemon finished.\n");
return retval;
}