Statistics
| Branch: | Revision:

root / hw / milkymist-softusb.c @ 439a97cc

History | View | Annotate | Download (9.3 kB)

1
/*
2
 *  QEMU model of the Milkymist SoftUSB block.
3
 *
4
 *  Copyright (c) 2010 Michael Walle <michael@walle.cc>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
 *
19
 *
20
 * Specification available at:
21
 *   not available yet
22
 */
23

    
24
#include "hw.h"
25
#include "sysbus.h"
26
#include "trace.h"
27
#include "console.h"
28
#include "usb.h"
29
#include "qemu-error.h"
30

    
31
enum {
32
    R_CTRL = 0,
33
    R_MAX
34
};
35

    
36
enum {
37
    CTRL_RESET = (1<<0),
38
};
39

    
40
#define COMLOC_DEBUG_PRODUCE 0x1000
41
#define COMLOC_DEBUG_BASE    0x1001
42
#define COMLOC_MEVT_PRODUCE  0x1101
43
#define COMLOC_MEVT_BASE     0x1102
44
#define COMLOC_KEVT_PRODUCE  0x1142
45
#define COMLOC_KEVT_BASE     0x1143
46

    
47
struct MilkymistSoftUsbState {
48
    SysBusDevice busdev;
49
    USBBus usbbus;
50
    USBPort usbport[2];
51
    USBDevice *usbdev;
52

    
53
    qemu_irq irq;
54

    
55
    /* device properties */
56
    uint32_t pmem_base;
57
    uint32_t pmem_size;
58
    uint32_t dmem_base;
59
    uint32_t dmem_size;
60

    
61
    /* device registers */
62
    uint32_t regs[R_MAX];
63

    
64
    /* mouse state */
65
    int mouse_dx;
66
    int mouse_dy;
67
    int mouse_dz;
68
    uint8_t mouse_buttons_state;
69

    
70
    /* keyboard state */
71
    uint8_t kbd_usb_buffer[8];
72
};
73
typedef struct MilkymistSoftUsbState MilkymistSoftUsbState;
74

    
75
static uint32_t softusb_read(void *opaque, target_phys_addr_t addr)
76
{
77
    MilkymistSoftUsbState *s = opaque;
78
    uint32_t r = 0;
79

    
80
    addr >>= 2;
81
    switch (addr) {
82
    case R_CTRL:
83
        r = s->regs[addr];
84
        break;
85

    
86
    default:
87
        error_report("milkymist_softusb: read access to unknown register 0x"
88
                TARGET_FMT_plx, addr << 2);
89
        break;
90
    }
91

    
92
    trace_milkymist_softusb_memory_read(addr << 2, r);
93

    
94
    return r;
95
}
96

    
97
static void
98
softusb_write(void *opaque, target_phys_addr_t addr, uint32_t value)
99
{
100
    MilkymistSoftUsbState *s = opaque;
101

    
102
    trace_milkymist_softusb_memory_write(addr, value);
103

    
104
    addr >>= 2;
105
    switch (addr) {
106
    case R_CTRL:
107
        s->regs[addr] = value;
108
        break;
109

    
110
    default:
111
        error_report("milkymist_softusb: write access to unknown register 0x"
112
                TARGET_FMT_plx, addr << 2);
113
        break;
114
    }
115
}
116

    
117
static CPUReadMemoryFunc * const softusb_read_fn[] = {
118
    NULL,
119
    NULL,
120
    &softusb_read,
121
};
122

    
123
static CPUWriteMemoryFunc * const softusb_write_fn[] = {
124
    NULL,
125
    NULL,
126
    &softusb_write,
127
};
128

    
129
static inline void softusb_read_dmem(MilkymistSoftUsbState *s,
130
        uint32_t offset, uint8_t *buf, uint32_t len)
131
{
132
    if (offset + len >= s->dmem_size) {
133
        error_report("milkymist_softusb: read dmem out of bounds "
134
                "at offset 0x%x, len %d\n", offset, len);
135
        return;
136
    }
137

    
138
    cpu_physical_memory_read(s->dmem_base + offset, buf, len);
139
}
140

    
141
static inline void softusb_write_dmem(MilkymistSoftUsbState *s,
142
        uint32_t offset, uint8_t *buf, uint32_t len)
