]> Git Repo - qemu.git/blob - hw/milkymist-softusb.c
Merge remote-tracking branch 'agraf/xen-next' into staging
[qemu.git] / hw / milkymist-softusb.c
1 /*
2  *  QEMU model of the Milkymist SoftUSB block.
3  *
4  *  Copyright (c) 2010 Michael Walle <[email protected]>
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", 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", 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", 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", 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 void softusb_detach(USBPort *port)
251 {
252 }
253
254 static void softusb_child_detach(USBPort *port, USBDevice *child)
255 {
256 }
257
258 static USBPortOps softusb_ops = {
259     .attach = softusb_attach,
260     .detach = softusb_detach,
261     .child_detach = softusb_child_detach,
262 };
263
264 static USBBusOps softusb_bus_ops = {
265 };
266
267 static 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
286 static 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 */
310     usb_bus_new(&s->usbbus, &softusb_bus_ops, NULL);
311
312     /* our two ports */
313     usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops,
314             USB_SPEED_MASK_LOW);
315     usb_register_port(&s->usbbus, &s->usbport[1], NULL, 1, &softusb_ops,
316             USB_SPEED_MASK_LOW);
317
318     /* and finally create an usb keyboard */
319     s->usbdev = usb_create_simple(&s->usbbus, "usb-kbd");
320     usb_hid_datain_cb(s->usbdev, s, softusb_usbdev_datain);
321     s->usbdev->info->handle_reset(s->usbdev);
322
323     return 0;
324 }
325
326 static const VMStateDescription vmstate_milkymist_softusb = {
327     .name = "milkymist-softusb",
328     .version_id = 1,
329     .minimum_version_id = 1,
330     .minimum_version_id_old = 1,
331     .fields      = (VMStateField[]) {
332         VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
333         VMSTATE_INT32(mouse_dx, MilkymistSoftUsbState),
334         VMSTATE_INT32(mouse_dy, MilkymistSoftUsbState),
335         VMSTATE_INT32(mouse_dz, MilkymistSoftUsbState),
336         VMSTATE_UINT8(mouse_buttons_state, MilkymistSoftUsbState),
337         VMSTATE_BUFFER(kbd_usb_buffer, MilkymistSoftUsbState),
338         VMSTATE_END_OF_LIST()
339     }
340 };
341
342 static SysBusDeviceInfo milkymist_softusb_info = {
343     .init = milkymist_softusb_init,
344     .qdev.name  = "milkymist-softusb",
345     .qdev.size  = sizeof(MilkymistSoftUsbState),
346     .qdev.vmsd  = &vmstate_milkymist_softusb,
347     .qdev.reset = milkymist_softusb_reset,
348     .qdev.props = (Property[]) {
349         DEFINE_PROP_UINT32(
350                 "pmem_base", MilkymistSoftUsbState, pmem_base, 0xa0000000
351         ),
352         DEFINE_PROP_UINT32(
353                 "pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000
354         ),
355         DEFINE_PROP_UINT32(
356                 "dmem_base", MilkymistSoftUsbState, dmem_base, 0xa0020000
357         ),
358         DEFINE_PROP_UINT32(
359                 "dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000
360         ),
361         DEFINE_PROP_END_OF_LIST(),
362     }
363 };
364
365 static void milkymist_softusb_register(void)
366 {
367     sysbus_register_withprop(&milkymist_softusb_info);
368 }
369
370 device_init(milkymist_softusb_register)
This page took 0.043193 seconds and 4 git commands to generate.