]> xenbits.xensource.com Git - qemu-xen-4.4-testing.git/commitdiff
Battery Management
authorIan Jackson <ian.jackson@eu.citrix.com>
Wed, 22 Oct 2008 17:38:26 +0000 (18:38 +0100)
committerIan Jackson <Ian.Jackson@eu.citrix.com>
Wed, 22 Oct 2008 17:38:26 +0000 (18:38 +0100)
qemu side change for battery support within HVM guest.
(as revised by Kamala `rev1' Tue, 21 Oct 2008 19:15:41 GMT)

Signed-off-by: Kamala Narasimhan <kamala.narasimhan@citrix.com>
hw/battery_mgmt.c [new file with mode: 0644]
hw/battery_mgmt.h [new file with mode: 0644]
hw/piix4acpi.c
qemu-xen.h
xen-hooks.mak
xenstore.c

diff --git a/hw/battery_mgmt.c b/hw/battery_mgmt.c
new file mode 100644 (file)
index 0000000..38b5658
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * battery_mgmt.c
+ *
+ * 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
+ */
+
+/* Battery Management Implementation. Implements the following -
+ *  1) Check xenstore for pm implementation type - Pass-through or non-passthrough
+ *  2) Map appropriate ports for pass-through battery information
+ *  3) Register for appropriate ports and respond to those port read/writes for
+ *     non-passthrough implementation
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "qemu-xen.h"
+#include "isa.h" //register_ioport_read declaration
+#include "battery_mgmt.h"
+
+#include <sys/io.h>
+
+//#define BATTERY_MGMT_DEBUG
+//#define BATTERY_MGMT_DEBUG_EXT
+
+#define BATTERY_PORT_1 0xb2
+#define BATTERY_PORT_2 0x86
+#define BATTERY_PORT_3 0x88
+
+#define BATTERY_OP_INIT            0x7b
+#define BATTERY_OP_SET_INFO_TYPE   0x7c /*BIF or BST */
+#define BATTERY_OP_GET_DATA_LENGTH 0x79
+#define BATTERY_OP_GET_DATA        0x7d
+
+#ifdef BATTERY_MGMT_DEBUG_EXT
+    #define BATTERY_DBG_PORT_1 0xB040
+    #define BATTERY_DBG_PORT_2 0xB044
+    #define BATTERY_DBG_PORT_3 0xB046
+    #define BATTERY_DBG_PORT_4 0xB048
+    static int monitor_battery_port = 0;
+#endif
+
+static enum POWER_MGMT_MODE power_mgmt_mode = PM_MODE_NONE;
+static battery_state_info battery_info;
+extern FILE *logfile;
+extern int domid;
+
+int is_battery_pt_feasible(void)
+{
+    int val;
+
+    if ( (ioperm(BATTERY_PORT_1, 1, 1) != 0) ||
+         (ioperm(BATTERY_PORT_2, 1, 1) != 0) )
+        return 0;
+
+    outb(BATTERY_OP_INIT, BATTERY_PORT_1);
+    outb(1 /*BIF*/, BATTERY_PORT_2);
+    outb(BATTERY_OP_SET_INFO_TYPE, BATTERY_PORT_1);
+    outb(0, BATTERY_PORT_2);
+    outb(BATTERY_OP_GET_DATA_LENGTH, BATTERY_PORT_1);
+    val = inb(BATTERY_PORT_2);
+
+    ioperm(BATTERY_PORT_1, 1, 0);
+    ioperm(BATTERY_PORT_2, 1, 0);
+    if ( !val )
+        fprintf(logfile, "xen_extended_power_mgmt set to 1 but this mode is incomapatible with the current firmware!\n");
+    return val;
+}
+
+void battery_mgmt_pt_mode_init(void)
+{
+    int xc;
+
+    xc = xc_interface_open();
+    if ( xc == -1 ) 
+    {
+        fprintf(logfile, "%s: xc_interface_open failed\n", __FUNCTION__);
+        return;
+    }
+
+    if ( xc_domain_ioport_mapping(xc, domid, BATTERY_PORT_1, BATTERY_PORT_1, 0x2, 1) != 0 ) 
+       fprintf(logfile, "Failed to map port %x to guest\n", BATTERY_PORT_1);
+
+    if ( xc_domain_ioport_mapping(xc, domid, BATTERY_PORT_2, BATTERY_PORT_2, 0x1, 1) != 0 ) 
+        fprintf(logfile, "Failed to map port %x to guest\n", BATTERY_PORT_2);
+
+    close(xc);
+}
+
+#ifdef BATTERY_MGMT_DEBUG_EXT
+
+static void battery_dbg_monitor(void *opaque, uint32_t addr, uint32_t val)
+{   
+    monitor_battery_port = !monitor_battery_port;
+}
+
+static void battery_dbg_port_b2_input(void *opaque, uint32_t addr, uint32_t val)
+{
+    if ( monitor_battery_port ) 
+        fprintf(logfile, "Input value to battery port 0xb2 - %d; port 0x86 -  ", val);
+}
+
+static void battery_dbg_port_86_output(void *opaque, uint32_t addr, uint32_t val)
+{
+    if ( monitor_battery_port ) 
+        fprintf(logfile, "Output value from battery port 0x86 - %d\n", val);
+}
+    
+static void battery_dbg_port_86_input(void *opaque, uint32_t addr, uint32_t val)
+{
+    if ( monitor_battery_port ) 
+        fprintf(logfile, "%d.  ", val); 
+}
+#endif //BATTERY_MGMT_DEBUG_EXT
+
+void get_battery_data_from_xenstore(void)
+{
+    battery_info.battery_data = 0;
+    battery_info.current_index = 0;
+
+    if ( battery_info.type == BIF ) 
+        battery_info.battery_data = xenstore_read_battery_data(0/*battery info*/);
+    else if ( battery_info.type == BST ) 
+        battery_info.battery_data = xenstore_read_battery_data(1/*battery status*/);
+}
+
+void write_battery_data_to_port(void)
+{
+    char temp[3];
+    char *data;
+
+    if ( battery_info.battery_data == NULL ) 
+        return;
+
+    data = battery_info.battery_data + battery_info.current_index;
+    //KN: @Todo - Revisit the hard coding below and add bounds checking
+    // for current index though the querying software is not likely to 
+    // ask for more than what we provide as initial data length.
+    if ( ( battery_info.current_index <= 74 ) ||
+         (( battery_info.current_index > 74 ) && (*(data - 1) == '\n' )) ) 
+    {
+        snprintf(temp, 3, "%s", data);
+        battery_info.port_86_val = (uint8_t)strtoull(temp, NULL, 16);
+        battery_info.current_index+=2;
+    } else 
+    {
+        if ( *data == '\n' )
+            battery_info.port_86_val = 0;
+        else
+            battery_info.port_86_val = *data;
+        battery_info.current_index+=1;
+    }
+
+#ifdef BATTERY_MGMT_DEBUG_EXT
+        fprintf(logfile, "Wrote %d to port 0x86\n", battery_info.port_86_val);
+#endif
+    return;
+}  
+
+static void battery_port_1_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    switch (val) 
+    {
+        case BATTERY_OP_GET_DATA_LENGTH:
+            get_battery_data_from_xenstore();
+            write_battery_data_to_port();
+            battery_info.port_b2_val = 0;
+            break;
+        case BATTERY_OP_INIT:
+            battery_info.type = BATT_NONE;
+            if ( battery_info.battery_data != 0 )
+                free(battery_info.battery_data);
+            battery_info.battery_data = 0; 
+            battery_info.current_index = 0;
+            battery_info.port_b2_val = 0;
+            break;
+        case BATTERY_OP_SET_INFO_TYPE:
+            if ( battery_info.type == BATT_NONE ) 
+            {
+                if ( battery_info.port_86_val == 1 ) 
+                    battery_info.type = BIF; 
+                else if ( battery_info.port_86_val == 2 ) 
+                {
+                    xenstore_refresh_battery_status();  
+                    battery_info.type = BST;
+                }
+            }   
+            battery_info.port_b2_val = 0;
+            break;
+        case BATTERY_OP_GET_DATA:
+            write_battery_data_to_port();
+            battery_info.port_b2_val = 0;
+            break;
+        default:
+            break;
+    }
+    return;
+}
+
+static uint32_t battery_port_1_readb(void *opaque, uint32_t addr)
+{
+    return battery_info.port_b2_val;
+}
+
+static void battery_port_2_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    battery_info.port_86_val = val;
+}
+
+static uint32_t battery_port_2_readb(void *opaque, uint32_t addr)
+{
+    return battery_info.port_86_val;
+}
+
+static uint32_t battery_port_3_readb(void *opaque, uint32_t addr)
+{
+    if ( power_mgmt_mode == PM_MODE_PT || power_mgmt_mode == PM_MODE_NON_PT ) {
+        if ( (power_mgmt_mode == PM_MODE_PT) && (is_battery_pt_feasible() == 0) )
+            return 0x0F;
+        return 0x1F;
+    }
+
+    return 0x0F;
+}
+
+
+void battery_mgmt_non_pt_mode_init(PCIDevice *device)
+{
+    memset(&battery_info, 0, sizeof(battery_state_info));
+    register_ioport_read(BATTERY_PORT_1, 2, 1, battery_port_1_readb, device);
+    register_ioport_write(BATTERY_PORT_1, 2, 1, battery_port_1_writeb, device);
+    register_ioport_read(BATTERY_PORT_2, 1, 1, battery_port_2_readb, device);
+    register_ioport_write(BATTERY_PORT_2, 1, 1, battery_port_2_writeb, device);
+
+#ifdef BATTERY_MGMT_DEBUG_EXT
+    register_ioport_write(BATTERY_DBG_PORT_1, 1, 1, battery_dbg_monitor , device);
+    register_ioport_write(BATTERY_DBG_PORT_2, 1, 1, battery_dbg_port_b2_input, device);
+    register_ioport_write(BATTERY_DBG_PORT_3, 1, 1, battery_dbg_port_86_output, device);
+    register_ioport_write(BATTERY_DBG_PORT_4, 1, 1, battery_dbg_port_86_input, device);
+#endif
+}
+void battery_mgmt_init(PCIDevice *device)
+{
+    char *mode_buffer = NULL;
+
+    //xen_extended_power_mgmt xenstore entry indicates whether or not extended power
+    //management support is requested for the hvm guest.  Extended power management
+    //support includes power management support beyond S3, S4, S5.  A value of 1
+    //indicates pass-through pm support where upon pm resources are mapped to the guest
+    //as appropriate where as a value of 2 as set in non pass-through mode, requires qemu
+    //to take the onus of responding to relevant pm port reads/writes.
+    mode_buffer = xenstore_device_model_read(domid, "xen_extended_power_mgmt", NULL);
+    if ( mode_buffer == NULL ) 
+    {
+#ifdef BATTERY_MGMT_DEBUG
+        fprintf(logfile,"Xenpm mode not set\n");
+#endif
+        return;
+    }
+
+    power_mgmt_mode = (enum POWER_MGMT_MODE) strtoull(mode_buffer, NULL, 10);
+    free(mode_buffer);
+    switch ( power_mgmt_mode ) 
+    {
+        case PM_MODE_PT:
+            battery_mgmt_pt_mode_init();
+            break;
+        case PM_MODE_NON_PT:
+            battery_mgmt_non_pt_mode_init(device);
+            break;
+        case PM_MODE_NONE:
+        default:
+            return;
+    }
+
+    register_ioport_read(BATTERY_PORT_3, 1, 1, battery_port_3_readb, device);
+
+#ifdef BATTERY_MGMT_DEBUG
+    fprintf(logfile, "Power management mode set to - %d\n", power_mgmt_mode);
+#endif    
+}
+
diff --git a/hw/battery_mgmt.h b/hw/battery_mgmt.h
new file mode 100644 (file)
index 0000000..9b9b427
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * battery_mgmt.h
+ *
+ * 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
+ */
+
+#ifndef _BATTERY_MGMT_H
+#define _BATTERY_MGMT_H
+
+enum POWER_MGMT_MODE { PM_MODE_NONE = 0, PM_MODE_PT, PM_MODE_NON_PT };
+enum BATTERY_INFO_TYPE { BATT_NONE, BIF, BST };
+typedef struct battery_state_info {
+    enum BATTERY_INFO_TYPE type;
+    uint8_t port_b2_val;
+    uint8_t port_86_val;
+    uint8_t port_66_val;
+    char *battery_data;
+    uint8_t current_index;
+} battery_state_info;
+
+void battery_mgmt_init(PCIDevice *device);
+
+#endif
index 74d41de9e54f37a6a92ea95bd8f2d510ebef5e42..39d564589931b27e3d38fe7d03ae666fb526c374 100644 (file)
@@ -28,6 +28,7 @@
 #include "pci.h"
 #include "sysemu.h"
 #include "qemu-xen.h"
+#include "battery_mgmt.h"
 
 #include <xen/hvm/ioreq.h>
 #include <xen/hvm/params.h>
@@ -188,6 +189,8 @@ static void acpi_map(PCIDevice *pci_dev, int region_num,
     /* Word access */
     register_ioport_write(addr + 4, 2, 2, acpiPm1Control_writew, d);
     register_ioport_read(addr + 4, 2, 2, acpiPm1Control_readw, d);
+
+    battery_mgmt_init(pci_dev);
 }
 
 #ifdef CONFIG_PASSTHROUGH
index b7ace3d292e20af518ed112025233eb6afd3536d..8704774b2ecca2e77baf156cb02fc14d290857cd 100644 (file)
@@ -85,6 +85,9 @@ int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
 
 int xenstore_vm_write(int domid, char *key, char *val);
 char *xenstore_vm_read(int domid, char *key, unsigned int *len);
+char *xenstore_device_model_read(int domid, char *key, unsigned int *len);
+char *xenstore_read_battery_data(int battery_status);
+int xenstore_refresh_battery_status();
 
 /* xenfbfront.c */
 int xenfb_pv_display_init(DisplayState *ds);
index 230d03ac5767daa521cb88d87e39012e5b1da200..e87455285d64133f1b9e0a86dc1cafa0c8d4a4aa 100644 (file)
@@ -32,6 +32,7 @@ OBJS += xen_console.o
 OBJS += xen_machine_fv.o
 OBJS += exec-dm.o
 OBJS += pci_emulation.o
+OBJS += battery_mgmt.o
 
 ifdef CONFIG_STUBDOM
 CPPFLAGS += $(TARGET_CPPFLAGS)
index 5cd50638c8e87eecdfeaeeaa1c84ee8bdacaf854..3b3883765f85b96d5f0c0e317c6dea653d201a70 100644 (file)
@@ -1154,6 +1154,86 @@ int xenstore_vm_write(int domid, char *key, char *value)
     return rc;
 }
 
+char *xenstore_device_model_read(int domid, char *key, unsigned int *len)
+{
+    char *path = NULL, *value = NULL;
+
+    if (pasprintf(&path, "/local/domain/0/device-model/%d/%s", domid, key) == -1)
+        return NULL;
+
+    value = xs_read(xsh, XBT_NULL, path, len);
+    if (value == NULL)
+        fprintf(logfile, "xs_read(%s): read error\n", path);
+
+    free(path);
+    return value;
+}
+
+char *xenstore_extended_power_mgmt_read(char *key, unsigned int *len)
+{
+    char *path = NULL, *value = NULL;
+    
+    if (pasprintf(&path, "/pm/%s", key) == -1)
+        return NULL;
+
+    value = xs_read(xsh, XBT_NULL, path, len);
+    if (value == NULL)
+        fprintf(logfile, "xs_read(%s): read error\n", path);
+
+    free(path);
+    return value;
+}
+
+int xenstore_extended_power_mgmt_write(char * key, char * value)
+{
+    int ret;
+    char *path = NULL;
+    
+    if (pasprintf(&path, "/pm/%s", key) == -1)
+        return NULL;
+
+    ret = xs_write(xsh, XBT_NULL, path, value, strlen(value));
+    free(path);
+    return ret;
+}
+
+int xenstore_extended_power_mgmt_event_trigger(char *key, char * value)
+{
+    int ret;
+    char *path = NULL;
+    
+    if (pasprintf(&path, "events/%s", key) == -1)
+        return NULL;
+
+    ret = xenstore_extended_power_mgmt_write(path, value);
+    free(path);
+    return ret;
+}
+
+/*
+ * Xen power management daemon stores battery generic information
+ * like model, make, design volt, capacity etc. under /pm/bif and 
+ * battery status information like charging/discharging rate
+ * under /pm/bst in xenstore.
+ */
+char *xenstore_read_battery_data(int battery_status)
+{
+    if ( battery_status == 1 )
+        return xenstore_extended_power_mgmt_read("bst", NULL);
+    else
+        return xenstore_extended_power_mgmt_read("bif", NULL);
+}
+
+/*
+ * We set /pm/events/refreshbatterystatus xenstore entry
+ * to refresh battert status info stored under /pm/bst
+ * Xen power management daemon watches for changes to this
+ * entry and triggers a refresh.   
+ */
+int xenstore_refresh_battery_status()
+{
+    return xenstore_extended_power_mgmt_event_trigger("refreshbatterystatus", "1");
+}
 
 /*
  * Create a store entry for a device (e.g., monitor, serial/parallel lines).