2 * Vhost-user i2c virtio device
4 * Copyright (c) 2021 Viresh Kumar <viresh.kumar@linaro.org>
6 * SPDX-License-Identifier: GPL-2.0-or-later
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"
17 static const int feature_bits
[] = {
18 VIRTIO_I2C_F_ZERO_LENGTH_REQUEST
,
20 VHOST_INVALID_FEATURE_BIT
23 static void vu_i2c_start(VirtIODevice
*vdev
)
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
);
30 if (!k
->set_guest_notifiers
) {
31 error_report("binding does not support guest notifiers");
35 ret
= vhost_dev_enable_notifiers(&i2c
->vhost_dev
, vdev
);
37 error_report("Error enabling host notifiers: %d", -ret
);
41 ret
= k
->set_guest_notifiers(qbus
->parent
, i2c
->vhost_dev
.nvqs
, true);
43 error_report("Error binding guest notifier: %d", -ret
);
44 goto err_host_notifiers
;
47 i2c
->vhost_dev
.acked_features
= vdev
->guest_features
;
49 ret
= vhost_dev_start(&i2c
->vhost_dev
, vdev
, true);
51 error_report("Error starting vhost-user-i2c: %d", -ret
);
52 goto err_guest_notifiers
;
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.
60 for (i
= 0; i
< i2c
->vhost_dev
.nvqs
; i
++) {
61 vhost_virtqueue_mask(&i2c
->vhost_dev
, vdev
, i
, false);
67 k
->set_guest_notifiers(qbus
->parent
, i2c
->vhost_dev
.nvqs
, false);
69 vhost_dev_disable_notifiers(&i2c
->vhost_dev
, vdev
);
72 static void vu_i2c_stop(VirtIODevice
*vdev
)
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
);
79 if (!k
->set_guest_notifiers
) {
83 vhost_dev_stop(&i2c
->vhost_dev
, vdev
, true);
85 ret
= k
->set_guest_notifiers(qbus
->parent
, i2c
->vhost_dev
.nvqs
, false);
87 error_report("vhost guest notifier cleanup failed: %d", ret
);
91 vhost_dev_disable_notifiers(&i2c
->vhost_dev
, vdev
);
94 static void vu_i2c_set_status(VirtIODevice
*vdev
, uint8_t status
)
96 VHostUserI2C
*i2c
= VHOST_USER_I2C(vdev
);
97 bool should_start
= virtio_device_should_start(vdev
, status
);
99 if (vhost_dev_is_started(&i2c
->vhost_dev
) == should_start
) {
110 static uint64_t vu_i2c_get_features(VirtIODevice
*vdev
,
111 uint64_t requested_features
, Error
**errp
)
113 VHostUserI2C
*i2c
= VHOST_USER_I2C(vdev
);
115 virtio_add_feature(&requested_features
, VIRTIO_I2C_F_ZERO_LENGTH_REQUEST
);
116 return vhost_get_features(&i2c
->vhost_dev
, feature_bits
, requested_features
);
119 static void vu_i2c_handle_output(VirtIODevice
*vdev
, VirtQueue
*vq
)
122 * Not normally called; it's the daemon that handles the queue;
123 * however virtio's cleanup path can call this.
127 static void vu_i2c_guest_notifier_mask(VirtIODevice
*vdev
, int idx
, bool mask
)
129 VHostUserI2C
*i2c
= VHOST_USER_I2C(vdev
);
132 * We don't support interrupts, return early if index is set to
133 * VIRTIO_CONFIG_IRQ_IDX.
135 if (idx
== VIRTIO_CONFIG_IRQ_IDX
) {
139 vhost_virtqueue_mask(&i2c
->vhost_dev
, vdev
, idx
, mask
);
142 static bool vu_i2c_guest_notifier_pending(VirtIODevice
*vdev
, int idx
)
144 VHostUserI2C
*i2c
= VHOST_USER_I2C(vdev
);
147 * We don't support interrupts, return early if index is set to
148 * VIRTIO_CONFIG_IRQ_IDX.
150 if (idx
== VIRTIO_CONFIG_IRQ_IDX
) {
154 return vhost_virtqueue_pending(&i2c
->vhost_dev
, idx
);
157 static void do_vhost_user_cleanup(VirtIODevice
*vdev
, VHostUserI2C
*i2c
)
159 vhost_user_cleanup(&i2c
->vhost_user
);
160 virtio_delete_queue(i2c
->vq
);
161 virtio_cleanup(vdev
);
164 static int vu_i2c_connect(DeviceState
*dev
)
166 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
167 VHostUserI2C
*i2c
= VHOST_USER_I2C(vdev
);
169 if (i2c
->connected
) {
172 i2c
->connected
= true;
174 /* restore vhost state */
175 if (virtio_device_started(vdev
, vdev
->status
)) {
182 static void vu_i2c_disconnect(DeviceState
*dev
)
184 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
185 VHostUserI2C
*i2c
= VHOST_USER_I2C(vdev
);
187 if (!i2c
->connected
) {
190 i2c
->connected
= false;
192 if (vhost_dev_is_started(&i2c
->vhost_dev
)) {
197 static void vu_i2c_event(void *opaque
, QEMUChrEvent event
)
199 DeviceState
*dev
= opaque
;
200 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
201 VHostUserI2C
*i2c
= VHOST_USER_I2C(vdev
);
204 case CHR_EVENT_OPENED
:
205 if (vu_i2c_connect(dev
) < 0) {
206 qemu_chr_fe_disconnect(&i2c
->chardev
);
210 case CHR_EVENT_CLOSED
:
211 vu_i2c_disconnect(dev
);
213 case CHR_EVENT_BREAK
:
214 case CHR_EVENT_MUX_IN
:
215 case CHR_EVENT_MUX_OUT
:
221 static void vu_i2c_device_realize(DeviceState
*dev
, Error
**errp
)
223 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
224 VHostUserI2C
*i2c
= VHOST_USER_I2C(dev
);
227 if (!i2c
->chardev
.chr
) {
228 error_setg(errp
, "vhost-user-i2c: missing chardev");
232 if (!vhost_user_init(&i2c
->vhost_user
, &i2c
->chardev
, errp
)) {
236 virtio_init(vdev
, VIRTIO_ID_I2C_ADAPTER
, 0);
238 i2c
->vhost_dev
.nvqs
= 1;
239 i2c
->vq
= virtio_add_queue(vdev
, 4, vu_i2c_handle_output
);
240 i2c
->vhost_dev
.vqs
= g_new0(struct vhost_virtqueue
, i2c
->vhost_dev
.nvqs
);
242 ret
= vhost_dev_init(&i2c
->vhost_dev
, &i2c
->vhost_user
,
243 VHOST_BACKEND_TYPE_USER
, 0, errp
);
245 g_free(i2c
->vhost_dev
.vqs
);
246 do_vhost_user_cleanup(vdev
, i2c
);
249 qemu_chr_fe_set_handlers(&i2c
->chardev
, NULL
, NULL
, vu_i2c_event
, NULL
,
253 static void vu_i2c_device_unrealize(DeviceState
*dev
)
255 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
256 VHostUserI2C
*i2c
= VHOST_USER_I2C(dev
);
257 struct vhost_virtqueue
*vhost_vqs
= i2c
->vhost_dev
.vqs
;
259 /* This will stop vhost backend if appropriate. */
260 vu_i2c_set_status(vdev
, 0);
261 vhost_dev_cleanup(&i2c
->vhost_dev
);
263 do_vhost_user_cleanup(vdev
, i2c
);
266 static const VMStateDescription vu_i2c_vmstate
= {
267 .name
= "vhost-user-i2c",
271 static Property vu_i2c_properties
[] = {
272 DEFINE_PROP_CHR("chardev", VHostUserI2C
, chardev
),
273 DEFINE_PROP_END_OF_LIST(),
276 static void vu_i2c_class_init(ObjectClass
*klass
, void *data
)
278 DeviceClass
*dc
= DEVICE_CLASS(klass
);
279 VirtioDeviceClass
*vdc
= VIRTIO_DEVICE_CLASS(klass
);
281 device_class_set_props(dc
, vu_i2c_properties
);
282 dc
->vmsd
= &vu_i2c_vmstate
;
283 set_bit(DEVICE_CATEGORY_INPUT
, dc
->categories
);
284 vdc
->realize
= vu_i2c_device_realize
;
285 vdc
->unrealize
= vu_i2c_device_unrealize
;
286 vdc
->get_features
= vu_i2c_get_features
;
287 vdc
->set_status
= vu_i2c_set_status
;
288 vdc
->guest_notifier_mask
= vu_i2c_guest_notifier_mask
;
289 vdc
->guest_notifier_pending
= vu_i2c_guest_notifier_pending
;
292 static const TypeInfo vu_i2c_info
= {
293 .name
= TYPE_VHOST_USER_I2C
,
294 .parent
= TYPE_VIRTIO_DEVICE
,
295 .instance_size
= sizeof(VHostUserI2C
),
296 .class_init
= vu_i2c_class_init
,
299 static void vu_i2c_register_types(void)
301 type_register_static(&vu_i2c_info
);
304 type_init(vu_i2c_register_types
)