diff mbox series

[v3] chardev/char-i2c: Implement Linux I2C character device

Message ID 20190510180410.GA10349@erokenlabserver (mailing list archive)
State New, archived
Headers show
Series [v3] chardev/char-i2c: Implement Linux I2C character device | expand

Commit Message

Ernest Esene May 10, 2019, 6:04 p.m. UTC
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 <eroken1@gmail.com>
---
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

Comments

Markus Armbruster May 13, 2019, 3:15 p.m. UTC | #1
Ernest Esene <eroken1@gmail.com> writes:

> 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 <eroken1@gmail.com>
> ---
> 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 <samuel.thibault@ens-lyon.org>
>  S: Maintained
>  F: chardev/baum.c
>  
> +Character Devices (I2C)
> +M: Ernest Esene <eroken1@gmail.com>
> +S: Maintained
> +F: chardev/char-linux-i2c.c
> +
>  Command line option argument parsing
>  M: Markus Armbruster <armbru@redhat.com>
>  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

Space after +=, please.

>  
>  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 <eroken1@gmail.com>
> + *
> + * 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',

Shouldn't this be 'if': 'defined(CONFIG_LINUX)'?

>              '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},
> -@option{spiceport}.
> +@option{spiceport},
> +@option{i2c}.
>  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).
> +
> +@item -chardev i2c,id=@var{id},address=@var{address},path=@var{path}
> +
> +@option{path} i2c character device (Eg: /dev/i2c-N on Linux)
> +
> +@option{address} address of the slave device.
> +
> +I2C device support as a character device.

This sentence no verb :)

>  ETEXI
>  
>  STEXI
Stefan Hajnoczi May 15, 2019, 2:17 p.m. UTC | #2
On Fri, May 10, 2019 at 07:04:10PM +0100, Ernest Esene wrote:
> 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 <eroken1@gmail.com>

How is -chardev i2c meant to be used?  Do you have code to connect this
new chardev type to an emulated I2C bus?

Stefan
Ernest Esene May 16, 2019, 1:49 p.m. UTC | #3
On Wed, May 15, 2019 at 03:17:12PM +0100, Stefan Hajnoczi wrote:
> On Fri, May 10, 2019 at 07:04:10PM +0100, Ernest Esene wrote:
> > 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 <eroken1@gmail.com>
> 
> How is -chardev i2c meant to be used?  Do you have code to connect this
> new chardev type to an emulated I2C bus?
It is meant to be connected to emulated I2C bus as you've stated, but I
don't have the code yet.
> 
> Stefan
Stefan Hajnoczi May 17, 2019, 9:15 a.m. UTC | #4
On Thu, May 16, 2019 at 02:49:45PM +0100, Ernest Esene wrote:
> On Wed, May 15, 2019 at 03:17:12PM +0100, Stefan Hajnoczi wrote:
> > On Fri, May 10, 2019 at 07:04:10PM +0100, Ernest Esene wrote:
> > > 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 <eroken1@gmail.com>
> > 
> > How is -chardev i2c meant to be used?  Do you have code to connect this
> > new chardev type to an emulated I2C bus?
> It is meant to be connected to emulated I2C bus as you've stated, but I
> don't have the code yet.

I'd like to see that code first, especially if it uses the chardev ioctl
to perform operations other than just read/write.  That could influence
the design of chardev-i2c.

Please keep this patch out-of-tree unless someone has an immediate use
for it.  If it gets merged too early (especially if a QEMU release is
made), then it's difficult to change the command-line interface if
changes are required to make it work with the I2C emulation.

Stefan
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 66ddbda9c9..d834a12241 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1801,6 +1801,11 @@  M: Samuel Thibault <samuel.thibault@ens-lyon.org>
 S: Maintained
 F: chardev/baum.c
 
+Character Devices (I2C)
+M: Ernest Esene <eroken1@gmail.com>
+S: Maintained
+F: chardev/char-linux-i2c.c
+
 Command line option argument parsing
 M: Markus Armbruster <armbru@redhat.com>
 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 <eroken1@gmail.com>
+ *
+ * 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},
-@option{spiceport}.
+@option{spiceport},
+@option{i2c}.
 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).
+
+@item -chardev i2c,id=@var{id},address=@var{address},path=@var{path}
+
+@option{path} i2c character device (Eg: /dev/i2c-N on Linux)
+
+@option{address} address of the slave device.
+
+I2C device support as a character device.
 ETEXI
 
 STEXI