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