]> git.proxmox.com Git - mirror_qemu.git/blob - hw/virtio/vhost-user-i2c.c
qapi: allow unions to contain further unions
[mirror_qemu.git] / hw / virtio / vhost-user-i2c.c
1 /*
2 * Vhost-user i2c virtio device
3 *
4 * Copyright (c) 2021 Viresh Kumar <viresh.kumar@linaro.org>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10 #include "qapi/error.h"
11 #include "hw/qdev-properties.h"
12 #include "hw/virtio/virtio-bus.h"
13 #include "hw/virtio/vhost-user-i2c.h"
14 #include "qemu/error-report.h"
15 #include "standard-headers/linux/virtio_ids.h"
16
17 static const int feature_bits[] = {
18 VIRTIO_I2C_F_ZERO_LENGTH_REQUEST,
19 VIRTIO_F_RING_RESET,
20 VHOST_INVALID_FEATURE_BIT
21 };
22
23 static void vu_i2c_start(VirtIODevice *vdev)
24 {
25 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
26 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
27 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
28 int ret, i;
29
30 if (!k->set_guest_notifiers) {
31 error_report("binding does not support guest notifiers");
32 return;
33 }
34
35 ret = vhost_dev_enable_notifiers(&i2c->vhost_dev, vdev);
36 if (ret < 0) {
37 error_report("Error enabling host notifiers: %d", -ret);
38 return;
39 }
40
41 ret = k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, true);
42 if (ret < 0) {
43 error_report("Error binding guest notifier: %d", -ret);
44 goto err_host_notifiers;
45 }
46
47 i2c->vhost_dev.acked_features = vdev->guest_features;
48
49 ret = vhost_dev_start(&i2c->vhost_dev, vdev, true);
50 if (ret < 0) {
51 error_report("Error starting vhost-user-i2c: %d", -ret);
52 goto err_guest_notifiers;
53 }
54
55 /*
56 * guest_notifier_mask/pending not used yet, so just unmask
57 * everything here. virtio-pci will do the right thing by
58 * enabling/disabling irqfd.
59 */
60 for (i = 0; i < i2c->vhost_dev.nvqs; i++) {
61 vhost_virtqueue_mask(&i2c->vhost_dev, vdev, i, false);
62 }
63
64 return;
65
66 err_guest_notifiers:
67 k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, false);
68 err_host_notifiers:
69 vhost_dev_disable_notifiers(&i2c->vhost_dev, vdev);
70 }
71
72 static void vu_i2c_stop(VirtIODevice *vdev)
73 {
74 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
75 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
76 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
77 int ret;
78
79 if (!k->set_guest_notifiers) {
80 return;
81 }
82
83 vhost_dev_stop(&i2c->vhost_dev, vdev, true);
84
85 ret = k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, false);
86 if (ret < 0) {
87 error_report("vhost guest notifier cleanup failed: %d", ret);
88 return;
89 }
90
91 vhost_dev_disable_notifiers(&i2c->vhost_dev, vdev);
92 }
93
94 static void vu_i2c_set_status(VirtIODevice *vdev, uint8_t status)
95 {
96 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
97 bool should_start = virtio_device_should_start(vdev, status);
98
99 if (vhost_dev_is_started(&i2c->vhost_dev) == should_start) {
100 return;
101 }
102
103 if (should_start) {
104 vu_i2c_start(vdev);
105 } else {
106 vu_i2c_stop(vdev);
107 }
108 }
109
110 static uint64_t vu_i2c_get_features(VirtIODevice *vdev,
111 uint64_t requested_features, Error **errp)
112 {
113 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
114
115 virtio_add_feature(&requested_features, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST);
116 return vhost_get_features(&i2c->vhost_dev, feature_bits, requested_features);
117 }
118
119 static void vu_i2c_handle_output(VirtIODevice *vdev, VirtQueue *vq)
120 {
121 /*
122 * Not normally called; it's the daemon that handles the queue;
123 * however virtio's cleanup path can call this.
124 */
125 }
126
127 static void vu_i2c_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask)
128 {
129 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
130
131 vhost_virtqueue_mask(&i2c->vhost_dev, vdev, idx, mask);
132 }
133
134 static bool vu_i2c_guest_notifier_pending(VirtIODevice *vdev, int idx)
135 {
136 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
137
138 return vhost_virtqueue_pending(&i2c->vhost_dev, idx);
139 }
140
141 static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserI2C *i2c)
142 {
143 vhost_user_cleanup(&i2c->vhost_user);
144 virtio_delete_queue(i2c->vq);
145 virtio_cleanup(vdev);
146 }
147
148 static int vu_i2c_connect(DeviceState *dev)
149 {
150 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
151 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
152
153 if (i2c->connected) {
154 return 0;
155 }
156 i2c->connected = true;
157
158 /* restore vhost state */
159 if (virtio_device_started(vdev, vdev->status)) {
160 vu_i2c_start(vdev);
161 }
162
163 return 0;
164 }
165
166 static void vu_i2c_disconnect(DeviceState *dev)
167 {
168 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
169 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
170
171 if (!i2c->connected) {
172 return;
173 }
174 i2c->connected = false;
175
176 if (vhost_dev_is_started(&i2c->vhost_dev)) {
177 vu_i2c_stop(vdev);
178 }
179 }
180
181 static void vu_i2c_event(void *opaque, QEMUChrEvent event)
182 {
183 DeviceState *dev = opaque;
184 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
185 VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
186
187 switch (event) {
188 case CHR_EVENT_OPENED:
189 if (vu_i2c_connect(dev) < 0) {
190 qemu_chr_fe_disconnect(&i2c->chardev);
191 return;
192 }
193 break;
194 case CHR_EVENT_CLOSED:
195 vu_i2c_disconnect(dev);
196 break;
197 case CHR_EVENT_BREAK:
198 case CHR_EVENT_MUX_IN:
199 case CHR_EVENT_MUX_OUT:
200 /* Ignore */
201 break;
202 }
203 }
204
205 static void vu_i2c_device_realize(DeviceState *dev, Error **errp)
206 {
207 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
208 VHostUserI2C *i2c = VHOST_USER_I2C(dev);
209 int ret;
210
211 if (!i2c->chardev.chr) {
212 error_setg(errp, "vhost-user-i2c: missing chardev");
213 return;
214 }
215
216 if (!vhost_user_init(&i2c->vhost_user, &i2c->chardev, errp)) {
217 return;
218 }
219
220 virtio_init(vdev, VIRTIO_ID_I2C_ADAPTER, 0);
221
222 i2c->vhost_dev.nvqs = 1;
223 i2c->vq = virtio_add_queue(vdev, 4, vu_i2c_handle_output);
224 i2c->vhost_dev.vqs = g_new0(struct vhost_virtqueue, i2c->vhost_dev.nvqs);
225
226 ret = vhost_dev_init(&i2c->vhost_dev, &i2c->vhost_user,
227 VHOST_BACKEND_TYPE_USER, 0, errp);
228 if (ret < 0) {
229 g_free(i2c->vhost_dev.vqs);
230 do_vhost_user_cleanup(vdev, i2c);
231 }
232
233 qemu_chr_fe_set_handlers(&i2c->chardev, NULL, NULL, vu_i2c_event, NULL,
234 dev, NULL, true);
235 }
236
237 static void vu_i2c_device_unrealize(DeviceState *dev)
238 {
239 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
240 VHostUserI2C *i2c = VHOST_USER_I2C(dev);
241 struct vhost_virtqueue *vhost_vqs = i2c->vhost_dev.vqs;
242
243 /* This will stop vhost backend if appropriate. */
244 vu_i2c_set_status(vdev, 0);
245 vhost_dev_cleanup(&i2c->vhost_dev);
246 g_free(vhost_vqs);
247 do_vhost_user_cleanup(vdev, i2c);
248 }
249
250 static const VMStateDescription vu_i2c_vmstate = {
251 .name = "vhost-user-i2c",
252 .unmigratable = 1,
253 };
254
255 static Property vu_i2c_properties[] = {
256 DEFINE_PROP_CHR("chardev", VHostUserI2C, chardev),
257 DEFINE_PROP_END_OF_LIST(),
258 };
259
260 static void vu_i2c_class_init(ObjectClass *klass, void *data)
261 {
262 DeviceClass *dc = DEVICE_CLASS(klass);
263 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
264
265 device_class_set_props(dc, vu_i2c_properties);
266 dc->vmsd = &vu_i2c_vmstate;
267 set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
268 vdc->realize = vu_i2c_device_realize;
269 vdc->unrealize = vu_i2c_device_unrealize;
270 vdc->get_features = vu_i2c_get_features;
271 vdc->set_status = vu_i2c_set_status;
272 vdc->guest_notifier_mask = vu_i2c_guest_notifier_mask;
273 vdc->guest_notifier_pending = vu_i2c_guest_notifier_pending;
274 }
275
276 static const TypeInfo vu_i2c_info = {
277 .name = TYPE_VHOST_USER_I2C,
278 .parent = TYPE_VIRTIO_DEVICE,
279 .instance_size = sizeof(VHostUserI2C),
280 .class_init = vu_i2c_class_init,
281 };
282
283 static void vu_i2c_register_types(void)
284 {
285 type_register_static(&vu_i2c_info);
286 }
287
288 type_init(vu_i2c_register_types)