/*
* xenpmd.c
*
* xen power management daemon - Facilitates power management
* functionality within xen guests.
*
* Copyright (c) 2008 Kamala Narasimhan
* Copyright (c) 2008 Citrix Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; If not, see .
*/
/* Xen extended power management support provides HVM guest power management
* features beyond S3, S4, S5. For example, it helps expose system level
* battery status and battery meter information and in future will be extended
* to include more power management support. This extended power management
* support is enabled by setting xen_extended_power_mgmt to 1 or 2 in the HVM
* config file. When set to 2, non-pass through mode is enabled which heavily
* relies on this power management daemon to glean battery information from
* dom0 and store it xenstore which would then be queries and used by qemu and
* passed to the guest when appropriate battery ports are read/written to.
*/
#define _GNU_SOURCE /* for asprintf() */
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* #define RUN_STANDALONE */
#define RUN_IN_SIMULATE_MODE
enum BATTERY_INFO_TYPE {
BIF,
BST
};
enum BATTERY_PRESENT {
NO,
YES
};
enum BATTERY_TECHNOLOGY {
NON_RECHARGEABLE,
RECHARGEABLE
};
struct battery_info {
enum BATTERY_PRESENT present;
unsigned long design_capacity;
unsigned long last_full_capacity;
enum BATTERY_TECHNOLOGY battery_technology;
unsigned long design_voltage;
unsigned long design_capacity_warning;
unsigned long design_capacity_low;
unsigned long capacity_granularity_1;
unsigned long capacity_granularity_2;
char model_number[32];
char serial_number[32];
char battery_type[32];
char oem_info[32];
};
struct battery_status {
enum BATTERY_PRESENT present;
unsigned long state;
unsigned long present_rate;
unsigned long remaining_capacity;
unsigned long present_voltage;
};
static struct xs_handle *xs;
#ifdef RUN_IN_SIMULATE_MODE
#define BATTERY_DIR_PATH "/tmp/battery"
#define BATTERY_INFO_FILE_PATH "/tmp/battery/%s/info"
#define BATTERY_STATE_FILE_PATH "/tmp/battery/%s/state"
#else
#define BATTERY_DIR_PATH "/proc/acpi/battery"
#define BATTERY_INFO_FILE_PATH "/proc/acpi/battery/%s/info"
#define BATTERY_STATE_FILE_PATH "/proc/acpi/battery/%s/state"
#endif
FILE *get_next_battery_file(DIR *battery_dir,
enum BATTERY_INFO_TYPE battery_info_type)
{
FILE *file = 0;
struct dirent *dir_entries;
char *file_name;
int ret;
do
{
dir_entries = readdir(battery_dir);
if ( !dir_entries )
return 0;
if ( strlen(dir_entries->d_name) < 4 )
continue;
if ( battery_info_type == BIF )
ret = asprintf(&file_name, BATTERY_INFO_FILE_PATH,
dir_entries->d_name);
else
ret = asprintf(&file_name, BATTERY_STATE_FILE_PATH,
dir_entries->d_name);
/* This should not happen but is needed to pass gcc checks */
if (ret < 0)
continue;
file = fopen(file_name, "r");
free(file_name);
} while ( !file );
return file;
}
void set_attribute_battery_info(char *attrib_name,
char *attrib_value,
struct battery_info *info)
{
if ( strstr(attrib_name, "present") )
{
if ( strstr(attrib_value, "yes") )
info->present = YES;
return;
}
if ( strstr(attrib_name, "design capacity warning") )
{
info->design_capacity_warning = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "design capacity low") )
{
info->design_capacity_low = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "design capacity") )
{
info->design_capacity = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "last full capacity") )
{
info->last_full_capacity = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "design voltage") )
{
info->design_voltage = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "capacity granularity 1") )
{
info->capacity_granularity_1 = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "capacity granularity 2") )
{
info->capacity_granularity_2 = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "battery technology") )
{
if ( strncmp(attrib_value, "rechargeable",
strlen("rechargeable")) == 0 )
info->battery_technology = RECHARGEABLE;
else
info->battery_technology = NON_RECHARGEABLE;
return;
}
if ( strstr(attrib_name, "model number") )
{
strncpy(info->model_number, attrib_value, 31);
info->model_number[31] = '\0';
return;
}
if ( strstr(attrib_name, "serial number") )
{
strncpy(info->serial_number, attrib_value, 31);
info->serial_number[31] = '\0';
return;
}
if ( strstr(attrib_name, "battery type") )
{
strncpy(info->battery_type, attrib_value, 31);
info->battery_type[31] = '\0';
return;
}
if ( strstr(attrib_name, "OEM info") )
{
strncpy(info->oem_info, attrib_value, 31);
info->oem_info[31] = '\0';
return;
}
return;
}
void set_attribute_battery_status(char *attrib_name,
char *attrib_value,
struct battery_status *status)
{
if ( strstr(attrib_name, "charging state") )
{
/* Check this, below is half baked */
if ( strstr(attrib_value, "charged") )
status->state = 0;
else
status->state = 1;
return;
}
if ( strstr(attrib_name, "present rate") )
{
status->present_rate = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "remaining capacity") )
{
status->remaining_capacity = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "present voltage") )
{
status->present_voltage = strtoull(attrib_value, NULL, 10);
return;
}
if ( strstr(attrib_name, "present") )
{
if ( strstr(attrib_value, "yes") )
status->present = YES;
return;
}
}
void parse_battery_info_or_status(char *line_info,
enum BATTERY_INFO_TYPE type,
void *info_or_status)
{
char attrib_name[128];
char attrib_value[64];
char *delimiter;
unsigned long length;
length = strlen(line_info);
delimiter = (char *) strchr( line_info, ':');
if ( (!delimiter) || (delimiter == line_info) ||
(delimiter == line_info + length) )
return;
strncpy(attrib_name, line_info, delimiter-line_info);
while ( *(delimiter+1) == ' ' )
{
delimiter++;
if ( delimiter+1 == line_info + length)
return;
}
strncpy(attrib_value, delimiter+1,
(unsigned long)line_info + length -(unsigned long)delimiter);
if ( type == BIF )
set_attribute_battery_info(attrib_name, attrib_value,
(struct battery_info *)info_or_status);
else
set_attribute_battery_status(attrib_name, attrib_value,
(struct battery_status *)info_or_status);
return;
}
int get_next_battery_info_or_status(DIR *battery_dir,
enum BATTERY_INFO_TYPE type,
void *info_or_status)
{
FILE *file;
char line_info[256];
if ( !info_or_status )
return 0;
if (type == BIF)
memset(info_or_status, 0, sizeof(struct battery_info));
else
memset(info_or_status, 0, sizeof(struct battery_status));
file = get_next_battery_file(battery_dir, type);
if ( !file )
return 0;
while ( fgets(line_info, sizeof(line_info), file) != NULL )
parse_battery_info_or_status(line_info, type, info_or_status);
fclose(file);
return 1;
}
#ifdef RUN_STANDALONE
void print_battery_info(struct battery_info *info)
{
printf("present: %d\n", info->present);
printf("design capacity: %d\n", info->design_capacity);
printf("last full capacity: %d\n", info->last_full_capacity);
printf("battery technology: %d\n", info->battery_technology);
printf("design voltage: %d\n", info->design_voltage);
printf("design capacity warning:%d\n", info->design_capacity_warning);
printf("design capacity low: %d\n", info->design_capacity_low);
printf("capacity granularity 1: %d\n", info->capacity_granularity_1);
printf("capacity granularity 2: %d\n", info->capacity_granularity_2);
printf("model number: %s\n", info->model_number);
printf("serial number: %s\n", info->serial_number);
printf("battery type: %s\n", info->battery_type);
printf("OEM info: %s\n", info->oem_info);
}
#endif /*RUN_STANDALONE*/
void write_ulong_lsb_first(char *temp_val, unsigned long val)
{
snprintf(temp_val, 9, "%02x%02x%02x%02x", (unsigned int)val & 0xff,
(unsigned int)(val & 0xff00) >> 8, (unsigned int)(val & 0xff0000) >> 16,
(unsigned int)(val & 0xff000000) >> 24);
}
void write_battery_info_to_xenstore(struct battery_info *info)
{
char val[1024], string_info[256];
unsigned int len;
xs_mkdir(xs, XBT_NULL, "/pm");
memset(val, 0, 1024);
memset(string_info, 0, 256);
/* write 9 dwords (so 9*4) + length of 4 strings + 4 null terminators */
len = 9 * 4 + strlen(info->model_number) + strlen(info->serial_number) +
strlen(info->battery_type) + strlen(info->oem_info) + 4;
assert(len < 255);
snprintf(val, 3, "%02x", len);
write_ulong_lsb_first(val+2, info->present);
write_ulong_lsb_first(val+10, info->design_capacity);
write_ulong_lsb_first(val+18, info->last_full_capacity);
write_ulong_lsb_first(val+26, info->battery_technology);
write_ulong_lsb_first(val+34, info->design_voltage);
write_ulong_lsb_first(val+42, info->design_capacity_warning);
write_ulong_lsb_first(val+50, info->design_capacity_low);
write_ulong_lsb_first(val+58, info->capacity_granularity_1);
write_ulong_lsb_first(val+66, info->capacity_granularity_2);
snprintf(string_info, 256, "%02x%s%02x%s%02x%s%02x%s",
(unsigned int)strlen(info->model_number), info->model_number,
(unsigned int)strlen(info->serial_number), info->serial_number,
(unsigned int)strlen(info->battery_type), info->battery_type,
(unsigned int)strlen(info->oem_info), info->oem_info);
strncat(val+73, string_info, 1024-73-1);
xs_write(xs, XBT_NULL, "/pm/bif",
val, 73+8+strlen(info->model_number)+strlen(info->serial_number)+
strlen(info->battery_type)+strlen(info->oem_info)+1);
}
int write_one_time_battery_info(void)
{
DIR *dir;
int ret = 0;
struct battery_info info;
dir = opendir(BATTERY_DIR_PATH);
if ( !dir )
return 0;
while ( get_next_battery_info_or_status(dir, BIF, (void *)&info) )
{
#ifdef RUN_STANDALONE
print_battery_info(&info);
#endif
if ( info.present == YES )
{
write_battery_info_to_xenstore(&info);
ret = 1;
break; /* rethink this... */
}
}
closedir(dir);
return ret;
}
#ifdef RUN_STANDALONE
void print_battery_status(struct battery_status *status)
{
printf("present: %d\n", status->present);
printf("Battery state %d\n", status->state);
printf("Battery present rate %d\n", status->present_rate);
printf("Battery remining capacity %d\n", status->remaining_capacity);
printf("Battery present voltage %d\n", status->present_voltage);
}
#endif /*RUN_STANDALONE*/
void write_battery_status_to_xenstore(struct battery_status *status)
{
char val[35];
xs_mkdir(xs, XBT_NULL, "/pm");
memset(val, 0, 35);
snprintf(val, 3, "%02x", 16);
write_ulong_lsb_first(val+2, status->state);
write_ulong_lsb_first(val+10, status->present_rate);
write_ulong_lsb_first(val+18, status->remaining_capacity);
write_ulong_lsb_first(val+26, status->present_voltage);
xs_write(xs, XBT_NULL, "/pm/bst", val, 35);
}
int wait_for_and_update_battery_status_request(void)
{
DIR *dir;
int ret = 0;
unsigned int count;
struct battery_status status;
while ( true )
{
/* KN:@TODO - It is rather inefficient to not cache the file handle.
* Switch to caching file handle.
*/
dir = opendir(BATTERY_DIR_PATH);
if ( !dir )
return 0;
while ( get_next_battery_info_or_status(dir, BST, (void *)&status) )
{
#ifdef RUN_STANDALONE
print_battery_status(&status);
#endif
if ( status.present == YES )
{
write_battery_status_to_xenstore(&status);
ret = 1;
/* rethink this; though I have never seen, there might be
* systems out there with more than one battery device
* present
*/
break;
}
}
closedir(dir);
xs_watch(xs, "/pm/events", "refreshbatterystatus");
xs_read_watch(xs, &count);
}
return ret;
}
/* Borrowed daemonize from xenstored - Initially written by Stevens. */
static void daemonize(void)
{
pid_t pid;
if ( (pid = fork()) < 0 )
exit(1);
if ( pid != 0 )
exit(0);
setsid();
if ( (pid = fork()) < 0 )
exit(1);
if ( pid != 0 )
exit(0);
if ( chdir("/") == -1 )
exit(1);
umask(0);
}
int main(int argc, char *argv[])
{
#ifndef RUN_STANDALONE
daemonize();
#endif
xs = xs_open(0);
if ( xs == NULL )
return -1;
if ( write_one_time_battery_info() == 0 )
{
xs_close(xs);
return -1;
}
wait_for_and_update_battery_status_request();
xs_close(xs);
return 0;
}