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#include "goldfish_vmem.h"
15
16enum {
17    SW_NAME_LEN     = 0x00,
18    SW_NAME_PTR     = 0x04,
19    SW_FLAGS        = 0x08,
20    SW_STATE        = 0x0c,
21    SW_INT_STATUS   = 0x10,
22    SW_INT_ENABLE   = 0x14,
23
24    SW_FLAGS_OUTPUT = 1U << 0
25};
26
27
28struct switch_state {
29    struct goldfish_device dev;
30    char *name;
31    uint32_t state;
32    uint32_t state_changed : 1;
33    uint32_t int_enable : 1;
34    uint32_t (*writefn)(void *opaque, uint32_t state);
35    void *writeopaque;
36};
37
38#define  GOLDFISH_SWITCH_SAVE_VERSION  1
39
40static void  goldfish_switch_save(QEMUFile*  f, void*  opaque)
41{
42    struct switch_state*  s = opaque;
43
44    qemu_put_be32(f, s->state);
45    qemu_put_byte(f, s->state_changed);
46    qemu_put_byte(f, s->int_enable);
47}
48
49static int  goldfish_switch_load(QEMUFile*  f, void*  opaque, int  version_id)
50{
51    struct switch_state*  s = opaque;
52
53    if (version_id != GOLDFISH_SWITCH_SAVE_VERSION)
54        return -1;
55
56    s->state         = qemu_get_be32(f);
57    s->state_changed = qemu_get_byte(f);
58    s->int_enable    = qemu_get_byte(f);
59
60    return 0;
61}
62
63static uint32_t goldfish_switch_read(void *opaque, target_phys_addr_t offset)
64{
65    struct switch_state *s = (struct switch_state *)opaque;
66
67    //printf("goldfish_switch_read %x %x\n", offset, size);
68
69    switch (offset) {
70        case SW_NAME_LEN:
71            return strlen(s->name);
72        case SW_FLAGS:
73            return s->writefn ? SW_FLAGS_OUTPUT : 0;
74        case SW_STATE:
75            return s->state;
76        case SW_INT_STATUS:
77            if(s->state_changed && s->int_enable) {
78                s->state_changed = 0;
79                goldfish_device_set_irq(&s->dev, 0, 0);
80                return 1;
81            }
82            return 0;
83    default:
84        cpu_abort (cpu_single_env, "goldfish_switch_read: Bad offset %x\n", offset);
85        return 0;
86    }
87}
88
89static void goldfish_switch_write(void *opaque, target_phys_addr_t offset, uint32_t value)
90{
91    struct switch_state *s = (struct switch_state *)opaque;
92
93    //printf("goldfish_switch_read %x %x %x\n", offset, value, size);
94
95    switch(offset) {
96        case SW_NAME_PTR:
97            safe_memory_rw_debug(cpu_single_env, value, (void*)s->name, strlen(s->name), 1);
98            break;
99
100        case SW_STATE:
101            if(s->writefn) {
102                uint32_t new_state;
103                new_state = s->writefn(s->writeopaque, value);
104                if(new_state != s->state) {
105                    goldfish_switch_set_state(s, new_state);
106                }
107            }
108            else
109                cpu_abort (cpu_single_env, "goldfish_switch_write: write to SW_STATE on input\n");
110            break;
111
112        case SW_INT_ENABLE:
113            value &= 1;
114            if(s->state_changed && s->int_enable != value)
115                goldfish_device_set_irq(&s->dev, 0, value);
116            s->int_enable = value;
117            break;
118
119        default:
120            cpu_abort (cpu_single_env, "goldfish_switch_write: Bad offset %x\n", offset);
121    }
122}
123
124static CPUReadMemoryFunc *goldfish_switch_readfn[] = {
125    goldfish_switch_read,
126    goldfish_switch_read,
127    goldfish_switch_read
128};
129
130static CPUWriteMemoryFunc *goldfish_switch_writefn[] = {
131    goldfish_switch_write,
132    goldfish_switch_write,
133    goldfish_switch_write
134};
135
136void goldfish_switch_set_state(void *opaque, uint32_t state)
137{
138    struct switch_state *s = opaque;
139    s->state_changed = 1;
140    s->state = state;
141    if(s->int_enable)
142        goldfish_device_set_irq(&s->dev, 0, 1);
143}
144
145void *goldfish_switch_add(char *name, uint32_t (*writefn)(void *opaque, uint32_t state), void *writeopaque, int id)
146{
147    int ret;
148    struct switch_state *s;
149
150    s = qemu_mallocz(sizeof(*s));
151    s->dev.name = "goldfish-switch";
152    s->dev.id = id;
153    s->dev.size = 0x1000;
154    s->dev.irq_count = 1;
155    s->name = name;
156    s->writefn = writefn;
157    s->writeopaque = writeopaque;
158
159
160    ret = goldfish_device_add(&s->dev, goldfish_switch_readfn, goldfish_switch_writefn, s);
161    if(ret) {
162        qemu_free(s);
163        return NULL;
164    }
165
166    register_savevm( "goldfish_switch", 0, GOLDFISH_SWITCH_SAVE_VERSION,
167                     goldfish_switch_save, goldfish_switch_load, s);
168
169    return s;
170}
171
172