...
...
26
thread by Alex Bennée for his RPMB work:
26
thread by Alex Bennée for his RPMB work:
27
27
28
https://lore.kernel.org/qemu-devel/20200925125147.26943-1-alex.bennee@linaro.org/
28
https://lore.kernel.org/qemu-devel/20200925125147.26943-1-alex.bennee@linaro.org/
29
29
30
30
31
Testing:
31
I2C Testing:
32
-------
32
------------
33
33
34
I didn't have access to a real hardware where I can play with a I2C
34
I didn't have access to a real hardware where I can play with a I2C
35
client device (like RTC, eeprom, etc) to verify the working of the
35
client device (like RTC, eeprom, etc) to verify the working of the
36
backend daemon, so I decided to test it on my x86 box itself with
36
backend daemon, so I decided to test it on my x86 box itself with
37
hierarchy of two ARM64 guests.
37
hierarchy of two ARM64 guests.
...
...
89
Reaches i2c-virtio.c (Linux bus driver in guest2) ->
89
Reaches i2c-virtio.c (Linux bus driver in guest2) ->
90
transfer over virtio ->
90
transfer over virtio ->
91
Reaches the qemu's vhost-i2c device emulation (running over guest1) ->
91
Reaches the qemu's vhost-i2c device emulation (running over guest1) ->
92
Reaches the backend daemon vhost-user-i2c started earlier (in guest1) ->
92
Reaches the backend daemon vhost-user-i2c started earlier (in guest1) ->
93
ioctl(/dev/i2c-0, I2C_RDWR, ..); (in guest1) ->
93
ioctl(/dev/i2c-0, I2C_RDWR, ..); (in guest1) ->
94
     reaches qemu's hw/rtc/ds1338.c (running over host)