143
{
144
    if (offset + len >= s->dmem_size) {
145
        error_report("milkymist_softusb: write dmem out of bounds "
146
                "at offset 0x%x, len %d\n", offset, len);
147
        return;
148
    }
149

    
150
    cpu_physical_memory_write(s->dmem_base + offset, buf, len);
151
}
152

    
153
static inline void softusb_read_pmem(MilkymistSoftUsbState *s,
154
        uint32_t offset, uint8_t *buf, uint32_t len)
155
{
156
    if (offset + len >= s->pmem_size) {
157
        error_report("milkymist_softusb: read pmem out of bounds "
158
                "at offset 0x%x, len %d\n", offset, len);
159
        return;
160
    }
161

    
162
    cpu_physical_memory_read(s->pmem_base + offset, buf, len);
163
}
164

    
165
static inline void softusb_write_pmem(MilkymistSoftUsbState *s,
166
        uint32_t offset, uint8_t *buf, uint32_t len)
167
{
168
    if (offset + len >= s->pmem_size) {
169
        error_report("milkymist_softusb: write pmem out of bounds "
170
                "at offset 0x%x, len %d\n", offset, len);
171
        return;
172
    }
173

    
174
    cpu_physical_memory_write(s->pmem_base + offset, buf, len);
175
}
176

    
177
static void softusb_mouse_changed(MilkymistSoftUsbState *s)
178
{
179
    uint8_t m;
180
    uint8_t buf[4];
181

    
182
    buf[0] = s->mouse_buttons_state;
183
    buf[1] = s->mouse_dx;
184
    buf[2] = s->mouse_dy;
185
    buf[3] = s->mouse_dz;
186

    
187
    softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
188
    trace_milkymist_softusb_mevt(m);
189
    softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, buf, 4);
190
    m = (m + 1) & 0xf;
191
    softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
192

    
193
    trace_milkymist_softusb_pulse_irq();
194
    qemu_irq_pulse(s->irq);
195
}
196

    
197
static void softusb_kbd_changed(MilkymistSoftUsbState *s)
198
{
199
    uint8_t m;
200

    
201
    softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
202
    trace_milkymist_softusb_kevt(m);
203
    softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_usb_buffer, 8);
204
    m = (m + 1) & 0x7;
205
    softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
206

    
207
    trace_milkymist_softusb_pulse_irq();
208
    qemu_irq_pulse(s->irq);
209
}
210

    
211
static void softusb_mouse_event(void *opaque,
212
       int dx, int dy, int dz, int buttons_state)
213
{
214
    MilkymistSoftUsbState *s = opaque;
215

    
216
    /* if device is in reset, do nothing */
217
    if (s->regs[R_CTRL] & CTRL_RESET) {
218
        return;
219
    }
220

    
221
    trace_milkymist_softusb_mouse_event(dx, dy, dz, buttons_state);
222

    
223
    s->mouse_dx = dx;
224
    s->mouse_dy = dy;
225
    s->mouse_dz = dz;
226
    s->mouse_buttons_state = buttons_state;
227

    
228
    softusb_mouse_changed(s);
229
}
230

    
231
static void softusb_usbdev_datain(void *opaque)
232
{
233
    MilkymistSoftUsbState *s = opaque;
234

    
235
    USBPacket p;
236

    
237
    p.pid = USB_TOKEN_IN;
238
    p.devep = 1;
239
    p.data = s->kbd_usb_buffer;
240
    p.len = sizeof(s->kbd_usb_buffer);
241
    s->usbdev->info->handle_data(s->usbdev, &p);
242

    
243
    softusb_kbd_changed(s);
244
}
245

    
246
static void softusb_attach(USBPort *port)
247
{
248
}
249

    
250
static USBPortOps softusb_ops = {
251
    .attach = softusb_attach,
252
};
253

    
254
static void milkymist_softusb_reset(DeviceState *d)
255
{
256
    MilkymistSoftUsbState *s =
257
            container_of(d, MilkymistSoftUsbState, busdev.qdev);
258
    int i;
259

    
260
    for (i = 0; i < R_MAX; i++) {
261
        s->regs[i] = 0;
262
    }
263
    s->mouse_dx = 0;
264
    s->mouse_dy = 0;
265
    s->mouse_dz = 0;
266
    s->mouse_buttons_state = 0;
267
    memset(s->kbd_usb_buffer, 0, sizeof(s->kbd_usb_buffer));
268

    
269
    /* defaults */
270
    s->regs[R_CTRL] = CTRL_RESET;
271
}
272

    
273
static int milkymist_softusb_init(SysBusDevice *dev)
274
{
275
    MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev);
