goldfish_tty.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /*
  2. * SPDX-License-Identifier: GPL-2.0-or-later
  3. *
  4. * Goldfish TTY
  5. *
  6. * (c) 2020 Laurent Vivier <laurent@vivier.eu>
  7. *
  8. */
  9. #include "qemu/osdep.h"
  10. #include "hw/irq.h"
  11. #include "hw/qdev-properties-system.h"
  12. #include "hw/sysbus.h"
  13. #include "migration/vmstate.h"
  14. #include "chardev/char-fe.h"
  15. #include "qemu/log.h"
  16. #include "trace.h"
  17. #include "exec/address-spaces.h"
  18. #include "system/dma.h"
  19. #include "hw/char/goldfish_tty.h"
  20. #define GOLDFISH_TTY_VERSION 1
  21. /* registers */
  22. enum {
  23. REG_PUT_CHAR = 0x00,
  24. REG_BYTES_READY = 0x04,
  25. REG_CMD = 0x08,
  26. REG_DATA_PTR = 0x10,
  27. REG_DATA_LEN = 0x14,
  28. REG_DATA_PTR_HIGH = 0x18,
  29. REG_VERSION = 0x20,
  30. };
  31. /* commands */
  32. enum {
  33. CMD_INT_DISABLE = 0x00,
  34. CMD_INT_ENABLE = 0x01,
  35. CMD_WRITE_BUFFER = 0x02,
  36. CMD_READ_BUFFER = 0x03,
  37. };
  38. static uint64_t goldfish_tty_read(void *opaque, hwaddr addr,
  39. unsigned size)
  40. {
  41. GoldfishTTYState *s = opaque;
  42. uint64_t value = 0;
  43. switch (addr) {
  44. case REG_BYTES_READY:
  45. value = fifo8_num_used(&s->rx_fifo);
  46. break;
  47. case REG_VERSION:
  48. value = GOLDFISH_TTY_VERSION;
  49. break;
  50. default:
  51. qemu_log_mask(LOG_UNIMP,
  52. "%s: unimplemented register read 0x%02"HWADDR_PRIx"\n",
  53. __func__, addr);
  54. break;
  55. }
  56. trace_goldfish_tty_read(s, addr, size, value);
  57. return value;
  58. }
  59. static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd)
  60. {
  61. uint32_t to_copy;
  62. uint8_t data_out[GOLFISH_TTY_BUFFER_SIZE];
  63. int len;
  64. uint64_t ptr;
  65. switch (cmd) {
  66. case CMD_INT_DISABLE:
  67. if (s->int_enabled) {
  68. if (!fifo8_is_empty(&s->rx_fifo)) {
  69. qemu_set_irq(s->irq, 0);
  70. }
  71. s->int_enabled = false;
  72. }
  73. break;
  74. case CMD_INT_ENABLE:
  75. if (!s->int_enabled) {
  76. if (!fifo8_is_empty(&s->rx_fifo)) {
  77. qemu_set_irq(s->irq, 1);
  78. }
  79. s->int_enabled = true;
  80. }
  81. break;
  82. case CMD_WRITE_BUFFER:
  83. len = s->data_len;
  84. ptr = s->data_ptr;
  85. while (len) {
  86. to_copy = MIN(GOLFISH_TTY_BUFFER_SIZE, len);
  87. dma_memory_read_relaxed(&address_space_memory, ptr,
  88. data_out, to_copy);
  89. qemu_chr_fe_write_all(&s->chr, data_out, to_copy);
  90. len -= to_copy;
  91. ptr += to_copy;
  92. }
  93. break;
  94. case CMD_READ_BUFFER:
  95. len = s->data_len;
  96. ptr = s->data_ptr;
  97. while (len && !fifo8_is_empty(&s->rx_fifo)) {
  98. const uint8_t *buf = fifo8_pop_bufptr(&s->rx_fifo, len, &to_copy);
  99. dma_memory_write_relaxed(&address_space_memory, ptr, buf, to_copy);
  100. len -= to_copy;
  101. ptr += to_copy;
  102. }
  103. if (s->int_enabled && fifo8_is_empty(&s->rx_fifo)) {
  104. qemu_set_irq(s->irq, 0);
  105. }
  106. break;
  107. }
  108. }
  109. static void goldfish_tty_write(void *opaque, hwaddr addr,
  110. uint64_t value, unsigned size)
  111. {
  112. GoldfishTTYState *s = opaque;
  113. unsigned char c;
  114. trace_goldfish_tty_write(s, addr, size, value);
  115. switch (addr) {
  116. case REG_PUT_CHAR:
  117. c = value;
  118. qemu_chr_fe_write_all(&s->chr, &c, sizeof(c));
  119. break;
  120. case REG_CMD:
  121. goldfish_tty_cmd(s, value);
  122. break;
  123. case REG_DATA_PTR:
  124. s->data_ptr = value;
  125. break;
  126. case REG_DATA_PTR_HIGH:
  127. s->data_ptr = deposit64(s->data_ptr, 32, 32, value);
  128. break;
  129. case REG_DATA_LEN:
  130. s->data_len = value;
  131. break;
  132. default:
  133. qemu_log_mask(LOG_UNIMP,
  134. "%s: unimplemented register write 0x%02"HWADDR_PRIx"\n",
  135. __func__, addr);
  136. break;
  137. }
  138. }
  139. static const MemoryRegionOps goldfish_tty_ops = {
  140. .read = goldfish_tty_read,
  141. .write = goldfish_tty_write,
  142. .endianness = DEVICE_NATIVE_ENDIAN,
  143. .valid.max_access_size = 4,
  144. .impl.max_access_size = 4,
  145. .impl.min_access_size = 4,
  146. };
  147. static int goldfish_tty_can_receive(void *opaque)
  148. {
  149. GoldfishTTYState *s = opaque;
  150. int available = fifo8_num_free(&s->rx_fifo);
  151. trace_goldfish_tty_can_receive(s, available);
  152. return available;
  153. }
  154. static void goldfish_tty_receive(void *opaque, const uint8_t *buffer, int size)
  155. {
  156. GoldfishTTYState *s = opaque;
  157. trace_goldfish_tty_receive(s, size);
  158. g_assert(size <= fifo8_num_free(&s->rx_fifo));
  159. fifo8_push_all(&s->rx_fifo, buffer, size);
  160. if (s->int_enabled && !fifo8_is_empty(&s->rx_fifo)) {
  161. qemu_set_irq(s->irq, 1);
  162. }
  163. }
  164. static void goldfish_tty_reset(DeviceState *dev)
  165. {
  166. GoldfishTTYState *s = GOLDFISH_TTY(dev);
  167. trace_goldfish_tty_reset(s);
  168. fifo8_reset(&s->rx_fifo);
  169. s->int_enabled = false;
  170. s->data_ptr = 0;
  171. s->data_len = 0;
  172. }
  173. static void goldfish_tty_realize(DeviceState *dev, Error **errp)
  174. {
  175. GoldfishTTYState *s = GOLDFISH_TTY(dev);
  176. trace_goldfish_tty_realize(s);
  177. fifo8_create(&s->rx_fifo, GOLFISH_TTY_BUFFER_SIZE);
  178. memory_region_init_io(&s->iomem, OBJECT(s), &goldfish_tty_ops, s,
  179. "goldfish_tty", 0x24);
  180. if (qemu_chr_fe_backend_connected(&s->chr)) {
  181. qemu_chr_fe_set_handlers(&s->chr, goldfish_tty_can_receive,
  182. goldfish_tty_receive, NULL, NULL,
  183. s, NULL, true);
  184. }
  185. }
  186. static void goldfish_tty_unrealize(DeviceState *dev)
  187. {
  188. GoldfishTTYState *s = GOLDFISH_TTY(dev);
  189. trace_goldfish_tty_unrealize(s);
  190. fifo8_destroy(&s->rx_fifo);
  191. }
  192. static const VMStateDescription vmstate_goldfish_tty = {
  193. .name = "goldfish_tty",
  194. .version_id = 1,
  195. .minimum_version_id = 1,
  196. .fields = (const VMStateField[]) {
  197. VMSTATE_UINT32(data_len, GoldfishTTYState),
  198. VMSTATE_UINT64(data_ptr, GoldfishTTYState),
  199. VMSTATE_BOOL(int_enabled, GoldfishTTYState),
  200. VMSTATE_FIFO8(rx_fifo, GoldfishTTYState),
  201. VMSTATE_END_OF_LIST()
  202. }
  203. };
  204. static const Property goldfish_tty_properties[] = {
  205. DEFINE_PROP_CHR("chardev", GoldfishTTYState, chr),
  206. };
  207. static void goldfish_tty_instance_init(Object *obj)
  208. {
  209. SysBusDevice *dev = SYS_BUS_DEVICE(obj);
  210. GoldfishTTYState *s = GOLDFISH_TTY(obj);
  211. trace_goldfish_tty_instance_init(s);
  212. sysbus_init_mmio(dev, &s->iomem);
  213. sysbus_init_irq(dev, &s->irq);
  214. }
  215. static void goldfish_tty_class_init(ObjectClass *oc, void *data)
  216. {
  217. DeviceClass *dc = DEVICE_CLASS(oc);
  218. device_class_set_props(dc, goldfish_tty_properties);
  219. device_class_set_legacy_reset(dc, goldfish_tty_reset);
  220. dc->realize = goldfish_tty_realize;
  221. dc->unrealize = goldfish_tty_unrealize;
  222. dc->vmsd = &vmstate_goldfish_tty;
  223. set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
  224. }
  225. static const TypeInfo goldfish_tty_info = {
  226. .name = TYPE_GOLDFISH_TTY,
  227. .parent = TYPE_SYS_BUS_DEVICE,
  228. .class_init = goldfish_tty_class_init,
  229. .instance_init = goldfish_tty_instance_init,
  230. .instance_size = sizeof(GoldfishTTYState),
  231. };
  232. static void goldfish_tty_register_types(void)
  233. {
  234. type_register_static(&goldfish_tty_info);
  235. }
  236. type_init(goldfish_tty_register_types)