qemu-devel
[Top][All Lists]
Advanced

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

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


From: Markus Armbruster
Subject: Re: [Qemu-devel] [PATCH v3] chardev/char-i2c: Implement Linux I2C character device
Date: Mon, 13 May 2019 17:15:42 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux)

Ernest Esene <address@hidden> 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 <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

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 <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',

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},
> 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.

This sentence no verb :)

>  ETEXI
>  
>  STEXI



reply via email to

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