276
    int softusb_regs;
277
    ram_addr_t pmem_ram;
278
    ram_addr_t dmem_ram;
279

    
280
    sysbus_init_irq(dev, &s->irq);
281

    
282
    softusb_regs = cpu_register_io_memory(softusb_read_fn, softusb_write_fn, s,
283
            DEVICE_NATIVE_ENDIAN);
284
    sysbus_init_mmio(dev, R_MAX * 4, softusb_regs);
285

    
286
    /* register pmem and dmem */
287
    pmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.pmem", s->pmem_size);
288
    cpu_register_physical_memory(s->pmem_base, s->pmem_size,
289
            pmem_ram | IO_MEM_RAM);
290
    dmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.dmem", s->dmem_size);
291
    cpu_register_physical_memory(s->dmem_base, s->dmem_size,
292
            dmem_ram | IO_MEM_RAM);
293

    
294
    qemu_add_mouse_event_handler(softusb_mouse_event, s, 0, "Milkymist Mouse");
295

    
296
    /* create our usb bus */
297
    usb_bus_new(&s->usbbus, NULL);
298

    
299
    /* our two ports */
300
    usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops,
301
            USB_SPEED_MASK_LOW);
302
    usb_register_port(&s->usbbus, &s->usbport[1], NULL, 1, &softusb_ops,
303
            USB_SPEED_MASK_LOW);
304

    
305
    /* and finally create an usb keyboard */
306
    s->usbdev = usb_create_simple(&s->usbbus, "usb-kbd");
307
    usb_hid_datain_cb(s->usbdev, s, softusb_usbdev_datain);
308
    s->usbdev->info->handle_reset(s->usbdev);
309

    
310
    return 0;
311
}
312

    
313
static const VMStateDescription vmstate_milkymist_softusb = {
314
    .name = "milkymist-softusb",
315
    .version_id = 1,
316
    .minimum_version_id = 1,
317
    .minimum_version_id_old = 1,
318
    .fields      = (VMStateField[]) {
319
        VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
320
        VMSTATE_INT32(mouse_dx, MilkymistSoftUsbState),
321
        VMSTATE_INT32(mouse_dy, MilkymistSoftUsbState),
322
        VMSTATE_INT32(mouse_dz, MilkymistSoftUsbState),
323
        VMSTATE_UINT8(mouse_buttons_state, MilkymistSoftUsbState),
324
        VMSTATE_BUFFER(kbd_usb_buffer, MilkymistSoftUsbState),
325
        VMSTATE_END_OF_LIST()
326
    }
327
};
328

    
329
static SysBusDeviceInfo milkymist_softusb_info = {
330
    .init = milkymist_softusb_init,
331
    .qdev.name  = "milkymist-softusb",
332
    .qdev.size  = sizeof(MilkymistSoftUsbState),
333
    .qdev.vmsd  = &vmstate_milkymist_softusb,
334
    .qdev.reset = milkymist_softusb_reset,
335
    .qdev.props = (Property[]) {
336
        DEFINE_PROP_UINT32(
337
                "pmem_base", MilkymistSoftUsbState, pmem_base, 0xa0000000
338
        ),
339
        DEFINE_PROP_UINT32(
340
                "pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000
341
        ),
342
        DEFINE_PROP_UINT32(
343
                "dmem_base", MilkymistSoftUsbState, dmem_base, 0xa0020000
344
        ),
345
        DEFINE_PROP_UINT32(
346
                "dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000
347
        ),
348
        DEFINE_PROP_END_OF_LIST(),
349
    }
350
};
351

    
352
static void milkymist_softusb_register(void)
353
{
354
    sysbus_register_withprop(&milkymist_softusb_info);
355
}
356

    
357
device_init(milkymist_softusb_register)