|  | /* Copyright (C) 2007-2008 The Android Open Source Project | 
|  | ** | 
|  | ** This software is licensed under the terms of the GNU General Public | 
|  | ** License version 2, as published by the Free Software Foundation, and | 
|  | ** may be copied, distributed, and modified under those terms. | 
|  | ** | 
|  | ** 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. | 
|  | */ | 
|  | #include "qemu_file.h" | 
|  | #include "goldfish_device.h" | 
|  | #include "power_supply.h" | 
|  |  | 
|  |  | 
|  | enum { | 
|  | /* status register */ | 
|  | BATTERY_INT_STATUS	    = 0x00, | 
|  | /* set this to enable IRQ */ | 
|  | BATTERY_INT_ENABLE	    = 0x04, | 
|  |  | 
|  | BATTERY_AC_ONLINE       = 0x08, | 
|  | BATTERY_STATUS          = 0x0C, | 
|  | BATTERY_HEALTH          = 0x10, | 
|  | BATTERY_PRESENT         = 0x14, | 
|  | BATTERY_CAPACITY        = 0x18, | 
|  |  | 
|  | BATTERY_STATUS_CHANGED	= 1U << 0, | 
|  | AC_STATUS_CHANGED   	= 1U << 1, | 
|  | BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, | 
|  | }; | 
|  |  | 
|  |  | 
|  | struct goldfish_battery_state { | 
|  | struct goldfish_device dev; | 
|  | // IRQs | 
|  | uint32_t int_status; | 
|  | // irq enable mask for int_status | 
|  | uint32_t int_enable; | 
|  |  | 
|  | int ac_online; | 
|  | int status; | 
|  | int health; | 
|  | int present; | 
|  | int capacity; | 
|  | }; | 
|  |  | 
|  | /* update this each time you update the battery_state struct */ | 
|  | #define  BATTERY_STATE_SAVE_VERSION  1 | 
|  |  | 
|  | #define  QFIELD_STRUCT  struct goldfish_battery_state | 
|  | QFIELD_BEGIN(goldfish_battery_fields) | 
|  | QFIELD_INT32(int_status), | 
|  | QFIELD_INT32(int_enable), | 
|  | QFIELD_INT32(ac_online), | 
|  | QFIELD_INT32(status), | 
|  | QFIELD_INT32(health), | 
|  | QFIELD_INT32(present), | 
|  | QFIELD_INT32(capacity), | 
|  | QFIELD_END | 
|  |  | 
|  | static void  goldfish_battery_save(QEMUFile*  f, void* opaque) | 
|  | { | 
|  | struct goldfish_battery_state*  s = opaque; | 
|  |  | 
|  | qemu_put_struct(f, goldfish_battery_fields, s); | 
|  | } | 
|  |  | 
|  | static int   goldfish_battery_load(QEMUFile*  f, void*  opaque, int  version_id) | 
|  | { | 
|  | struct goldfish_battery_state*  s = opaque; | 
|  |  | 
|  | if (version_id != BATTERY_STATE_SAVE_VERSION) | 
|  | return -1; | 
|  |  | 
|  | return qemu_get_struct(f, goldfish_battery_fields, s); | 
|  | } | 
|  |  | 
|  | static struct goldfish_battery_state *battery_state; | 
|  |  | 
|  | static uint32_t goldfish_battery_read(void *opaque, target_phys_addr_t offset) | 
|  | { | 
|  | uint32_t ret; | 
|  | struct goldfish_battery_state *s = opaque; | 
|  | offset -= s->dev.base; | 
|  | switch(offset) { | 
|  | case BATTERY_INT_STATUS: | 
|  | // return current buffer status flags | 
|  | ret = s->int_status & s->int_enable; | 
|  | if (ret) { | 
|  | goldfish_device_set_irq(&s->dev, 0, 0); | 
|  | s->int_status = 0; | 
|  | } | 
|  | return ret; | 
|  |  | 
|  | case BATTERY_INT_ENABLE: | 
|  | return s->int_enable; | 
|  | case BATTERY_AC_ONLINE: | 
|  | return s->ac_online; | 
|  | case BATTERY_STATUS: | 
|  | return s->status; | 
|  | case BATTERY_HEALTH: | 
|  | return s->health; | 
|  | case BATTERY_PRESENT: | 
|  | return s->present; | 
|  | case BATTERY_CAPACITY: | 
|  | return s->capacity; | 
|  |  | 
|  | default: | 
|  | cpu_abort (cpu_single_env, "goldfish_battery_read: Bad offset %x\n", offset); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void goldfish_battery_write(void *opaque, target_phys_addr_t offset, uint32_t val) | 
|  | { | 
|  | struct goldfish_battery_state *s = opaque; | 
|  | offset -= s->dev.base; | 
|  |  | 
|  | switch(offset) { | 
|  | case BATTERY_INT_ENABLE: | 
|  | /* enable interrupts */ | 
|  | s->int_enable = val; | 
|  | //            s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY); | 
|  | //            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable)); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset); | 
|  | } | 
|  | } | 
|  |  | 
|  | static CPUReadMemoryFunc *goldfish_battery_readfn[] = { | 
|  | goldfish_battery_read, | 
|  | goldfish_battery_read, | 
|  | goldfish_battery_read | 
|  | }; | 
|  |  | 
|  |  | 
|  | static CPUWriteMemoryFunc *goldfish_battery_writefn[] = { | 
|  | goldfish_battery_write, | 
|  | goldfish_battery_write, | 
|  | goldfish_battery_write | 
|  | }; | 
|  |  | 
|  | void goldfish_battery_init() | 
|  | { | 
|  | struct goldfish_battery_state *s; | 
|  |  | 
|  | s = (struct goldfish_battery_state *)qemu_mallocz(sizeof(*s)); | 
|  | s->dev.name = "goldfish-battery"; | 
|  | s->dev.base = 0;    // will be allocated dynamically | 
|  | s->dev.size = 0x1000; | 
|  | s->dev.irq_count = 1; | 
|  |  | 
|  | // default values for the battery | 
|  | s->ac_online = 1; | 
|  | s->status = POWER_SUPPLY_STATUS_CHARGING; | 
|  | s->health = POWER_SUPPLY_HEALTH_GOOD; | 
|  | s->present = 1;     // battery is present | 
|  | s->capacity = 50;   // 50% charged | 
|  |  | 
|  | battery_state = s; | 
|  |  | 
|  | goldfish_device_add(&s->dev, goldfish_battery_readfn, goldfish_battery_writefn, s); | 
|  |  | 
|  | register_savevm( "battery_state", 0, BATTERY_STATE_SAVE_VERSION, | 
|  | goldfish_battery_save, goldfish_battery_load, s); | 
|  | } | 
|  |  | 
|  | void goldfish_battery_set_prop(int ac, int property, int value) | 
|  | { | 
|  | int new_status = (ac ? AC_STATUS_CHANGED : BATTERY_STATUS_CHANGED); | 
|  |  | 
|  | if (ac) { | 
|  | switch (property) { | 
|  | case POWER_SUPPLY_PROP_ONLINE: | 
|  | battery_state->ac_online = value; | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | switch (property) { | 
|  | case POWER_SUPPLY_PROP_STATUS: | 
|  | battery_state->status = value; | 
|  | break; | 
|  | case POWER_SUPPLY_PROP_HEALTH: | 
|  | battery_state->health = value; | 
|  | break; | 
|  | case POWER_SUPPLY_PROP_PRESENT: | 
|  | battery_state->present = value; | 
|  | break; | 
|  | case POWER_SUPPLY_PROP_CAPACITY: | 
|  | battery_state->capacity = value; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (new_status != battery_state->int_status) { | 
|  | battery_state->int_status |= new_status; | 
|  | goldfish_device_set_irq(&battery_state->dev, 0, (battery_state->int_status & battery_state->int_enable)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data) | 
|  | { | 
|  | char    buffer[100]; | 
|  | char*   value; | 
|  |  | 
|  | sprintf(buffer, "AC: %s\r\n", (battery_state->ac_online ? "online" : "offline")); | 
|  | callback(data, buffer); | 
|  |  | 
|  | switch (battery_state->status) { | 
|  | case POWER_SUPPLY_STATUS_CHARGING: | 
|  | value = "Charging"; | 
|  | break; | 
|  | case POWER_SUPPLY_STATUS_DISCHARGING: | 
|  | value = "Discharging"; | 
|  | break; | 
|  | case POWER_SUPPLY_STATUS_NOT_CHARGING: | 
|  | value = "Not charging"; | 
|  | break; | 
|  | case POWER_SUPPLY_STATUS_FULL: | 
|  | value = "Full"; | 
|  | break; | 
|  | default: | 
|  | value = "Unknown"; | 
|  | break; | 
|  | } | 
|  | sprintf(buffer, "status: %s\r\n", value); | 
|  | callback(data, buffer); | 
|  |  | 
|  | switch (battery_state->health) { | 
|  | case POWER_SUPPLY_HEALTH_GOOD: | 
|  | value = "Good"; | 
|  | break; | 
|  | case POWER_SUPPLY_HEALTH_OVERHEAT: | 
|  | value = "Overhead"; | 
|  | break; | 
|  | case POWER_SUPPLY_HEALTH_DEAD: | 
|  | value = "Dead"; | 
|  | break; | 
|  | case POWER_SUPPLY_HEALTH_OVERVOLTAGE: | 
|  | value = "Overvoltage"; | 
|  | break; | 
|  | case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: | 
|  | value = "Unspecified failure"; | 
|  | break; | 
|  | default: | 
|  | value = "Unknown"; | 
|  | break; | 
|  | } | 
|  | sprintf(buffer, "health: %s\r\n", value); | 
|  | callback(data, buffer); | 
|  |  | 
|  | sprintf(buffer, "present: %s\r\n", (battery_state->present ? "true" : "false")); | 
|  | callback(data, buffer); | 
|  |  | 
|  | sprintf(buffer, "capacity: %d\r\n", battery_state->capacity); | 
|  | callback(data, buffer); | 
|  | } |