qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v3] chardev/char-i2c: Implement Linux I2C character


From: Ernest Esene
Subject: [Qemu-devel] [PATCH v3] chardev/char-i2c: Implement Linux I2C character device
Date: Fri, 10 May 2019 19:04:10 +0100
User-agent: Mutt/1.11.4 (2019-03-13)

Add support for Linux I2C character device for I2C device passthrough
For example:
-chardev i2c,address=0x46,path=/dev/i2c-N,id=i2c-chardev

QEMU supports emulation of I2C devices in software but currently can't
passthrough to real I2C devices. This feature is needed by developers
using QEMU for writing and testing software for I2C devices.

Signed-off-by: Ernest Esene <address@hidden>
---
v3:
  * change licence to GPLv2+
  * use non blocking IO for the chardev
  * change "address" to QEMU_OPT_NUMBER
  * update qemu-options.hx
---
v2:
  * Fixed errors
  * update "MAINTAINERS" file.
---
 MAINTAINERS              |   5 ++
 chardev/Makefile.objs    |   1 +
 chardev/char-linux-i2c.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++
 chardev/char.c           |   3 ++
 include/chardev/char.h   |   1 +
 qapi/char.json           |  17 +++++++
 qemu-options.hx          |  14 +++++-
 7 files changed, 166 insertions(+), 1 deletion(-)
 create mode 100644 chardev/char-linux-i2c.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 66ddbda9c9..d834a12241 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1801,6 +1801,11 @@ M: Samuel Thibault <address@hidden>
 S: Maintained
 F: chardev/baum.c
 
+Character Devices (I2C)
+M: Ernest Esene <address@hidden>
+S: Maintained
+F: chardev/char-linux-i2c.c
+
 Command line option argument parsing
 M: Markus Armbruster <address@hidden>
 S: Supported
diff --git a/chardev/Makefile.objs b/chardev/Makefile.objs
index d68e1347f9..7b64009aa6 100644
--- a/chardev/Makefile.objs
+++ b/chardev/Makefile.objs
@@ -16,6 +16,7 @@ chardev-obj-y += char-stdio.o
 chardev-obj-y += char-udp.o
 chardev-obj-$(CONFIG_WIN32) += char-win.o
 chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o
+chardev-obj-$(CONFIG_LINUX) +=char-linux-i2c.o
 
 common-obj-y += msmouse.o wctablet.o testdev.o
 common-obj-$(CONFIG_BRLAPI) += baum.o
diff --git a/chardev/char-linux-i2c.c b/chardev/char-linux-i2c.c
new file mode 100644
index 0000000000..847a18f611
--- /dev/null
+++ b/chardev/char-linux-i2c.c
@@ -0,0 +1,126 @@
+/*
+ * QEMU System Emulator
+ * Linux I2C device support as a character device.
+ *
+ * Copyright (c) 2019 Ernest Esene <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later.  See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/option.h"
+#include "qemu-common.h"
+#include "io/channel-file.h"
+#include "qemu/cutils.h"
+#include "qemu/sockets.h"
+
+#include "chardev/char-fd.h"
+#include "chardev/char.h"
+
+#include <sys/ioctl.h>
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
+
+#define CHR_IOCTL_I2C_SET_ADDR 1
+
+#define CHR_I2C_ADDR_10BIT_MAX 1023
+#define CHR_I2C_ADDR_7BIT_MAX 127
+
+static int i2c_ioctl(Chardev *chr, int cmd, void *arg)
+{
+    FDChardev *fd_chr = FD_CHARDEV(chr);
+    QIOChannelFile *floc = QIO_CHANNEL_FILE(fd_chr->ioc_in);
+    int fd = floc->fd;
+    int addr;
+    unsigned long funcs;
+
+    switch (cmd) {
+    case CHR_IOCTL_I2C_SET_ADDR:
+        addr = (intptr_t)arg;
+
+        if (addr > CHR_I2C_ADDR_7BIT_MAX) {
+            if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
+                goto err;
+            }
+            if (!(funcs & I2C_FUNC_10BIT_ADDR)) {
+                goto err;
+            }
+            if (ioctl(fd, I2C_TENBIT, addr) < 0) {
+                goto err;
+            }
+        } else {
+            if (ioctl(fd, I2C_SLAVE, addr) < 0) {
+                goto err;
+            }
+        }
+        break;
+
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+err:
+    return -ENOTSUP;
+}
+
+static void qmp_chardev_open_i2c(Chardev *chr, ChardevBackend *backend,
+                                 bool *be_opened, Error **errp)
+{
+    ChardevI2c *i2c = backend->u.i2c.data;
+    void *addr;
+    int fd;
+
+    fd = qmp_chardev_open_file_source(i2c->device, O_RDWR | O_NONBLOCK, errp);
+    if (fd < 0) {
+        return;
+    }
+    qemu_set_nonblock(fd);
+    qemu_chr_open_fd(chr, fd, fd);
+    addr = (void *)(intptr_t)i2c->address;
+    i2c_ioctl(chr, CHR_IOCTL_I2C_SET_ADDR, addr);
+}
+
+static void qemu_chr_parse_i2c(QemuOpts *opts, ChardevBackend *backend,
+                               Error **errp)
+{
+    const char *device = qemu_opt_get(opts, "path");
+    long address = qemu_opt_get_number(opts, "address", LONG_MAX);
+    ChardevI2c *i2c;
+
+    if (device == NULL) {
+        error_setg(errp, "chardev: i2c: no device path given");
+        return;
+    }
+    if (address < 0 || address > CHR_I2C_ADDR_10BIT_MAX) {
+        error_setg(errp, "chardev: i2c: device address out of range");
+        return;
+    }
+    backend->type = CHARDEV_BACKEND_KIND_I2C;
+    i2c = backend->u.i2c.data = g_new0(ChardevI2c, 1);
+    qemu_chr_parse_common(opts, qapi_ChardevI2c_base(i2c));
+    i2c->device = g_strdup(device);
+    i2c->address = (int16_t)address;
+}
+
+static void char_i2c_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_i2c;
+    cc->open = qmp_chardev_open_i2c;
+    cc->chr_ioctl = i2c_ioctl;
+}
+
+static const TypeInfo char_i2c_type_info = {
+    .name = TYPE_CHARDEV_I2C,
+    .parent = TYPE_CHARDEV_FD,
+    .class_init = char_i2c_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&char_i2c_type_info);
+}
+
+type_init(register_types);
diff --git a/chardev/char.c b/chardev/char.c
index 54724a56b1..8f5ffe16e6 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -926,6 +926,9 @@ QemuOptsList qemu_chardev_opts = {
         },{
             .name = "logappend",
             .type = QEMU_OPT_BOOL,
+        },{
+            .name = "address",
+            .type = QEMU_OPT_NUMBER,
         },
         { /* end of list */ }
     },
