1/* Copyright (C) 2007-2008 The Android Open Source Project 2** 3** This software is licensed under the terms of the GNU General Public 4** License version 2, as published by the Free Software Foundation, and 5** may be copied, distributed, and modified under those terms. 6** 7** This program is distributed in the hope that it will be useful, 8** but WITHOUT ANY WARRANTY; without even the implied warranty of 9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10** GNU General Public License for more details. 11*/ 12#include "qemu_file.h" 13#include "goldfish_device.h" 14 15enum { 16 SW_NAME_LEN = 0x00, 17 SW_NAME_PTR = 0x04, 18 SW_FLAGS = 0x08, 19 SW_STATE = 0x0c, 20 SW_INT_STATUS = 0x10, 21 SW_INT_ENABLE = 0x14, 22 23 SW_FLAGS_OUTPUT = 1U << 0 24}; 25 26 27struct switch_state { 28 struct goldfish_device dev; 29 char *name; 30 uint32_t state; 31 uint32_t state_changed : 1; 32 uint32_t int_enable : 1; 33 uint32_t (*writefn)(void *opaque, uint32_t state); 34 void *writeopaque; 35}; 36 37#define GOLDFISH_SWITCH_SAVE_VERSION 1 38 39static void goldfish_switch_save(QEMUFile* f, void* opaque) 40{ 41 struct switch_state* s = opaque; 42 43 qemu_put_be32(f, s->state); 44 qemu_put_byte(f, s->state_changed); 45 qemu_put_byte(f, s->int_enable); 46} 47 48static int goldfish_switch_load(QEMUFile* f, void* opaque, int version_id) 49{ 50 struct switch_state* s = opaque; 51 52 if (version_id != GOLDFISH_SWITCH_SAVE_VERSION) 53 return -1; 54 55 s->state = qemu_get_be32(f); 56 s->state_changed = qemu_get_byte(f); 57 s->int_enable = qemu_get_byte(f); 58 59 return 0; 60} 61 62static uint32_t goldfish_switch_read(void *opaque, target_phys_addr_t offset) 63{ 64 struct switch_state *s = (struct switch_state *)opaque; 65 66 //printf("goldfish_switch_read %x %x\n", offset, size); 67 68 switch (offset) { 69 case SW_NAME_LEN: 70 return strlen(s->name); 71 case SW_FLAGS: 72 return s->writefn ? SW_FLAGS_OUTPUT : 0; 73 case SW_STATE: 74 return s->state; 75 case SW_INT_STATUS: 76 if(s->state_changed && s->int_enable) { 77 s->state_changed = 0; 78 goldfish_device_set_irq(&s->dev, 0, 0); 79 return 1; 80 } 81 return 0; 82 default: 83 cpu_abort (cpu_single_env, "goldfish_switch_read: Bad offset %x\n", offset); 84 return 0; 85 } 86} 87 88static void goldfish_switch_write(void *opaque, target_phys_addr_t offset, uint32_t value) 89{ 90 struct switch_state *s = (struct switch_state *)opaque; 91 92 //printf("goldfish_switch_read %x %x %x\n", offset, value, size); 93 94 switch(offset) { 95 case SW_NAME_PTR: 96 cpu_memory_rw_debug(cpu_single_env, value, (void*)s->name, strlen(s->name), 1); 97 break; 98 99 case SW_STATE: 100 if(s->writefn) { 101 uint32_t new_state; 102 new_state = s->writefn(s->writeopaque, value); 103 if(new_state != s->state) { 104 goldfish_switch_set_state(s, new_state); 105 } 106 } 107 else 108 cpu_abort (cpu_single_env, "goldfish_switch_write: write to SW_STATE on input\n"); 109 break; 110 111 case SW_INT_ENABLE: 112 value &= 1; 113 if(s->state_changed && s->int_enable != value) 114 goldfish_device_set_irq(&s->dev, 0, value); 115 s->int_enable = value; 116 break; 117 118 default: 119 cpu_abort (cpu_single_env, "goldfish_switch_write: Bad offset %x\n", offset); 120 } 121} 122 123static CPUReadMemoryFunc *goldfish_switch_readfn[] = { 124 goldfish_switch_read, 125 goldfish_switch_read, 126 goldfish_switch_read 127}; 128 129static CPUWriteMemoryFunc *goldfish_switch_writefn[] = { 130 goldfish_switch_write, 131 goldfish_switch_write, 132 goldfish_switch_write 133}; 134 135void goldfish_switch_set_state(void *opaque, uint32_t state) 136{ 137 struct switch_state *s = opaque; 138 s->state_changed = 1; 139 s->state = state; 140 if(s->int_enable) 141 goldfish_device_set_irq(&s->dev, 0, 1); 142} 143 144void *goldfish_switch_add(char *name, uint32_t (*writefn)(void *opaque, uint32_t state), void *writeopaque, int id) 145{ 146 int ret; 147 struct switch_state *s; 148 149 s = qemu_mallocz(sizeof(*s)); 150 s->dev.name = "goldfish-switch"; 151 s->dev.id = id; 152 s->dev.size = 0x1000; 153 s->dev.irq_count = 1; 154 s->name = name; 155 s->writefn = writefn; 156 s->writeopaque = writeopaque; 157 158 159 ret = goldfish_device_add(&s->dev, goldfish_switch_readfn, goldfish_switch_writefn, s); 160 if(ret) { 161 qemu_free(s); 162 return NULL; 163 } 164 165 register_savevm( "goldfish_switch", 0, GOLDFISH_SWITCH_SAVE_VERSION, 166 goldfish_switch_save, goldfish_switch_load, s); 167 168 return s; 169} 170 171