From aa2c5565dc0725cf69771f376c9bec03b3524362 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Thu, 23 Oct 2008 11:17:25 +0100 Subject: Xen power management daemon patch. Signed-off-by: Kamala Narasimhan --- tools/xenpmd/Makefile | 15 ++ tools/xenpmd/xenpmd | Bin 0 -> 26838 bytes tools/xenpmd/xenpmd.c | 520 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 535 insertions(+) create mode 100644 tools/xenpmd/Makefile create mode 100755 tools/xenpmd/xenpmd create mode 100644 tools/xenpmd/xenpmd.c (limited to 'tools/xenpmd') diff --git a/tools/xenpmd/Makefile b/tools/xenpmd/Makefile new file mode 100644 index 0000000000..0e9f54040c --- /dev/null +++ b/tools/xenpmd/Makefile @@ -0,0 +1,15 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -Werror +CFLAGS += $(CFLAGS_libxenstore) +LDFLAGS += $(LDFLAGS_libxenstore) + +BIN = xenpmd + +.PHONY: all +all: $(BIN) + +.PHONY: clean +clean: + $(RM) -f $(BIN) diff --git a/tools/xenpmd/xenpmd b/tools/xenpmd/xenpmd new file mode 100755 index 0000000000..034050c578 Binary files /dev/null and b/tools/xenpmd/xenpmd differ diff --git a/tools/xenpmd/xenpmd.c b/tools/xenpmd/xenpmd.c new file mode 100644 index 0000000000..0dcf365619 --- /dev/null +++ b/tools/xenpmd/xenpmd.c @@ -0,0 +1,520 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* 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. + */ + +#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[32]; + + do + { + dir_entries = readdir(battery_dir); + if ( !dir_entries ) + return 0; + if ( strlen(dir_entries->d_name) < 4 ) + continue; + if ( battery_info_type == BIF ) + snprintf(file_name, 32, BATTERY_INFO_FILE_PATH, + dir_entries->d_name); + else + snprintf(file_name, 32, BATTERY_STATE_FILE_PATH, + dir_entries->d_name); + file = fopen(file_name, "r"); + } 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, 32); + return; + } + + if ( strstr(attrib_name, "serial number") ) + { + strncpy(info->serial_number, attrib_value, 32); + return; + } + + if ( strstr(attrib_name, "battery type") ) + { + strncpy(info->battery_type, attrib_value, 32); + return; + } + + if ( strstr(attrib_name, "OEM info") ) + { + strncpy(info->oem_info, attrib_value, 32); + 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; + + memset(line_info, 0, 256); + 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, 1024, file) != NULL ) + { + parse_battery_info_or_status(line_info, type, info_or_status); + memset(line_info, 0, 256); + } + + 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]; + + 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 */ + snprintf(val, 3, "%02x", + (unsigned int)(9*4 + + strlen(info->model_number) + + strlen(info->serial_number) + + strlen(info->battery_type) + + strlen(info->oem_info) + 4)); + 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); + 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 = (struct xs_handle *)xs_daemon_open(); + if ( xs == NULL ) + return -1; + + if ( write_one_time_battery_info() == 0 ) + { + xs_daemon_close(xs); + return -1; + } + + wait_for_and_update_battery_status_request(); + xs_daemon_close(xs); + return 0; +} + -- cgit v1.2.3