[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