]>
Commit | Line | Data |
---|---|---|
98b19252 AS |
1 | /* |
2 | * Virtio Console and Generic Serial Port Devices | |
3 | * | |
71c092e9 | 4 | * Copyright Red Hat, Inc. 2009, 2010 |
98b19252 AS |
5 | * |
6 | * Authors: | |
7 | * Amit Shah <[email protected]> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2. See | |
10 | * the COPYING file in the top-level directory. | |
11 | */ | |
12 | ||
13 | #include "qemu-char.h" | |
0b8b716d | 14 | #include "qemu-error.h" |
d02e4fa4 | 15 | #include "trace.h" |
98b19252 AS |
16 | #include "virtio-serial.h" |
17 | ||
18 | typedef struct VirtConsole { | |
19 | VirtIOSerialPort port; | |
20 | CharDriverState *chr; | |
21 | } VirtConsole; | |
22 | ||
23 | ||
24 | /* Callback function that's called when the guest sends us data */ | |
e300ac27 | 25 | static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) |
98b19252 AS |
26 | { |
27 | VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); | |
d02e4fa4 | 28 | ssize_t ret; |
98b19252 | 29 | |
2cc6e0a1 | 30 | ret = qemu_chr_fe_write(vcon->chr, buf, len); |
d02e4fa4 | 31 | trace_virtio_console_flush_buf(port->id, len, ret); |
0219d732 AS |
32 | |
33 | if (ret < 0) { | |
34 | /* | |
35 | * Ideally we'd get a better error code than just -1, but | |
36 | * that's what the chardev interface gives us right now. If | |
37 | * we had a finer-grained message, like -EPIPE, we could close | |
38 | * this connection. Absent such error messages, the most we | |
39 | * can do is to return 0 here. | |
40 | * | |
41 | * This will prevent stray -1 values to go to | |
42 | * virtio-serial-bus.c and cause abort()s in | |
43 | * do_flush_queued_data(). | |
44 | */ | |
45 | ret = 0; | |
46 | } | |
d02e4fa4 | 47 | return ret; |
98b19252 AS |
48 | } |
49 | ||
0b6d2266 HG |
50 | /* Callback function that's called when the guest opens the port */ |
51 | static void guest_open(VirtIOSerialPort *port) | |
52 | { | |
53 | VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); | |
54 | ||
c9d830ed | 55 | qemu_chr_fe_open(vcon->chr); |
0b6d2266 HG |
56 | } |
57 | ||
58 | /* Callback function that's called when the guest closes the port */ | |
59 | static void guest_close(VirtIOSerialPort *port) | |
60 | { | |
61 | VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); | |
62 | ||
2817822d | 63 | qemu_chr_fe_close(vcon->chr); |
0b6d2266 HG |
64 | } |
65 | ||
98b19252 AS |
66 | /* Readiness of the guest to accept data on a port */ |
67 | static int chr_can_read(void *opaque) | |
68 | { | |
69 | VirtConsole *vcon = opaque; | |
70 | ||
71 | return virtio_serial_guest_ready(&vcon->port); | |
72 | } | |
73 | ||
74 | /* Send data from a char device over to the guest */ | |
75 | static void chr_read(void *opaque, const uint8_t *buf, int size) | |
76 | { | |
77 | VirtConsole *vcon = opaque; | |
78 | ||
d02e4fa4 | 79 | trace_virtio_console_chr_read(vcon->port.id, size); |
98b19252 AS |
80 | virtio_serial_write(&vcon->port, buf, size); |
81 | } | |
82 | ||
83 | static void chr_event(void *opaque, int event) | |
84 | { | |
85 | VirtConsole *vcon = opaque; | |
86 | ||
d02e4fa4 | 87 | trace_virtio_console_chr_event(vcon->port.id, event); |
98b19252 | 88 | switch (event) { |
28eaf465 | 89 | case CHR_EVENT_OPENED: |
98b19252 AS |
90 | virtio_serial_open(&vcon->port); |
91 | break; | |
98b19252 AS |
92 | case CHR_EVENT_CLOSED: |
93 | virtio_serial_close(&vcon->port); | |
94 | break; | |
95 | } | |
96 | } | |
97 | ||
7edfe652 | 98 | static int virtconsole_initfn(VirtIOSerialPort *port) |
98b19252 | 99 | { |
7edfe652 | 100 | VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); |
a15bb0d6 MA |
101 | VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, |
102 | vcon->port.dev.info); | |
103 | ||
7edfe652 MA |
104 | if (port->id == 0 && !info->is_console) { |
105 | error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility."); | |
106 | return -1; | |
107 | } | |
108 | ||
98b19252 AS |
109 | if (vcon->chr) { |
110 | qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, | |
111 | vcon); | |
a15bb0d6 MA |
112 | info->have_data = flush_buf; |
113 | info->guest_open = guest_open; | |
114 | info->guest_close = guest_close; | |
98b19252 | 115 | } |
cbe77b61 | 116 | |
7edfe652 | 117 | return 0; |
cbe77b61 AS |
118 | } |
119 | ||
a43f9c90 | 120 | static int virtconsole_exitfn(VirtIOSerialPort *port) |
98b19252 | 121 | { |
98b19252 AS |
122 | VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); |
123 | ||
124 | if (vcon->chr) { | |
f9a90f18 AS |
125 | /* |
126 | * Instead of closing the chardev, free it so it can be used | |
127 | * for other purposes. | |
128 | */ | |
129 | qemu_chr_add_handlers(vcon->chr, NULL, NULL, NULL, NULL); | |
98b19252 AS |
130 | } |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
135 | static VirtIOSerialPortInfo virtconsole_info = { | |
136 | .qdev.name = "virtconsole", | |
137 | .qdev.size = sizeof(VirtConsole), | |
2a3d57ce | 138 | .is_console = true, |
98b19252 AS |
139 | .init = virtconsole_initfn, |
140 | .exit = virtconsole_exitfn, | |
141 | .qdev.props = (Property[]) { | |
98b19252 AS |
142 | DEFINE_PROP_CHR("chardev", VirtConsole, chr), |
143 | DEFINE_PROP_END_OF_LIST(), | |
144 | }, | |
145 | }; | |
146 | ||
147 | static void virtconsole_register(void) | |
148 | { | |
149 | virtio_serial_port_qdev_register(&virtconsole_info); | |
150 | } | |
151 | device_init(virtconsole_register) | |
b60c470b | 152 | |
b60c470b AS |
153 | static VirtIOSerialPortInfo virtserialport_info = { |
154 | .qdev.name = "virtserialport", | |
155 | .qdev.size = sizeof(VirtConsole), | |
7edfe652 | 156 | .init = virtconsole_initfn, |
b60c470b AS |
157 | .exit = virtconsole_exitfn, |
158 | .qdev.props = (Property[]) { | |
159 | DEFINE_PROP_CHR("chardev", VirtConsole, chr), | |
b60c470b AS |
160 | DEFINE_PROP_END_OF_LIST(), |
161 | }, | |
162 | }; | |
163 | ||
164 | static void virtserialport_register(void) | |
165 | { | |
166 | virtio_serial_port_qdev_register(&virtserialport_info); | |
167 | } | |
168 | device_init(virtserialport_register) |