diff --git a/include/chardev/char.h b/include/chardev/char.h
index c0b57f7685..0e08b70fc9 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -245,6 +245,7 @@ int qemu_chr_wait_connected(Chardev *chr, Error **errp);
 #define TYPE_CHARDEV_SERIAL "chardev-serial"
 #define TYPE_CHARDEV_SOCKET "chardev-socket"
 #define TYPE_CHARDEV_UDP "chardev-udp"
+#define TYPE_CHARDEV_I2C "chardev-i2c"
 
 #define CHARDEV_IS_RINGBUF(chr) \
     object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_RINGBUF)
diff --git a/qapi/char.json b/qapi/char.json
index a6e81ac7bc..7168b58cfe 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -240,6 +240,22 @@
   'data': { 'device': 'str' },
   'base': 'ChardevCommon' }
 
+##
+# @ChardevI2c:
+#
+# Configuration info for i2c chardev.
+#
+# @device: The name of the special file for the device,
+#          i.e. /dev/i2c-0 on linux
+# @address: The address of the i2c device on the host.
+#
+# Since: 4.1
+##
+{ 'struct': 'ChardevI2c',
+  'data': { 'device': 'str',
+            'address': 'int16'},
+  'base': 'ChardevCommon' }
+
 ##
 # @ChardevSocket:
 #
@@ -398,6 +414,7 @@
   'data': { 'file': 'ChardevFile',
             'serial': 'ChardevHostdev',
             'parallel': 'ChardevHostdev',
+            'i2c': 'ChardevI2c',
             'pipe': 'ChardevHostdev',
             'socket': 'ChardevSocket',
             'udp': 'ChardevUdp',
diff --git a/qemu-options.hx b/qemu-options.hx
index 51802cbb26..435b6975dd 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2695,6 +2695,9 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
 #if defined(CONFIG_SPICE)
     "-chardev 
spicevmc,id=id,name=name[,debug=debug][,logfile=PATH][,logappend=on|off]\n"
     "-chardev 
spiceport,id=id,name=name[,debug=debug][,logfile=PATH][,logappend=on|off]\n"
+#endif
+#ifdef CONFIG_LINUX
+    "-chardev 
i2c,id=id,address=address[,path=path][,logfile=PATH][,logappend=on|off]\n"
 #endif
     , QEMU_ARCH_ALL
 )
@@ -2723,7 +2726,8 @@ Backend is one of:
 @option{parallel},
 @option{parport},
 @option{spicevmc},
address@hidden
address@hidden,
address@hidden
 The specific backend will determine the applicable options.
 
 Use @code{-chardev help} to print all available chardev backend types.
@@ -2990,6 +2994,14 @@ Connect to a spice virtual machine channel, such as 
vdiport.
 
 Connect to a spice port, allowing a Spice client to handle the traffic
 identified by a name (preferably a fqdn).
+
address@hidden -chardev i2c,address@hidden,address@hidden,address@hidden
+
address@hidden i2c character device (Eg: /dev/i2c-N on Linux)
+
address@hidden address of the slave device.
+
+I2C device support as a character device.
 ETEXI
 
 STEXI
-- 
2.14.2

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]