1
2
3
4
5
6
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
18#ifndef VIRTIO_ID_I2C_ADAPTER
19#define VIRTIO_ID_I2C_ADAPTER 34
20#endif
21
22static const int feature_bits[] = {
23 VIRTIO_I2C_F_ZERO_LENGTH_REQUEST,
24 VHOST_INVALID_FEATURE_BIT
25};
26
27static 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
61
62
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
70err_guest_notifiers:
71 k->set_guest_notifiers(qbus->parent, i2c->vhost_dev.nvqs, false);
72err_host_notifiers:
73 vhost_dev_disable_notifiers(&i2c->vhost_dev, vdev);
74}
75
76static 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
98static 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
118static 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
127static void vu_i2c_handle_output(VirtIODevice *vdev, VirtQueue *vq)
128{
129
130
131
132
133}
134
135static 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
142static 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
149static 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
158static 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
169 if (virtio_device_started(vdev, vdev->status)) {
170 vu_i2c_start(vdev);
171 }
172
173 return 0;
174}
175
176static 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
191static 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
211 break;
212 }
213}
214
215static 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
246static void vu_i2c_device_unrealize(DeviceState *dev)
247{
248 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
249 VHostUserI2C *i2c = VHOST_USER_I2C(dev);
250
251
252 vu_i2c_set_status(vdev, 0);
253 vhost_dev_cleanup(&i2c->vhost_dev);
254 do_vhost_user_cleanup(vdev, i2c);
255}
256
257static const VMStateDescription vu_i2c_vmstate = {
258 .name = "vhost-user-i2c",
259 .unmigratable = 1,
260};
261
262static Property vu_i2c_properties[] = {
263 DEFINE_PROP_CHR("chardev", VHostUserI2C, chardev),
264 DEFINE_PROP_END_OF_LIST(),
265};
266
267static 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
283static 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
290static void vu_i2c_register_types(void)
291{
292 type_register_static(&vu_i2c_info);
293}
294
295type_init(vu_i2c_register_types)
296