]> git.proxmox.com Git - qemu.git/blame - hw/milkymist-softusb.c
Merge branch 'stable-0.15' of git://git.qemu.org/qemu
[qemu.git] / hw / milkymist-softusb.c
CommitLineData
87a381ec
MW
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
31enum {
32 R_CTRL = 0,
33 R_MAX
34};
35
36enum {
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
47struct 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};
73typedef struct MilkymistSoftUsbState MilkymistSoftUsbState;
74
75static 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
97static void
98softusb_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
117static CPUReadMemoryFunc * const softusb_read_fn[] = {
118 NULL,
119 NULL,
120 &softusb_read,
121};
122
123static CPUWriteMemoryFunc * const softusb_write_fn[] = {
124 NULL,
125 NULL,
126 &softusb_write,
127};
128
129static 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 "
6daf194d 134 "at offset 0x%x, len %d", offset, len);
87a381ec
MW
135 return;
136 }
137
138 cpu_physical_memory_read(s->dmem_base + offset, buf, len);
139}
140
141static 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 "
6daf194d 146 "at offset 0x%x, len %d", offset, len);
87a381ec
MW
147 return;
148 }
149
150 cpu_physical_memory_write(s->dmem_base + offset, buf, len);
151}
152
153static 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 "
6daf194d 158 "at offset 0x%x, len %d", offset, len);
87a381ec
MW
159 return;
160 }
161
162 cpu_physical_memory_read(s->pmem_base + offset, buf, len);
163}
164
165static 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 "
6daf194d 170 "at offset 0x%x, len %d", offset, len);
87a381ec
MW
171 return;
172 }
173
174 cpu_physical_memory_write(s->pmem_base + offset, buf, len);
175}
176
177static 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
197static 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
211static 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
231static 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
246static void softusb_attach(USBPort *port)
247{
248}
249
4706ab6c
HG
250static void softusb_detach(USBPort *port)
251{
252}
253
254static void softusb_child_detach(USBPort *port, USBDevice *child)
07771f6f
GH
255{
256}
257
87a381ec
MW
258static USBPortOps softusb_ops = {
259 .attach = softusb_attach,
4706ab6c
HG
260 .detach = softusb_detach,
261 .child_detach = softusb_child_detach,
87a381ec
MW
262};
263
07771f6f 264static USBBusOps softusb_bus_ops = {
07771f6f
GH
265};
266
87a381ec
MW
267static void milkymist_softusb_reset(DeviceState *d)
268{
269 MilkymistSoftUsbState *s =
270 container_of(d, MilkymistSoftUsbState, busdev.qdev);
271 int i;
272
273 for (i = 0; i < R_MAX; i++) {
274 s->regs[i] = 0;
275 }
276 s->mouse_dx = 0;
277 s->mouse_dy = 0;
278 s->mouse_dz = 0;
279 s->mouse_buttons_state = 0;
280 memset(s->kbd_usb_buffer, 0, sizeof(s->kbd_usb_buffer));
281
282 /* defaults */
283 s->regs[R_CTRL] = CTRL_RESET;
284}
285
286static int milkymist_softusb_init(SysBusDevice *dev)
287{
288 MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev);
289 int softusb_regs;
290 ram_addr_t pmem_ram;
291 ram_addr_t dmem_ram;
292
293 sysbus_init_irq(dev, &s->irq);
294
295 softusb_regs = cpu_register_io_memory(softusb_read_fn, softusb_write_fn, s,
296 DEVICE_NATIVE_ENDIAN);
297 sysbus_init_mmio(dev, R_MAX * 4, softusb_regs);
298
299 /* register pmem and dmem */
300 pmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.pmem", s->pmem_size);
301 cpu_register_physical_memory(s->pmem_base, s->pmem_size,
302 pmem_ram | IO_MEM_RAM);
303 dmem_ram = qemu_ram_alloc(NULL, "milkymist_softusb.dmem", s->dmem_size);
304 cpu_register_physical_memory(s->dmem_base, s->dmem_size,
305 dmem_ram | IO_MEM_RAM);
306
307 qemu_add_mouse_event_handler(softusb_mouse_event, s, 0, "Milkymist Mouse");
308
309 /* create our usb bus */
07771f6f 310 usb_bus_new(&s->usbbus, &softusb_bus_ops, NULL);
87a381ec
MW
311
312 /* our two ports */
ae2dd336
MW
313 /* FIXME: claim to support full speed devices. qemu mouse and keyboard
314 * report themselves as full speed devices. */
87a381ec 315 usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops,
ae2dd336 316 USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
87a381ec 317 usb_register_port(&s->usbbus, &s->usbport[1], NULL, 1, &softusb_ops,
ae2dd336 318 USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
87a381ec
MW
319
320 /* and finally create an usb keyboard */
321 s->usbdev = usb_create_simple(&s->usbbus, "usb-kbd");
322 usb_hid_datain_cb(s->usbdev, s, softusb_usbdev_datain);
323 s->usbdev->info->handle_reset(s->usbdev);
324
325 return 0;
326}
327
328static const VMStateDescription vmstate_milkymist_softusb = {
329 .name = "milkymist-softusb",
330 .version_id = 1,
331 .minimum_version_id = 1,
332 .minimum_version_id_old = 1,
333 .fields = (VMStateField[]) {
334 VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
335 VMSTATE_INT32(mouse_dx, MilkymistSoftUsbState),
336 VMSTATE_INT32(mouse_dy, MilkymistSoftUsbState),
337 VMSTATE_INT32(mouse_dz, MilkymistSoftUsbState),
338 VMSTATE_UINT8(mouse_buttons_state, MilkymistSoftUsbState),
339 VMSTATE_BUFFER(kbd_usb_buffer, MilkymistSoftUsbState),
340 VMSTATE_END_OF_LIST()
341 }
342};
343
344static SysBusDeviceInfo milkymist_softusb_info = {
345 .init = milkymist_softusb_init,
346 .qdev.name = "milkymist-softusb",
347 .qdev.size = sizeof(MilkymistSoftUsbState),
348 .qdev.vmsd = &vmstate_milkymist_softusb,
349 .qdev.reset = milkymist_softusb_reset,
350 .qdev.props = (Property[]) {
351 DEFINE_PROP_UINT32(
352 "pmem_base", MilkymistSoftUsbState, pmem_base, 0xa0000000
353 ),
354 DEFINE_PROP_UINT32(
355 "pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000
356 ),
357 DEFINE_PROP_UINT32(
358 "dmem_base", MilkymistSoftUsbState, dmem_base, 0xa0020000
359 ),
360 DEFINE_PROP_UINT32(
361 "dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000
362 ),
363 DEFINE_PROP_END_OF_LIST(),
364 }
365};
366
367static void milkymist_softusb_register(void)
368{
369 sysbus_register_withprop(&milkymist_softusb_info);
370}
371
372device_init(milkymist_softusb_register)