Hi all,
GPIO controllers are exported to userspace using /dev/gpiochip*
character devices. Access control to these devices is provided by
standard UNIX file system permissions, on an all-or-nothing basis:
either a GPIO controller is accessible for a user, or it is not.
Currently no mechanism exists to control access to individual GPIOs.
Hence this adds a GPIO driver to aggregate existing GPIOs, and expose
them as a new gpiochip. This is useful for implementing access control,
and assigning a set of GPIOs to a specific user. Furthermore, this
simplifies and hardens exporting GPIOs to a virtual machine, as the VM
can just grab the full GPIO controller, and no longer needs to care
about which GPIOs to grab and which not, reducing the attack surface.
Recently, other use cases have been discovered[1]:
- Describing simple GPIO-operated devices in DT, and using the GPIO
Aggregator as a generic GPIO driver for userspace, which is useful
for industrial control.
Changes compared to v4[2]:
- Add Reviewed-by, Tested-by,
- Fix inconsistent indentation in documentation.
Changes compared to v3[3] (more details in the individual patches):
- Drop controversial GPIO repeater,
- Drop support for legacy sysfs interface based name matching,
- Drop applied "gpiolib: Add GPIOCHIP_NAME definition",
- Documentation improvements,
- Lots of small cleanups.
Changes compared to v2[4] (more details in the individual patches):
- Integrate GPIO Repeater functionality,
- Absorb GPIO forwarder library, as the Aggregator and Repeater are
now a single driver,
- Use the aggregator parameters to create a GPIO lookup table instead
of an array of GPIO descriptors,
- Add documentation,
- New patches:
- "gpiolib: Add GPIOCHIP_NAME definition",
- "gpiolib: Add support for gpiochipN-based table lookup",
- "gpiolib: Add support for GPIO line table lookup",
- "dt-bindings: gpio: Add gpio-repeater bindings",
- "docs: gpio: Add GPIO Aggregator/Repeater documentation",
- "MAINTAINERS: Add GPIO Aggregator/Repeater section".
- Dropped patches:
- "gpio: Export gpiod_{request,free}() to modular GPIO code",
- "gpio: Export gpiochip_get_desc() to modular GPIO code",
- "gpio: Export gpio_name_to_desc() to modular GPIO code",
- "gpio: Add GPIO Forwarder Helper".
Changes compared to v1[5]:
- Drop "virtual", rename to gpio-aggregator,
- Create and use new GPIO Forwarder Helper, to allow sharing code with
the GPIO inverter,
- Lift limit on the maximum number of GPIOs,
- Improve parsing of GPIO specifiers,
- Fix modular build.
Aggregating GPIOs and exposing them as a new gpiochip was suggested in
response to my proof-of-concept for GPIO virtualization with QEMU[6][7].
For the first use case, aggregated GPIO controllers are instantiated and
destroyed by writing to atribute files in sysfs.
Sample session on the Renesas Koelsch development board:
- Unbind LEDs from leds-gpio driver:
echo leds > /sys/bus/platform/drivers/leds-gpio/unbind
- Create aggregators:
$ echo e6052000.gpio 19,20 \
> /sys/bus/platform/drivers/gpio-aggregator/new_device
gpio-aggregator gpio-aggregator.0: gpio 0 => gpio-953 (gpio-aggregator.0)
gpio-aggregator gpio-aggregator.0: gpio 1 => gpio-954 (gpio-aggregator.0)
gpiochip_find_base: found new base at 778
gpio gpiochip8: (gpio-aggregator.0): added GPIO chardev (254:8)
gpiochip_setup_dev: registered GPIOs 778 to 779 on device: gpiochip8 (gpio-aggregator.0)
$ echo e6052000.gpio 21 e6050000.gpio 20-22 \
> /sys/bus/platform/drivers/gpio-aggregator/new_device
gpio-aggregator gpio-aggregator.1: gpio 0 => gpio-955 (gpio-aggregator.1)
gpio-aggregator gpio-aggregator.1: gpio 1 => gpio-1012 (gpio-aggregator.1)
gpio-aggregator gpio-aggregator.1: gpio 2 => gpio-1013 (gpio-aggregator.1)
gpio-aggregator gpio-aggregator.1: gpio 3 => gpio-1014 (gpio-aggregator.1)
gpiochip_find_base: found new base at 774
gpio gpiochip9: (gpio-aggregator.1): added GPIO chardev (254:9)
gpiochip_setup_dev: registered GPIOs 774 to 777 on device: gpiochip9 (gpio-aggregator.1)
- Adjust permissions on /dev/gpiochip[89] (optional)
- Control LEDs:
$ gpioset gpiochip8 0=0 1=1 # LED6 OFF, LED7 ON
$ gpioset gpiochip8 0=1 1=0 # LED6 ON, LED7 OFF
$ gpioset gpiochip9 0=0 # LED8 OFF
$ gpioset gpiochip9 0=1 # LED8 ON
- Destroy aggregators:
$ echo gpio-aggregator.0 \
> /sys/bus/platform/drivers/gpio-aggregator/delete_device
$ echo gpio-aggregator.1 \
> /sys/bus/platform/drivers/gpio-aggregator/delete_device
Thanks!
References:
[1] "[PATCH V4 2/2] gpio: inverter: document the inverter bindings"
(https://lore.kernel.org/r/[email protected]/)
[2] "[PATCH v4 0/5] gpio: Add GPIO Aggregator"
(https://lore.kernel.org/r/[email protected])
[3] "[PATCH v3 0/7] gpio: Add GPIO Aggregator/Repeater"
(https://lore.kernel.org/r/[email protected]/)
[4] "[PATCH/RFC v2 0/5] gpio: Add GPIO Aggregator Driver"
(https://lore.kernel.org/r/[email protected]/)
[5] "[PATCH RFC] gpio: Add Virtual Aggregator GPIO Driver"
(https://lore.kernel.org/r/[email protected]/)
[6] "[PATCH QEMU POC] Add a GPIO backend"
(https://lore.kernel.org/r/[email protected]/)
[7] "Getting To Blinky: Virt Edition / Making device pass-through
work on embedded ARM"
(https://fosdem.org/2019/schedule/event/vai_getting_to_blinky/)
Geert Uytterhoeven (5):
gpiolib: Add support for gpiochipN-based table lookup
gpiolib: Add support for GPIO line table lookup
gpio: Add GPIO Aggregator
docs: gpio: Add GPIO Aggregator documentation
MAINTAINERS: Add GPIO Aggregator section
.../admin-guide/gpio/gpio-aggregator.rst | 102 ++++
Documentation/admin-guide/gpio/index.rst | 1 +
MAINTAINERS | 7 +
drivers/gpio/Kconfig | 12 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-aggregator.c | 574 ++++++++++++++++++
drivers/gpio/gpiolib.c | 33 +-
include/linux/gpio/machine.h | 15 +-
8 files changed, 732 insertions(+), 13 deletions(-)
create mode 100644 Documentation/admin-guide/gpio/gpio-aggregator.rst
create mode 100644 drivers/gpio/gpio-aggregator.c
--
2.17.1
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Document the GPIO Aggregator, and the two typical use-cases.
Signed-off-by: Geert Uytterhoeven <[email protected]>
Reviewed-by: Ulrich Hecht <[email protected]>
Reviewed-by: Eugeniu Rosca <[email protected]>
Tested-by: Eugeniu Rosca <[email protected]>
---
v5:
- Add Reviewed-by, Tested-by,
- Fix inconsistent indentation.
v4:
- Add Reviewed-by,
- Drop controversial GPIO repeater,
- Clarify industrial control use case,
- Fix typo s/communicated/communicate/,
- Replace abstract frobnicator example by concrete door example with
gpio-line-names,
v3:
- New.
---
.../admin-guide/gpio/gpio-aggregator.rst | 102 ++++++++++++++++++
Documentation/admin-guide/gpio/index.rst | 1 +
2 files changed, 103 insertions(+)
create mode 100644 Documentation/admin-guide/gpio/gpio-aggregator.rst
diff --git a/Documentation/admin-guide/gpio/gpio-aggregator.rst b/Documentation/admin-guide/gpio/gpio-aggregator.rst
new file mode 100644
index 0000000000000000..114f72be33c2571e
--- /dev/null
+++ b/Documentation/admin-guide/gpio/gpio-aggregator.rst
@@ -0,0 +1,102 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+GPIO Aggregator
+===============
+
+The GPIO Aggregator allows to aggregate GPIOs, and expose them as a new
+gpio_chip. This supports the following use cases.
+
+
+Aggregating GPIOs using Sysfs
+-----------------------------
+
+GPIO controllers are exported to userspace using /dev/gpiochip* character
+devices. Access control to these devices is provided by standard UNIX file
+system permissions, on an all-or-nothing basis: either a GPIO controller is
+accessible for a user, or it is not.
+
+The GPIO Aggregator allows access control for individual GPIOs, by aggregating
+them into a new gpio_chip, which can be assigned to a group or user using
+standard UNIX file ownership and permissions. Furthermore, this simplifies and
+hardens exporting GPIOs to a virtual machine, as the VM can just grab the full
+GPIO controller, and no longer needs to care about which GPIOs to grab and
+which not, reducing the attack surface.
+
+Aggregated GPIO controllers are instantiated and destroyed by writing to
+write-only attribute files in sysfs.
+
+ /sys/bus/platform/drivers/gpio-aggregator/
+
+ "new_device" ...
+ Userspace may ask the kernel to instantiate an aggregated GPIO
+ controller by writing a string describing the GPIOs to
+ aggregate to the "new_device" file, using the format
+
+ .. code-block:: none
+
+ [<gpioA>] [<gpiochipB> <offsets>] ...
+
+ Where:
+
+ "<gpioA>" ...
+ is a GPIO line name,
+
+ "<gpiochipB>" ...
+ is a GPIO chip label or name, and
+
+ "<offsets>" ...
+ is a comma-separated list of GPIO offsets and/or
+ GPIO offset ranges denoted by dashes.
+
+ Example: Instantiate a new GPIO aggregator by aggregating GPIO
+ 19 of "e6052000.gpio" and GPIOs 20-21 of "gpiochip2" into a new
+ gpio_chip:
+
+ .. code-block:: bash
+
+ echo 'e6052000.gpio 19 gpiochip2 20-21' > new_device
+
+ "delete_device" ...
+ Userspace may ask the kernel to destroy an aggregated GPIO
+ controller after use by writing its device name to the
+ "delete_device" file.
+
+ Example: Destroy the previously-created aggregated GPIO
+ controller "gpio-aggregator.0":
+
+ .. code-block:: bash
+
+ echo gpio-aggregator.0 > delete_device
+
+
+Generic GPIO Driver
+-------------------
+
+The GPIO Aggregator can also be used as a generic driver for a simple
+GPIO-operated device described in DT, without a dedicated in-kernel driver.
+This is useful in industrial control, and is not unlike e.g. spidev, which
+allows to communicate with an SPI device from userspace.
+
+Binding a device to the GPIO Aggregator is performed either by modifying the
+gpio-aggregator driver, or by writing to the "driver_override" file in Sysfs.
+
+Example: If "door" is a GPIO-operated device described in DT, using its own
+compatible value::
+
+ door {
+ compatible = "myvendor,mydoor";
+
+ gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>,
+ <&gpio2 20 GPIO_ACTIVE_LOW>;
+ gpio-line-names = "open", "lock";
+ };
+
+it can be bound to the GPIO Aggregator by either:
+
+1. Adding its compatible value to ``gpio_aggregator_dt_ids[]``,
+2. Binding manually using "driver_override":
+
+.. code-block:: bash
+
+ echo gpio-aggregator > /sys/bus/platform/devices/door/driver_override
+ echo door > /sys/bus/platform/drivers/gpio-aggregator/bind
diff --git a/Documentation/admin-guide/gpio/index.rst b/Documentation/admin-guide/gpio/index.rst
index a244ba4e87d5398a..ef2838638e967777 100644
--- a/Documentation/admin-guide/gpio/index.rst
+++ b/Documentation/admin-guide/gpio/index.rst
@@ -7,6 +7,7 @@ gpio
.. toctree::
:maxdepth: 1
+ gpio-aggregator
sysfs
.. only:: subproject and html
--
2.17.1
Currently GPIO controllers can only be referred to by label in GPIO
lookup tables.
Add support for looking them up by "gpiochipN" name, with "N" the
corresponding GPIO device's ID number.
Signed-off-by: Geert Uytterhoeven <[email protected]>
Reviewed-by: Ulrich Hecht <[email protected]>
Reviewed-by: Eugeniu Rosca <[email protected]>
Tested-by: Eugeniu Rosca <[email protected]>
---
v5:
- Add Reviewed-by, Tested-by,
v4:
- Add Reviewed-by,
- Drop support for legacy sysfs interface based name matching,
- Replace complex custom matching by a simple additional check in the
existing gpiochip_match_name() function,
- Add kerneldoc() for find_chip_by_name(), documenting matching order.
v3:
- New.
---
drivers/gpio/gpiolib.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4d0106ceeba7bb24..200c2d2be4b78043 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1738,9 +1738,18 @@ static int gpiochip_match_name(struct gpio_chip *chip, void *data)
{
const char *name = data;
- return !strcmp(chip->label, name);
+ return !strcmp(chip->label, name) ||
+ !strcmp(dev_name(&chip->gpiodev->dev), name);
}
+/**
+ * find_chip_by_name() - Find a specific gpio_chip by name
+ * @name: Name to match
+ *
+ * Return a reference to a gpio_chip that matches the passed name.
+ * This function first tries matching on the gpio_chip's label, followed by
+ * matching on dev_name() of the corresponding gpio_device.
+ */
static struct gpio_chip *find_chip_by_name(const char *name)
{
return gpiochip_find((void *)name, gpiochip_match_name);
--
2.17.1
GPIO controllers are exported to userspace using /dev/gpiochip*
character devices. Access control to these devices is provided by
standard UNIX file system permissions, on an all-or-nothing basis:
either a GPIO controller is accessible for a user, or it is not.
Currently no mechanism exists to control access to individual GPIOs.
Hence add a GPIO driver to aggregate existing GPIOs, and expose them as
a new gpiochip.
This supports the following use cases:
- Aggregating GPIOs using Sysfs
This is useful for implementing access control, and assigning a set
of GPIOs to a specific user or virtual machine.
- Generic GPIO Driver
This is useful for industrial control, where it can provide
userspace access to a simple GPIO-operated device described in DT,
cfr. e.g. spidev for SPI-operated devices.
Signed-off-by: Geert Uytterhoeven <[email protected]>
Reviewed-by: Eugeniu Rosca <[email protected]>
Tested-by: Eugeniu Rosca <[email protected]>
---
v5:
- Add Reviewed-by, Tested-by,
v4:
- Remove unused assignment to n in isrange(),
- Check correct pointer after aggr->lookups->dev_id allocation,
- Preinitialize flags to 0 in gpio_fwd_[gs]et_multiple() to avoid
may-be-used-uninitialized warning,
- Drop controversial GPIO repeater,
- Update for gpiod_lookup.chip_label rename,
- Use %pe to format error pointers,
- Use U16_MAX instead of (u16)-1,
- Correct comment indentation,
- Use skip_spaces() helper,
- Rename a and b to first_index resp. last_index,
- Add comment to tmp[] use,
- Improve Kconfig help text,
- Include <linux/gpio/consumer.h> for gpiod_[gs]et_*(),
- Drop unneeded valid_mask handling,
- Add comment about sleeping and .set_config() support,
v3:
- Absorb GPIO forwarder,
- Integrate GPIO Repeater and Generic GPIO driver functionality,
- Use the aggregator parameters to create a GPIO lookup table instead
of an array of GPIO descriptors, which allows to simplify the code:
1. This removes the need for calling gpio_name_to_desc(),
gpiochip_find(), gpiochip_get_desc(), and gpiod_request(),
2. This allows the platform device to always use
devm_gpiod_get_index(), regardless of the origin of the GPIOs,
- Move parameter parsing from platform device probe to sysfs attribute
store, removing the need for platform data passing,
- Use more devm_*() functions to simplify cleanup,
- Add pr_fmt(),
- General refactoring,
v2:
- Add missing initialization of i in gpio_virt_agg_probe(),
- Update for removed .need_valid_mask field and changed
.init_valid_mask() signature,
- Drop "virtual", rename to gpio-aggregator,
- Drop bogus FIXME related to gpiod_set_transitory() expectations,
- Use new GPIO Forwarder Helper,
- Lift limit on the maximum number of GPIOs,
- Improve parsing:
- add support for specifying GPIOs by line name,
- add support for specifying GPIO chips by ID,
- add support for GPIO offset ranges,
- names and offset specifiers must be separated by whitespace,
- GPIO offsets must separated by spaces,
- Use str_has_prefix() and kstrtouint().
---
drivers/gpio/Kconfig | 12 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-aggregator.c | 574 +++++++++++++++++++++++++++++++++
3 files changed, 587 insertions(+)
create mode 100644 drivers/gpio/gpio-aggregator.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b8013cf90064d505..b701984fdc930aa6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1534,6 +1534,18 @@ config GPIO_VIPERBOARD
endmenu
+config GPIO_AGGREGATOR
+ tristate "GPIO Aggregator"
+ help
+ Say yes here to enable the GPIO Aggregator, which provides a way to
+ aggregate existing GPIO lines into a new virtual GPIO chip.
+ This can serve the following purposes:
+ - Assign permissions for a collection of GPIO lines to a user,
+ - Export a collection of GPIO lines to a virtual machine,
+ - Provide a generic driver for a GPIO-operated device in an
+ industrial control context, to be operated from userspace using
+ the GPIO chardev interface.
+
config GPIO_MOCKUP
tristate "GPIO Testing Driver"
select IRQ_SIM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 0b571264ddbcdb49..2a7d85a0004a6f41 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
+obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o
diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
new file mode 100644
index 0000000000000000..339335660d1c40c2
--- /dev/null
+++ b/drivers/gpio/gpio-aggregator.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// GPIO Aggregator
+//
+// Copyright (C) 2019 Glider bvba
+
+#define DRV_NAME "gpio-aggregator"
+#define pr_fmt(fmt) DRV_NAME ": " fmt
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/ctype.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include "gpiolib.h"
+
+
+/*
+ * GPIO Aggregator sysfs interface
+ */
+
+struct gpio_aggregator {
+ struct gpiod_lookup_table *lookups;
+ struct platform_device *pdev;
+ char args[];
+};
+
+static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */
+static DEFINE_IDR(gpio_aggregator_idr);
+
+static char *get_arg(char **args)
+{
+ char *start = *args, *end;
+
+ start = skip_spaces(start);
+ if (!*start)
+ return NULL;
+
+ if (*start == '"') {
+ /* Quoted arg */
+ end = strchr(++start, '"');
+ if (!end)
+ return ERR_PTR(-EINVAL);
+ } else {
+ /* Unquoted arg */
+ for (end = start; *end && !isspace(*end); end++) ;
+ }
+
+ if (*end)
+ *end++ = '\0';
+
+ *args = end;
+ return start;
+}
+
+static bool isrange(const char *s)
+{
+ size_t n;
+
+ if (IS_ERR_OR_NULL(s))
+ return false;
+
+ while (1) {
+ n = strspn(s, "0123456789");
+ if (!n)
+ return false;
+
+ s += n;
+
+ switch (*s++) {
+ case '\0':
+ return true;
+
+ case '-':
+ case ',':
+ break;
+
+ default:
+ return false;
+ }
+ }
+}
+
+static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
+ int hwnum, unsigned int *n)
+{
+ struct gpiod_lookup_table *lookups;
+
+ lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2),
+ GFP_KERNEL);
+ if (!lookups)
+ return -ENOMEM;
+
+ lookups->table[*n].key = key;
+ lookups->table[*n].chip_hwnum = hwnum;
+ lookups->table[*n].idx = *n;
+
+ (*n)++;
+ memset(&lookups->table[*n], 0, sizeof(lookups->table[*n]));
+
+ aggr->lookups = lookups;
+ return 0;
+}
+
+static int aggr_parse(struct gpio_aggregator *aggr)
+{
+ unsigned int first_index, last_index, i, n = 0;
+ char *name, *offsets, *first, *last, *next;
+ char *args = aggr->args;
+ int error;
+
+ for (name = get_arg(&args), offsets = get_arg(&args); name;
+ offsets = get_arg(&args)) {
+ if (IS_ERR(name)) {
+ pr_err("Cannot get GPIO specifier: %pe\n", name);
+ return PTR_ERR(name);
+ }
+
+ if (!isrange(offsets)) {
+ /* Named GPIO line */
+ error = aggr_add_gpio(aggr, name, U16_MAX, &n);
+ if (error)
+ return error;
+
+ name = offsets;
+ continue;
+ }
+
+ /* GPIO chip + offset(s) */
+ for (first = offsets; *first; first = next) {
+ next = strchrnul(first, ',');
+ if (*next)
+ *next++ = '\0';
+
+ last = strchr(first, '-');
+ if (last)
+ *last++ = '\0';
+
+ if (kstrtouint(first, 10, &first_index)) {
+ pr_err("Cannot parse GPIO index %s\n", first);
+ return -EINVAL;
+ }
+
+ if (!last) {
+ last_index = first_index;
+ } else if (kstrtouint(last, 10, &last_index)) {
+ pr_err("Cannot parse GPIO index %s\n", last);
+ return -EINVAL;
+ }
+
+ for (i = first_index; i <= last_index; i++) {
+ error = aggr_add_gpio(aggr, name, i, &n);
+ if (error)
+ return error;
+ }
+ }
+
+ name = get_arg(&args);
+ }
+
+ if (!n) {
+ pr_err("No GPIOs specified\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t new_device_store(struct device_driver *driver, const char *buf,
+ size_t count)
+{
+ struct gpio_aggregator *aggr;
+ struct platform_device *pdev;
+ int res, id;
+
+ /* kernfs guarantees string termination, so count + 1 is safe */
+ aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL);
+ if (!aggr)
+ return -ENOMEM;
+
+ memcpy(aggr->args, buf, count + 1);
+
+ aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1),
+ GFP_KERNEL);
+ if (!aggr->lookups) {
+ res = -ENOMEM;
+ goto free_ga;
+ }
+
+ mutex_lock(&gpio_aggregator_lock);
+ id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL);
+ mutex_unlock(&gpio_aggregator_lock);
+
+ if (id < 0) {
+ res = id;
+ goto free_table;
+ }
+
+ aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id);
+ if (!aggr->lookups->dev_id) {
+ res = -ENOMEM;
+ goto remove_idr;
+ }
+
+ res = aggr_parse(aggr);
+ if (res)
+ goto free_dev_id;
+
+ gpiod_add_lookup_table(aggr->lookups);
+
+ pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0);
+ if (IS_ERR(pdev)) {
+ res = PTR_ERR(pdev);
+ goto remove_table;
+ }
+
+ aggr->pdev = pdev;
+ return count;
+
+remove_table:
+ gpiod_remove_lookup_table(aggr->lookups);
+free_dev_id:
+ kfree(aggr->lookups->dev_id);
+remove_idr:
+ mutex_lock(&gpio_aggregator_lock);
+ idr_remove(&gpio_aggregator_idr, id);
+ mutex_unlock(&gpio_aggregator_lock);
+free_table:
+ kfree(aggr->lookups);
+free_ga:
+ kfree(aggr);
+ return res;
+}
+
+static DRIVER_ATTR_WO(new_device);
+
+static void gpio_aggregator_free(struct gpio_aggregator *aggr)
+{
+ platform_device_unregister(aggr->pdev);
+ gpiod_remove_lookup_table(aggr->lookups);
+ kfree(aggr->lookups->dev_id);
+ kfree(aggr->lookups);
+ kfree(aggr);
+}
+
+static ssize_t delete_device_store(struct device_driver *driver,
+ const char *buf, size_t count)
+{
+ struct gpio_aggregator *aggr;
+ unsigned int id;
+ int error;
+
+ if (!str_has_prefix(buf, DRV_NAME "."))
+ return -EINVAL;
+
+ error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id);
+ if (error)
+ return error;
+
+ mutex_lock(&gpio_aggregator_lock);
+ aggr = idr_remove(&gpio_aggregator_idr, id);
+ mutex_unlock(&gpio_aggregator_lock);
+ if (!aggr)
+ return -ENOENT;
+
+ gpio_aggregator_free(aggr);
+ return count;
+}
+static DRIVER_ATTR_WO(delete_device);
+
+static struct attribute *gpio_aggregator_attrs[] = {
+ &driver_attr_new_device.attr,
+ &driver_attr_delete_device.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(gpio_aggregator);
+
+static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data)
+{
+ gpio_aggregator_free(p);
+ return 0;
+}
+
+static void __exit gpio_aggregator_remove_all(void)
+{
+ mutex_lock(&gpio_aggregator_lock);
+ idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL);
+ idr_destroy(&gpio_aggregator_idr);
+ mutex_unlock(&gpio_aggregator_lock);
+}
+
+
+/*
+ * GPIO Forwarder
+ */
+
+struct gpiochip_fwd {
+ struct gpio_chip chip;
+ struct gpio_desc **descs;
+ union {
+ struct mutex mlock; /* protects tmp[] if can_sleep */
+ spinlock_t slock; /* protects tmp[] if !can_sleep */
+ };
+ unsigned long tmp[]; /* values and descs for multiple ops */
+};
+
+static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return gpiod_get_direction(fwd->descs[offset]);
+}
+
+static int gpio_fwd_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return gpiod_direction_input(fwd->descs[offset]);
+}
+
+static int gpio_fwd_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return gpiod_direction_output(fwd->descs[offset], value);
+}
+
+static int gpio_fwd_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ return gpiod_get_value(fwd->descs[offset]);
+}
+
+static int gpio_fwd_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+ unsigned long *values, flags = 0;
+ struct gpio_desc **descs;
+ unsigned int i, j = 0;
+ int error;
+
+ if (chip->can_sleep)
+ mutex_lock(&fwd->mlock);
+ else
+ spin_lock_irqsave(&fwd->slock, flags);
+
+ /* Both values bitmap and desc pointers are stored in tmp[] */
+ values = &fwd->tmp[0];
+ descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)];
+
+ bitmap_clear(values, 0, fwd->chip.ngpio);
+ for_each_set_bit(i, mask, fwd->chip.ngpio)
+ descs[j++] = fwd->descs[i];
+
+ error = gpiod_get_array_value(j, descs, NULL, values);
+ if (!error) {
+ j = 0;
+ for_each_set_bit(i, mask, fwd->chip.ngpio)
+ __assign_bit(i, bits, test_bit(j++, values));
+ }
+
+ if (chip->can_sleep)
+ mutex_unlock(&fwd->mlock);
+ else
+ spin_unlock_irqrestore(&fwd->slock, flags);
+
+ return error;
+}
+
+static void gpio_fwd_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ gpiod_set_value(fwd->descs[offset], value);
+}
+
+static void gpio_fwd_set_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+ unsigned long *values, flags = 0;
+ struct gpio_desc **descs;
+ unsigned int i, j = 0;
+
+ if (chip->can_sleep)
+ mutex_lock(&fwd->mlock);
+ else
+ spin_lock_irqsave(&fwd->slock, flags);
+
+ /* Both values bitmap and desc pointers are stored in tmp[] */
+ values = &fwd->tmp[0];
+ descs = (void *)&fwd->tmp[BITS_TO_LONGS(fwd->chip.ngpio)];
+
+ for_each_set_bit(i, mask, fwd->chip.ngpio) {
+ __assign_bit(j, values, test_bit(i, bits));
+ descs[j++] = fwd->descs[i];
+ }
+
+ gpiod_set_array_value(j, descs, NULL, values);
+
+ if (chip->can_sleep)
+ mutex_unlock(&fwd->mlock);
+ else
+ spin_unlock_irqrestore(&fwd->slock, flags);
+}
+
+static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
+
+ chip = fwd->descs[offset]->gdev->chip;
+ if (chip->set_config)
+ return chip->set_config(chip, offset, config);
+
+ return -ENOTSUPP;
+}
+
+/**
+ * gpiochip_fwd_create() - Create a new GPIO forwarder
+ * @dev: Parent device pointer
+ * @ngpios: Number of GPIOs in the forwarder.
+ * @descs: Array containing the GPIO descriptors to forward to.
+ * This array must contain @ngpios entries, and must not be deallocated
+ * before the forwarder has been destroyed again.
+ *
+ * This function creates a new gpiochip, which forwards all GPIO operations to
+ * the passed GPIO descriptors.
+ *
+ * Return: An opaque object pointer, or an ERR_PTR()-encoded negative error
+ * code on failure.
+ */
+static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
+ unsigned int ngpios,
+ struct gpio_desc *descs[])
+{
+ const char *label = dev_name(dev);
+ struct gpiochip_fwd *fwd;
+ struct gpio_chip *chip;
+ unsigned int i;
+ int error;
+
+ fwd = devm_kzalloc(dev, struct_size(fwd, tmp,
+ BITS_TO_LONGS(ngpios) + ngpios), GFP_KERNEL);
+ if (!fwd)
+ return ERR_PTR(-ENOMEM);
+
+ chip = &fwd->chip;
+
+ /*
+ * If any of the GPIO lines are sleeping, then the entire forwarder
+ * will be sleeping.
+ * If any of the chips support .set_config(), then the forwarder will
+ * support setting configs.
+ */
+ for (i = 0; i < ngpios; i++) {
+ dev_dbg(dev, "gpio %u => gpio-%d (%s)\n", i,
+ desc_to_gpio(descs[i]), descs[i]->label ? : "?");
+
+ if (gpiod_cansleep(descs[i]))
+ chip->can_sleep = true;
+ if (descs[i]->gdev->chip->set_config)
+ chip->set_config = gpio_fwd_set_config;
+ }
+
+ chip->label = label;
+ chip->parent = dev;
+ chip->owner = THIS_MODULE;
+ chip->get_direction = gpio_fwd_get_direction;
+ chip->direction_input = gpio_fwd_direction_input;
+ chip->direction_output = gpio_fwd_direction_output;
+ chip->get = gpio_fwd_get;
+ chip->get_multiple = gpio_fwd_get_multiple;
+ chip->set = gpio_fwd_set;
+ chip->set_multiple = gpio_fwd_set_multiple;
+ chip->base = -1;
+ chip->ngpio = ngpios;
+ fwd->descs = descs;
+
+ if (chip->can_sleep)
+ mutex_init(&fwd->mlock);
+ else
+ spin_lock_init(&fwd->slock);
+
+ error = devm_gpiochip_add_data(dev, chip, fwd);
+ if (error)
+ return ERR_PTR(error);
+
+ return fwd;
+}
+
+
+/*
+ * GPIO Aggregator platform device
+ */
+
+static int gpio_aggregator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_desc **descs;
+ struct gpiochip_fwd *fwd;
+ int i, n;
+
+ n = gpiod_count(dev, NULL);
+ if (n < 0)
+ return n;
+
+ descs = devm_kmalloc_array(dev, n, sizeof(*descs), GFP_KERNEL);
+ if (!descs)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS);
+ if (IS_ERR(descs[i]))
+ return PTR_ERR(descs[i]);
+ }
+
+ fwd = gpiochip_fwd_create(dev, n, descs);
+ if (IS_ERR(fwd))
+ return PTR_ERR(fwd);
+
+ platform_set_drvdata(pdev, fwd);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpio_aggregator_dt_ids[] = {
+ /*
+ * Add GPIO-operated devices controlled from userspace below,
+ * or use "driver_override" in sysfs
+ */
+ {},
+};
+MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids);
+#endif
+
+static struct platform_driver gpio_aggregator_driver = {
+ .probe = gpio_aggregator_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .groups = gpio_aggregator_groups,
+ .of_match_table = of_match_ptr(gpio_aggregator_dt_ids),
+ },
+};
+
+static int __init gpio_aggregator_init(void)
+{
+ return platform_driver_register(&gpio_aggregator_driver);
+}
+module_init(gpio_aggregator_init);
+
+static void __exit gpio_aggregator_exit(void)
+{
+ gpio_aggregator_remove_all();
+ platform_driver_unregister(&gpio_aggregator_driver);
+}
+module_exit(gpio_aggregator_exit);
+
+MODULE_AUTHOR("Geert Uytterhoeven <[email protected]>");
+MODULE_DESCRIPTION("GPIO Aggregator");
+MODULE_LICENSE("GPL v2");
--
2.17.1
Currently GPIOs can only be referred to by GPIO controller and offset in
GPIO lookup tables.
Add support for looking them up by line name.
Rename gpiod_lookup.chip_label to gpiod_lookup.key, to make it clear
that this field can have two meanings, and update the kerneldoc and
GPIO_LOOKUP*() macros.
Signed-off-by: Geert Uytterhoeven <[email protected]>
Reviewed-by: Ulrich Hecht <[email protected]>
Reviewed-by: Eugeniu Rosca <[email protected]>
Tested-by: Eugeniu Rosca <[email protected]>
---
v5:
- Add Reviewed-by, Tested-by,
v4:
- Add Reviewed-by,
- Rename gpiod_lookup.chip_label.
- Use U16_MAX instead of (u16)-1,
v3:
- New.
---
drivers/gpio/gpiolib.c | 22 +++++++++++++++++-----
include/linux/gpio/machine.h | 15 ++++++++-------
2 files changed, 25 insertions(+), 12 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 200c2d2be4b78043..24c02167f9e5472f 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4453,7 +4453,7 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
if (!table)
return desc;
- for (p = &table->table[0]; p->chip_label; p++) {
+ for (p = &table->table[0]; p->key; p++) {
struct gpio_chip *chip;
/* idx must always match exactly */
@@ -4464,18 +4464,30 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id,
if (p->con_id && (!con_id || strcmp(p->con_id, con_id)))
continue;
- chip = find_chip_by_name(p->chip_label);
+ if (p->chip_hwnum == U16_MAX) {
+ desc = gpio_name_to_desc(p->key);
+ if (desc) {
+ *flags = p->flags;
+ return desc;
+ }
+
+ dev_warn(dev, "cannot find GPIO line %s, deferring\n",
+ p->key);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ chip = find_chip_by_name(p->key);
if (!chip) {
/*
* As the lookup table indicates a chip with
- * p->chip_label should exist, assume it may
+ * p->key should exist, assume it may
* still appear later and let the interested
* consumer be probed again or let the Deferred
* Probe infrastructure handle the error.
*/
dev_warn(dev, "cannot find GPIO chip %s, deferring\n",
- p->chip_label);
+ p->key);
return ERR_PTR(-EPROBE_DEFER);
}
@@ -4506,7 +4518,7 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
if (!table)
return -ENOENT;
- for (p = &table->table[0]; p->chip_label; p++) {
+ for (p = &table->table[0]; p->key; p++) {
if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) ||
(!con_id && !p->con_id))
count++;
diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h
index 1ebe5be05d5f81fa..84c66fbf54fd5811 100644
--- a/include/linux/gpio/machine.h
+++ b/include/linux/gpio/machine.h
@@ -20,8 +20,9 @@ enum gpio_lookup_flags {
/**
* struct gpiod_lookup - lookup table
- * @chip_label: name of the chip the GPIO belongs to
- * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO
+ * @key: either the name of the chip the GPIO belongs to, or the GPIO line name
+ * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO, or
+ * U16_MAX to indicate that @key is a GPIO line name
* @con_id: name of the GPIO from the device's point of view
* @idx: index of the GPIO in case several GPIOs share the same name
* @flags: bitmask of gpio_lookup_flags GPIO_* values
@@ -30,7 +31,7 @@ enum gpio_lookup_flags {
* functions using platform data.
*/
struct gpiod_lookup {
- const char *chip_label;
+ const char *key;
u16 chip_hwnum;
const char *con_id;
unsigned int idx;
@@ -63,17 +64,17 @@ struct gpiod_hog {
/*
* Simple definition of a single GPIO under a con_id
*/
-#define GPIO_LOOKUP(_chip_label, _chip_hwnum, _con_id, _flags) \
- GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, 0, _flags)
+#define GPIO_LOOKUP(_key, _chip_hwnum, _con_id, _flags) \
+ GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, 0, _flags)
/*
* Use this macro if you need to have several GPIOs under the same con_id.
* Each GPIO needs to use a different index and can be accessed using
* gpiod_get_index()
*/
-#define GPIO_LOOKUP_IDX(_chip_label, _chip_hwnum, _con_id, _idx, _flags) \
+#define GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, _idx, _flags) \
{ \
- .chip_label = _chip_label, \
+ .key = _key, \
.chip_hwnum = _chip_hwnum, \
.con_id = _con_id, \
.idx = _idx, \
--
2.17.1
Add a maintainership section for the GPIO Aggregator, covering
documentation and driver source code.
Signed-off-by: Geert Uytterhoeven <[email protected]>
Reviewed-by: Eugeniu Rosca <[email protected]>
Tested-by: Eugeniu Rosca <[email protected]>
---
v5:
- Add Reviewed-by, Tested-by,
v4:
- Drop controversial GPIO repeater,
v3:
- New.
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 634376400709d6e8..d39f550ab1555a87 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7128,6 +7128,13 @@ F: Documentation/firmware-guide/acpi/gpio-properties.rst
F: drivers/gpio/gpiolib-acpi.c
F: drivers/gpio/gpiolib-acpi.h
+GPIO AGGREGATOR
+M: Geert Uytterhoeven <[email protected]>
+L: [email protected]
+S: Maintained
+F: Documentation/admin-guide/gpio/gpio-aggregator.rst
+F: drivers/gpio/gpio-aggregator.c
+
GPIO IR Transmitter
M: Sean Young <[email protected]>
L: [email protected]
--
2.17.1
Hi Geert,
On Tue, Feb 18, 2020 at 04:18:07PM +0100, Geert Uytterhoeven wrote:
> Hi all,
>
> GPIO controllers are exported to userspace using /dev/gpiochip*
> character devices. Access control to these devices is provided by
> standard UNIX file system permissions, on an all-or-nothing basis:
> either a GPIO controller is accessible for a user, or it is not.
> Currently no mechanism exists to control access to individual GPIOs.
>
> Hence this adds a GPIO driver to aggregate existing GPIOs, and expose
> them as a new gpiochip. This is useful for implementing access control,
> and assigning a set of GPIOs to a specific user. Furthermore, this
> simplifies and hardens exporting GPIOs to a virtual machine, as the VM
> can just grab the full GPIO controller, and no longer needs to care
> about which GPIOs to grab and which not, reducing the attack surface.
>
> Recently, other use cases have been discovered[1]:
> - Describing simple GPIO-operated devices in DT, and using the GPIO
> Aggregator as a generic GPIO driver for userspace, which is useful
> for industrial control.
>
> Changes compared to v4[2]:
> - Add Reviewed-by, Tested-by,
> - Fix inconsistent indentation in documentation.
I confirm that the diff between v4 and v5 comprises whitespace only.
Thanks for your time to develop this useful functionality!
--
Best Regards
Eugeniu Rosca
Hi Geert,
Just a few comments. Please see below.
On 2/18/20 7:18 AM, Geert Uytterhoeven wrote:
> Document the GPIO Aggregator, and the two typical use-cases.
>
> Signed-off-by: Geert Uytterhoeven <[email protected]>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Eugeniu Rosca <[email protected]>
> Tested-by: Eugeniu Rosca <[email protected]>
> ---
> v5:
> - Add Reviewed-by, Tested-by,
> - Fix inconsistent indentation.
>
> v4:
> - Add Reviewed-by,
> - Drop controversial GPIO repeater,
> - Clarify industrial control use case,
> - Fix typo s/communicated/communicate/,
> - Replace abstract frobnicator example by concrete door example with
> gpio-line-names,
>
> v3:
> - New.
> ---
> .../admin-guide/gpio/gpio-aggregator.rst | 102 ++++++++++++++++++
> Documentation/admin-guide/gpio/index.rst | 1 +
> 2 files changed, 103 insertions(+)
> create mode 100644 Documentation/admin-guide/gpio/gpio-aggregator.rst
>
> diff --git a/Documentation/admin-guide/gpio/gpio-aggregator.rst b/Documentation/admin-guide/gpio/gpio-aggregator.rst
> new file mode 100644
> index 0000000000000000..114f72be33c2571e
> --- /dev/null
> +++ b/Documentation/admin-guide/gpio/gpio-aggregator.rst
> @@ -0,0 +1,102 @@
> +.. SPDX-License-Identifier: GPL-2.0-only
> +
> +GPIO Aggregator
> +===============
> +
> +The GPIO Aggregator allows to aggregate GPIOs, and expose them as a new
"allows" really wants an object following the verb [although the kernel sources
and docs have many cases of it not having an object]. Something like
allows {you, one, someone, users, a user} to aggregate
> +gpio_chip. This supports the following use cases.
> +
> +
> +Aggregating GPIOs using Sysfs
> +-----------------------------
> +
> +GPIO controllers are exported to userspace using /dev/gpiochip* character
> +devices. Access control to these devices is provided by standard UNIX file
> +system permissions, on an all-or-nothing basis: either a GPIO controller is
> +accessible for a user, or it is not.
> +
> +The GPIO Aggregator allows access control for individual GPIOs, by aggregating
> +them into a new gpio_chip, which can be assigned to a group or user using
> +standard UNIX file ownership and permissions. Furthermore, this simplifies and
> +hardens exporting GPIOs to a virtual machine, as the VM can just grab the full
> +GPIO controller, and no longer needs to care about which GPIOs to grab and
> +which not, reducing the attack surface.
> +
> +Aggregated GPIO controllers are instantiated and destroyed by writing to
> +write-only attribute files in sysfs.
> +
> + /sys/bus/platform/drivers/gpio-aggregator/
> +
> + "new_device" ...
> + Userspace may ask the kernel to instantiate an aggregated GPIO
> + controller by writing a string describing the GPIOs to
> + aggregate to the "new_device" file, using the format
> +
> + .. code-block:: none
> +
> + [<gpioA>] [<gpiochipB> <offsets>] ...
> +
> + Where:
> +
> + "<gpioA>" ...
> + is a GPIO line name,
> +
> + "<gpiochipB>" ...
> + is a GPIO chip label or name, and
> +
> + "<offsets>" ...
> + is a comma-separated list of GPIO offsets and/or
> + GPIO offset ranges denoted by dashes.
> +
> + Example: Instantiate a new GPIO aggregator by aggregating GPIO
> + 19 of "e6052000.gpio" and GPIOs 20-21 of "gpiochip2" into a new
> + gpio_chip:
> +
> + .. code-block:: bash
> +
> + echo 'e6052000.gpio 19 gpiochip2 20-21' > new_device
> +
Does the above command tell the user that the new device is named
"gpio-aggregator.0", as used below?
> + "delete_device" ...
> + Userspace may ask the kernel to destroy an aggregated GPIO
> + controller after use by writing its device name to the
> + "delete_device" file.
> +
> + Example: Destroy the previously-created aggregated GPIO
> + controller "gpio-aggregator.0":
> +
> + .. code-block:: bash
> +
> + echo gpio-aggregator.0 > delete_device
> +
> +
> +Generic GPIO Driver
> +-------------------
> +
> +The GPIO Aggregator can also be used as a generic driver for a simple
> +GPIO-operated device described in DT, without a dedicated in-kernel driver.
> +This is useful in industrial control, and is not unlike e.g. spidev, which
> +allows to communicate with an SPI device from userspace.
allows {choose an object} to communicate
> +
> +Binding a device to the GPIO Aggregator is performed either by modifying the
> +gpio-aggregator driver, or by writing to the "driver_override" file in Sysfs.
> +
> +Example: If "door" is a GPIO-operated device described in DT, using its own
> +compatible value::
> +
> + door {
> + compatible = "myvendor,mydoor";
> +
> + gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>,
> + <&gpio2 20 GPIO_ACTIVE_LOW>;
> + gpio-line-names = "open", "lock";
> + };
> +
> +it can be bound to the GPIO Aggregator by either:
> +
> +1. Adding its compatible value to ``gpio_aggregator_dt_ids[]``,
> +2. Binding manually using "driver_override":
> +
> +.. code-block:: bash
> +
> + echo gpio-aggregator > /sys/bus/platform/devices/door/driver_override
> + echo door > /sys/bus/platform/drivers/gpio-aggregator/bind
HTH. Thanks.
--
~Randy
Hi Randy,
On Tue, Feb 18, 2020 at 7:30 PM Randy Dunlap <[email protected]> wrote:
> On 2/18/20 7:18 AM, Geert Uytterhoeven wrote:
> > Document the GPIO Aggregator, and the two typical use-cases.
> >
> > Signed-off-by: Geert Uytterhoeven <[email protected]>
> > --- /dev/null
> > +++ b/Documentation/admin-guide/gpio/gpio-aggregator.rst
> > @@ -0,0 +1,102 @@
> > +.. SPDX-License-Identifier: GPL-2.0-only
> > +
> > +GPIO Aggregator
> > +===============
> > +
> > +The GPIO Aggregator allows to aggregate GPIOs, and expose them as a new
>
> "allows" really wants an object following the verb [although the kernel sources
> and docs have many cases of it not having an object]. Something like
>
> allows {you, one, someone, users, a user} to aggregate
Thanks for the hint!
> > + Example: Instantiate a new GPIO aggregator by aggregating GPIO
> > + 19 of "e6052000.gpio" and GPIOs 20-21 of "gpiochip2" into a new
> > + gpio_chip:
> > +
> > + .. code-block:: bash
> > +
> > + echo 'e6052000.gpio 19 gpiochip2 20-21' > new_device
> > +
>
> Does the above command tell the user that the new device is named
> "gpio-aggregator.0", as used below?
Yes, it will be printed through the kernel log, cfr. the sample session in
the cover letter.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
<[email protected]> wrote:
> Currently GPIOs can only be referred to by GPIO controller and offset in
> GPIO lookup tables.
>
> Add support for looking them up by line name.
> Rename gpiod_lookup.chip_label to gpiod_lookup.key, to make it clear
> that this field can have two meanings, and update the kerneldoc and
> GPIO_LOOKUP*() macros.
>
> Signed-off-by: Geert Uytterhoeven <[email protected]>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Eugeniu Rosca <[email protected]>
> Tested-by: Eugeniu Rosca <[email protected]>
> --- a/include/linux/gpio/machine.h
> +++ b/include/linux/gpio/machine.h
> @@ -20,8 +20,9 @@ enum gpio_lookup_flags {
>
> /**
> * struct gpiod_lookup - lookup table
> - * @chip_label: name of the chip the GPIO belongs to
> - * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO
> + * @key: either the name of the chip the GPIO belongs to, or the GPIO line name
> + * @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO, or
> + * U16_MAX to indicate that @key is a GPIO line name
> * @con_id: name of the GPIO from the device's point of view
> * @idx: index of the GPIO in case several GPIOs share the same name
> * @flags: bitmask of gpio_lookup_flags GPIO_* values
> @@ -30,7 +31,7 @@ enum gpio_lookup_flags {
> * functions using platform data.
> */
> struct gpiod_lookup {
> - const char *chip_label;
> + const char *key;
> u16 chip_hwnum;
> const char *con_id;
> unsigned int idx;
This needs an update in the documentation:
--- a/Documentation/driver-api/gpio/board.rst
+++ b/Documentation/driver-api/gpio/board.rst
@@ -113,13 +113,15 @@ files that desire to do so need to include the
following header::
GPIOs are mapped by the means of tables of lookups, containing instances of the
gpiod_lookup structure. Two macros are defined to help declaring such
mappings::
- GPIO_LOOKUP(chip_label, chip_hwnum, con_id, flags)
- GPIO_LOOKUP_IDX(chip_label, chip_hwnum, con_id, idx, flags)
+ GPIO_LOOKUP(key, chip_hwnum, con_id, flags)
+ GPIO_LOOKUP_IDX(key, chip_hwnum, con_id, idx, flags)
where
- - chip_label is the label of the gpiod_chip instance providing the GPIO
- - chip_hwnum is the hardware number of the GPIO within the chip
+ - key is either the label of the gpiod_chip instance providing the GPIO, or
+ the GPIO line name
+ - chip_hwnum is the hardware number of the GPIO within the chip, or U16_MAX
+ to indicate that key is a GPIO line name
- con_id is the name of the GPIO function from the device point of view. It
can be NULL, in which case it will match any function.
- idx is the index of the GPIO within the function.
Furthermore, a few drivers populate the gpiod_lookup members directly,
instead of using the convenience macros:
arch/arm/mach-integrator/impd1.c
drivers/i2c/busses/i2c-i801.c
drivers/mfd/sm501.c
Either they have to be updated s/chip_label/key/, or start using the macros,
e.g.
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -1444,9 +1444,9 @@ static int i801_add_mux(struct i801_priv *priv)
return -ENOMEM;
lookup->dev_id = "i2c-mux-gpio";
for (i = 0; i < mux_config->n_gpios; i++) {
- lookup->table[i].chip_label = mux_config->gpio_chip;
- lookup->table[i].chip_hwnum = mux_config->gpios[i];
- lookup->table[i].con_id = "mux";
+ lookup->table[i] = (struct gpiod_lookup)
+ GPIO_LOOKUP(mux_config->gpio_chip,
+ mux_config->gpios[i], "mux", 0);
}
gpiod_add_lookup_table(lookup);
priv->lookup = lookup;
Do you have any preference?
Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Randy,
On Tue, Feb 18, 2020 at 7:30 PM Randy Dunlap <[email protected]> wrote:
> On 2/18/20 7:18 AM, Geert Uytterhoeven wrote:
> > Document the GPIO Aggregator, and the two typical use-cases.
> >
> > Signed-off-by: Geert Uytterhoeven <[email protected]>
> > --- /dev/null
> > +++ b/Documentation/admin-guide/gpio/gpio-aggregator.rst
> > @@ -0,0 +1,102 @@
> > +.. SPDX-License-Identifier: GPL-2.0-only
> > +
> > +GPIO Aggregator
> > +===============
> > +
> > +The GPIO Aggregator allows to aggregate GPIOs, and expose them as a new
>
> "allows" really wants an object following the verb [although the kernel sources
> and docs have many cases of it not having an object]. Something like
>
> allows {you, one, someone, users, a user} to aggregate
Changing to:
provides a mechanism to aggregate GPIOs
> > +gpio_chip. This supports the following use cases.
> > +
> > +
> > +Aggregating GPIOs using Sysfs
> > +-----------------------------
> > +
> > +GPIO controllers are exported to userspace using /dev/gpiochip* character
> > +devices. Access control to these devices is provided by standard UNIX file
> > +system permissions, on an all-or-nothing basis: either a GPIO controller is
> > +accessible for a user, or it is not.
> > +
> > +The GPIO Aggregator allows access control for individual GPIOs, by aggregating
Changing to:
provides access control for a set of one or more GPIOs
> > +them into a new gpio_chip, which can be assigned to a group or user using
> > +standard UNIX file ownership and permissions. Furthermore, this simplifies and
> > +hardens exporting GPIOs to a virtual machine, as the VM can just grab the full
> > +GPIO controller, and no longer needs to care about which GPIOs to grab and
> > +which not, reducing the attack surface.
> > +Generic GPIO Driver
> > +-------------------
> > +
> > +The GPIO Aggregator can also be used as a generic driver for a simple
> > +GPIO-operated device described in DT, without a dedicated in-kernel driver.
> > +This is useful in industrial control, and is not unlike e.g. spidev, which
> > +allows to communicate with an SPI device from userspace.
>
> allows {choose an object} to communicate
Changing to:
allows the user to communicate
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
On 2/21/20 8:34 AM, Geert Uytterhoeven wrote:
> Hi Randy,
>
Hi Geert,
Those changes look good. Thanks.
--
~Randy
Hi Linus and Bartosz,
On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
<[email protected]> wrote:
> GPIO controllers are exported to userspace using /dev/gpiochip*
> character devices. Access control to these devices is provided by
> standard UNIX file system permissions, on an all-or-nothing basis:
> either a GPIO controller is accessible for a user, or it is not.
> Currently no mechanism exists to control access to individual GPIOs.
>
> Hence this adds a GPIO driver to aggregate existing GPIOs, and expose
> them as a new gpiochip. This is useful for implementing access control,
> and assigning a set of GPIOs to a specific user. Furthermore, this
> simplifies and hardens exporting GPIOs to a virtual machine, as the VM
> can just grab the full GPIO controller, and no longer needs to care
> about which GPIOs to grab and which not, reducing the attack surface.
Do you have any more comments, before I respin and post v6?
Thanks, and have a niec weekend!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Geert,
I'm sorry for the slow review, it's a large patch set and
takes some time to sit down and review, and see whether my
earlier comments have been addressed.
On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
<[email protected]> wrote:
> Currently GPIOs can only be referred to by GPIO controller and offset in
> GPIO lookup tables.
>
> Add support for looking them up by line name.
> Rename gpiod_lookup.chip_label to gpiod_lookup.key, to make it clear
> that this field can have two meanings, and update the kerneldoc and
> GPIO_LOOKUP*() macros.
>
> Signed-off-by: Geert Uytterhoeven <[email protected]>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Eugeniu Rosca <[email protected]>
> Tested-by: Eugeniu Rosca <[email protected]>
I will try to understand why this change is necessary to implement
the gpio aggregator (probablt I will comment that on the other
patches like "aha now I see it" or so, but it would help a lot if the
commit message
would state the technical reason to why we need to do this change,
like what it is that you want to do and why you cannot do it without
this change.
Yours,
Linus Walleij
On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
<[email protected]> wrote:
> Currently GPIO controllers can only be referred to by label in GPIO
> lookup tables.
>
> Add support for looking them up by "gpiochipN" name, with "N" the
> corresponding GPIO device's ID number.
>
> Signed-off-by: Geert Uytterhoeven <[email protected]>
> Reviewed-by: Ulrich Hecht <[email protected]>
> Reviewed-by: Eugeniu Rosca <[email protected]>
> Tested-by: Eugeniu Rosca <[email protected]>
Just like with patch 2/5 I have the same problem here that
the commit message doesn't state the technical reason why
we need to change this and support the device name in these
tables and not just labels.
(Possibly again I will realize it...)
Yours,
Linus Walleij
Hi Geert,
thanks for your patience and again sorry for procrastination on my part :(
Overall I start to like this driver a lot. It has come a long way.
Some comments below are nitpicky, bear with me if they seem stupid.
On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
<[email protected]> wrote:
> +#define DRV_NAME "gpio-aggregator"
> +#define pr_fmt(fmt) DRV_NAME ": " fmt
I would just use dev_[info|err] for all messages to get rid of this.
> +#include <linux/bitmap.h>
> +#include <linux/bitops.h>
> +#include <linux/ctype.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/idr.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/overflow.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +
> +#include "gpiolib.h"
When this file is includes I prefer if there is a comment next to
this include saying why we have to touch internals and which
ones.
> +struct gpio_aggregator {
> + struct gpiod_lookup_table *lookups;
> + struct platform_device *pdev;
What about just storing struct device *dev?
Then callbacks can just
dev_err(aggregator->dev, "myerror\n");
> +static char *get_arg(char **args)
> +{
> + char *start = *args, *end;
> +
> + start = skip_spaces(start);
> + if (!*start)
> + return NULL;
> +
> + if (*start == '"') {
> + /* Quoted arg */
> + end = strchr(++start, '"');
> + if (!end)
> + return ERR_PTR(-EINVAL);
> + } else {
> + /* Unquoted arg */
> + for (end = start; *end && !isspace(*end); end++) ;
> + }
> +
> + if (*end)
> + *end++ = '\0';
> +
> + *args = end;
> + return start;
> +}
Isn't this function reimplementing strsep()?
while ((s = strsep(&p, " \""))) {
or something.
I'm not the best with strings, just asking so I know you tried it
already.
> +static int aggr_parse(struct gpio_aggregator *aggr)
> +{
> + unsigned int first_index, last_index, i, n = 0;
> + char *name, *offsets, *first, *last, *next;
> + char *args = aggr->args;
> + int error;
> +
> + for (name = get_arg(&args), offsets = get_arg(&args); name;
> + offsets = get_arg(&args)) {
> + if (IS_ERR(name)) {
> + pr_err("Cannot get GPIO specifier: %pe\n", name);
If gpio_aggregrator contained struct device *dev this would be
dev_err(aggr->dev, "...\n");
> +static void gpio_aggregator_free(struct gpio_aggregator *aggr)
> +{
> + platform_device_unregister(aggr->pdev);
Aha maybe store both the pdev and the dev in the struct then?
Or print using &aggr->pdev.dev.
> + /*
> + * If any of the GPIO lines are sleeping, then the entire forwarder
> + * will be sleeping.
> + * If any of the chips support .set_config(), then the forwarder will
> + * support setting configs.
> + */
> + for (i = 0; i < ngpios; i++) {
> + dev_dbg(dev, "gpio %u => gpio-%d (%s)\n", i,
> + desc_to_gpio(descs[i]), descs[i]->label ? : "?");
If this desc->label business is why you need to include
"gpiolib.h" then I'd prefer if you just add a
const char *gpiod_get_producer_name(struct gpio_desc *desc);
to gpiolib (add in <linux/gpio/consumer.h> so that gpiolib can
try to give you something reasonable to print for the label here.
I ran into that problem before (wanting to print something like this)
and usually just printed the offset.
But if it is a serious debug issue, let's fix a helper for this.
gpiod_get_producer_name() could return the thing in
desc->label if that is set or else something along
"chipname-offset" or "unknown", I'm not very picky
with that.
> error = aggr_add_gpio(aggr, name, U16_MAX, &n);
Is the reason why you use e.g. "gpiochip0" as name here that this
is a simple ABI for userspace?
Such like obtained from /sys/bus/gpio/devices/<chipname>?
I would actually prefer to just add a sysfs attribute
such as "name" and set it to the value of gpiochip->label.
These labels are compulsory and supposed to be unique.
Then whatever creates an aggregator can just use
cat /sys/bus/gpio/devices/gpiochipN/name to send in
through the sysfs interface to this kernel driver.
This will protect you in the following way:
When a system is booted and populated the N in
gpiochipN is not stable and this aggregator will be used
by scripts that assume it is. We already had this dilemma
with things like network interfaces like eth0/1.
This can be because of things like probe order which
can be random, or because someone compiled a
kernel with a new driver for a gpiochip that wasn't
detected before. This recently happened to Raspberry Pi,
that added gpio driver for "firmware GPIOs" (IIRC).
The label on the chip is going to be more stable
I think, so it is better to use that.
This should also rid the need to include "gpiolib.h"
which makes me nervous.
Yours,
Linus Walleij
Hi Linus,
On Thu, Mar 12, 2020 at 3:23 PM Linus Walleij <[email protected]> wrote:
> On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
> <[email protected]> wrote:
>
> > Currently GPIO controllers can only be referred to by label in GPIO
> > lookup tables.
> >
> > Add support for looking them up by "gpiochipN" name, with "N" the
> > corresponding GPIO device's ID number.
> >
> > Signed-off-by: Geert Uytterhoeven <[email protected]>
> > Reviewed-by: Ulrich Hecht <[email protected]>
> > Reviewed-by: Eugeniu Rosca <[email protected]>
> > Tested-by: Eugeniu Rosca <[email protected]>
>
> Just like with patch 2/5 I have the same problem here that
> the commit message doesn't state the technical reason why
> we need to change this and support the device name in these
> tables and not just labels.
As these "gpiochipN" names are not stable, I will drop this patch, and
the related support.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Linus,
On Thu, Mar 12, 2020 at 3:21 PM Linus Walleij <[email protected]> wrote:
> On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
> <[email protected]> wrote:
> > Currently GPIOs can only be referred to by GPIO controller and offset in
> > GPIO lookup tables.
> >
> > Add support for looking them up by line name.
> > Rename gpiod_lookup.chip_label to gpiod_lookup.key, to make it clear
> > that this field can have two meanings, and update the kerneldoc and
> > GPIO_LOOKUP*() macros.
> >
> > Signed-off-by: Geert Uytterhoeven <[email protected]>
> > Reviewed-by: Ulrich Hecht <[email protected]>
> > Reviewed-by: Eugeniu Rosca <[email protected]>
> > Tested-by: Eugeniu Rosca <[email protected]>
>
> I will try to understand why this change is necessary to implement
> the gpio aggregator (probablt I will comment that on the other
> patches like "aha now I see it" or so, but it would help a lot if the
> commit message
> would state the technical reason to why we need to do this change,
> like what it is that you want to do and why you cannot do it without
> this change.
It's very simple: how do you want the user to refer to a specific GPIO
line? Currently he can only do so by gpiochip label and index.
However, there exists another stable reference: the (optional) line name,
which can be attached using "gpio-line-names" in DT or ACPI.
As this is the most use-centric way to refer to a GPIO, it makes sense
to support lookup based on that, too.
Will reword to make this clearer.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
Hi Linus,
On Thu, Mar 12, 2020 at 3:57 PM Linus Walleij <[email protected]> wrote:
> thanks for your patience and again sorry for procrastination on my part :(
>
> Overall I start to like this driver a lot. It has come a long way.
>
> Some comments below are nitpicky, bear with me if they seem stupid.
Thanks a lot for your comments!
> On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
> <[email protected]> wrote:
> > +#define DRV_NAME "gpio-aggregator"
> > +#define pr_fmt(fmt) DRV_NAME ": " fmt
>
> I would just use dev_[info|err] for all messages to get rid of this.
See below.
> > +#include <linux/bitmap.h>
> > +#include <linux/bitops.h>
> > +#include <linux/ctype.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/gpio/driver.h>
> > +#include <linux/gpio/machine.h>
> > +#include <linux/idr.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/overflow.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/string.h>
> > +
> > +#include "gpiolib.h"
>
> When this file is includes I prefer if there is a comment next to
> this include saying why we have to touch internals and which
> ones.
I have just discovered gpiod_to_chip(), which removes the need for two
of the three users ;-)
> > +struct gpio_aggregator {
> > + struct gpiod_lookup_table *lookups;
> > + struct platform_device *pdev;
>
> What about just storing struct device *dev?
>
> Then callbacks can just
>
> dev_err(aggregator->dev, "myerror\n");
&aggr->pdev.dev or aggr->dev does't make much of a difference.
> > +static char *get_arg(char **args)
> > +{
> > + char *start = *args, *end;
> > +
> > + start = skip_spaces(start);
> > + if (!*start)
> > + return NULL;
> > +
> > + if (*start == '"') {
> > + /* Quoted arg */
> > + end = strchr(++start, '"');
> > + if (!end)
> > + return ERR_PTR(-EINVAL);
> > + } else {
> > + /* Unquoted arg */
> > + for (end = start; *end && !isspace(*end); end++) ;
> > + }
> > +
> > + if (*end)
> > + *end++ = '\0';
> > +
> > + *args = end;
> > + return start;
> > +}
>
> Isn't this function reimplementing strsep()?
> while ((s = strsep(&p, " \""))) {
> or something.
>
> I'm not the best with strings, just asking so I know you tried it
> already.
strsep(&p, " \"") would terminate the token if a space or double quote is
seen. I.e. it wouldn't handle spaces in quoted arguments.
There's also argv_split(), but that doesn't handle quoted args, and
duplicates all arguments.
Line names assigned by "gpio-lines-names" may contain spaces, so support
for quoted args is mandatory.
> > +static int aggr_parse(struct gpio_aggregator *aggr)
> > +{
> > + unsigned int first_index, last_index, i, n = 0;
> > + char *name, *offsets, *first, *last, *next;
> > + char *args = aggr->args;
> > + int error;
> > +
> > + for (name = get_arg(&args), offsets = get_arg(&args); name;
> > + offsets = get_arg(&args)) {
> > + if (IS_ERR(name)) {
> > + pr_err("Cannot get GPIO specifier: %pe\n", name);
>
> If gpio_aggregrator contained struct device *dev this would be
> dev_err(aggr->dev, "...\n");
aggr_parse() is called before the platform device is created, and before
aggr->pdev is populated. So there is no device to print yet.
> > +static void gpio_aggregator_free(struct gpio_aggregator *aggr)
> > +{
> > + platform_device_unregister(aggr->pdev);
>
> Aha maybe store both the pdev and the dev in the struct then?
>
> Or print using &aggr->pdev.dev.
Same for aggr->pdev.dev (or aggr->dev).
> > + /*
> > + * If any of the GPIO lines are sleeping, then the entire forwarder
> > + * will be sleeping.
> > + * If any of the chips support .set_config(), then the forwarder will
> > + * support setting configs.
> > + */
> > + for (i = 0; i < ngpios; i++) {
> > + dev_dbg(dev, "gpio %u => gpio-%d (%s)\n", i,
> > + desc_to_gpio(descs[i]), descs[i]->label ? : "?");
>
> If this desc->label business is why you need to include
> "gpiolib.h" then I'd prefer if you just add a
It was the third reason to include that file...
> const char *gpiod_get_producer_name(struct gpio_desc *desc);
>
> to gpiolib (add in <linux/gpio/consumer.h> so that gpiolib can
> try to give you something reasonable to print for the label here.
> I ran into that problem before (wanting to print something like this)
> and usually just printed the offset.
>
> But if it is a serious debug issue, let's fix a helper for this.
>
> gpiod_get_producer_name() could return the thing in
> desc->label if that is set or else something along
> "chipname-offset" or "unknown", I'm not very picky
> with that.
I will just remove the printing of the label, as it is no longer useful.
Since I started using gpiod_lookup, the descriptor has already been
requested at this point, which means its label will usually be
"gpio-aggregator.N", i.e. it doesn't provide any help.
The only exception is for a GPIO line which has an associated line name
through "gpio-line-names" in DT. But just seeing the global GPIO number
should be good enough for debugging.
BTW, one day you may want to have your our printk() format specifier for
GPIOs? Oh, no "%pg" and "%pG" are already taken; "%pp" is still
available.
> > error = aggr_add_gpio(aggr, name, U16_MAX, &n);
>
> Is the reason why you use e.g. "gpiochip0" as name here that this
> is a simple ABI for userspace?
"name" is not the "gpiochipN" name here, but the line name, cfr. the
U16_MAX value for chip index, and the comment just above:
+ /* Named GPIO line */
That one is supposed to be stable, right?
Note that this is the most use-centric way to refer to a GPIO.
In the other caller:
+ error = aggr_add_gpio(aggr, name, i, &n);
"name" is a reference to the gpiochip, i.e. either its label, or the
"gpiochipN" name.
> Such like obtained from /sys/bus/gpio/devices/<chipname>?
>
> I would actually prefer to just add a sysfs attribute
> such as "name" and set it to the value of gpiochip->label.
Makes sense, but that would be a separate, unrelated patch, right?
> These labels are compulsory and supposed to be unique.
>
> Then whatever creates an aggregator can just use
> cat /sys/bus/gpio/devices/gpiochipN/name to send in
> through the sysfs interface to this kernel driver.
>
> This will protect you in the following way:
>
> When a system is booted and populated the N in
> gpiochipN is not stable and this aggregator will be used
> by scripts that assume it is. We already had this dilemma
> with things like network interfaces like eth0/1.
>
> This can be because of things like probe order which
> can be random, or because someone compiled a
> kernel with a new driver for a gpiochip that wasn't
> detected before. This recently happened to Raspberry Pi,
> that added gpio driver for "firmware GPIOs" (IIRC).
>
> The label on the chip is going to be more stable
> I think, so it is better to use that.
OK, so support for "gpiochipN" matching can be dropped, obsoleting
"[PATCH v5 1/5] gpiolib: Add support for gpiochipN-based table lookup".
Note that I added support for that in response to Bartosz' first try
https://lore.kernel.org/linux-gpio/CAMpxmJUF1s1zyXVtoUGfbV7Yk+heua4rNjY=DrX=jr-v8UfNxA@mail.gmail.com/
> This should also rid the need to include "gpiolib.h"
> which makes me nervous.
Consider it done!
Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
On Tue, Feb 18, 2020 at 4:18 PM Geert Uytterhoeven
<[email protected]> wrote:
> GPIO controllers are exported to userspace using /dev/gpiochip*
> character devices. Access control to these devices is provided by
> standard UNIX file system permissions, on an all-or-nothing basis:
> either a GPIO controller is accessible for a user, or it is not.
> Currently no mechanism exists to control access to individual GPIOs.
>
> Hence add a GPIO driver to aggregate existing GPIOs, and expose them as
> a new gpiochip.
>
> This supports the following use cases:
> - Aggregating GPIOs using Sysfs
> This is useful for implementing access control, and assigning a set
> of GPIOs to a specific user or virtual machine.
> - Generic GPIO Driver
> This is useful for industrial control, where it can provide
> userspace access to a simple GPIO-operated device described in DT,
> cfr. e.g. spidev for SPI-operated devices.
>
> Signed-off-by: Geert Uytterhoeven <[email protected]>
> --- /dev/null
> +++ b/drivers/gpio/gpio-aggregator.c
> +static int gpio_fwd_set_config(struct gpio_chip *chip, unsigned int offset,
> + unsigned long config)
> +{
> + struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
> +
> + chip = fwd->descs[offset]->gdev->chip;
> + if (chip->set_config)
- chip = fwd->descs[offset]->gdev->chip;
- if (chip->set_config)
+ chip = gpiod_to_chip(fwd->descs[offset]);
+ if (chip && chip->set_config)
> + return chip->set_config(chip, offset, config);
This is not correct: offset should be translated, too, i.e.
offset = gpio_chip_hwgpio(fwd->descs[offset]);
Which adds a new dependency on "gpiolib.h"...
Is there a better alternative, than providing a public gpiod_set_config()
helper?
Thanks!
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds