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