94
reaches qemu's hw/rtc/ds1338.c (running over host)
95
95
96
97
98
SMBUS Testing:
99
--------------
100
101
I wasn't required to have such a tedious setup for testing out with
102
SMBUS devices. I was able to emulate a SMBUS device on my x86 machine
103
using i2c-stub driver.
104
105
$ modprobe i2c-stub chip_addr=0x20
106
//Boot the arm64 guest now with i2c-virtio driver and then do:
107
$ echo al3320a 0x20 > /sys/class/i2c-adapter/i2c-0/new_device
108
$ cat /sys/bus/iio/devices/iio:device0/in_illuminance_raw
109
110
That's it.
96
111
97
I hope I was able to give a clear picture of my test setup here :)
112
I hope I was able to give a clear picture of my test setup here :)
98
113
99
Thanks.
114
Thanks.
100
115
101
Viresh Kumar (5):
116
V1->V2:
117
- Add a separate patch (not to be merged) to keep stuff temporarily
118
taken from Linux kernel.
119
120
- Support SMBUS devices/busses in the backend daemon.
121
122
- Fix lots of checkpatch warnings/errors.
123
124
- Some other bug fixes, suggestions, etc.
125
126
Viresh Kumar (6):
127
!Merge: Update virtio headers from kernel
102
hw/virtio: add boilerplate for vhost-user-i2c device
128
hw/virtio: add boilerplate for vhost-user-i2c device
103
hw/virtio: add vhost-user-i2c-pci boilerplate
129
hw/virtio: add vhost-user-i2c-pci boilerplate
104
tools/vhost-user-i2c: Add backend driver
130
tools/vhost-user-i2c: Add backend driver
105
docs: add a man page for vhost-user-i2c
131
docs: add a man page for vhost-user-i2c
106
MAINTAINERS: Add entry for virtio-i2c
132
MAINTAINERS: Add entry for virtio-i2c
107
133
108
MAINTAINERS | 9 +
134
MAINTAINERS | 9 +
109
docs/tools/index.rst | 1 +
135
docs/tools/index.rst | 1 +
110
docs/tools/vhost-user-i2c.rst | 75 +++
136
docs/tools/vhost-user-i2c.rst | 75 ++
111
hw/virtio/Kconfig | 5 +
137
hw/virtio/Kconfig | 5 +
112
hw/virtio/meson.build | 2 +
138
hw/virtio/meson.build | 2 +
113
hw/virtio/vhost-user-i2c-pci.c | 79 +++
139
hw/virtio/vhost-user-i2c-pci.c | 69 ++
114
hw/virtio/vhost-user-i2c.c | 286 +++++++++
140
hw/virtio/vhost-user-i2c.c | 285 +++++++
115
include/hw/virtio/vhost-user-i2c.h | 37 ++
141
include/hw/virtio/vhost-user-i2c.h | 37 +
142
include/standard-headers/linux/virtio_i2c.h | 40 +
116
include/standard-headers/linux/virtio_ids.h | 1 +
143
include/standard-headers/linux/virtio_ids.h | 1 +
117
tools/meson.build | 8 +
144
tools/meson.build | 8 +
118
tools/vhost-user-i2c/50-qemu-i2c.json.in | 5 +
145
tools/vhost-user-i2c/50-qemu-i2c.json.in | 5 +
119
tools/vhost-user-i2c/main.c | 652 ++++++++++++++++++++
146
tools/vhost-user-i2c/main.c | 809 ++++++++++++++++++++
120
tools/vhost-user-i2c/meson.build | 10 +
147
tools/vhost-user-i2c/meson.build | 10 +
121
13 files changed, 1170 insertions(+)
148
14 files changed, 1356 insertions(+)
122
create mode 100644 docs/tools/vhost-user-i2c.rst
149
create mode 100644 docs/tools/vhost-user-i2c.rst
123
create mode 100644 hw/virtio/vhost-user-i2c-pci.c
150
create mode 100644 hw/virtio/vhost-user-i2c-pci.c
124
create mode 100644 hw/virtio/vhost-user-i2c.c
151
create mode 100644 hw/virtio/vhost-user-i2c.c
125
create mode 100644 include/hw/virtio/vhost-user-i2c.h
152
create mode 100644 include/hw/virtio/vhost-user-i2c.h
153
create mode 100644 include/standard-headers/linux/virtio_i2c.h
126
create mode 100644 tools/vhost-user-i2c/50-qemu-i2c.json.in
154
create mode 100644 tools/vhost-user-i2c/50-qemu-i2c.json.in
127
create mode 100644 tools/vhost-user-i2c/main.c
155
create mode 100644 tools/vhost-user-i2c/main.c
128
create mode 100644 tools/vhost-user-i2c/meson.build
156
create mode 100644 tools/vhost-user-i2c/meson.build
129
157
130
--
158
--
131
2.25.0.rc1.19.g042ed3e048af
159
2.25.0.rc1.19.g042ed3e048af
132
160
133
161
diff view generated by jsdifflib
New patch
1
DO NOT MERGE
1
2
3
Update Linux virtio headers with help of
4
5
./scripts/update-linux-headers.sh ~/lsrc/linux.git
6
7
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
8
---
9
include/standard-headers/linux/virtio_i2c.h | 40 +++++++++++++++++++++
10
include/standard-headers/linux/virtio_ids.h | 1 +
11
2 files changed, 41 insertions(+)
12
create mode 100644 include/standard-headers/linux/virtio_i2c.h
13
14
diff --git a/include/standard-headers/linux/virtio_i2c.h b/include/standard-headers/linux/virtio_i2c.h
15
new file mode 100644
16
index XXXXXXX..XXXXXXX
17
--- /dev/null
18
+++ b/include/standard-headers/linux/virtio_i2c.h
19
@@ -XXX,XX +XXX,XX @@
20
+/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
21
+/*
22
+ * Definitions for virtio I2C Adpter
23
+ *
24
+ * Copyright (c) 2021 Intel Corporation. All rights reserved.
25
+ */
26
+
27
+#ifndef _LINUX_VIRTIO_I2C_H
28
+#define _LINUX_VIRTIO_I2C_H
29
+
30
+#include "standard-headers/linux/types.h"
31
+
32
+/* The bit 0 of the @virtio_i2c_out_hdr.@flags, used to group the requests */
33
+#define VIRTIO_I2C_FLAGS_FAIL_NEXT    0x00000001
34
+
35
+/**
36
+ * struct virtio_i2c_out_hdr - the virtio I2C message OUT header
37
+ * @addr: the controlled device address
38
+ * @padding: used to pad to full dword
39
+ * @flags: used for feature extensibility
40
+ */
41
+struct virtio_i2c_out_hdr {
42
+    uint16_t addr;
43
+    uint16_t padding;
44
+    uint32_t flags;
45
+};
46
+
47
+/**
48
+ * struct virtio_i2c_in_hdr - the virtio I2C message IN header
49
+ * @status: the processing result from the backend
50
+ */
51
+struct virtio_i2c_in_hdr {
52
+    uint8_t status;
53
+};
54
+
55
+/* The final status written by the device */
56
+#define VIRTIO_I2C_MSG_OK    0
57
+#define VIRTIO_I2C_MSG_ERR    1
58
+
59
+#endif /* _LINUX_VIRTIO_I2C_H */
60
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h
61
index XXXXXXX..XXXXXXX 100644
62
--- a/include/standard-headers/linux/virtio_ids.h
63
+++ b/include/standard-headers/linux/virtio_ids.h
64
@@ -XXX,XX +XXX,XX @@
65
#define VIRTIO_ID_FS            26 /* virtio filesystem */
66
#define VIRTIO_ID_PMEM            27 /* virtio pmem */
67
#define VIRTIO_ID_MAC80211_HWSIM    29 /* virtio mac80211-hwsim */
68
+#define VIRTIO_ID_I2C_ADAPTER        34 /* virtio i2c adapter */
69
70
#endif /* _LINUX_VIRTIO_IDS_H */
71
--
72
2.25.0.rc1.19.g042ed3e048af
73
74
diff view generated by jsdifflib
1
This creates the QEMU side of the vhost-user-i2c device which connects
1
This creates the QEMU side of the vhost-user-i2c device which connects
2
to the remote daemon. It is based of vhost-user-fs code.
2
to the remote daemon. It is based of vhost-user-fs code.
3
3
4
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
4
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
5
---
5
---
6
hw/virtio/Kconfig | 5 +
6
hw/virtio/Kconfig | 5 +
7
hw/virtio/meson.build | 1 +
7
hw/virtio/meson.build | 1 +
8
hw/virtio/vhost-user-i2c.c | 286 ++++++++++++++++++++
8
hw/virtio/vhost-user-i2c.c | 285 +++++++++++++++++++++++++++++
9
include/hw/virtio/vhost-user-i2c.h | 37 +++
9
include/hw/virtio/vhost-user-i2c.h | 37 ++++
10
include/standard-headers/linux/virtio_ids.h | 1 +
10
4 files changed, 328 insertions(+)
11
5 files changed, 330 insertions(+)
12
create mode 100644 hw/virtio/vhost-user-i2c.c
11
create mode 100644 hw/virtio/vhost-user-i2c.c
13
create mode 100644 include/hw/virtio/vhost-user-i2c.h
12
create mode 100644 include/hw/virtio/vhost-user-i2c.h
14
13
15
diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
14
diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
16
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
...
...
181
+
180
+
182
+static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserI2C *i2c)
181
+static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserI2C *i2c)
183
+{
182
+{
184
+ vhost_user_cleanup(&i2c->vhost_user);
183
+ vhost_user_cleanup(&i2c->vhost_user);
185
+ virtio_delete_queue(i2c->req_vq);
184
+ virtio_delete_queue(i2c->req_vq);
186
+ g_free(i2c->vhost_dev.vqs);
187
+ virtio_cleanup(vdev);
185
+ virtio_cleanup(vdev);
188
+ g_free(i2c->vhost_dev.vqs);
186
+ g_free(i2c->vhost_dev.vqs);
189
+ i2c->vhost_dev.vqs = NULL;
187
+ i2c->vhost_dev.vqs = NULL;
190
+}
188
+}
191
+
189
+
...
...
259
+
257
+
260
+ if (!vhost_user_init(&i2c->vhost_user, &i2c->conf.chardev, errp)) {
258
+ if (!vhost_user_init(&i2c->vhost_user, &i2c->conf.chardev, errp)) {
261
+ return;
259
+ return;
262
+ }
260
+ }
263
+
261
+
264
+ virtio_init(vdev, "vhost-user-i2c", VIRTIO_ID_I2C, 0);
262
+ virtio_init(vdev, "vhost-user-i2c", VIRTIO_ID_I2C_ADAPTER, 0);
265
+
263
+
266
+ i2c->req_vq = virtio_add_queue(vdev, 3, vu_i2c_handle_output);
264
+ i2c->req_vq = virtio_add_queue(vdev, 4, vu_i2c_handle_output);
267
+ i2c->vhost_dev.nvqs = 1;
265
+ i2c->vhost_dev.nvqs = 1;
268
+ i2c->vhost_dev.vqs = g_new0(struct vhost_virtqueue, i2c->vhost_dev.nvqs);
266
+ i2c->vhost_dev.vqs = g_new0(struct vhost_virtqueue, i2c->vhost_dev.nvqs);
269
+ ret = vhost_dev_init(&i2c->vhost_dev, &i2c->vhost_user,
267
+ ret = vhost_dev_init(&i2c->vhost_dev, &i2c->vhost_user,
270
+ VHOST_BACKEND_TYPE_USER, 0);
268
+ VHOST_BACKEND_TYPE_USER, 0);
271
+ if (ret < 0) {
269
+ if (ret < 0) {
...
...
370
+
368
+
371
+ /*< public >*/
369
+ /*< public >*/
372
+};
370
+};
373
+
371
+
374
+#endif /* _QEMU_VHOST_USER_I2C_H */
372
+#endif /* _QEMU_VHOST_USER_I2C_H */
375
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h
376
index XXXXXXX..XXXXXXX 100644
377
--- a/include/standard-headers/linux/virtio_ids.h
378
+++ b/include/standard-headers/linux/virtio_ids.h
379
@@ -XXX,XX +XXX,XX @@
380
#define VIRTIO_ID_FS            26 /* virtio filesystem */
381
#define VIRTIO_ID_PMEM            27 /* virtio pmem */
382
#define VIRTIO_ID_MAC80211_HWSIM    29 /* virtio mac80211-hwsim */
383
+#define VIRTIO_ID_I2C            34 /* virtio i2c */
384
385
#endif /* _LINUX_VIRTIO_IDS_H */
386
--
373
--
387
2.25.0.rc1.19.g042ed3e048af
374
2.25.0.rc1.19.g042ed3e048af
388
375
389
376
diff view generated by jsdifflib
1
This allows is to instantiate a vhost-user-i2c device as part of a PCI
1
This allows is to instantiate a vhost-user-i2c device as part of a PCI
2
bus. It is mostly boilerplate which looks pretty similar to the
2
bus. It is mostly boilerplate which looks pretty similar to the
3
vhost-user-fs-pci device.
3
vhost-user-fs-pci device.
4
4
5
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
5
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
6
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
6
---
7
---
7
hw/virtio/meson.build | 1 +
8
hw/virtio/meson.build | 1 +
8
hw/virtio/vhost-user-i2c-pci.c | 79 ++++++++++++++++++++++++++++++++++
9
hw/virtio/vhost-user-i2c-pci.c | 69 ++++++++++++++++++++++++++++++++++
9
2 files changed, 80 insertions(+)
10
2 files changed, 70 insertions(+)
10
create mode 100644 hw/virtio/vhost-user-i2c-pci.c
11
create mode 100644 hw/virtio/vhost-user-i2c-pci.c
11
12
12
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
13
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
13
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
14
--- a/hw/virtio/meson.build
15
--- a/hw/virtio/meson.build
...
...
50
+#define TYPE_VHOST_USER_I2C_PCI "vhost-user-i2c-pci-base"
51
+#define TYPE_VHOST_USER_I2C_PCI "vhost-user-i2c-pci-base"
51
+
52
+
52
+DECLARE_INSTANCE_CHECKER(VHostUserI2CPCI, VHOST_USER_I2C_PCI,
53
+DECLARE_INSTANCE_CHECKER(VHostUserI2CPCI, VHOST_USER_I2C_PCI,
53
+ TYPE_VHOST_USER_I2C_PCI)
54
+ TYPE_VHOST_USER_I2C_PCI)
54
+
55
+
55
+static Property vhost_user_i2c_pci_properties[] = {
56
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
57
+ DEV_NVECTORS_UNSPECIFIED),
58
+ DEFINE_PROP_END_OF_LIST(),
59
+};
60
+
61
+static void vhost_user_i2c_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
56
+static void vhost_user_i2c_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
62
+{
57
+{
63
+ VHostUserI2CPCI *dev = VHOST_USER_I2C_PCI(vpci_dev);
58
+ VHostUserI2CPCI *dev = VHOST_USER_I2C_PCI(vpci_dev);
64
+ DeviceState *vdev = DEVICE(&dev->vdev);
59
+ DeviceState *vdev = DEVICE(&dev->vdev);
65
+
60
+
66
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
61
+ vpci_dev->nvectors = 1;
67
+ vpci_dev->nvectors = 1;
68
+ }
69
+
70
+ qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
62
+ qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
71
+}
63
+}
72
+
64
+
73
+static void vhost_user_i2c_pci_class_init(ObjectClass *klass, void *data)
65
+static void vhost_user_i2c_pci_class_init(ObjectClass *klass, void *data)
74
+{
66
+{
75
+ DeviceClass *dc = DEVICE_CLASS(klass);
67
+ DeviceClass *dc = DEVICE_CLASS(klass);
76
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
68
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
77
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
69
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
78
+ k->realize = vhost_user_i2c_pci_realize;
70
+ k->realize = vhost_user_i2c_pci_realize;
79
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
71
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
80
+ device_class_set_props(dc, vhost_user_i2c_pci_properties);
81
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
72
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
82
+ pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */
73
+ pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */
83
+ pcidev_k->revision = 0x00;
74
+ pcidev_k->revision = 0x00;
84
+ pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
75
+ pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
85
+}
76
+}
...
...
diff view generated by jsdifflib
1
This adds the vhost-user backend driver to support virtio based I2C
1
This adds the vhost-user backend driver to support virtio based I2C and
2
devices.
2
SMBUS devices.
3
3
4
vhost-user-i2c --help
4
vhost-user-i2c --help
5
5
6
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
6
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
7
---
7
---
8
hw/virtio/vhost-user-i2c.c | 2 +-
9
tools/meson.build | 8 +
8
tools/meson.build | 8 +
10
tools/vhost-user-i2c/50-qemu-i2c.json.in | 5 +
9
tools/vhost-user-i2c/50-qemu-i2c.json.in | 5 +
11
tools/vhost-user-i2c/main.c | 652 +++++++++++++++++++++++
10
tools/vhost-user-i2c/main.c | 809 +++++++++++++++++++++++
12
tools/vhost-user-i2c/meson.build | 10 +
11
tools/vhost-user-i2c/meson.build | 10 +
13
5 files changed, 676 insertions(+), 1 deletion(-)
12
4 files changed, 832 insertions(+)
14
create mode 100644 tools/vhost-user-i2c/50-qemu-i2c.json.in
13
create mode 100644 tools/vhost-user-i2c/50-qemu-i2c.json.in
15
create mode 100644 tools/vhost-user-i2c/main.c
14
create mode 100644 tools/vhost-user-i2c/main.c
16
create mode 100644 tools/vhost-user-i2c/meson.build
15
create mode 100644 tools/vhost-user-i2c/meson.build
17
16
18
diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/hw/virtio/vhost-user-i2c.c
21
+++ b/hw/virtio/vhost-user-i2c.c
22
@@ -XXX,XX +XXX,XX @@ static void vu_i2c_device_realize(DeviceState *dev, Error **errp)
23
24
virtio_init(vdev, "vhost-user-i2c", VIRTIO_ID_I2C, 0);
25
26
- i2c->req_vq = virtio_add_queue(vdev, 3, vu_i2c_handle_output);
27
+ i2c->req_vq = virtio_add_queue(vdev, 4, vu_i2c_handle_output);
28
i2c->vhost_dev.nvqs = 1;
29
i2c->vhost_dev.vqs = g_new0(struct vhost_virtqueue, i2c->vhost_dev.nvqs);
30
ret = vhost_dev_init(&i2c->vhost_dev, &i2c->vhost_user,
31
diff --git a/tools/meson.build b/tools/meson.build
17
diff --git a/tools/meson.build b/tools/meson.build
32
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
33
--- a/tools/meson.build
19
--- a/tools/meson.build
34
+++ b/tools/meson.build
20
+++ b/tools/meson.build
35
@@ -XXX,XX +XXX,XX @@ endif
21
@@ -XXX,XX +XXX,XX @@ endif
...
...
70
+ */
56
+ */
71
+
57
+
72
+#define G_LOG_DOMAIN "vhost-user-i2c"
58
+#define G_LOG_DOMAIN "vhost-user-i2c"
73
+#define G_LOG_USE_STRUCTURED 1
59
+#define G_LOG_USE_STRUCTURED 1
74
+
60
+
75
+#include <glib.h>
61
+#include <assert.h>
62
+#include <endian.h>
63
+#include <fcntl.h>
76
+#include <gio/gio.h>
64
+#include <gio/gio.h>
77
+#include <gio/gunixsocketaddress.h>
65
+#include <gio/gunixsocketaddress.h>
66
+#include <glib.h>
78
+#include <glib-unix.h>
67
+#include <glib-unix.h>
79
+#include <glib/gstdio.h>
68
+#include <glib/gstdio.h>
80
+#include <stdio.h>
81
+#include <stdbool.h>
82
+#include <string.h>
83
+#include <inttypes.h>
69
+#include <inttypes.h>
84
+#include <fcntl.h>
85
+#include <sys/ioctl.h>
86
+#include <sys/types.h>
87
+#include <sys/stat.h>
88
+#include <sys/mman.h>
89
+#include <unistd.h>
90
+#include <endian.h>
91
+#include <assert.h>
92
+#include <linux/i2c.h>
70
+#include <linux/i2c.h>
93
+#include <linux/i2c-dev.h>
71
+#include <linux/i2c-dev.h>
94
+
72
+#include <stdbool.h>
73
+#include <stdio.h>
74
+#include <string.h>
75
+#include <sys/ioctl.h>
76
+#include <sys/mman.h>
77
+#include <sys/stat.h>
78
+#include <sys/types.h>
79
+#include <unistd.h>
80
+
81
+#include "qemu/compiler.h"
95
+#include "qemu/cutils.h"
82
+#include "qemu/cutils.h"
83
+#include "standard-headers/linux/virtio_i2c.h"
96
+#include "subprojects/libvhost-user/libvhost-user-glib.h"
84
+#include "subprojects/libvhost-user/libvhost-user-glib.h"
97
+#include "subprojects/libvhost-user/libvhost-user.h"
85
+#include "subprojects/libvhost-user/libvhost-user.h"
98
+
86
+
99
+/* Definitions from virtio-i2c specifications */
100
+#define VHOST_USER_I2C_MAX_QUEUES 1
87
+#define VHOST_USER_I2C_MAX_QUEUES 1
101
+
88
+
102
+/* Status */
103
+#define VIRTIO_I2C_MSG_OK 0
104
+#define VIRTIO_I2C_MSG_ERR 1
105
+
106
+/* The bit 0 of the @virtio_i2c_out_hdr.@flags, used to group the requests */
107
+#define VIRTIO_I2C_FLAGS_FAIL_NEXT 0x00000001
108
+
109
+/**
110
+ * struct virtio_i2c_out_hdr - the virtio I2C message OUT header
111
+ * @addr: the controlled device's address
112
+ * @padding: used to pad to full dword
113
+ * @flags: used for feature extensibility
114
+ */
115
+struct virtio_i2c_out_hdr {
116
+ uint16_t addr;
117
+ uint16_t padding;
118
+ uint32_t flags;
119
+} __attribute__((packed));
120
+
121
+/**
122
+ * struct virtio_i2c_in_hdr - the virtio I2C message IN header
123
+ * @status: the processing result from the backend
124
+ */
125
+struct virtio_i2c_in_hdr {
126
+ uint8_t status;
127
+} __attribute__((packed));
128
+
129
+/* vhost-user-i2c definitions */
89
+/* vhost-user-i2c definitions */
130
+
131
+#ifndef container_of
132
+#define container_of(ptr, type, member) ({ \
133
+ const typeof(((type *) 0)->member) *__mptr = (ptr); \
134
+ (type *) ((char *) __mptr - offsetof(type, member));})
135
+#endif
136
+
90
+
137
+#define MAX_I2C_VDEV (1 << 7)
91
+#define MAX_I2C_VDEV (1 << 7)
138
+#define MAX_I2C_ADAPTER 16
92
+#define MAX_I2C_ADAPTER 16
139
+
93
+
140
+typedef struct {
94
+typedef struct {
141
+ int32_t fd;
95
+ int32_t fd;
142
+ int32_t bus;
96
+ int32_t bus;
97
+ bool smbus;
143
+ bool clients[MAX_I2C_VDEV];
98
+ bool clients[MAX_I2C_VDEV];
144
+} VI2cAdapter;
99
+} VI2cAdapter;
145
+
100
+
146
+typedef struct {
101
+typedef struct {
147
+ VugDev dev;
102
+ VugDev dev;
...
...
154
+static gboolean print_cap, verbose;
109
+static gboolean print_cap, verbose;
155
+static gchar *socket_path, *device_list;
110
+static gchar *socket_path, *device_list;
156
+static gint socket_fd = -1;
111
+static gint socket_fd = -1;
157
+
112
+
158
+static GOptionEntry options[] = {
113
+static GOptionEntry options[] = {
159
+ { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &socket_path, "Location of vhost-user Unix domain socket, incompatible with --fd", "PATH" },
114
+ { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &socket_path,
160
+ { "fd", 'f', 0, G_OPTION_ARG_INT, &socket_fd, "Specify the file-descriptor of the backend, incompatible with --socket-path", "FD" },
115
+ "Location of vhost-user Unix domain socket, incompatible with --fd",
161
+ { "device-list", 'l', 0, G_OPTION_ARG_STRING, &device_list, "List of i2c-dev bus and attached devices", "I2C Devices" },
116
+ "PATH" },
162
+ { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &print_cap, "Output to stdout the backend capabilities in JSON format and exit", NULL},
117
+ { "fd", 'f', 0, G_OPTION_ARG_INT, &socket_fd,
163
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be more verbose in output", NULL},
118
+ "Specify file-descriptor of the backend, don't use with --socket-path",
119
+ "FD" },
120
+ { "device-list", 'l', 0, G_OPTION_ARG_STRING, &device_list,
121
+ "List of i2c-dev bus and attached devices", "I2C Devices" },
122
+ { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &print_cap,
123
+ "Output to stdout the backend capabilities in JSON format and exit",
124
+ NULL},
125
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
126
+ "Be more verbose in output", NULL},
164
+ { NULL }
127
+ { NULL }
165
+};
128
+};
166
+
129
+
167
+
130
+
168
+/* I2c helpers */
131
+/* I2c helpers */
...
...
175
+ }
138
+ }
176
+ g_string_append_printf(s, "%x ", bytes[i]);
139
+ g_string_append_printf(s, "%x ", bytes[i]);
177
+ }
140
+ }
178
+}
141
+}
179
+
142
+
180
+static void vi2c_dump_msg(struct i2c_msg *msg)
143
+static void vi2c_dump_msg(struct i2c_msg *msgs, size_t count)
181
+{
144
+{
182
+ g_autoptr(GString) s = g_string_new("\nI2c request: ");
145
+ int32_t i;
183
+
146
+
184
+ g_string_append_printf(s, "addr: %x\n", msg->addr);
147
+ for (i = 0; i < count; i++) {
185
+ g_string_append_printf(s, "transfer len: %x\n", msg->len);
148
+ g_autoptr(GString) s = g_string_new("\nI2c request: ");
186
+
149
+
187
+ g_string_append_printf(s, "%s: ", msg->flags & I2C_M_RD ? "Data read" :
150
+ g_string_append_printf(s, "addr: %x\n", msgs[i].addr);
188
+ "Data Written");
151
+ g_string_append_printf(s, "transfer len: %x\n", msgs[i].len);
189
+ fmt_bytes(s, (uint8_t *)msg->buf, msg->len);
152
+
190
+ g_string_append_printf(s, "\n");
153
+ g_string_append_printf(s, "%s: ", msgs[i].flags & I2C_M_RD ?
191
+
154
+ "Data read" : "Data Written");
192
+ g_debug("%s: %s", __func__, s->str);
155
+ fmt_bytes(s, (uint8_t *)msgs[i].buf, msgs[i].len);
156
+ g_string_append_printf(s, "\n");
157
+
158
+ g_debug("%s: %s", __func__, s->str);
159
+ }
193
+}
160
+}
194
+
161
+
195
+static int vi2c_map_adapters(VuI2c *i2c)
162
+static int vi2c_map_adapters(VuI2c *i2c)
196
+{
163
+{
197
+ VI2cAdapter *adapter;
164
+ VI2cAdapter *adapter;
...
...
200
+ /*
167
+ /*
201
+ * Flatten the map for client address and adapter to the array:
168
+ * Flatten the map for client address and adapter to the array:
202
+ *
169
+ *
203
+ * adapter_map[MAX_I2C_VDEV]:
170
+ * adapter_map[MAX_I2C_VDEV]:
204
+ *
171
+ *
205
+ * Adapter | adapter2 | none | adapter1 | adapter3 | none | none| (val)
172
+ * Adapter | adapter2 | none | adapter1 | adapter3 | none| (val)
206
+ * |----------|-------|----------|----------|------|-----|
173
+ * |----------|-------|----------|----------|-----|
207
+ * Slave Address | addr 1 | none | addr 2 | addr 3 | none | none| (idx)
174
+ * Slave Address | addr 1 | none | addr 2 | addr 3 | none| (idx)
208
+ * |<-----------------------MAX_I2C_VDEV---------------->|
175
+ * |<-----------------------MAX_I2C_VDEV---------------->|
209
+ */
176
+ */
210
+ for (i = 0; i < i2c->adapter_num; i++) {
177
+ for (i = 0; i < i2c->adapter_num; i++) {
211
+ adapter = i2c->adapter[i];
178
+ adapter = i2c->adapter[i];
212
+
179
+
...
...
230
+ return 0;
197
+ return 0;
231
+}
198
+}
232
+
199
+
233
+static VI2cAdapter *vi2c_find_adapter(VuI2c *i2c, uint16_t addr)
200
+static VI2cAdapter *vi2c_find_adapter(VuI2c *i2c, uint16_t addr)
234
+{
201
+{
235
+ int32_t idx;
202
+ if (addr < MAX_I2C_VDEV && (i2c->adapter_map[addr] != 0)) {
236
+
203
+ return i2c->adapter[i2c->adapter_map[addr] - 1];
237
+ if (addr < MAX_I2C_VDEV && ((idx = i2c->adapter_map[addr]) != 0)) {
238
+ return i2c->adapter[idx - 1];
239
+ }
204
+ }
240
+
205
+
241
+ return NULL;
206
+ return NULL;
242
+}
207
+}
243
+
208
+
244
+static bool vi2c_client_access_ok(VI2cAdapter *adapter, uint16_t addr)
209
+static bool vi2c_set_client_addr(VI2cAdapter *adapter, uint16_t addr)
245
+{
210
+{
246
+ if (ioctl(adapter->fd, I2C_SLAVE, addr) < 0) {
211
+ if (ioctl(adapter->fd, I2C_SLAVE, addr) < 0) {
247
+ if (errno == EBUSY) {
212
+ if (errno == EBUSY) {
248
+ g_printerr("client device %x is busy!\n", addr);
213
+ g_printerr("client device %x is busy!\n", addr);
249
+ } else {
214
+ } else {
...
...
257
+static void vi2c_remove_adapters(VuI2c *i2c)
222
+static void vi2c_remove_adapters(VuI2c *i2c)
258
+{
223
+{
259
+ VI2cAdapter *adapter;
224
+ VI2cAdapter *adapter;
260
+ int32_t i;
225
+ int32_t i;
261
+
226
+
262
+ for (i = 0; i < i2c->adapter_num; i++) {
227
+ for (i = 0; i < MAX_I2C_ADAPTER; i++) {
263
+ adapter = i2c->adapter[i];
228
+ adapter = i2c->adapter[i];
264
+ if (!adapter) {
229
+ if (!adapter) {
265
+ break;
230
+ break;
266
+ }
231
+ }
267
+
232
+
...
...
277
+static VI2cAdapter *vi2c_create_adapter(int32_t bus, uint16_t client_addr[],
242
+static VI2cAdapter *vi2c_create_adapter(int32_t bus, uint16_t client_addr[],
278
+ int32_t n_client)
243
+ int32_t n_client)
279
+{
244
+{
280
+ VI2cAdapter *adapter;
245
+ VI2cAdapter *adapter;
281
+ char path[20];
246
+ char path[20];
247
+ uint64_t funcs;
282
+ int32_t fd, i;
248
+ int32_t fd, i;
283
+
249
+
284
+ if (bus < 0)
250
+ if (bus < 0) {
285
+ return NULL;
251
+ return NULL;
252
+ }
286
+
253
+
287
+ adapter = g_malloc0(sizeof(*adapter));
254
+ adapter = g_malloc0(sizeof(*adapter));
288
+ if (!adapter) {
255
+ if (!adapter) {
289
+ g_printerr("failed to alloc adapter");
256
+ g_printerr("failed to alloc adapter");
290
+ return NULL;
257
+ return NULL;
...
...
300
+ }
267
+ }
301
+
268
+
302
+ adapter->fd = fd;
269
+ adapter->fd = fd;
303
+ adapter->bus = bus;
270
+ adapter->bus = bus;
304
+
271
+
272
+ if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
273
+ g_printerr("virtio_i2c: failed to get functionality %s: %d\n", path,
274
+ errno);
275
+ goto close_fd;
276
+ }
277
+
278
+ if (funcs & I2C_FUNC_I2C) {
279
+ adapter->smbus = false;
280
+ } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
281
+ adapter->smbus = true;
282
+ } else {
283
+ g_printerr("virtio_i2c: invalid functionality %lx\n", funcs);
284
+ goto close_fd;
285
+ }
286
+
305
+ for (i = 0; i < n_client; i++) {
287
+ for (i = 0; i < n_client; i++) {
306
+ if (client_addr[i]) {
288
+ if (client_addr[i]) {
307
+ if (!vi2c_client_access_ok(adapter, client_addr[i])) {
289
+ if (!vi2c_set_client_addr(adapter, client_addr[i])) {
308
+ goto fail;
290
+ goto close_fd;
309
+ }
291
+ }
310
+
292
+
311
+ if (adapter->clients[client_addr[i]]) {
293
+ if (adapter->clients[client_addr[i]]) {
312
+ g_printerr("client addr 0x%x repeat, not allowed.\n", client_addr[i]);
294
+ g_printerr("client addr 0x%x repeat, not allowed.\n",
313
+ goto fail;
295
+ client_addr[i]);
296
+ goto close_fd;
314
+ }
297
+ }
315
+
298
+
316
+ adapter->clients[client_addr[i]] = true;
299
+ adapter->clients[client_addr[i]] = true;
317
+ if (verbose) {
300
+ if (verbose) {
318
+ g_print("Added client 0x%x to bus %u\n", client_addr[i], bus);
301
+ g_print("Added client 0x%x to bus %u\n", client_addr[i], bus);
319
+ }
302
+ }
320
+ }
303
+ }
321
+ }
304
+ }
305
+
306
+ if (verbose) {
307
+ g_print("Added adapter: bus: %d, func %s\n", bus,
308
+ adapter->smbus ? "smbus" : "i2c");
309
+ }
322
+ return adapter;
310
+ return adapter;
323
+
311
+
312
+close_fd:
313
+ close(fd);
324
+fail:
314
+fail:
325
+ g_free(adapter);
315
+ g_free(adapter);
326
+ return NULL;
316
+ return NULL;
327
+}
317
+}
328
+
318
+
...
...
331
+ *
321
+ *
332
+ * <bus>:<client_addr>[:<client_addr>],
322
+ * <bus>:<client_addr>[:<client_addr>],
333
+ * [<bus>:<client_addr>[:<client_addr>]]
323
+ * [<bus>:<client_addr>[:<client_addr>]]
334
+ *
324
+ *
335
+ * bus (dec): adatper bus number.
325
+ * bus (dec): adatper bus number.
336
+ *     e.g. 2 for /dev/i2c-2
326
+ * e.g. 2 for /dev/i2c-2
337
+ * client_addr (hex): address for client device
327
+ * client_addr (hex): address for client device
338
+ *     e.g. 0x1C or 1C
328
+ * e.g. 0x1C or 1C
339
+ *
329
+ *
340
+ * Example: --device-list="2:0x1c:0x20,3:0x10:0x2c"
330
+ * Example: --device-list="2:0x1c:0x20,3:0x10:0x2c"
341
+ *
331
+ *
342
+ * Note: client address can not repeat.
332
+ * Note: client address can not repeat.
343
+ */
333
+ */
344
+static int vi2c_parse(VuI2c *i2c)
334
+static int32_t vi2c_parse(VuI2c *i2c)
345
+{
335
+{
346
+ uint16_t client_addr[MAX_I2C_VDEV];
336
+ uint16_t client_addr[MAX_I2C_VDEV];
347
+ int32_t n_adapter = 0, n_client;
337
+ int32_t n_adapter = 0, n_client;
348
+ int64_t addr, bus;
338
+ int64_t addr, bus;
349
+ const char *cp, *t;
339
+ const char *cp, *t;
350
+
340
+
351
+ while (device_list) {
341
+ while (device_list) {
352
+ /* Read <bus>:<client_addr>[:<client_addr>] entries one by one */
342
+ /* Read <bus>:<client_addr>[:<client_addr>] entries one by one */
353
+ cp = strsep(&device_list, ",");
343
+ cp = strsep(&device_list, ",");
354
+
344
+
355
+ if (!cp || *cp =='\0') {
345
+ if (!cp || *cp == '\0') {
356
+ break;
346
+ break;
357
+ }
347
+ }
358
+
348
+
359
+ if (n_adapter == MAX_I2C_ADAPTER) {
349
+ if (n_adapter == MAX_I2C_ADAPTER) {
360
+ g_printerr("too many adapter (%d), only support %d \n", n_adapter,
350
+ g_printerr("too many adapter (%d), only support %d\n", n_adapter,
361
+ MAX_I2C_ADAPTER);
351
+ MAX_I2C_ADAPTER);
362
+ goto out;
352
+ goto out;
363
+ }
353
+ }
364
+
354
+
365
+ if (qemu_strtol(cp, &t, 10, &bus) || bus < 0) {
355
+ if (qemu_strtol(cp, &t, 10, &bus) || bus < 0) {
...
...
369
+
359
+
370
+ cp = t;
360
+ cp = t;
371
+ n_client = 0;
361
+ n_client = 0;
372
+
362
+
373
+ /* Parse clients <client_addr>[:<client_addr>] entries one by one */
363
+ /* Parse clients <client_addr>[:<client_addr>] entries one by one */
374
+ while (cp != NULL && *cp !='\0') {
364
+ while (cp != NULL && *cp != '\0') {
375
+ if (*cp == ':')
365
+ if (*cp == ':') {
376
+ cp++;
366
+ cp++;
367
+ }
377
+
368
+
378
+ if (n_client == MAX_I2C_VDEV) {
369
+ if (n_client == MAX_I2C_VDEV) {
379
+ g_printerr("too many devices (%d), only support %d \n",
370
+ g_printerr("too many devices (%d), only support %d\n",
380
+ n_client, MAX_I2C_VDEV);
371
+ n_client, MAX_I2C_VDEV);
381
+ goto out;
372
+ goto out;
382
+ }
373
+ }
383
+
374
+
384
+ if (qemu_strtol(cp, &t, 16, &addr) || addr < 0 || addr > MAX_I2C_VDEV) {
375
+ if (qemu_strtol(cp, &t, 16, &addr) ||
376
+ addr < 0 || addr > MAX_I2C_VDEV) {
385
+ g_printerr("Invalid address %s : %lx\n", cp, addr);
377
+ g_printerr("Invalid address %s : %lx\n", cp, addr);
386
+ goto out;
378
+ goto out;
387
+ }
379
+ }
388
+
380
+
389
+ client_addr[n_client++] = addr;
381
+ client_addr[n_client++] = addr;
390
+ cp = t;
382
+ cp = t;
391
+ if (verbose) {
383
+ if (verbose) {
392
+ g_print("i2c adapter %ld:0x%lx\n", bus, addr);
384
+ g_print("i2c adapter %ld:0x%lx\n", bus, addr);
393
+ }
385
+ }
394
+ }
386
+ }
395
+
387
+
396
+ i2c->adapter[n_adapter] = vi2c_create_adapter(bus, client_addr, n_client);
388
+ i2c->adapter[n_adapter] = vi2c_create_adapter(bus, client_addr,
397
+ if (!i2c->adapter[n_adapter])
389
+ n_client);
390
+ if (!i2c->adapter[n_adapter]) {
398
+ goto out;
391
+ goto out;
392
+ }
393
+
399
+ n_adapter++;
394
+ n_adapter++;
400
+ }
395
+ }
401
+
396
+
402
+ if (!n_adapter) {
397
+ if (!n_adapter) {
403
+ g_printerr("Failed to add any adapters\n");
398
+ g_printerr("Failed to add any adapters\n");
...
...
413
+out:
408
+out:
414
+ vi2c_remove_adapters(i2c);
409
+ vi2c_remove_adapters(i2c);
415
+ return -1;
410
+ return -1;
416
+}
411
+}
417
+
412
+
418
+static uint8_t vi2c_xfer(VuDev *dev, struct i2c_msg *msg)
413
+static int32_t i2c_xfer(VI2cAdapter *adapter, struct i2c_msg *msgs,
414
+ size_t count)
415
+{
416
+ struct i2c_rdwr_ioctl_data data;
417
+
418
+ data.nmsgs = count;
419
+ data.msgs = msgs;
420
+
421
+ return ioctl(adapter->fd, I2C_RDWR, &data);
422
+}
423
+
424
+/*
425
+ * Based on Linux's drivers/i2c/i2c-core-smbus.c:i2c_smbus_xfer_emulated(). This
426
+ * function tries to reverse what Linux does, only support basic modes (up to
427
+ * word transfer).
428
+ */
429
+static int32_t smbus_xfer(VI2cAdapter *adapter, struct i2c_msg *msgs,
430
+ size_t count)
431
+{
432
+ union i2c_smbus_data data;
433
+ struct i2c_smbus_ioctl_data smbus_data = { .data = &data };
434
+ int32_t ret;
435
+
436
+ smbus_data.command = msgs[0].buf[0];
437
+
438
+ switch (count) {
439
+ case 1:
440
+ if (msgs[0].flags & I2C_M_RD) {
441
+ if (msgs[0].len > 1) {
442
+ g_printerr("Incorrect message length for read operation: %d\n",
443
+ msgs[0].len);
444
+ return -1;
445
+ }
446
+ smbus_data.read_write = I2C_SMBUS_READ;
447
+ } else {
448
+ smbus_data.read_write = I2C_SMBUS_WRITE;
449
+ }
450
+
451
+ if (!msgs[0].len) {
452
+ smbus_data.size = I2C_SMBUS_QUICK;
453
+ smbus_data.data = NULL;
454
+ } else if (msgs[0].len == 1) {
455
+ smbus_data.size = I2C_SMBUS_BYTE;
456
+ if (smbus_data.read_write == I2C_SMBUS_WRITE) {
457
+ smbus_data.data = NULL;
458
+ }
459
+ } else if (msgs[0].len == 2) {
460
+ smbus_data.size = I2C_SMBUS_BYTE_DATA;
461
+ data.byte = msgs[0].buf[1];
462
+ } else if (msgs[0].len == 3) {
463
+ smbus_data.size = I2C_SMBUS_WORD_DATA;
464
+ data.word = msgs[0].buf[1] | (msgs[0].buf[2] << 8);
465
+ } else {
466
+ g_printerr("Message length not supported for write operation: %d\n",
467
+ msgs[0].len);
468
+ return -1;
469
+ }
470
+
471
+ break;
472
+ case 2:
473
+ if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD) ||
474
+ (msgs[0].len != 1) || (msgs[1].len > 2)) {
475
+ g_printerr("Expecting a valid read smbus transfer: %ld: %d: %d\n",
476
+ count, msgs[0].len, msgs[1].len);
477
+ return -1;
478
+ }
479
+
480
+ smbus_data.read_write = I2C_SMBUS_READ;
481
+ smbus_data.size = msgs[1].len == 1 ?
482
+ I2C_SMBUS_BYTE_DATA : I2C_SMBUS_WORD_DATA;
483
+ break;
484
+ default:
485
+ g_printerr("Invalid number of messages for smbus xfer: %ld\n", count);
486
+ return -1;
487
+ }
488
+
489
+ if (verbose) {
490
+ g_print("SMBUS command: %x: %x: %x\n", smbus_data.read_write,
491
+ smbus_data.command, smbus_data.size);
492
+ }
493
+
494
+ ret = ioctl(adapter->fd, I2C_SMBUS, &smbus_data);
495
+ if (ret < 0) {
496
+ return ret;
497
+ }
498
+
499
+ if (smbus_data.read_write == I2C_SMBUS_WRITE) {
500
+ return 0;
501
+ }
502
+
503
+ switch (smbus_data.size) {
504
+ case I2C_SMBUS_BYTE:
505
+ msgs[0].buf[0] = data.byte;
506
+ break;
507
+ case I2C_SMBUS_BYTE_DATA:
508
+ msgs[1].buf[0] = data.byte;
509
+ break;
510
+ case I2C_SMBUS_WORD_DATA:
511
+ msgs[1].buf[0] = data.word & 0xff;
512
+ msgs[1].buf[1] = data.word >> 8;
513
+ break;
514
+ }
515
+
516
+ return 0;
517
+}
518
+
519
+static uint8_t vi2c_xfer(VuDev *dev, struct i2c_msg *msgs, size_t count)
419
+{
520
+{
420
+ VuI2c *i2c = container_of(dev, VuI2c, dev.parent);
521
+ VuI2c *i2c = container_of(dev, VuI2c, dev.parent);
421
+ struct i2c_rdwr_ioctl_data data;
422
+ VI2cAdapter *adapter;
522
+ VI2cAdapter *adapter;
423
+
523
+ int8_t ret;
424
+ adapter = vi2c_find_adapter(i2c, msg->addr);
524
+
525
+ /* Assume that adapter should be same for all messages sent together */
526
+ adapter = vi2c_find_adapter(i2c, msgs[0].addr);
425
+ if (!adapter) {
527
+ if (!adapter) {
426
+ g_printerr("Failed to find adapter for address: %x\n", msg->addr);
528
+ g_printerr("Failed to find adapter for address: %x\n", msgs[0].addr);
427
+ return VIRTIO_I2C_MSG_ERR;
529
+ return VIRTIO_I2C_MSG_ERR;
428
+ }
530
+ }
429
+
531
+
430
+ data.nmsgs = 1;
532
+ /* Set client's address */
431
+ data.msgs = msg;
533
+ if (!vi2c_set_client_addr(adapter, msgs[0].addr)) {
432
+
433
+ if (ioctl(adapter->fd, I2C_RDWR, &data) < 0) {
434
+ g_printerr("Failed to transfer data to address %x : %d\n", msg->addr, errno);
435
+ return VIRTIO_I2C_MSG_ERR;
534
+ return VIRTIO_I2C_MSG_ERR;
436
+ }
535
+ }
437
+
536
+
537
+ if (adapter->smbus) {
538
+ ret = smbus_xfer(adapter, msgs, count);
539
+ } else {
540
+ ret = i2c_xfer(adapter, msgs, count);
541
+ }
542
+
543
+ if (ret < 0) {
544
+ g_printerr("Failed to transfer data to address %x : %d\n",
545
+ msgs[0].addr, errno);
546
+ return VIRTIO_I2C_MSG_ERR;
547
+ }
548
+
438
+ if (verbose) {
549
+ if (verbose) {
439
+ vi2c_dump_msg(msg);
550
+ vi2c_dump_msg(msgs, count);
440
+ }
551
+ }
441
+
552
+
442
+ return VIRTIO_I2C_MSG_OK;
553
+ return VIRTIO_I2C_MSG_OK;
443
+}
554
+}
444
+
445
+
555
+
446
+/* Virtio helpers */
556
+/* Virtio helpers */
447
+static uint64_t vi2c_get_features(VuDev *dev)
557
+static uint64_t vi2c_get_features(VuDev *dev)
448
+{
558
+{
449
+ if (verbose) {
559
+ if (verbose) {
...
...
462
+}
572
+}
463
+
573
+
464
+static void vi2c_handle_ctrl(VuDev *dev, int qidx)
574
+static void vi2c_handle_ctrl(VuDev *dev, int qidx)
465
+{
575
+{
466
+ VuVirtq *vq = vu_get_queue(dev, qidx);
576
+ VuVirtq *vq = vu_get_queue(dev, qidx);
467
+ struct i2c_msg msg;
468
+ struct virtio_i2c_out_hdr *out_hdr;
577
+ struct virtio_i2c_out_hdr *out_hdr;
469
+ struct virtio_i2c_in_hdr *in_hdr;
578
+ size_t i, count = 0, len, in_hdr_len;
470
+ bool fail_next = false;
579
+ struct i2c_msg *msgs;
471
+ size_t len, in_hdr_len;
580
+ VuVirtqElement *elem;
472
+
581
+ uint8_t status;
473
+ for (;;) {
582
+ struct {
583
+ struct virtio_i2c_in_hdr *in_hdr;
474
+ VuVirtqElement *elem;
584
+ VuVirtqElement *elem;
475
+
585
+ size_t size;
586
+ } *info;
587
+
588
+ /* Count number of messages */
589
+ do {
590
+ elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
591
+ } while (elem && ++count);
592
+
593
+ if (!count) {
594
+ if (verbose) {
595
+ g_printerr("Virtqueue can't have 0 elements\n");
596
+ }
597
+ return;
598
+ }
599
+
600
+ /* Rewind everything, now that we have counted the number of messages */
601
+ vu_queue_rewind(dev, vq, count);
602
+
603
+ if (verbose) {
604
+ g_print("Received %ld messages in virtqueue\n", count);
605
+ }
606
+
607
+ /* Allocate memory for msgs and info */
608
+ msgs = g_malloc0_n(count, sizeof(*msgs) + sizeof(*info));
609
+ if (!msgs) {
610
+ g_printerr("failed to allocate space for messages\n");
611
+ return;
612
+ }
613
+ info = (void *)(msgs + count);
614
+
615
+ for (i = 0; i < count; i++) {
476
+ elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
616
+ elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
477
+ if (!elem) {
617
+ if (!elem) {
478
+ break;
618
+ g_printerr("Failed to pop element: %ld : %ld\n", i, count);
479
+ }
619
+ goto out;
620
+ }
621
+ info[i].elem = elem;
622
+ info[i].size = sizeof(*info[i].in_hdr);
480
+
623
+
481
+ g_debug("%s: got queue (in %d, out %d)", __func__, elem->in_num,
624
+ g_debug("%s: got queue (in %d, out %d)", __func__, elem->in_num,
482
+ elem->out_num);
625
+ elem->out_num);
483
+
626
+
484
+ /* Validate size of out header */
627
+ /* Validate size of "out" header */
485
+ if (elem->out_sg[0].iov_len != sizeof(*out_hdr)) {
628
+ if (elem->out_sg[0].iov_len != sizeof(*out_hdr)) {
486
+ g_warning("%s: Invalid out hdr %zu : %zu\n", __func__,
629
+ g_warning("%s: Invalid out hdr %zu : %zu\n", __func__,
487
+ elem->out_sg[0].iov_len, sizeof(*out_hdr));
630
+ elem->out_sg[0].iov_len, sizeof(*out_hdr));
488
+ continue;
631
+ goto out;
489
+ }
632
+ }
490
+
633
+
491
+ out_hdr = elem->out_sg[0].iov_base;
634
+ out_hdr = elem->out_sg[0].iov_base;
492
+
635
+
493
+ /* Bit 0 is reserved in virtio spec */
636
+ /* Bit 0 is reserved in virtio spec */
494
+ msg.addr = out_hdr->addr >> 1;
637
+ msgs[i].addr = le16toh(out_hdr->addr) >> 1;
495
+
638
+
496
+ /* Read Operation */
639
+ /* Read Operation */
497
+ if (elem->out_num == 1 && elem->in_num == 2) {
640
+ if (elem->out_num == 1 && elem->in_num == 2) {
498
+ len = elem->in_sg[0].iov_len;
641
+ len = elem->in_sg[0].iov_len;
499
+ if (!len) {
642
+ if (!len) {
500
+ g_warning("%s: Read buffer length can't be zero\n", __func__);
643
+ g_warning("%s: Read buffer length can't be zero\n", __func__);
501
+ continue;
644
+ goto out;
502
+ }
645
+ }
503
+
646
+
504
+ msg.buf = elem->in_sg[0].iov_base;
647
+ msgs[i].buf = elem->in_sg[0].iov_base;
505
+ msg.flags = I2C_M_RD;
648
+ msgs[i].flags = I2C_M_RD;
506
+ msg.len = len;
649
+ msgs[i].len = len;
507
+
650
+
508
+ in_hdr = elem->in_sg[1].iov_base;
651
+ info[i].in_hdr = elem->in_sg[1].iov_base;
652
+ info[i].size += len;
509
+ in_hdr_len = elem->in_sg[1].iov_len;
653
+ in_hdr_len = elem->in_sg[1].iov_len;
510
+ } else if (elem->out_num == 2 && elem->in_num == 1) {
654
+ } else if (elem->out_num == 2 && elem->in_num == 1) {
511
+ /* Write Operation */
655
+ /* Write Operation */
512
+ len = elem->out_sg[1].iov_len;
656
+ len = elem->out_sg[1].iov_len;
513
+ if (!len) {
657
+ if (!len) {
514
+ g_warning("%s: Write buffer length can't be zero\n", __func__);
658
+ g_warning("%s: Write buffer length can't be zero\n", __func__);
515
+ continue;
659
+ goto out;
516
+ }
660
+ }
517
+
661
+
518
+ msg.buf = elem->out_sg[1].iov_base;
662
+ msgs[i].buf = elem->out_sg[1].iov_base;
519
+ msg.flags = 0;
663
+ msgs[i].flags = 0;
520
+ msg.len = len;
664
+ msgs[i].len = len;
521
+
665
+
522
+ in_hdr = elem->in_sg[0].iov_base;
666
+ info[i].in_hdr = elem->in_sg[0].iov_base;
523
+ in_hdr_len = elem->in_sg[0].iov_len;
667
+ in_hdr_len = elem->in_sg[0].iov_len;
524
+ len = 0;
525
+ } else {
668
+ } else {
526
+ g_warning("%s: Transfer type not supported (in %d, out %d)\n",
669
+ g_warning("%s: Transfer type not supported (in %d, out %d)\n",
527
+ __func__, elem->in_num, elem->out_num);
670
+ __func__, elem->in_num, elem->out_num);
528
+ continue;
671
+ goto out;
529
+ }
672
+ }
530
+
673
+
531
+ /* Validate size of in header */
674
+ /* Validate size of "in" header */
532
+ if (in_hdr_len != sizeof(*in_hdr)) {
675
+ if (in_hdr_len != sizeof(*(info[i].in_hdr))) {
533
+ g_warning("%s: Invalid in hdr %zu : %zu\n", __func__, in_hdr_len,
676
+ g_warning("%s: Invalid in hdr %zu : %zu\n", __func__, in_hdr_len,
534
+ sizeof(*in_hdr));
677
+ sizeof(*(info[i].in_hdr)));
535
+ continue;
678
+ goto out;
536
+ }
679
+ }
537
+
680
+ }
538
+ in_hdr->status = fail_next ? VIRTIO_I2C_MSG_ERR : vi2c_xfer(dev, &msg);
681
+
539
+ if (in_hdr->status == VIRTIO_I2C_MSG_ERR) {
682
+ status = vi2c_xfer(dev, msgs, count);
540
+ /* We need to fail remaining transfers as well */
683
+
541
+ fail_next = out_hdr->flags & VIRTIO_I2C_FLAGS_FAIL_NEXT;
684
+ for (i = 0; i < count; i++) {
542
+ }
685
+ info[i].in_hdr->status = status;
543
+
686
+ vu_queue_push(dev, vq, info[i].elem, info[i].size);
544
+ vu_queue_push(dev, vq, elem, len + sizeof(*in_hdr));
545
+ }
687
+ }
546
+
688
+
547
+ vu_queue_notify(dev, vq);
689
+ vu_queue_notify(dev, vq);
690
+
691
+out:
692
+ g_free(msgs);
548
+}
693
+}
549
+
694
+
550
+static void
695
+static void
551
+vi2c_queue_set_started(VuDev *dev, int qidx, bool started)
696
+vi2c_queue_set_started(VuDev *dev, int qidx, bool started)
552
+{
697
+{
...
...
623
+ g_autoptr(GSocket) socket = NULL;
768
+ g_autoptr(GSocket) socket = NULL;
624
+ VuI2c i2c = {0};
769
+ VuI2c i2c = {0};
625
+
770
+
626
+ context = g_option_context_new("vhost-user emulation of I2C device");
771
+ context = g_option_context_new("vhost-user emulation of I2C device");
627
+ g_option_context_add_main_entries(context, options, "vhost-user-i2c");
772
+ g_option_context_add_main_entries(context, options, "vhost-user-i2c");
628
+ if (!g_option_context_parse(context, &argc, &argv, &error))
773
+ if (!g_option_context_parse(context, &argc, &argv, &error)) {
629
+ {
630
+ g_printerr("option parsing failed: %s\n", error->message);
774
+ g_printerr("option parsing failed: %s\n", error->message);
631
+ exit(1);
775
+ exit(1);
632
+ }
776
+ }
633
+
777
+
634
+ if (print_cap) {
778
+ if (print_cap) {
...
...
643
+
787
+
644
+ if (verbose) {
788
+ if (verbose) {
645
+ g_log_set_handler(NULL, G_LOG_LEVEL_MASK, g_log_default_handler, NULL);
789
+ g_log_set_handler(NULL, G_LOG_LEVEL_MASK, g_log_default_handler, NULL);
646
+ g_setenv("G_MESSAGES_DEBUG", "all", true);
790
+ g_setenv("G_MESSAGES_DEBUG", "all", true);
647
+ } else {
791
+ } else {
648
+ g_log_set_handler(NULL,
792
+ g_log_set_handler(NULL, G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL |
649
+ G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR,
793
+ G_LOG_LEVEL_ERROR, g_log_default_handler, NULL);
650
+ g_log_default_handler, NULL);
651
+ }
794
+ }
652
+
795
+
653
+ /*
796
+ /*
654
+ * Now create a vhost-user socket that we will receive messages
797
+ * Now create a vhost-user socket that we will receive messages
655
+ * on. Once we have our handler set up we can enter the glib main
798
+ * on. Once we have our handler set up we can enter the glib main
656
+ * loop.
799
+ * loop.
657
+ */
800
+ */
658
+ if (socket_path) {
801
+ if (socket_path) {
659
+ g_autoptr(GSocketAddress) addr = g_unix_socket_address_new(socket_path);
802
+ g_autoptr(GSocketAddress) addr = g_unix_socket_address_new(socket_path);
660
+ g_autoptr(GSocket) bind_socket = g_socket_new(G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM,
803
+ g_autoptr(GSocket) bind_socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
661
+ G_SOCKET_PROTOCOL_DEFAULT, &error);
804
+ G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
662
+
805
+
663
+ if (!g_socket_bind(bind_socket, addr, false, &error)) {
806
+ if (!g_socket_bind(bind_socket, addr, false, &error)) {
664
+ g_printerr("Failed to bind to socket at %s (%s).\n",
807
+ g_printerr("Failed to bind to socket at %s (%s).\n",
665
+ socket_path, error->message);
808
+ socket_path, error->message);
666
+ exit(EXIT_FAILURE);
809
+ exit(EXIT_FAILURE);
...
...
diff view generated by jsdifflib
...
...
34
+-----------
34
+-----------
35
+
35
+
36
+This program is a vhost-user backend that emulates a VirtIO I2C bus.
36
+This program is a vhost-user backend that emulates a VirtIO I2C bus.
37
+This program takes the layout of the i2c bus and its devices on the host
37
+This program takes the layout of the i2c bus and its devices on the host
38
+OS and then talks to them via the /dev/i2c-X interface when a request
38
+OS and then talks to them via the /dev/i2c-X interface when a request
39
+comes from the guest OS for an I2C device.
39
+comes from the guest OS for an I2C or SMBUS device.
40
+
40
+
41
+This program is designed to work with QEMU's ``-device
41
+This program is designed to work with QEMU's ``-device
42
+vhost-user-i2c-pci`` but should work with any virtual machine monitor
42
+vhost-user-i2c-pci`` but should work with any virtual machine monitor
43
+(VMM) that supports vhost-user. See the Examples section below.
43
+(VMM) that supports vhost-user. See the Examples section below.
44
+
44
+
...
...
diff view generated by jsdifflib
...
...
7
7
8
diff --git a/MAINTAINERS b/MAINTAINERS
8
diff --git a/MAINTAINERS b/MAINTAINERS
9
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
10
--- a/MAINTAINERS
10
--- a/MAINTAINERS
11
+++ b/MAINTAINERS
11
+++ b/MAINTAINERS
12
@@ -XXX,XX +XXX,XX @@ F: hw/virtio/virtio-mem-pci.h
12
@@ -XXX,XX +XXX,XX @@ F: docs/interop/vhost-user-gpu.rst
13
F: hw/virtio/virtio-mem-pci.c
13
F: contrib/vhost-user-gpu
14
F: include/hw/virtio/virtio-mem.h
14
F: hw/display/vhost-user-*
15
15
16
+virtio-i2c
16
+vhost-user-i2c
17
+M: Viresh Kumar <viresh.kumar@linaro.org>
17
+M: Viresh Kumar <viresh.kumar@linaro.org>
18
+S: Supported
18
+S: Supported
19
+F: docs/tools/vhost-user-i2c.rst
19
+F: docs/tools/vhost-user-i2c.rst
20
+F: hw/virtio/vhost-user-i2c.c
20
+F: hw/virtio/vhost-user-i2c.c
21
+F: hw/virtio/vhost-user-i2c-pci.c
21
+F: hw/virtio/vhost-user-i2c-pci.c
22
+F: include/hw/virtio/vhost-user-i2c.h
22
+F: include/hw/virtio/vhost-user-i2c.h
23
+F: tools/vhost-user-i2c/*
23
+F: tools/vhost-user-i2c/*
24
+
24
+
25
nvme
25
Cirrus VGA
26
M: Keith Busch <kbusch@kernel.org>
26
M: Gerd Hoffmann <kraxel@redhat.com>
27
M: Klaus Jensen <its@irrelevant.dk>
27
S: Odd Fixes
28
--
28
--
29
2.25.0.rc1.19.g042ed3e048af
29
2.25.0.rc1.19.g042ed3e048af
30
30
31
31
diff view generated by jsdifflib