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