While vhost-user-gpio-pci is a good solution to pass gpios inside guest
it still doesn't cover the following cases:

- AFAIK the board needs to have a PCI bus;
- we add new hot-plugged gpios instead of re-using the existing ones;
- we can't connect it to existing gpio models;

This functionality is quite desired (numerous emails observed 
since i released https://github.com/maquefel/virtual_gpio_basic which 
is ivshmem based and have same disadvantages as above) - the ability 
to manipulate existing gpios, including MMIO mmaped like 
aspeed or bcm2835/bcm2838 gpios.

This becomes even more important for MCUs where virtio gpio application
is limited.

For example:

https://stackoverflow.com/questions/76834038/qemu-gpio-connect
https://stackoverflow.com/questions/60764018/how-to-connect-gpio-in-qemu-emulated-machine-to-an-object-in-host

And so on.

So the most obvious thing is to create something similar to existing
chardev, to provide common interface for models to interact with.

I propose new gpiodev, which consists of gpiodev itself, frontend
and backends just like chardev does.

It provides the abilities which mostly replicate the current
state of Linux GPIO UAPI, i.e.:

- getting GPIO chip/line info;
- monitoring line state change externally on rising/falling events;
- monitoring configuration change (output swith to input and vise-versa);
- setting/getting gpio pins;

The interface for GPIO Models is described in qemu/include/gpiodev/gpio-fe.h.

The model should provide callbacks for the following:

- LineInfoHandler to provide info aboud line specified by offset;
- LineGetValueHandler to get gpio line value specified by offset;
- LineGetValueHandler to set gpio line value specified by offset;

Also the model can notify the Gpiodev and connected back about:

- line value;
- line config;

changed by the guest witch 
qemu_gpio_fe_line_event()/qemu_gpio_fe_config_event().

A nice choice for demonstation purpose as heavily used and, maybe,
the most full board simulation in QEMU.

It seems to have some flaws in GPIO model hovewer (not sure if it's really a 
flaw through).

Andrew provided some clarification on Linux driver implementation, and
agreed that it might be a bug in QEMU model.

First write to pin has no effect to gpiomon/gpio-event-mon as direction is set 
AFTER pin value
which results in qemu_gpio_fe_line_event() not called in aspeed_gpio_update(),
so qemu_set_irq() won't be called for the same reason by the way.

```
aspeed # gpioset 0 8=1
aspeed_gpio_write offset: 0x1c value 0x100
aspeed_gpio_write offset: 0x0 value 0x100       <-- VALUE
aspeed_gpio_write offset: 0x4 value 0x100       <-- DIRECTION
```

Also ast2600 machine was modified to set DEVICE()->id for gpios, as ASPEED gpio 
relies
on this to connect to gpiodev.

A simple backend over any chadev transport, while this solution seems good at 
first glance,
it lacks solid protocol (currently used Linux GPIO UAPI structs) and I couldn't 
find any
suitable/standardized solution, something like MOXA uses for ioLogik devices 
for example.
So using this backend requires specials tools like qemu-gpio-tools (just Linux 
gpio tools
with some modification for demo purpose).

I someone can propose a ready protocol or an idea please do so.

Some demo interacting with ASPEED:

```
host $ build-qemu/qemu-system-arm -M ast2600-evb,bmc-console=uart5 -kernel 
buildroot/output/build/linux-6.6.14/arch/arm/boot/zImage \
-dtb 
buildroot/output/build/linux-6.6.14/arch/arm/boot/dts/aspeed/aspeed-ast2600-evb.dtb
 \
-initrd buildroot/output/images/rootfs.cpio -nographic -serial mon:stdio \
-gpiodev chardev,id=aspeed-gpio0,chardev=gpio0 -chardev 
socket,path=/tmp/gpio0,id=gpio0,server=on,wait=off
```

```
host $ qemu-gpio-tools/lsgpio -n /tmp/gpio0
sending 0x8044b401
GPIO chip: aspeed-gpio0, "ASPEED GPIO", 208 GPIO lines
        line  0: "gpioA0" unused [input]
        line  1: "gpioA1" unused [input]
        line  2: "gpioA2" unused [input]
        line  3: "gpioA3" unused [input]
        line  4: "gpioA4" unused [input]
        line  5: "gpioA5" unused [input]
        line  6: "gpioA6" unused [input]
        line  7: "gpioA7" unused [input]
        line  8: "gpioB0" unused [input]
[...]
        line 200: "gpioZ0" unused [input]
        line 201: "gpioZ1" unused [input]
        line 202: "gpioZ2" unused [input]
        line 203: "gpioZ3" unused [input]
        line 204: "gpioZ4" unused [input]
        line 205: "gpioZ5" unused [input]
        line 206: "gpioZ6" unused [input]
        line 207: "gpioZ7" unused [input]
```

