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