root / hw / virtio-serial-bus.c @ 9ed7b059
History | View | Annotate | Download (21.1 kB)
1 | 98b19252 | Amit Shah | /*
|
---|---|---|---|
2 | 98b19252 | Amit Shah | * A bus for connecting virtio serial and console ports
|
3 | 98b19252 | Amit Shah | *
|
4 | 71c092e9 | Amit Shah | * Copyright (C) 2009, 2010 Red Hat, Inc.
|
5 | 98b19252 | Amit Shah | *
|
6 | 98b19252 | Amit Shah | * Author(s):
|
7 | 98b19252 | Amit Shah | * Amit Shah <amit.shah@redhat.com>
|
8 | 98b19252 | Amit Shah | *
|
9 | 98b19252 | Amit Shah | * Some earlier parts are:
|
10 | 98b19252 | Amit Shah | * Copyright IBM, Corp. 2008
|
11 | 98b19252 | Amit Shah | * authored by
|
12 | 98b19252 | Amit Shah | * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
|
13 | 98b19252 | Amit Shah | *
|
14 | 98b19252 | Amit Shah | * This work is licensed under the terms of the GNU GPL, version 2. See
|
15 | 98b19252 | Amit Shah | * the COPYING file in the top-level directory.
|
16 | 98b19252 | Amit Shah | */
|
17 | 98b19252 | Amit Shah | |
18 | e4d5639d | Amit Shah | #include "iov.h" |
19 | 98b19252 | Amit Shah | #include "monitor.h" |
20 | 98b19252 | Amit Shah | #include "qemu-queue.h" |
21 | 98b19252 | Amit Shah | #include "sysbus.h" |
22 | 98b19252 | Amit Shah | #include "virtio-serial.h" |
23 | 98b19252 | Amit Shah | |
24 | 98b19252 | Amit Shah | /* The virtio-serial bus on top of which the ports will ride as devices */
|
25 | 98b19252 | Amit Shah | struct VirtIOSerialBus {
|
26 | 98b19252 | Amit Shah | BusState qbus; |
27 | 98b19252 | Amit Shah | |
28 | 98b19252 | Amit Shah | /* This is the parent device that provides the bus for ports. */
|
29 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
30 | 98b19252 | Amit Shah | |
31 | 98b19252 | Amit Shah | /* The maximum number of ports that can ride on top of this bus */
|
32 | 98b19252 | Amit Shah | uint32_t max_nr_ports; |
33 | 98b19252 | Amit Shah | }; |
34 | 98b19252 | Amit Shah | |
35 | 98b19252 | Amit Shah | struct VirtIOSerial {
|
36 | 98b19252 | Amit Shah | VirtIODevice vdev; |
37 | 98b19252 | Amit Shah | |
38 | 98b19252 | Amit Shah | VirtQueue *c_ivq, *c_ovq; |
39 | 98b19252 | Amit Shah | /* Arrays of ivqs and ovqs: one per port */
|
40 | 98b19252 | Amit Shah | VirtQueue **ivqs, **ovqs; |
41 | 98b19252 | Amit Shah | |
42 | 98b19252 | Amit Shah | VirtIOSerialBus *bus; |
43 | 98b19252 | Amit Shah | |
44 | 98b19252 | Amit Shah | QTAILQ_HEAD(, VirtIOSerialPort) ports; |
45 | 055b889f | Amit Shah | |
46 | 055b889f | Amit Shah | /* bitmap for identifying active ports */
|
47 | 055b889f | Amit Shah | uint32_t *ports_map; |
48 | 055b889f | Amit Shah | |
49 | 98b19252 | Amit Shah | struct virtio_console_config config;
|
50 | 98b19252 | Amit Shah | }; |
51 | 98b19252 | Amit Shah | |
52 | 98b19252 | Amit Shah | static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id)
|
53 | 98b19252 | Amit Shah | { |
54 | 98b19252 | Amit Shah | VirtIOSerialPort *port; |
55 | 98b19252 | Amit Shah | |
56 | 055b889f | Amit Shah | if (id == VIRTIO_CONSOLE_BAD_ID) {
|
57 | 055b889f | Amit Shah | return NULL; |
58 | 055b889f | Amit Shah | } |
59 | 055b889f | Amit Shah | |
60 | 98b19252 | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
61 | 98b19252 | Amit Shah | if (port->id == id)
|
62 | 98b19252 | Amit Shah | return port;
|
63 | 98b19252 | Amit Shah | } |
64 | 98b19252 | Amit Shah | return NULL; |
65 | 98b19252 | Amit Shah | } |
66 | 98b19252 | Amit Shah | |
67 | 98b19252 | Amit Shah | static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq)
|
68 | 98b19252 | Amit Shah | { |
69 | 98b19252 | Amit Shah | VirtIOSerialPort *port; |
70 | 98b19252 | Amit Shah | |
71 | 98b19252 | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
72 | 98b19252 | Amit Shah | if (port->ivq == vq || port->ovq == vq)
|
73 | 98b19252 | Amit Shah | return port;
|
74 | 98b19252 | Amit Shah | } |
75 | 98b19252 | Amit Shah | return NULL; |
76 | 98b19252 | Amit Shah | } |
77 | 98b19252 | Amit Shah | |
78 | 6663a195 | Amit Shah | static bool use_multiport(VirtIOSerial *vser) |
79 | 6663a195 | Amit Shah | { |
80 | 6663a195 | Amit Shah | return vser->vdev.guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); |
81 | 6663a195 | Amit Shah | } |
82 | 6663a195 | Amit Shah | |
83 | 98b19252 | Amit Shah | static size_t write_to_port(VirtIOSerialPort *port,
|
84 | 98b19252 | Amit Shah | const uint8_t *buf, size_t size)
|
85 | 98b19252 | Amit Shah | { |
86 | 98b19252 | Amit Shah | VirtQueueElement elem; |
87 | 98b19252 | Amit Shah | VirtQueue *vq; |
88 | e4d5639d | Amit Shah | size_t offset; |
89 | 98b19252 | Amit Shah | |
90 | 98b19252 | Amit Shah | vq = port->ivq; |
91 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq)) {
|
92 | 98b19252 | Amit Shah | return 0; |
93 | 98b19252 | Amit Shah | } |
94 | 98b19252 | Amit Shah | |
95 | e4d5639d | Amit Shah | offset = 0;
|
96 | 98b19252 | Amit Shah | while (offset < size) {
|
97 | e4d5639d | Amit Shah | size_t len; |
98 | 98b19252 | Amit Shah | |
99 | 98b19252 | Amit Shah | if (!virtqueue_pop(vq, &elem)) {
|
100 | 98b19252 | Amit Shah | break;
|
101 | 98b19252 | Amit Shah | } |
102 | 98b19252 | Amit Shah | |
103 | e4d5639d | Amit Shah | len = iov_from_buf(elem.in_sg, elem.in_num, |
104 | e4d5639d | Amit Shah | buf + offset, size - offset); |
105 | e4d5639d | Amit Shah | offset += len; |
106 | 98b19252 | Amit Shah | |
107 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, len); |
108 | 98b19252 | Amit Shah | } |
109 | 98b19252 | Amit Shah | |
110 | 98b19252 | Amit Shah | virtio_notify(&port->vser->vdev, vq); |
111 | 98b19252 | Amit Shah | return offset;
|
112 | 98b19252 | Amit Shah | } |
113 | 98b19252 | Amit Shah | |
114 | 9ed7b059 | Amit Shah | static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, |
115 | 9ed7b059 | Amit Shah | VirtIODevice *vdev, bool discard)
|
116 | a69c7600 | Amit Shah | { |
117 | a69c7600 | Amit Shah | VirtQueueElement elem; |
118 | a69c7600 | Amit Shah | |
119 | a69c7600 | Amit Shah | assert(port || discard); |
120 | a69c7600 | Amit Shah | |
121 | 9ed7b059 | Amit Shah | while ((discard || !port->throttled) && virtqueue_pop(vq, &elem)) {
|
122 | a69c7600 | Amit Shah | uint8_t *buf; |
123 | a69c7600 | Amit Shah | size_t ret, buf_size; |
124 | a69c7600 | Amit Shah | |
125 | a69c7600 | Amit Shah | if (!discard) {
|
126 | a69c7600 | Amit Shah | buf_size = iov_size(elem.out_sg, elem.out_num); |
127 | a69c7600 | Amit Shah | buf = qemu_malloc(buf_size); |
128 | a69c7600 | Amit Shah | ret = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, buf_size);
|
129 | a69c7600 | Amit Shah | |
130 | a69c7600 | Amit Shah | port->info->have_data(port, buf, ret); |
131 | a69c7600 | Amit Shah | qemu_free(buf); |
132 | a69c7600 | Amit Shah | } |
133 | a69c7600 | Amit Shah | virtqueue_push(vq, &elem, 0);
|
134 | a69c7600 | Amit Shah | } |
135 | a69c7600 | Amit Shah | virtio_notify(vdev, vq); |
136 | a69c7600 | Amit Shah | } |
137 | a69c7600 | Amit Shah | |
138 | 9ed7b059 | Amit Shah | static void flush_queued_data(VirtIOSerialPort *port, bool discard) |
139 | 9ed7b059 | Amit Shah | { |
140 | 9ed7b059 | Amit Shah | assert(port || discard); |
141 | 9ed7b059 | Amit Shah | |
142 | 9ed7b059 | Amit Shah | do_flush_queued_data(port, port->ovq, &port->vser->vdev, discard); |
143 | 9ed7b059 | Amit Shah | } |
144 | 9ed7b059 | Amit Shah | |
145 | 98b19252 | Amit Shah | static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len) |
146 | 98b19252 | Amit Shah | { |
147 | 98b19252 | Amit Shah | VirtQueueElement elem; |
148 | 98b19252 | Amit Shah | VirtQueue *vq; |
149 | 98b19252 | Amit Shah | struct virtio_console_control *cpkt;
|
150 | 98b19252 | Amit Shah | |
151 | 98b19252 | Amit Shah | vq = port->vser->c_ivq; |
152 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq)) {
|
153 | 98b19252 | Amit Shah | return 0; |
154 | 98b19252 | Amit Shah | } |
155 | 98b19252 | Amit Shah | if (!virtqueue_pop(vq, &elem)) {
|
156 | 98b19252 | Amit Shah | return 0; |
157 | 98b19252 | Amit Shah | } |
158 | 98b19252 | Amit Shah | |
159 | 98b19252 | Amit Shah | cpkt = (struct virtio_console_control *)buf;
|
160 | 98b19252 | Amit Shah | stl_p(&cpkt->id, port->id); |
161 | 98b19252 | Amit Shah | memcpy(elem.in_sg[0].iov_base, buf, len);
|
162 | 98b19252 | Amit Shah | |
163 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, len); |
164 | 98b19252 | Amit Shah | virtio_notify(&port->vser->vdev, vq); |
165 | 98b19252 | Amit Shah | return len;
|
166 | 98b19252 | Amit Shah | } |
167 | 98b19252 | Amit Shah | |
168 | 98b19252 | Amit Shah | static size_t send_control_event(VirtIOSerialPort *port, uint16_t event,
|
169 | 98b19252 | Amit Shah | uint16_t value) |
170 | 98b19252 | Amit Shah | { |
171 | 98b19252 | Amit Shah | struct virtio_console_control cpkt;
|
172 | 98b19252 | Amit Shah | |
173 | 98b19252 | Amit Shah | stw_p(&cpkt.event, event); |
174 | 98b19252 | Amit Shah | stw_p(&cpkt.value, value); |
175 | 98b19252 | Amit Shah | |
176 | 98b19252 | Amit Shah | return send_control_msg(port, &cpkt, sizeof(cpkt)); |
177 | 98b19252 | Amit Shah | } |
178 | 98b19252 | Amit Shah | |
179 | 98b19252 | Amit Shah | /* Functions for use inside qemu to open and read from/write to ports */
|
180 | 98b19252 | Amit Shah | int virtio_serial_open(VirtIOSerialPort *port)
|
181 | 98b19252 | Amit Shah | { |
182 | 6663a195 | Amit Shah | /* Don't allow opening an already-open port */
|
183 | 6663a195 | Amit Shah | if (port->host_connected) {
|
184 | 6663a195 | Amit Shah | return 0; |
185 | 6663a195 | Amit Shah | } |
186 | 6663a195 | Amit Shah | /* Send port open notification to the guest */
|
187 | 6663a195 | Amit Shah | port->host_connected = true;
|
188 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
189 | 6663a195 | Amit Shah | |
190 | 98b19252 | Amit Shah | return 0; |
191 | 98b19252 | Amit Shah | } |
192 | 98b19252 | Amit Shah | |
193 | 98b19252 | Amit Shah | int virtio_serial_close(VirtIOSerialPort *port)
|
194 | 98b19252 | Amit Shah | { |
195 | 6663a195 | Amit Shah | port->host_connected = false;
|
196 | 9ed7b059 | Amit Shah | /*
|
197 | 9ed7b059 | Amit Shah | * If there's any data the guest sent which the app didn't
|
198 | 9ed7b059 | Amit Shah | * consume, reset the throttling flag and discard the data.
|
199 | 9ed7b059 | Amit Shah | */
|
200 | 9ed7b059 | Amit Shah | port->throttled = false;
|
201 | 9ed7b059 | Amit Shah | flush_queued_data(port, true);
|
202 | 9ed7b059 | Amit Shah | |
203 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
|
204 | 6663a195 | Amit Shah | |
205 | 98b19252 | Amit Shah | return 0; |
206 | 98b19252 | Amit Shah | } |
207 | 98b19252 | Amit Shah | |
208 | 98b19252 | Amit Shah | /* Individual ports/apps call this function to write to the guest. */
|
209 | 98b19252 | Amit Shah | ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
|
210 | 98b19252 | Amit Shah | size_t size) |
211 | 98b19252 | Amit Shah | { |
212 | 6663a195 | Amit Shah | if (!port || !port->host_connected || !port->guest_connected) {
|
213 | 6663a195 | Amit Shah | return 0; |
214 | 6663a195 | Amit Shah | } |
215 | 98b19252 | Amit Shah | return write_to_port(port, buf, size);
|
216 | 98b19252 | Amit Shah | } |
217 | 98b19252 | Amit Shah | |
218 | 98b19252 | Amit Shah | /*
|
219 | 98b19252 | Amit Shah | * Readiness of the guest to accept data on a port.
|
220 | 98b19252 | Amit Shah | * Returns max. data the guest can receive
|
221 | 98b19252 | Amit Shah | */
|
222 | 98b19252 | Amit Shah | size_t virtio_serial_guest_ready(VirtIOSerialPort *port) |
223 | 98b19252 | Amit Shah | { |
224 | 98b19252 | Amit Shah | VirtQueue *vq = port->ivq; |
225 | 98b19252 | Amit Shah | |
226 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq) ||
|
227 | 98b19252 | Amit Shah | !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || |
228 | 98b19252 | Amit Shah | virtio_queue_empty(vq)) { |
229 | 98b19252 | Amit Shah | return 0; |
230 | 98b19252 | Amit Shah | } |
231 | 6663a195 | Amit Shah | if (use_multiport(port->vser) && !port->guest_connected) {
|
232 | 6663a195 | Amit Shah | return 0; |
233 | 6663a195 | Amit Shah | } |
234 | 98b19252 | Amit Shah | |
235 | 98b19252 | Amit Shah | if (virtqueue_avail_bytes(vq, 4096, 0)) { |
236 | 98b19252 | Amit Shah | return 4096; |
237 | 98b19252 | Amit Shah | } |
238 | 98b19252 | Amit Shah | if (virtqueue_avail_bytes(vq, 1, 0)) { |
239 | 98b19252 | Amit Shah | return 1; |
240 | 98b19252 | Amit Shah | } |
241 | 98b19252 | Amit Shah | return 0; |
242 | 98b19252 | Amit Shah | } |
243 | 98b19252 | Amit Shah | |
244 | 9ed7b059 | Amit Shah | void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) |
245 | 9ed7b059 | Amit Shah | { |
246 | 9ed7b059 | Amit Shah | if (!port) {
|
247 | 9ed7b059 | Amit Shah | return;
|
248 | 9ed7b059 | Amit Shah | } |
249 | 9ed7b059 | Amit Shah | |
250 | 9ed7b059 | Amit Shah | port->throttled = throttle; |
251 | 9ed7b059 | Amit Shah | if (throttle) {
|
252 | 9ed7b059 | Amit Shah | return;
|
253 | 9ed7b059 | Amit Shah | } |
254 | 9ed7b059 | Amit Shah | |
255 | 9ed7b059 | Amit Shah | flush_queued_data(port, false);
|
256 | 9ed7b059 | Amit Shah | } |
257 | 9ed7b059 | Amit Shah | |
258 | 98b19252 | Amit Shah | /* Guest wants to notify us of some event */
|
259 | e61da14d | Amit Shah | static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) |
260 | 98b19252 | Amit Shah | { |
261 | 98b19252 | Amit Shah | struct VirtIOSerialPort *port;
|
262 | 98b19252 | Amit Shah | struct virtio_console_control cpkt, *gcpkt;
|
263 | 160600fd | Amit Shah | uint8_t *buffer; |
264 | 160600fd | Amit Shah | size_t buffer_len; |
265 | 98b19252 | Amit Shah | |
266 | 98b19252 | Amit Shah | gcpkt = buf; |
267 | 98b19252 | Amit Shah | |
268 | e61da14d | Amit Shah | if (len < sizeof(cpkt)) { |
269 | e61da14d | Amit Shah | /* The guest sent an invalid control packet */
|
270 | e61da14d | Amit Shah | return;
|
271 | e61da14d | Amit Shah | } |
272 | e61da14d | Amit Shah | |
273 | 98b19252 | Amit Shah | cpkt.event = lduw_p(&gcpkt->event); |
274 | 98b19252 | Amit Shah | cpkt.value = lduw_p(&gcpkt->value); |
275 | 98b19252 | Amit Shah | |
276 | 055b889f | Amit Shah | port = find_port_by_id(vser, ldl_p(&gcpkt->id)); |
277 | 055b889f | Amit Shah | if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY)
|
278 | 055b889f | Amit Shah | return;
|
279 | 055b889f | Amit Shah | |
280 | 98b19252 | Amit Shah | switch(cpkt.event) {
|
281 | 055b889f | Amit Shah | case VIRTIO_CONSOLE_DEVICE_READY:
|
282 | 4048c7c3 | Amit Shah | if (!cpkt.value) {
|
283 | 4048c7c3 | Amit Shah | error_report("virtio-serial-bus: Guest failure in adding device %s\n",
|
284 | 4048c7c3 | Amit Shah | vser->bus->qbus.name); |
285 | 4048c7c3 | Amit Shah | break;
|
286 | 4048c7c3 | Amit Shah | } |
287 | 055b889f | Amit Shah | /*
|
288 | 055b889f | Amit Shah | * The device is up, we can now tell the device about all the
|
289 | 055b889f | Amit Shah | * ports we have here.
|
290 | 055b889f | Amit Shah | */
|
291 | 055b889f | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
292 | 055b889f | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1);
|
293 | 055b889f | Amit Shah | } |
294 | 055b889f | Amit Shah | break;
|
295 | 055b889f | Amit Shah | |
296 | 98b19252 | Amit Shah | case VIRTIO_CONSOLE_PORT_READY:
|
297 | 4048c7c3 | Amit Shah | if (!cpkt.value) {
|
298 | 4048c7c3 | Amit Shah | error_report("virtio-serial-bus: Guest failure in adding port %u for device %s\n",
|
299 | 4048c7c3 | Amit Shah | port->id, vser->bus->qbus.name); |
300 | 4048c7c3 | Amit Shah | break;
|
301 | 4048c7c3 | Amit Shah | } |
302 | 98b19252 | Amit Shah | /*
|
303 | 98b19252 | Amit Shah | * Now that we know the guest asked for the port name, we're
|
304 | 98b19252 | Amit Shah | * sure the guest has initialised whatever state is necessary
|
305 | 98b19252 | Amit Shah | * for this port. Now's a good time to let the guest know if
|
306 | 98b19252 | Amit Shah | * this port is a console port so that the guest can hook it
|
307 | 98b19252 | Amit Shah | * up to hvc.
|
308 | 98b19252 | Amit Shah | */
|
309 | 98b19252 | Amit Shah | if (port->is_console) {
|
310 | 98b19252 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
|
311 | 98b19252 | Amit Shah | } |
312 | 6663a195 | Amit Shah | |
313 | 160600fd | Amit Shah | if (port->name) {
|
314 | 160600fd | Amit Shah | stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); |
315 | 160600fd | Amit Shah | stw_p(&cpkt.value, 1);
|
316 | 160600fd | Amit Shah | |
317 | 160600fd | Amit Shah | buffer_len = sizeof(cpkt) + strlen(port->name) + 1; |
318 | 160600fd | Amit Shah | buffer = qemu_malloc(buffer_len); |
319 | 160600fd | Amit Shah | |
320 | 160600fd | Amit Shah | memcpy(buffer, &cpkt, sizeof(cpkt));
|
321 | 160600fd | Amit Shah | memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name));
|
322 | 160600fd | Amit Shah | buffer[buffer_len - 1] = 0; |
323 | 160600fd | Amit Shah | |
324 | 160600fd | Amit Shah | send_control_msg(port, buffer, buffer_len); |
325 | 160600fd | Amit Shah | qemu_free(buffer); |
326 | 160600fd | Amit Shah | } |
327 | 160600fd | Amit Shah | |
328 | 6663a195 | Amit Shah | if (port->host_connected) {
|
329 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
330 | 6663a195 | Amit Shah | } |
331 | 6663a195 | Amit Shah | |
332 | 98b19252 | Amit Shah | /*
|
333 | 98b19252 | Amit Shah | * When the guest has asked us for this information it means
|
334 | 98b19252 | Amit Shah | * the guest is all setup and has its virtqueues
|
335 | 98b19252 | Amit Shah | * initialised. If some app is interested in knowing about
|
336 | 98b19252 | Amit Shah | * this event, let it know.
|
337 | 98b19252 | Amit Shah | */
|
338 | 98b19252 | Amit Shah | if (port->info->guest_ready) {
|
339 | 98b19252 | Amit Shah | port->info->guest_ready(port); |
340 | 98b19252 | Amit Shah | } |
341 | 98b19252 | Amit Shah | break;
|
342 | 6663a195 | Amit Shah | |
343 | 6663a195 | Amit Shah | case VIRTIO_CONSOLE_PORT_OPEN:
|
344 | 6663a195 | Amit Shah | port->guest_connected = cpkt.value; |
345 | 6663a195 | Amit Shah | if (cpkt.value && port->info->guest_open) {
|
346 | 6663a195 | Amit Shah | /* Send the guest opened notification if an app is interested */
|
347 | 6663a195 | Amit Shah | port->info->guest_open(port); |
348 | 6663a195 | Amit Shah | } |
349 | 6663a195 | Amit Shah | |
350 | 6663a195 | Amit Shah | if (!cpkt.value && port->info->guest_close) {
|
351 | 6663a195 | Amit Shah | /* Send the guest closed notification if an app is interested */
|
352 | 6663a195 | Amit Shah | port->info->guest_close(port); |
353 | 6663a195 | Amit Shah | } |
354 | 6663a195 | Amit Shah | break;
|
355 | 98b19252 | Amit Shah | } |
356 | 98b19252 | Amit Shah | } |
357 | 98b19252 | Amit Shah | |
358 | 98b19252 | Amit Shah | static void control_in(VirtIODevice *vdev, VirtQueue *vq) |
359 | 98b19252 | Amit Shah | { |
360 | 98b19252 | Amit Shah | } |
361 | 98b19252 | Amit Shah | |
362 | 98b19252 | Amit Shah | static void control_out(VirtIODevice *vdev, VirtQueue *vq) |
363 | 98b19252 | Amit Shah | { |
364 | 98b19252 | Amit Shah | VirtQueueElement elem; |
365 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
366 | e61da14d | Amit Shah | uint8_t *buf; |
367 | e61da14d | Amit Shah | size_t len; |
368 | 98b19252 | Amit Shah | |
369 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
370 | 98b19252 | Amit Shah | |
371 | e61da14d | Amit Shah | len = 0;
|
372 | e61da14d | Amit Shah | buf = NULL;
|
373 | 98b19252 | Amit Shah | while (virtqueue_pop(vq, &elem)) {
|
374 | e61da14d | Amit Shah | size_t cur_len, copied; |
375 | e61da14d | Amit Shah | |
376 | e61da14d | Amit Shah | cur_len = iov_size(elem.out_sg, elem.out_num); |
377 | e61da14d | Amit Shah | /*
|
378 | e61da14d | Amit Shah | * Allocate a new buf only if we didn't have one previously or
|
379 | e61da14d | Amit Shah | * if the size of the buf differs
|
380 | e61da14d | Amit Shah | */
|
381 | e61da14d | Amit Shah | if (cur_len > len) {
|
382 | e61da14d | Amit Shah | qemu_free(buf); |
383 | e61da14d | Amit Shah | |
384 | e61da14d | Amit Shah | buf = qemu_malloc(cur_len); |
385 | e61da14d | Amit Shah | len = cur_len; |
386 | e61da14d | Amit Shah | } |
387 | e61da14d | Amit Shah | copied = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, len);
|
388 | e61da14d | Amit Shah | |
389 | e61da14d | Amit Shah | handle_control_message(vser, buf, copied); |
390 | 1e4476aa | Amit Shah | virtqueue_push(vq, &elem, 0);
|
391 | 98b19252 | Amit Shah | } |
392 | e61da14d | Amit Shah | qemu_free(buf); |
393 | 98b19252 | Amit Shah | virtio_notify(vdev, vq); |
394 | 98b19252 | Amit Shah | } |
395 | 98b19252 | Amit Shah | |
396 | 98b19252 | Amit Shah | /* Guest wrote something to some port. */
|
397 | 98b19252 | Amit Shah | static void handle_output(VirtIODevice *vdev, VirtQueue *vq) |
398 | 98b19252 | Amit Shah | { |
399 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
400 | a69c7600 | Amit Shah | VirtIOSerialPort *port; |
401 | a69c7600 | Amit Shah | bool discard;
|
402 | 98b19252 | Amit Shah | |
403 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
404 | a69c7600 | Amit Shah | port = find_port_by_vq(vser, vq); |
405 | 98b19252 | Amit Shah | |
406 | a69c7600 | Amit Shah | discard = false;
|
407 | a69c7600 | Amit Shah | if (!port || !port->host_connected || !port->info->have_data) {
|
408 | a69c7600 | Amit Shah | discard = true;
|
409 | 98b19252 | Amit Shah | } |
410 | a69c7600 | Amit Shah | |
411 | 9ed7b059 | Amit Shah | if (!discard && port->throttled) {
|
412 | 9ed7b059 | Amit Shah | return;
|
413 | 9ed7b059 | Amit Shah | } |
414 | 9ed7b059 | Amit Shah | |
415 | 9ed7b059 | Amit Shah | do_flush_queued_data(port, vq, vdev, discard); |
416 | 98b19252 | Amit Shah | } |
417 | 98b19252 | Amit Shah | |
418 | 98b19252 | Amit Shah | static void handle_input(VirtIODevice *vdev, VirtQueue *vq) |
419 | 98b19252 | Amit Shah | { |
420 | 98b19252 | Amit Shah | } |
421 | 98b19252 | Amit Shah | |
422 | 98b19252 | Amit Shah | static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
|
423 | 98b19252 | Amit Shah | { |
424 | 306eb457 | Amit Shah | VirtIOSerial *vser; |
425 | 306eb457 | Amit Shah | |
426 | 306eb457 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
427 | 306eb457 | Amit Shah | |
428 | ee4d45be | Michael S. Tsirkin | if (vser->bus->max_nr_ports > 1) { |
429 | ee4d45be | Michael S. Tsirkin | features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT);
|
430 | ee4d45be | Michael S. Tsirkin | } |
431 | 98b19252 | Amit Shah | return features;
|
432 | 98b19252 | Amit Shah | } |
433 | 98b19252 | Amit Shah | |
434 | 98b19252 | Amit Shah | /* Guest requested config info */
|
435 | 98b19252 | Amit Shah | static void get_config(VirtIODevice *vdev, uint8_t *config_data) |
436 | 98b19252 | Amit Shah | { |
437 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
438 | 98b19252 | Amit Shah | |
439 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
440 | 98b19252 | Amit Shah | memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); |
441 | 98b19252 | Amit Shah | } |
442 | 98b19252 | Amit Shah | |
443 | 98b19252 | Amit Shah | static void set_config(VirtIODevice *vdev, const uint8_t *config_data) |
444 | 98b19252 | Amit Shah | { |
445 | 98b19252 | Amit Shah | struct virtio_console_config config;
|
446 | 98b19252 | Amit Shah | |
447 | 98b19252 | Amit Shah | memcpy(&config, config_data, sizeof(config));
|
448 | 98b19252 | Amit Shah | } |
449 | 98b19252 | Amit Shah | |
450 | 98b19252 | Amit Shah | static void virtio_serial_save(QEMUFile *f, void *opaque) |
451 | 98b19252 | Amit Shah | { |
452 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
453 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
454 | 6663a195 | Amit Shah | uint32_t nr_active_ports; |
455 | 055b889f | Amit Shah | unsigned int i; |
456 | 98b19252 | Amit Shah | |
457 | 98b19252 | Amit Shah | /* The virtio device */
|
458 | 98b19252 | Amit Shah | virtio_save(&s->vdev, f); |
459 | 98b19252 | Amit Shah | |
460 | 98b19252 | Amit Shah | /* The config space */
|
461 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.cols); |
462 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.rows); |
463 | 6663a195 | Amit Shah | |
464 | 055b889f | Amit Shah | qemu_put_be32s(f, &s->config.max_nr_ports); |
465 | 055b889f | Amit Shah | |
466 | 055b889f | Amit Shah | /* The ports map */
|
467 | 055b889f | Amit Shah | |
468 | 055b889f | Amit Shah | for (i = 0; i < (s->config.max_nr_ports + 31) / 32; i++) { |
469 | 055b889f | Amit Shah | qemu_put_be32s(f, &s->ports_map[i]); |
470 | 055b889f | Amit Shah | } |
471 | 6663a195 | Amit Shah | |
472 | 055b889f | Amit Shah | /* Ports */
|
473 | e245795b | Amit Shah | |
474 | 6663a195 | Amit Shah | nr_active_ports = 0;
|
475 | e245795b | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
476 | 6663a195 | Amit Shah | nr_active_ports++; |
477 | e245795b | Amit Shah | } |
478 | 6663a195 | Amit Shah | |
479 | 6663a195 | Amit Shah | qemu_put_be32s(f, &nr_active_ports); |
480 | 6663a195 | Amit Shah | |
481 | 6663a195 | Amit Shah | /*
|
482 | 6663a195 | Amit Shah | * Items in struct VirtIOSerialPort.
|
483 | 6663a195 | Amit Shah | */
|
484 | 6663a195 | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
485 | 6663a195 | Amit Shah | qemu_put_be32s(f, &port->id); |
486 | 6663a195 | Amit Shah | qemu_put_byte(f, port->guest_connected); |
487 | 31abe21f | Amit Shah | qemu_put_byte(f, port->host_connected); |
488 | 6663a195 | Amit Shah | } |
489 | 98b19252 | Amit Shah | } |
490 | 98b19252 | Amit Shah | |
491 | 98b19252 | Amit Shah | static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) |
492 | 98b19252 | Amit Shah | { |
493 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
494 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
495 | 055b889f | Amit Shah | size_t ports_map_size; |
496 | 055b889f | Amit Shah | uint32_t max_nr_ports, nr_active_ports, *ports_map; |
497 | 6663a195 | Amit Shah | unsigned int i; |
498 | 98b19252 | Amit Shah | |
499 | 98b19252 | Amit Shah | if (version_id > 2) { |
500 | 98b19252 | Amit Shah | return -EINVAL;
|
501 | 98b19252 | Amit Shah | } |
502 | 6663a195 | Amit Shah | |
503 | 98b19252 | Amit Shah | /* The virtio device */
|
504 | 98b19252 | Amit Shah | virtio_load(&s->vdev, f); |
505 | 98b19252 | Amit Shah | |
506 | 98b19252 | Amit Shah | if (version_id < 2) { |
507 | 98b19252 | Amit Shah | return 0; |
508 | 98b19252 | Amit Shah | } |
509 | 98b19252 | Amit Shah | |
510 | 98b19252 | Amit Shah | /* The config space */
|
511 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.cols); |
512 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.rows); |
513 | 295587f7 | Amit Shah | |
514 | 055b889f | Amit Shah | qemu_get_be32s(f, &max_nr_ports); |
515 | 055b889f | Amit Shah | if (max_nr_ports > s->config.max_nr_ports) {
|
516 | 055b889f | Amit Shah | /* Source could have had more ports than us. Fail migration. */
|
517 | 295587f7 | Amit Shah | return -EINVAL;
|
518 | 295587f7 | Amit Shah | } |
519 | 98b19252 | Amit Shah | |
520 | 055b889f | Amit Shah | ports_map_size = sizeof(uint32_t) * (max_nr_ports + 31) / 32; |
521 | 055b889f | Amit Shah | ports_map = qemu_malloc(ports_map_size); |
522 | 6663a195 | Amit Shah | |
523 | 055b889f | Amit Shah | for (i = 0; i < (max_nr_ports + 31) / 32; i++) { |
524 | 055b889f | Amit Shah | qemu_get_be32s(f, &ports_map[i]); |
525 | 055b889f | Amit Shah | |
526 | 055b889f | Amit Shah | if (ports_map[i] != s->ports_map[i]) {
|
527 | 055b889f | Amit Shah | /*
|
528 | 055b889f | Amit Shah | * Ports active on source and destination don't
|
529 | 055b889f | Amit Shah | * match. Fail migration.
|
530 | 055b889f | Amit Shah | */
|
531 | 055b889f | Amit Shah | qemu_free(ports_map); |
532 | 055b889f | Amit Shah | return -EINVAL;
|
533 | 055b889f | Amit Shah | } |
534 | e245795b | Amit Shah | } |
535 | 055b889f | Amit Shah | qemu_free(ports_map); |
536 | e245795b | Amit Shah | |
537 | 6663a195 | Amit Shah | qemu_get_be32s(f, &nr_active_ports); |
538 | 6663a195 | Amit Shah | |
539 | 6663a195 | Amit Shah | /* Items in struct VirtIOSerialPort */
|
540 | 6663a195 | Amit Shah | for (i = 0; i < nr_active_ports; i++) { |
541 | 6663a195 | Amit Shah | uint32_t id; |
542 | 31abe21f | Amit Shah | bool host_connected;
|
543 | 6663a195 | Amit Shah | |
544 | 6663a195 | Amit Shah | id = qemu_get_be32(f); |
545 | 6663a195 | Amit Shah | port = find_port_by_id(s, id); |
546 | 6663a195 | Amit Shah | |
547 | 6663a195 | Amit Shah | port->guest_connected = qemu_get_byte(f); |
548 | 31abe21f | Amit Shah | host_connected = qemu_get_byte(f); |
549 | 31abe21f | Amit Shah | if (host_connected != port->host_connected) {
|
550 | 31abe21f | Amit Shah | /*
|
551 | 31abe21f | Amit Shah | * We have to let the guest know of the host connection
|
552 | 31abe21f | Amit Shah | * status change
|
553 | 31abe21f | Amit Shah | */
|
554 | 31abe21f | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, |
555 | 31abe21f | Amit Shah | port->host_connected); |
556 | 31abe21f | Amit Shah | } |
557 | 6663a195 | Amit Shah | } |
558 | 98b19252 | Amit Shah | return 0; |
559 | 98b19252 | Amit Shah | } |
560 | 98b19252 | Amit Shah | |
561 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); |
562 | 98b19252 | Amit Shah | |
563 | 98b19252 | Amit Shah | static struct BusInfo virtser_bus_info = { |
564 | 98b19252 | Amit Shah | .name = "virtio-serial-bus",
|
565 | 98b19252 | Amit Shah | .size = sizeof(VirtIOSerialBus),
|
566 | 98b19252 | Amit Shah | .print_dev = virtser_bus_dev_print, |
567 | 98b19252 | Amit Shah | }; |
568 | 98b19252 | Amit Shah | |
569 | 98b19252 | Amit Shah | static VirtIOSerialBus *virtser_bus_new(DeviceState *dev)
|
570 | 98b19252 | Amit Shah | { |
571 | 98b19252 | Amit Shah | VirtIOSerialBus *bus; |
572 | 98b19252 | Amit Shah | |
573 | 9ae84f0a | Amit Shah | bus = FROM_QBUS(VirtIOSerialBus, qbus_create(&virtser_bus_info, dev, NULL));
|
574 | 98b19252 | Amit Shah | bus->qbus.allow_hotplug = 1;
|
575 | 98b19252 | Amit Shah | |
576 | 98b19252 | Amit Shah | return bus;
|
577 | 98b19252 | Amit Shah | } |
578 | 98b19252 | Amit Shah | |
579 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) |
580 | 98b19252 | Amit Shah | { |
581 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
582 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
583 | 98b19252 | Amit Shah | |
584 | 98b19252 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: id: %u\n",
|
585 | 98b19252 | Amit Shah | indent, "", port->id);
|
586 | 6663a195 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n",
|
587 | 6663a195 | Amit Shah | indent, "", port->guest_connected);
|
588 | 6663a195 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n",
|
589 | 6663a195 | Amit Shah | indent, "", port->host_connected);
|
590 | 9ed7b059 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n",
|
591 | 9ed7b059 | Amit Shah | indent, "", port->throttled);
|
592 | 98b19252 | Amit Shah | } |
593 | 98b19252 | Amit Shah | |
594 | 055b889f | Amit Shah | /* This function is only used if a port id is not provided by the user */
|
595 | 055b889f | Amit Shah | static uint32_t find_free_port_id(VirtIOSerial *vser)
|
596 | 055b889f | Amit Shah | { |
597 | 055b889f | Amit Shah | unsigned int i; |
598 | 055b889f | Amit Shah | |
599 | 055b889f | Amit Shah | for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) { |
600 | 055b889f | Amit Shah | uint32_t map, bit; |
601 | 055b889f | Amit Shah | |
602 | 055b889f | Amit Shah | map = vser->ports_map[i]; |
603 | 055b889f | Amit Shah | bit = ffs(~map); |
604 | 055b889f | Amit Shah | if (bit) {
|
605 | 055b889f | Amit Shah | return (bit - 1) + i * 32; |
606 | 055b889f | Amit Shah | } |
607 | 055b889f | Amit Shah | } |
608 | 055b889f | Amit Shah | return VIRTIO_CONSOLE_BAD_ID;
|
609 | 055b889f | Amit Shah | } |
610 | 055b889f | Amit Shah | |
611 | 055b889f | Amit Shah | static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) |
612 | 055b889f | Amit Shah | { |
613 | 055b889f | Amit Shah | unsigned int i; |
614 | 055b889f | Amit Shah | |
615 | 055b889f | Amit Shah | i = port_id / 32;
|
616 | 055b889f | Amit Shah | vser->ports_map[i] |= 1U << (port_id % 32); |
617 | 055b889f | Amit Shah | } |
618 | 055b889f | Amit Shah | |
619 | 055b889f | Amit Shah | static void add_port(VirtIOSerial *vser, uint32_t port_id) |
620 | 055b889f | Amit Shah | { |
621 | 055b889f | Amit Shah | mark_port_added(vser, port_id); |
622 | 055b889f | Amit Shah | |
623 | 055b889f | Amit Shah | send_control_event(find_port_by_id(vser, port_id), |
624 | 055b889f | Amit Shah | VIRTIO_CONSOLE_PORT_ADD, 1);
|
625 | 055b889f | Amit Shah | } |
626 | 055b889f | Amit Shah | |
627 | 055b889f | Amit Shah | static void remove_port(VirtIOSerial *vser, uint32_t port_id) |
628 | 055b889f | Amit Shah | { |
629 | 9ed7b059 | Amit Shah | VirtIOSerialPort *port; |
630 | 055b889f | Amit Shah | unsigned int i; |
631 | 055b889f | Amit Shah | |
632 | 055b889f | Amit Shah | i = port_id / 32;
|
633 | 055b889f | Amit Shah | vser->ports_map[i] &= ~(1U << (port_id % 32)); |
634 | 055b889f | Amit Shah | |
635 | 9ed7b059 | Amit Shah | port = find_port_by_id(vser, port_id); |
636 | 9ed7b059 | Amit Shah | /* Flush out any unconsumed buffers first */
|
637 | 9ed7b059 | Amit Shah | flush_queued_data(port, true);
|
638 | 9ed7b059 | Amit Shah | |
639 | 9ed7b059 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1);
|
640 | 055b889f | Amit Shah | } |
641 | 055b889f | Amit Shah | |
642 | 98b19252 | Amit Shah | static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) |
643 | 98b19252 | Amit Shah | { |
644 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
645 | 98b19252 | Amit Shah | VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, base); |
646 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
647 | 98b19252 | Amit Shah | VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus); |
648 | 98b19252 | Amit Shah | int ret;
|
649 | 98b19252 | Amit Shah | bool plugging_port0;
|
650 | 98b19252 | Amit Shah | |
651 | 98b19252 | Amit Shah | port->vser = bus->vser; |
652 | 98b19252 | Amit Shah | |
653 | 98b19252 | Amit Shah | /*
|
654 | 98b19252 | Amit Shah | * Is the first console port we're seeing? If so, put it up at
|
655 | 98b19252 | Amit Shah | * location 0. This is done for backward compatibility (old
|
656 | 98b19252 | Amit Shah | * kernel, new qemu).
|
657 | 98b19252 | Amit Shah | */
|
658 | 98b19252 | Amit Shah | plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0);
|
659 | 98b19252 | Amit Shah | |
660 | 055b889f | Amit Shah | if (find_port_by_id(port->vser, port->id)) {
|
661 | 055b889f | Amit Shah | error_report("virtio-serial-bus: A port already exists at id %u\n",
|
662 | 055b889f | Amit Shah | port->id); |
663 | 98b19252 | Amit Shah | return -1; |
664 | 98b19252 | Amit Shah | } |
665 | 98b19252 | Amit Shah | |
666 | 055b889f | Amit Shah | if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
667 | 055b889f | Amit Shah | if (plugging_port0) {
|
668 | 055b889f | Amit Shah | port->id = 0;
|
669 | 055b889f | Amit Shah | } else {
|
670 | 055b889f | Amit Shah | port->id = find_free_port_id(port->vser); |
671 | 055b889f | Amit Shah | if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
672 | 055b889f | Amit Shah | error_report("virtio-serial-bus: Maximum port limit for this device reached\n");
|
673 | 055b889f | Amit Shah | return -1; |
674 | 055b889f | Amit Shah | } |
675 | 055b889f | Amit Shah | } |
676 | 055b889f | Amit Shah | } |
677 | 055b889f | Amit Shah | |
678 | 055b889f | Amit Shah | if (port->id >= port->vser->config.max_nr_ports) {
|
679 | 055b889f | Amit Shah | error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n",
|
680 | 055b889f | Amit Shah | port->vser->config.max_nr_ports - 1);
|
681 | 055b889f | Amit Shah | return -1; |
682 | 055b889f | Amit Shah | } |
683 | 055b889f | Amit Shah | |
684 | 055b889f | Amit Shah | dev->info = info; |
685 | 98b19252 | Amit Shah | ret = info->init(dev); |
686 | 98b19252 | Amit Shah | if (ret) {
|
687 | 98b19252 | Amit Shah | return ret;
|
688 | 98b19252 | Amit Shah | } |
689 | 98b19252 | Amit Shah | |
690 | 6663a195 | Amit Shah | if (!use_multiport(port->vser)) {
|
691 | 6663a195 | Amit Shah | /*
|
692 | 6663a195 | Amit Shah | * Allow writes to guest in this case; we have no way of
|
693 | 6663a195 | Amit Shah | * knowing if a guest port is connected.
|
694 | 6663a195 | Amit Shah | */
|
695 | 6663a195 | Amit Shah | port->guest_connected = true;
|
696 | 6663a195 | Amit Shah | } |
697 | 6663a195 | Amit Shah | |
698 | 98b19252 | Amit Shah | QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); |
699 | 98b19252 | Amit Shah | port->ivq = port->vser->ivqs[port->id]; |
700 | 98b19252 | Amit Shah | port->ovq = port->vser->ovqs[port->id]; |
701 | 98b19252 | Amit Shah | |
702 | 055b889f | Amit Shah | add_port(port->vser, port->id); |
703 | 055b889f | Amit Shah | |
704 | 98b19252 | Amit Shah | /* Send an update to the guest about this new port added */
|
705 | 98b19252 | Amit Shah | virtio_notify_config(&port->vser->vdev); |
706 | 98b19252 | Amit Shah | |
707 | 98b19252 | Amit Shah | return ret;
|
708 | 98b19252 | Amit Shah | } |
709 | 98b19252 | Amit Shah | |
710 | 98b19252 | Amit Shah | static int virtser_port_qdev_exit(DeviceState *qdev) |
711 | 98b19252 | Amit Shah | { |
712 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
713 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
714 | 98b19252 | Amit Shah | VirtIOSerial *vser = port->vser; |
715 | 98b19252 | Amit Shah | |
716 | 055b889f | Amit Shah | remove_port(port->vser, port->id); |
717 | f146ec9a | Amit Shah | |
718 | 98b19252 | Amit Shah | QTAILQ_REMOVE(&vser->ports, port, next); |
719 | 98b19252 | Amit Shah | |
720 | 98b19252 | Amit Shah | if (port->info->exit)
|
721 | 98b19252 | Amit Shah | port->info->exit(dev); |
722 | 98b19252 | Amit Shah | |
723 | 98b19252 | Amit Shah | return 0; |
724 | 98b19252 | Amit Shah | } |
725 | 98b19252 | Amit Shah | |
726 | 98b19252 | Amit Shah | void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info)
|
727 | 98b19252 | Amit Shah | { |
728 | 98b19252 | Amit Shah | info->qdev.init = virtser_port_qdev_init; |
729 | 98b19252 | Amit Shah | info->qdev.bus_info = &virtser_bus_info; |
730 | 98b19252 | Amit Shah | info->qdev.exit = virtser_port_qdev_exit; |
731 | 98b19252 | Amit Shah | info->qdev.unplug = qdev_simple_unplug_cb; |
732 | 98b19252 | Amit Shah | qdev_register(&info->qdev); |
733 | 98b19252 | Amit Shah | } |
734 | 98b19252 | Amit Shah | |
735 | 98b19252 | Amit Shah | VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) |
736 | 98b19252 | Amit Shah | { |
737 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
738 | 98b19252 | Amit Shah | VirtIODevice *vdev; |
739 | 98b19252 | Amit Shah | uint32_t i; |
740 | 98b19252 | Amit Shah | |
741 | 98b19252 | Amit Shah | if (!max_nr_ports)
|
742 | 98b19252 | Amit Shah | return NULL; |
743 | 98b19252 | Amit Shah | |
744 | 98b19252 | Amit Shah | vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE,
|
745 | 98b19252 | Amit Shah | sizeof(struct virtio_console_config), |
746 | 98b19252 | Amit Shah | sizeof(VirtIOSerial));
|
747 | 98b19252 | Amit Shah | |
748 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
749 | 98b19252 | Amit Shah | |
750 | 98b19252 | Amit Shah | /* Spawn a new virtio-serial bus on which the ports will ride as devices */
|
751 | 98b19252 | Amit Shah | vser->bus = virtser_bus_new(dev); |
752 | 98b19252 | Amit Shah | vser->bus->vser = vser; |
753 | 98b19252 | Amit Shah | QTAILQ_INIT(&vser->ports); |
754 | 98b19252 | Amit Shah | |
755 | 98b19252 | Amit Shah | vser->bus->max_nr_ports = max_nr_ports; |
756 | 98b19252 | Amit Shah | vser->ivqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *));
|
757 | 98b19252 | Amit Shah | vser->ovqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *));
|
758 | 98b19252 | Amit Shah | |
759 | 98b19252 | Amit Shah | /* Add a queue for host to guest transfers for port 0 (backward compat) */
|
760 | 98b19252 | Amit Shah | vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); |
761 | 98b19252 | Amit Shah | /* Add a queue for guest to host transfers for port 0 (backward compat) */
|
762 | 98b19252 | Amit Shah | vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); |
763 | 98b19252 | Amit Shah | |
764 | 98b19252 | Amit Shah | /* control queue: host to guest */
|
765 | 98b19252 | Amit Shah | vser->c_ivq = virtio_add_queue(vdev, 16, control_in);
|
766 | 98b19252 | Amit Shah | /* control queue: guest to host */
|
767 | 98b19252 | Amit Shah | vser->c_ovq = virtio_add_queue(vdev, 16, control_out);
|
768 | 98b19252 | Amit Shah | |
769 | 98b19252 | Amit Shah | for (i = 1; i < vser->bus->max_nr_ports; i++) { |
770 | 98b19252 | Amit Shah | /* Add a per-port queue for host to guest transfers */
|
771 | 98b19252 | Amit Shah | vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input);
|
772 | 98b19252 | Amit Shah | /* Add a per-per queue for guest to host transfers */
|
773 | 98b19252 | Amit Shah | vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
|
774 | 98b19252 | Amit Shah | } |
775 | 98b19252 | Amit Shah | |
776 | 98b19252 | Amit Shah | vser->config.max_nr_ports = max_nr_ports; |
777 | 055b889f | Amit Shah | vser->ports_map = qemu_mallocz((max_nr_ports + 31) / 32); |
778 | 98b19252 | Amit Shah | /*
|
779 | 98b19252 | Amit Shah | * Reserve location 0 for a console port for backward compat
|
780 | 98b19252 | Amit Shah | * (old kernel, new qemu)
|
781 | 98b19252 | Amit Shah | */
|
782 | 055b889f | Amit Shah | mark_port_added(vser, 0);
|
783 | 98b19252 | Amit Shah | |
784 | 98b19252 | Amit Shah | vser->vdev.get_features = get_features; |
785 | 98b19252 | Amit Shah | vser->vdev.get_config = get_config; |
786 | 98b19252 | Amit Shah | vser->vdev.set_config = set_config; |
787 | 98b19252 | Amit Shah | |
788 | 98b19252 | Amit Shah | /*
|
789 | 98b19252 | Amit Shah | * Register for the savevm section with the virtio-console name
|
790 | 98b19252 | Amit Shah | * to preserve backward compat
|
791 | 98b19252 | Amit Shah | */
|
792 | 98b19252 | Amit Shah | register_savevm("virtio-console", -1, 2, virtio_serial_save, |
793 | 98b19252 | Amit Shah | virtio_serial_load, vser); |
794 | 98b19252 | Amit Shah | |
795 | 98b19252 | Amit Shah | return vdev;
|
796 | 98b19252 | Amit Shah | } |