milkymist-softusb.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. #include "qemu/osdep.h"
  24. #include "qapi/error.h"
  25. #include "hw/sysbus.h"
  26. #include "migration/vmstate.h"
  27. #include "trace.h"
  28. #include "ui/console.h"
  29. #include "hw/input/hid.h"
  30. #include "hw/irq.h"
  31. #include "hw/qdev-properties.h"
  32. #include "qemu/error-report.h"
  33. #include "qemu/module.h"
  34. enum {
  35. R_CTRL = 0,
  36. R_MAX
  37. };
  38. enum {
  39. CTRL_RESET = (1<<0),
  40. };
  41. #define COMLOC_DEBUG_PRODUCE 0x1000
  42. #define COMLOC_DEBUG_BASE 0x1001
  43. #define COMLOC_MEVT_PRODUCE 0x1101
  44. #define COMLOC_MEVT_BASE 0x1102
  45. #define COMLOC_KEVT_PRODUCE 0x1142
  46. #define COMLOC_KEVT_BASE 0x1143
  47. #define TYPE_MILKYMIST_SOFTUSB "milkymist-softusb"
  48. #define MILKYMIST_SOFTUSB(obj) \
  49. OBJECT_CHECK(MilkymistSoftUsbState, (obj), TYPE_MILKYMIST_SOFTUSB)
  50. struct MilkymistSoftUsbState {
  51. SysBusDevice parent_obj;
  52. HIDState hid_kbd;
  53. HIDState hid_mouse;
  54. MemoryRegion regs_region;
  55. MemoryRegion pmem;
  56. MemoryRegion dmem;
  57. qemu_irq irq;
  58. void *pmem_ptr;
  59. void *dmem_ptr;
  60. /* device properties */
  61. uint32_t pmem_size;
  62. uint32_t dmem_size;
  63. /* device registers */
  64. uint32_t regs[R_MAX];
  65. /* mouse state */
  66. uint8_t mouse_hid_buffer[4];
  67. /* keyboard state */
  68. uint8_t kbd_hid_buffer[8];
  69. };
  70. typedef struct MilkymistSoftUsbState MilkymistSoftUsbState;
  71. static uint64_t softusb_read(void *opaque, hwaddr addr,
  72. unsigned size)
  73. {
  74. MilkymistSoftUsbState *s = opaque;
  75. uint32_t r = 0;
  76. addr >>= 2;
  77. switch (addr) {
  78. case R_CTRL:
  79. r = s->regs[addr];
  80. break;
  81. default:
  82. error_report("milkymist_softusb: read access to unknown register 0x"
  83. TARGET_FMT_plx, addr << 2);
  84. break;
  85. }
  86. trace_milkymist_softusb_memory_read(addr << 2, r);
  87. return r;
  88. }
  89. static void
  90. softusb_write(void *opaque, hwaddr addr, uint64_t value,
  91. unsigned size)
  92. {
  93. MilkymistSoftUsbState *s = opaque;
  94. trace_milkymist_softusb_memory_write(addr, value);
  95. addr >>= 2;
  96. switch (addr) {
  97. case R_CTRL:
  98. s->regs[addr] = value;
  99. break;
  100. default:
  101. error_report("milkymist_softusb: write access to unknown register 0x"
  102. TARGET_FMT_plx, addr << 2);
  103. break;
  104. }
  105. }
  106. static const MemoryRegionOps softusb_mmio_ops = {
  107. .read = softusb_read,
  108. .write = softusb_write,
  109. .endianness = DEVICE_NATIVE_ENDIAN,
  110. .valid = {
  111. .min_access_size = 4,
  112. .max_access_size = 4,
  113. },
  114. };
  115. static inline void softusb_read_dmem(MilkymistSoftUsbState *s,
  116. uint32_t offset, uint8_t *buf, uint32_t len)
  117. {
  118. if (offset + len >= s->dmem_size) {
  119. error_report("milkymist_softusb: read dmem out of bounds "
  120. "at offset 0x%x, len %d", offset, len);
  121. memset(buf, 0, len);
  122. return;
  123. }
  124. memcpy(buf, s->dmem_ptr + offset, len);
  125. }
  126. static inline void softusb_write_dmem(MilkymistSoftUsbState *s,
  127. uint32_t offset, uint8_t *buf, uint32_t len)
  128. {
  129. if (offset + len >= s->dmem_size) {
  130. error_report("milkymist_softusb: write dmem out of bounds "
  131. "at offset 0x%x, len %d", offset, len);
  132. return;
  133. }
  134. memcpy(s->dmem_ptr + offset, buf, len);
  135. }
  136. static void softusb_mouse_changed(MilkymistSoftUsbState *s)
  137. {
  138. uint8_t m;
  139. softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
  140. trace_milkymist_softusb_mevt(m);
  141. softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4);
  142. m = (m + 1) & 0xf;
  143. softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
  144. trace_milkymist_softusb_pulse_irq();
  145. qemu_irq_pulse(s->irq);
  146. }
  147. static void softusb_kbd_changed(MilkymistSoftUsbState *s)
  148. {
  149. uint8_t m;
  150. softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
  151. trace_milkymist_softusb_kevt(m);
  152. softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8);
  153. m = (m + 1) & 0x7;
  154. softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
  155. trace_milkymist_softusb_pulse_irq();
  156. qemu_irq_pulse(s->irq);
  157. }
  158. static void softusb_kbd_hid_datain(HIDState *hs)
  159. {
  160. MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd);
  161. int len;
  162. /* if device is in reset, do nothing */
  163. if (s->regs[R_CTRL] & CTRL_RESET) {
  164. return;
  165. }
  166. while (hid_has_events(hs)) {
  167. len = hid_keyboard_poll(hs, s->kbd_hid_buffer,
  168. sizeof(s->kbd_hid_buffer));
  169. if (len == 8) {
  170. softusb_kbd_changed(s);
  171. }
  172. }
  173. }
  174. static void softusb_mouse_hid_datain(HIDState *hs)
  175. {
  176. MilkymistSoftUsbState *s =
  177. container_of(hs, MilkymistSoftUsbState, hid_mouse);
  178. int len;
  179. /* if device is in reset, do nothing */
  180. if (s->regs[R_CTRL] & CTRL_RESET) {
  181. return;
  182. }
  183. while (hid_has_events(hs)) {
  184. len = hid_pointer_poll(hs, s->mouse_hid_buffer,
  185. sizeof(s->mouse_hid_buffer));
  186. if (len == 4) {
  187. softusb_mouse_changed(s);
  188. }
  189. }
  190. }
  191. static void milkymist_softusb_reset(DeviceState *d)
  192. {
  193. MilkymistSoftUsbState *s = MILKYMIST_SOFTUSB(d);
  194. int i;
  195. for (i = 0; i < R_MAX; i++) {
  196. s->regs[i] = 0;
  197. }
  198. memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer));
  199. memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer));
  200. hid_reset(&s->hid_kbd);
  201. hid_reset(&s->hid_mouse);
  202. /* defaults */
  203. s->regs[R_CTRL] = CTRL_RESET;
  204. }
  205. static void milkymist_softusb_realize(DeviceState *dev, Error **errp)
  206. {
  207. MilkymistSoftUsbState *s = MILKYMIST_SOFTUSB(dev);
  208. SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
  209. sysbus_init_irq(sbd, &s->irq);
  210. memory_region_init_io(&s->regs_region, OBJECT(s), &softusb_mmio_ops, s,
  211. "milkymist-softusb", R_MAX * 4);
  212. sysbus_init_mmio(sbd, &s->regs_region);
  213. /* register pmem and dmem */
  214. memory_region_init_ram_nomigrate(&s->pmem, OBJECT(s), "milkymist-softusb.pmem",
  215. s->pmem_size, &error_fatal);
  216. vmstate_register_ram_global(&s->pmem);
  217. s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem);
  218. sysbus_init_mmio(sbd, &s->pmem);
  219. memory_region_init_ram_nomigrate(&s->dmem, OBJECT(s), "milkymist-softusb.dmem",
  220. s->dmem_size, &error_fatal);
  221. vmstate_register_ram_global(&s->dmem);
  222. s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem);
  223. sysbus_init_mmio(sbd, &s->dmem);
  224. hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain);
  225. hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain);
  226. }
  227. static const VMStateDescription vmstate_milkymist_softusb = {
  228. .name = "milkymist-softusb",
  229. .version_id = 1,
  230. .minimum_version_id = 1,
  231. .fields = (VMStateField[]) {
  232. VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
  233. VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState),
  234. VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState),
  235. VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState),
  236. VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState),
  237. VMSTATE_END_OF_LIST()
  238. }
  239. };
  240. static Property milkymist_softusb_properties[] = {
  241. DEFINE_PROP_UINT32("pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000),
  242. DEFINE_PROP_UINT32("dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000),
  243. DEFINE_PROP_END_OF_LIST(),
  244. };
  245. static void milkymist_softusb_class_init(ObjectClass *klass, void *data)
  246. {
  247. DeviceClass *dc = DEVICE_CLASS(klass);
  248. dc->realize = milkymist_softusb_realize;
  249. dc->reset = milkymist_softusb_reset;
  250. dc->vmsd = &vmstate_milkymist_softusb;
  251. dc->props = milkymist_softusb_properties;
  252. }
  253. static const TypeInfo milkymist_softusb_info = {
  254. .name = TYPE_MILKYMIST_SOFTUSB,
  255. .parent = TYPE_SYS_BUS_DEVICE,
  256. .instance_size = sizeof(MilkymistSoftUsbState),
  257. .class_init = milkymist_softusb_class_init,
  258. };
  259. static void milkymist_softusb_register_types(void)
  260. {
  261. type_register_static(&milkymist_softusb_info);
  262. }
  263. type_init(milkymist_softusb_register_types)