I did some experiments with CUSE, but it lacks the most important functionality,
creating new file descriptors (creating new file could also do). While it's 
quite simple
to implement something like sysfs GPIO i don't think it's a good solution 
(hehehehehe, Bartosz and Linus i hope you appreciate this),
so i decided to make something similiar to CUSE.

The price is high:

- a kernel module (also kernel modifitions are also required to make it pretty 
- export fuse_mknod function for example);
- new guse bindings for libfuse;
- libgpiod modification (a small one to allow "/sys/class/guse" in 
gpiod_check_gpiochip_device());
- and the QEMU of course;

But i was obsessed with the idea of using the same tools like in Linux 
(libgpiod for example ;)),
and still it was a fun thing to do.

With this we can have a FULL support of Linux GPIO UAPI.

Module ony test with 6.11.*, v6.13.*+ (pages replaced with folio) under KSAN, 
kmemleak.

```
$ sudo insmod guse/guse.ko
```

```
sudo build-qemu/qemu-system-arm -M ast2600-evb,bmc-console=uart5 -kernel 
buildroot/output/build/linux-6.6.14/arch/arm/boot/zImage \
-dtb 
buildroot/output/build/linux-6.6.14/arch/arm/boot/dts/aspeed/aspeed-ast2600-evb.dtb
 \
-initrd buildroot/output/images/rootfs.cpio -nographic -serial mon:stdio 
-gpiodev guse,id=aspeed-gpio0,devname=gpiochip10
```

```
aspeed # gpioset 0 8=1
aspeed # gpioget 0 8
```

```
host $ sudo libgpiod/tools/gpionotify -c 10 8
1741686422.149345616    reconfigured    aspeed-gpio0 8
1741686430.466103054    reconfigured    aspeed-gpio0 8
```

```
aspeed # gpioset 0 8=1
aspeed # gpioset 0 8=0
```

```
$ sudo libgpiod/tools/gpiomon -c 10 8
1741687220.025006346    rising  aspeed-gpio0 8
1741687222.634353601    falling aspeed-gpio0 8
```

gpio-sim also can serve as a backend, but the only thing we can get from
it is monitoring lines set by QEMU guest. So i didn't bother
implementing it yet.

I prepare a suite with whing nessary if someone wants to test/tinker
with current series:

Link: git://git.maquefel.me/qemu-gpiodev/qemu-guse-suite.git

Signed-off-by: Nikita Shubin <n.shu...@yadro.com>
---
Nikita Shubin (7):
      QAPI: gpio JSON
      Add gpiodev dummy
      gpiodev: Add GPIO device frontend
      gpiodev: Add GPIO backend over chardev
      hw/gpio/aspeed: Add gpiodev support
      hw/arm: ast2600: set id for gpio
      gpiodev: Add gpiobackend over GUSE

 gpiodev/gpio-chardev.c        | 479 +++++++++++++++++++++++++++
 gpiodev/gpio-fe.c             | 103 ++++++
 gpiodev/gpio-guse.c           | 747 ++++++++++++++++++++++++++++++++++++++++++
 gpiodev/gpio.c                | 399 ++++++++++++++++++++++
 gpiodev/meson.build           |   8 +
 hw/arm/aspeed_ast2600.c       |   2 +
 hw/gpio/aspeed_gpio.c         | 127 +++++++
 include/gpiodev/gpio-fe.h     | 122 +++++++
 include/gpiodev/gpio.h        | 233 +++++++++++++
 include/hw/gpio/aspeed_gpio.h |   3 +
 meson.build                   |  11 +-
 qapi/gpio.json                |  93 ++++++
 qapi/meson.build              |   1 +
 qapi/qapi-schema.json         |   1 +
 qemu-options.hx               |   9 +
 qom/object.c                  |   1 +
 system/vl.c                   |  25 ++
 17 files changed, 2363 insertions(+), 1 deletion(-)
---
base-commit: 98c7362b1efe651327385a25874a73e008c6549e
change-id: 20250313-gpiodev-d24e6d9be90d

Best regards,
-- 
Nikita Shubin <n.shu...@yadro.com>



Reply via email to