1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include "qemu/osdep.h"
16#include <hw/qdev.h>
17#include "qemu/thread.h"
18#include "qemu/error-report.h"
19
20#include "hw/s390x/sclp.h"
21#include "hw/s390x/event-facility.h"
22#include "sysemu/char.h"
23
24typedef struct ASCIIConsoleData {
25 EventBufferHeader ebh;
26 char data[0];
27} QEMU_PACKED ASCIIConsoleData;
28
29
30#define SIZE_BUFFER_VT220 4080
31
32typedef struct SCLPConsole {
33 SCLPEvent event;
34 CharDriverState *chr;
35 uint8_t iov[SIZE_BUFFER_VT220];
36 uint32_t iov_sclp;
37 uint32_t iov_bs;
38 uint32_t iov_data_len;
39 uint32_t iov_sclp_rest;
40 bool notify;
41} SCLPConsole;
42
43
44
45
46static int chr_can_read(void *opaque)
47{
48 SCLPConsole *scon = opaque;
49 int avail = SIZE_BUFFER_VT220 - scon->iov_data_len;
50
51 if (avail == 0) {
52 scon->notify = true;
53 }
54 return avail;
55}
56
57
58static void chr_read(void *opaque, const uint8_t *buf, int size)
59{
60 SCLPConsole *scon = opaque;
61
62 assert(scon);
63
64 assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len);
65
66
67 memcpy(&scon->iov[scon->iov_bs], buf, size);
68 scon->iov_data_len += size;
69 scon->iov_sclp_rest += size;
70 scon->iov_bs += size;
71 scon->event.event_pending = true;
72 sclp_service_interrupt(0);
73}
74
75
76
77static bool can_handle_event(uint8_t type)
78{
79 return type == SCLP_EVENT_ASCII_CONSOLE_DATA;
80}
81
82static unsigned int send_mask(void)
83{
84 return SCLP_EVENT_MASK_MSG_ASCII;
85}
86
87static unsigned int receive_mask(void)
88{
89 return SCLP_EVENT_MASK_MSG_ASCII;
90}
91
92
93
94
95static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
96 int avail)
97{
98 SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event);
99
100
101 *buf++ = '\0';
102 avail--;
103
104 if (avail >= cons->iov_sclp_rest) {
105
106 memcpy(buf, &cons->iov[cons->iov_sclp], cons->iov_sclp_rest);
107 *size = cons->iov_sclp_rest + 1;
108 cons->iov_sclp = 0;
109 cons->iov_bs = 0;
110 cons->iov_data_len = 0;
111 cons->iov_sclp_rest = 0;
112 event->event_pending = false;
113
114 } else {
115
116 memcpy(buf, &cons->iov[cons->iov_sclp], avail);
117 *size = avail + 1;
118 cons->iov_sclp_rest -= avail;
119 cons->iov_sclp += avail;
120
121 }
122 if (cons->notify) {
123 cons->notify = false;
124 qemu_notify_event();
125 }
126}
127
128static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
129 int *slen)
130{
131 int avail;
132 size_t src_len;
133 uint8_t *to;
134 ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
135
136 if (!event->event_pending) {
137
138 return 0;
139 }
140
141 to = (uint8_t *)&acd->data;
142 avail = *slen - sizeof(ASCIIConsoleData);
143 get_console_data(event, to, &src_len, avail);
144
145 acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len);
146 acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
147 acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED;
148 *slen = avail - src_len;
149
150 return 1;
151}
152
153
154
155
156
157static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf,
158 size_t len)
159{
160 SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
161
162 if (!scon->chr) {
163
164 return len;
165 }
166
167 return qemu_chr_fe_write_all(scon->chr, buf, len);
168}
169
170static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
171{
172 int rc;
173 int length;
174 ssize_t written;
175 ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
176
177 length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader);
178 written = write_console_data(event, (uint8_t *)acd->data, length);
179
180 rc = SCLP_RC_NORMAL_COMPLETION;
181
182 evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED;
183
184
185 if (written < 0) {
186
187 evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED);
188 rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK;
189 }
190
191 return rc;
192}
193
194static const VMStateDescription vmstate_sclpconsole = {
195 .name = "sclpconsole",
196 .version_id = 0,
197 .minimum_version_id = 0,
198 .fields = (VMStateField[]) {
199 VMSTATE_BOOL(event.event_pending, SCLPConsole),
200 VMSTATE_UINT8_ARRAY(iov, SCLPConsole, SIZE_BUFFER_VT220),
201 VMSTATE_UINT32(iov_sclp, SCLPConsole),
202 VMSTATE_UINT32(iov_bs, SCLPConsole),
203 VMSTATE_UINT32(iov_data_len, SCLPConsole),
204 VMSTATE_UINT32(iov_sclp_rest, SCLPConsole),
205 VMSTATE_END_OF_LIST()
206 }
207};
208
209
210
211
212
213static int console_init(SCLPEvent *event)
214{
215 static bool console_available;
216
217 SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
218
219 if (console_available) {
220 error_report("Multiple VT220 operator consoles are not supported");
221 return -1;
222 }
223 console_available = true;
224 if (scon->chr) {
225 qemu_chr_add_handlers(scon->chr, chr_can_read,
226 chr_read, NULL, scon);
227 }
228
229 return 0;
230}
231
232static void console_reset(DeviceState *dev)
233{
234 SCLPEvent *event = SCLP_EVENT(dev);
235 SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
236
237 event->event_pending = false;
238 scon->iov_sclp = 0;
239 scon->iov_bs = 0;
240 scon->iov_data_len = 0;
241 scon->iov_sclp_rest = 0;
242 scon->notify = false;
243}
244
245static int console_exit(SCLPEvent *event)
246{
247 return 0;
248}
249
250static Property console_properties[] = {
251 DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
252 DEFINE_PROP_END_OF_LIST(),
253};
254
255static void console_class_init(ObjectClass *klass, void *data)
256{
257 DeviceClass *dc = DEVICE_CLASS(klass);
258 SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
259
260 dc->props = console_properties;
261 dc->reset = console_reset;
262 dc->vmsd = &vmstate_sclpconsole;
263 ec->init = console_init;
264 ec->exit = console_exit;
265 ec->get_send_mask = send_mask;
266 ec->get_receive_mask = receive_mask;
267 ec->can_handle_event = can_handle_event;
268 ec->read_event_data = read_event_data;
269 ec->write_event_data = write_event_data;
270 set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
271}
272
273static const TypeInfo sclp_console_info = {
274 .name = "sclpconsole",
275 .parent = TYPE_SCLP_EVENT,
276 .instance_size = sizeof(SCLPConsole),
277 .class_init = console_class_init,
278 .class_size = sizeof(SCLPEventClass),
279};
280
281static void register_types(void)
282{
283 type_register_static(&sclp_console_info);
284}
285
286type_init(register_types)
287