mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-08 00:29:36 -04:00
Merge tag 'gpio-updates-for-v5.10-part2' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux into devel
gpio updates for v5.10 - part 2 - refactor gpio-mockup testing module - simplify the code in gpio-mpc8xxx - implement v2 of the GPIO user API
This commit is contained in:
50
Documentation/admin-guide/gpio/gpio-mockup.rst
Normal file
50
Documentation/admin-guide/gpio/gpio-mockup.rst
Normal file
@@ -0,0 +1,50 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
GPIO Testing Driver
|
||||
===================
|
||||
|
||||
The GPIO Testing Driver (gpio-mockup) provides a way to create simulated GPIO
|
||||
chips for testing purposes. The lines exposed by these chips can be accessed
|
||||
using the standard GPIO character device interface as well as manipulated
|
||||
using the dedicated debugfs directory structure.
|
||||
|
||||
Creating simulated chips using module params
|
||||
--------------------------------------------
|
||||
|
||||
When loading the gpio-mockup driver a number of parameters can be passed to the
|
||||
module.
|
||||
|
||||
gpio_mockup_ranges
|
||||
|
||||
This parameter takes an argument in the form of an array of integer
|
||||
pairs. Each pair defines the base GPIO number (if any) and the number
|
||||
of lines exposed by the chip. If the base GPIO is -1, the gpiolib
|
||||
will assign it automatically.
|
||||
|
||||
Example: gpio_mockup_ranges=-1,8,-1,16,405,4
|
||||
|
||||
The line above creates three chips. The first one will expose 8 lines,
|
||||
the second 16 and the third 4. The base GPIO for the third chip is set
|
||||
to 405 while for two first chips it will be assigned automatically.
|
||||
|
||||
gpio_named_lines
|
||||
|
||||
This parameter doesn't take any arguments. It lets the driver know that
|
||||
GPIO lines exposed by it should be named.
|
||||
|
||||
The name format is: gpio-mockup-X-Y where X is mockup chip's ID
|
||||
and Y is the line offset.
|
||||
|
||||
Manipulating simulated lines
|
||||
----------------------------
|
||||
|
||||
Each mockup chip creates its own subdirectory in /sys/kernel/debug/gpio-mockup/.
|
||||
The directory is named after the chip's label. A symlink is also created, named
|
||||
after the chip's name, which points to the label directory.
|
||||
|
||||
Inside each subdirectory, there's a separate attribute for each GPIO line. The
|
||||
name of the attribute represents the line's offset in the chip.
|
||||
|
||||
Reading from a line attribute returns the current value. Writing to it (0 or 1)
|
||||
changes the configuration of the simulated pull-up/pull-down resistor
|
||||
(1 - pull-up, 0 - pull-down).
|
||||
@@ -66,8 +66,33 @@ config GPIO_SYSFS
|
||||
|
||||
This ABI is deprecated. If you want to use GPIO from userspace,
|
||||
use the character device /dev/gpiochipN with the appropriate
|
||||
ioctl() operations instead. The character device is always
|
||||
available.
|
||||
ioctl() operations instead.
|
||||
|
||||
config GPIO_CDEV
|
||||
bool
|
||||
prompt "Character device (/dev/gpiochipN) support" if EXPERT
|
||||
default y
|
||||
help
|
||||
Say Y here to add the character device /dev/gpiochipN interface
|
||||
for GPIOs. The character device allows userspace to control GPIOs
|
||||
using ioctl() operations.
|
||||
|
||||
Only say N if you are sure that the GPIO character device is not
|
||||
required.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config GPIO_CDEV_V1
|
||||
bool "Support GPIO ABI Version 1"
|
||||
default y
|
||||
depends on GPIO_CDEV
|
||||
help
|
||||
Say Y here to support version 1 of the GPIO CDEV ABI.
|
||||
|
||||
This ABI version is deprecated.
|
||||
Please use the latest ABI for new developments.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config GPIO_GENERIC
|
||||
depends on HAS_IOMEM # Only for IOMEM drivers
|
||||
|
||||
@@ -6,8 +6,8 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib-cdev.o
|
||||
obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
|
||||
obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o
|
||||
obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
|
||||
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irq_sim.h>
|
||||
@@ -19,21 +19,19 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
|
||||
#define GPIO_MOCKUP_NAME "gpio-mockup"
|
||||
#define GPIO_MOCKUP_MAX_GC 10
|
||||
/*
|
||||
* We're storing two values per chip: the GPIO base and the number
|
||||
* of GPIO lines.
|
||||
*/
|
||||
#define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2)
|
||||
/* Maximum of three properties + the sentinel. */
|
||||
#define GPIO_MOCKUP_MAX_PROP 4
|
||||
|
||||
#define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__)
|
||||
/* Maximum of four properties + the sentinel. */
|
||||
#define GPIO_MOCKUP_MAX_PROP 5
|
||||
|
||||
/*
|
||||
* struct gpio_pin_status - structure describing a GPIO status
|
||||
@@ -375,31 +373,6 @@ static void gpio_mockup_debugfs_setup(struct device *dev,
|
||||
debugfs_create_file(name, 0200, chip->dbg_dir, priv,
|
||||
&gpio_mockup_debugfs_ops);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int gpio_mockup_name_lines(struct device *dev,
|
||||
struct gpio_mockup_chip *chip)
|
||||
{
|
||||
struct gpio_chip *gc = &chip->gc;
|
||||
char **names;
|
||||
int i;
|
||||
|
||||
names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL);
|
||||
if (!names)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < gc->ngpio; i++) {
|
||||
names[i] = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"%s-%d", gc->label, i);
|
||||
if (!names[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
gc->names = (const char *const *)names;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpio_mockup_dispose_mappings(void *data)
|
||||
@@ -434,21 +407,14 @@ static int gpio_mockup_probe(struct platform_device *pdev)
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv = device_property_read_string(dev, "chip-name", &name);
|
||||
rv = device_property_read_string(dev, "chip-label", &name);
|
||||
if (rv)
|
||||
name = NULL;
|
||||
name = dev_name(dev);
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!name) {
|
||||
name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"%s-%c", pdev->name, pdev->id + 'A');
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&chip->lock);
|
||||
|
||||
gc = &chip->gc;
|
||||
@@ -476,12 +442,6 @@ static int gpio_mockup_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < gc->ngpio; i++)
|
||||
chip->lines[i].dir = GPIO_LINE_DIRECTION_IN;
|
||||
|
||||
if (device_property_read_bool(dev, "named-gpio-lines")) {
|
||||
rv = gpio_mockup_name_lines(dev, chip);
|
||||
if (rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
chip->irq_sim_domain = devm_irq_domain_create_sim(dev, NULL,
|
||||
gc->ngpio);
|
||||
if (IS_ERR(chip->irq_sim_domain))
|
||||
@@ -502,7 +462,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver gpio_mockup_driver = {
|
||||
.driver = {
|
||||
.name = GPIO_MOCKUP_NAME,
|
||||
.name = "gpio-mockup",
|
||||
},
|
||||
.probe = gpio_mockup_probe,
|
||||
};
|
||||
@@ -522,14 +482,80 @@ static void gpio_mockup_unregister_pdevs(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int __init gpio_mockup_init(void)
|
||||
static __init char **gpio_mockup_make_line_names(const char *label,
|
||||
unsigned int num_lines)
|
||||
{
|
||||
unsigned int i;
|
||||
char **names;
|
||||
|
||||
names = kcalloc(num_lines + 1, sizeof(char *), GFP_KERNEL);
|
||||
if (!names)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < num_lines; i++) {
|
||||
names[i] = kasprintf(GFP_KERNEL, "%s-%u", label, i);
|
||||
if (!names[i]) {
|
||||
kfree_strarray(names, i);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
static int __init gpio_mockup_register_chip(int idx)
|
||||
{
|
||||
struct property_entry properties[GPIO_MOCKUP_MAX_PROP];
|
||||
int i, prop, num_chips, err = 0, base;
|
||||
struct platform_device_info pdevinfo;
|
||||
struct platform_device *pdev;
|
||||
char **line_names = NULL;
|
||||
char chip_label[32];
|
||||
int prop = 0, base;
|
||||
u16 ngpio;
|
||||
|
||||
memset(properties, 0, sizeof(properties));
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
|
||||
snprintf(chip_label, sizeof(chip_label), "gpio-mockup-%c", idx + 'A');
|
||||
properties[prop++] = PROPERTY_ENTRY_STRING("chip-label", chip_label);
|
||||
|
||||
base = gpio_mockup_range_base(idx);
|
||||
if (base >= 0)
|
||||
properties[prop++] = PROPERTY_ENTRY_U32("gpio-base", base);
|
||||
|
||||
ngpio = base < 0 ? gpio_mockup_range_ngpio(idx)
|
||||
: gpio_mockup_range_ngpio(idx) - base;
|
||||
properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);
|
||||
|
||||
if (gpio_mockup_named_lines) {
|
||||
line_names = gpio_mockup_make_line_names(chip_label, ngpio);
|
||||
if (!line_names)
|
||||
return -ENOMEM;
|
||||
|
||||
properties[prop++] = PROPERTY_ENTRY_STRING_ARRAY_LEN(
|
||||
"gpio-line-names", line_names, ngpio);
|
||||
}
|
||||
|
||||
pdevinfo.name = "gpio-mockup";
|
||||
pdevinfo.id = idx;
|
||||
pdevinfo.properties = properties;
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
kfree_strarray(line_names, ngpio);
|
||||
if (IS_ERR(pdev)) {
|
||||
pr_err("error registering device");
|
||||
return PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
gpio_mockup_pdevs[idx] = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init gpio_mockup_init(void)
|
||||
{
|
||||
int i, num_chips, err;
|
||||
|
||||
if ((gpio_mockup_num_ranges < 2) ||
|
||||
(gpio_mockup_num_ranges % 2) ||
|
||||
(gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES))
|
||||
@@ -551,41 +577,19 @@ static int __init gpio_mockup_init(void)
|
||||
|
||||
err = platform_driver_register(&gpio_mockup_driver);
|
||||
if (err) {
|
||||
gpio_mockup_err("error registering platform driver\n");
|
||||
pr_err("error registering platform driver\n");
|
||||
debugfs_remove_recursive(gpio_mockup_dbg_dir);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_chips; i++) {
|
||||
memset(properties, 0, sizeof(properties));
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
prop = 0;
|
||||
|
||||
base = gpio_mockup_range_base(i);
|
||||
if (base >= 0)
|
||||
properties[prop++] = PROPERTY_ENTRY_U32("gpio-base",
|
||||
base);
|
||||
|
||||
ngpio = base < 0 ? gpio_mockup_range_ngpio(i)
|
||||
: gpio_mockup_range_ngpio(i) - base;
|
||||
properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);
|
||||
|
||||
if (gpio_mockup_named_lines)
|
||||
properties[prop++] = PROPERTY_ENTRY_BOOL(
|
||||
"named-gpio-lines");
|
||||
|
||||
pdevinfo.name = GPIO_MOCKUP_NAME;
|
||||
pdevinfo.id = i;
|
||||
pdevinfo.properties = properties;
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
gpio_mockup_err("error registering device");
|
||||
err = gpio_mockup_register_chip(i);
|
||||
if (err) {
|
||||
platform_driver_unregister(&gpio_mockup_driver);
|
||||
gpio_mockup_unregister_pdevs();
|
||||
return PTR_ERR(pdev);
|
||||
debugfs_remove_recursive(gpio_mockup_dbg_dir);
|
||||
return err;
|
||||
}
|
||||
|
||||
gpio_mockup_pdevs[i] = pdev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -47,27 +47,6 @@ struct mpc8xxx_gpio_chip {
|
||||
unsigned int irqn;
|
||||
};
|
||||
|
||||
/* The GPIO Input Buffer Enable register(GPIO_IBE) is used to
|
||||
* control the input enable of each individual GPIO port.
|
||||
* When an individual GPIO port’s direction is set to
|
||||
* input (GPIO_GPDIR[DRn=0]), the associated input enable must be
|
||||
* set (GPIOxGPIE[IEn]=1) to propagate the port value to the GPIO
|
||||
* Data Register.
|
||||
*/
|
||||
static int ls1028a_gpio_dir_in_init(struct gpio_chip *gc)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc);
|
||||
|
||||
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
||||
|
||||
gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff);
|
||||
|
||||
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This hardware has a big endian bit assignment such that GPIO line 0 is
|
||||
* connected to bit 31, line 1 to bit 30 ... line 31 to bit 0.
|
||||
@@ -283,7 +262,6 @@ static const struct irq_domain_ops mpc8xxx_gpio_irq_ops = {
|
||||
};
|
||||
|
||||
struct mpc8xxx_gpio_devtype {
|
||||
int (*gpio_dir_in_init)(struct gpio_chip *chip);
|
||||
int (*gpio_dir_out)(struct gpio_chip *, unsigned int, int);
|
||||
int (*gpio_get)(struct gpio_chip *, unsigned int);
|
||||
int (*irq_set_type)(struct irq_data *, unsigned int);
|
||||
@@ -294,11 +272,6 @@ static const struct mpc8xxx_gpio_devtype mpc512x_gpio_devtype = {
|
||||
.irq_set_type = mpc512x_irq_set_type,
|
||||
};
|
||||
|
||||
static const struct mpc8xxx_gpio_devtype ls1028a_gpio_devtype = {
|
||||
.gpio_dir_in_init = ls1028a_gpio_dir_in_init,
|
||||
.irq_set_type = mpc8xxx_irq_set_type,
|
||||
};
|
||||
|
||||
static const struct mpc8xxx_gpio_devtype mpc5125_gpio_devtype = {
|
||||
.gpio_dir_out = mpc5125_gpio_dir_out,
|
||||
.irq_set_type = mpc512x_irq_set_type,
|
||||
@@ -319,8 +292,8 @@ static const struct of_device_id mpc8xxx_gpio_ids[] = {
|
||||
{ .compatible = "fsl,mpc5121-gpio", .data = &mpc512x_gpio_devtype, },
|
||||
{ .compatible = "fsl,mpc5125-gpio", .data = &mpc5125_gpio_devtype, },
|
||||
{ .compatible = "fsl,pq3-gpio", },
|
||||
{ .compatible = "fsl,ls1028a-gpio", .data = &ls1028a_gpio_devtype, },
|
||||
{ .compatible = "fsl,ls1088a-gpio", .data = &ls1028a_gpio_devtype, },
|
||||
{ .compatible = "fsl,ls1028a-gpio", },
|
||||
{ .compatible = "fsl,ls1088a-gpio", },
|
||||
{ .compatible = "fsl,qoriq-gpio", },
|
||||
{}
|
||||
};
|
||||
@@ -389,7 +362,16 @@ static int mpc8xxx_probe(struct platform_device *pdev)
|
||||
|
||||
gc->to_irq = mpc8xxx_gpio_to_irq;
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,qoriq-gpio"))
|
||||
/*
|
||||
* The GPIO Input Buffer Enable register(GPIO_IBE) is used to control
|
||||
* the input enable of each individual GPIO port. When an individual
|
||||
* GPIO port’s direction is set to input (GPIO_GPDIR[DRn=0]), the
|
||||
* associated input enable must be set (GPIOxGPIE[IEn]=1) to propagate
|
||||
* the port value to the GPIO Data Register.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "fsl,qoriq-gpio") ||
|
||||
of_device_is_compatible(np, "fsl,ls1028a-gpio") ||
|
||||
of_device_is_compatible(np, "fsl,ls1088a-gpio"))
|
||||
gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff);
|
||||
|
||||
ret = gpiochip_add_data(gc, mpc8xxx_gc);
|
||||
@@ -411,9 +393,6 @@ static int mpc8xxx_probe(struct platform_device *pdev)
|
||||
/* ack and mask all irqs */
|
||||
gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff);
|
||||
gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0);
|
||||
/* enable input buffer */
|
||||
if (devtype->gpio_dir_in_init)
|
||||
devtype->gpio_dir_in_init(gc);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn,
|
||||
mpc8xxx_gpio_irq_cascade,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,22 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
#ifdef CONFIG_GPIO_CDEV
|
||||
|
||||
int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt);
|
||||
void gpiolib_cdev_unregister(struct gpio_device *gdev);
|
||||
|
||||
#else
|
||||
|
||||
static inline int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void gpiolib_cdev_unregister(struct gpio_device *gdev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_GPIO_CDEV */
|
||||
|
||||
#endif /* GPIOLIB_CDEV_H */
|
||||
|
||||
@@ -2092,9 +2092,14 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
|
||||
clear_bit(FLAG_PULL_UP, &desc->flags);
|
||||
clear_bit(FLAG_PULL_DOWN, &desc->flags);
|
||||
clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
|
||||
clear_bit(FLAG_EDGE_RISING, &desc->flags);
|
||||
clear_bit(FLAG_EDGE_FALLING, &desc->flags);
|
||||
clear_bit(FLAG_IS_HOGGED, &desc->flags);
|
||||
#ifdef CONFIG_OF_DYNAMIC
|
||||
desc->hog = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_GPIO_CDEV
|
||||
WRITE_ONCE(desc->debounce_period_us, 0);
|
||||
#endif
|
||||
ret = true;
|
||||
}
|
||||
|
||||
@@ -114,6 +114,8 @@ struct gpio_desc {
|
||||
#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
|
||||
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
|
||||
#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
|
||||
#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
|
||||
#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
|
||||
|
||||
/* Connection label */
|
||||
const char *label;
|
||||
@@ -122,6 +124,10 @@ struct gpio_desc {
|
||||
#ifdef CONFIG_OF_DYNAMIC
|
||||
struct device_node *hog;
|
||||
#endif
|
||||
#ifdef CONFIG_GPIO_CDEV
|
||||
/* debounce period in microseconds */
|
||||
unsigned int debounce_period_us;
|
||||
#endif
|
||||
};
|
||||
|
||||
int gpiod_request(struct gpio_desc *desc, const char *label);
|
||||
|
||||
@@ -94,4 +94,6 @@ char *kstrdup_quotable(const char *src, gfp_t gfp);
|
||||
char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp);
|
||||
char *kstrdup_quotable_file(struct file *file, gfp_t gfp);
|
||||
|
||||
void kfree_strarray(char **array, size_t n);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,9 +11,17 @@
|
||||
#ifndef _UAPI_GPIO_H_
|
||||
#define _UAPI_GPIO_H_
|
||||
|
||||
#include <linux/const.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* The maximum size of name and label arrays.
|
||||
*
|
||||
* Must be a multiple of 8 to ensure 32/64-bit alignment of structs.
|
||||
*/
|
||||
#define GPIO_MAX_NAME_SIZE 32
|
||||
|
||||
/**
|
||||
* struct gpiochip_info - Information about a certain GPIO chip
|
||||
* @name: the Linux kernel name of this GPIO chip
|
||||
@@ -22,11 +30,273 @@
|
||||
* @lines: number of GPIO lines on this chip
|
||||
*/
|
||||
struct gpiochip_info {
|
||||
char name[32];
|
||||
char label[32];
|
||||
char name[GPIO_MAX_NAME_SIZE];
|
||||
char label[GPIO_MAX_NAME_SIZE];
|
||||
__u32 lines;
|
||||
};
|
||||
|
||||
/*
|
||||
* Maximum number of requested lines.
|
||||
*
|
||||
* Must be no greater than 64, as bitmaps are restricted here to 64-bits
|
||||
* for simplicity, and a multiple of 2 to ensure 32/64-bit alignment of
|
||||
* structs.
|
||||
*/
|
||||
#define GPIO_V2_LINES_MAX 64
|
||||
|
||||
/*
|
||||
* The maximum number of configuration attributes associated with a line
|
||||
* request.
|
||||
*/
|
||||
#define GPIO_V2_LINE_NUM_ATTRS_MAX 10
|
||||
|
||||
/**
|
||||
* enum gpio_v2_line_flag - &struct gpio_v2_line_attribute.flags values
|
||||
* @GPIO_V2_LINE_FLAG_USED: line is not available for request
|
||||
* @GPIO_V2_LINE_FLAG_ACTIVE_LOW: line active state is physical low
|
||||
* @GPIO_V2_LINE_FLAG_INPUT: line is an input
|
||||
* @GPIO_V2_LINE_FLAG_OUTPUT: line is an output
|
||||
* @GPIO_V2_LINE_FLAG_EDGE_RISING: line detects rising (inactive to active)
|
||||
* edges
|
||||
* @GPIO_V2_LINE_FLAG_EDGE_FALLING: line detects falling (active to
|
||||
* inactive) edges
|
||||
* @GPIO_V2_LINE_FLAG_OPEN_DRAIN: line is an open drain output
|
||||
* @GPIO_V2_LINE_FLAG_OPEN_SOURCE: line is an open source output
|
||||
* @GPIO_V2_LINE_FLAG_BIAS_PULL_UP: line has pull-up bias enabled
|
||||
* @GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN: line has pull-down bias enabled
|
||||
* @GPIO_V2_LINE_FLAG_BIAS_DISABLED: line has bias disabled
|
||||
*/
|
||||
enum gpio_v2_line_flag {
|
||||
GPIO_V2_LINE_FLAG_USED = _BITULL(0),
|
||||
GPIO_V2_LINE_FLAG_ACTIVE_LOW = _BITULL(1),
|
||||
GPIO_V2_LINE_FLAG_INPUT = _BITULL(2),
|
||||
GPIO_V2_LINE_FLAG_OUTPUT = _BITULL(3),
|
||||
GPIO_V2_LINE_FLAG_EDGE_RISING = _BITULL(4),
|
||||
GPIO_V2_LINE_FLAG_EDGE_FALLING = _BITULL(5),
|
||||
GPIO_V2_LINE_FLAG_OPEN_DRAIN = _BITULL(6),
|
||||
GPIO_V2_LINE_FLAG_OPEN_SOURCE = _BITULL(7),
|
||||
GPIO_V2_LINE_FLAG_BIAS_PULL_UP = _BITULL(8),
|
||||
GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN = _BITULL(9),
|
||||
GPIO_V2_LINE_FLAG_BIAS_DISABLED = _BITULL(10),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_v2_line_values - Values of GPIO lines
|
||||
* @bits: a bitmap containing the value of the lines, set to 1 for active
|
||||
* and 0 for inactive.
|
||||
* @mask: a bitmap identifying the lines to get or set, with each bit
|
||||
* number corresponding to the index into &struct
|
||||
* gpio_v2_line_request.offsets.
|
||||
*/
|
||||
struct gpio_v2_line_values {
|
||||
__aligned_u64 bits;
|
||||
__aligned_u64 mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum gpio_v2_line_attr_id - &struct gpio_v2_line_attribute.id values
|
||||
* identifying which field of the attribute union is in use.
|
||||
* @GPIO_V2_LINE_ATTR_ID_FLAGS: flags field is in use
|
||||
* @GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES: values field is in use
|
||||
* @GPIO_V2_LINE_ATTR_ID_DEBOUNCE: debounce_period_us is in use
|
||||
*/
|
||||
enum gpio_v2_line_attr_id {
|
||||
GPIO_V2_LINE_ATTR_ID_FLAGS = 1,
|
||||
GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES = 2,
|
||||
GPIO_V2_LINE_ATTR_ID_DEBOUNCE = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_v2_line_attribute - a configurable attribute of a line
|
||||
* @id: attribute identifier with value from &enum gpio_v2_line_attr_id
|
||||
* @padding: reserved for future use and must be zero filled
|
||||
* @flags: if id is GPIO_V2_LINE_ATTR_ID_FLAGS, the flags for the GPIO
|
||||
* line, with values from enum gpio_v2_line_flag, such as
|
||||
* GPIO_V2_LINE_FLAG_ACTIVE_LOW, GPIO_V2_LINE_FLAG_OUTPUT etc, OR:ed
|
||||
* together. This overrides the default flags contained in the &struct
|
||||
* gpio_v2_line_config for the associated line.
|
||||
* @values: if id is GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES, a bitmap
|
||||
* containing the values to which the lines will be set, with each bit
|
||||
* number corresponding to the index into &struct
|
||||
* gpio_v2_line_request.offsets.
|
||||
* @debounce_period_us: if id is GPIO_V2_LINE_ATTR_ID_DEBOUNCE, the desired
|
||||
* debounce period, in microseconds
|
||||
*/
|
||||
struct gpio_v2_line_attribute {
|
||||
__u32 id;
|
||||
__u32 padding;
|
||||
union {
|
||||
__aligned_u64 flags;
|
||||
__aligned_u64 values;
|
||||
__u32 debounce_period_us;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_v2_line_config_attribute - a configuration attribute
|
||||
* associated with one or more of the requested lines.
|
||||
* @attr: the configurable attribute
|
||||
* @mask: a bitmap identifying the lines to which the attribute applies,
|
||||
* with each bit number corresponding to the index into &struct
|
||||
* gpio_v2_line_request.offsets.
|
||||
*/
|
||||
struct gpio_v2_line_config_attribute {
|
||||
struct gpio_v2_line_attribute attr;
|
||||
__aligned_u64 mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_v2_line_config - Configuration for GPIO lines
|
||||
* @flags: flags for the GPIO lines, with values from enum
|
||||
* gpio_v2_line_flag, such as GPIO_V2_LINE_FLAG_ACTIVE_LOW,
|
||||
* GPIO_V2_LINE_FLAG_OUTPUT etc, OR:ed together. This is the default for
|
||||
* all requested lines but may be overridden for particular lines using
|
||||
* attrs.
|
||||
* @num_attrs: the number of attributes in attrs
|
||||
* @padding: reserved for future use and must be zero filled
|
||||
* @attrs: the configuration attributes associated with the requested
|
||||
* lines. Any attribute should only be associated with a particular line
|
||||
* once. If an attribute is associated with a line multiple times then the
|
||||
* first occurrence (i.e. lowest index) has precedence.
|
||||
*/
|
||||
struct gpio_v2_line_config {
|
||||
__aligned_u64 flags;
|
||||
__u32 num_attrs;
|
||||
/* Pad to fill implicit padding and reserve space for future use. */
|
||||
__u32 padding[5];
|
||||
struct gpio_v2_line_config_attribute attrs[GPIO_V2_LINE_NUM_ATTRS_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_v2_line_request - Information about a request for GPIO lines
|
||||
* @offsets: an array of desired lines, specified by offset index for the
|
||||
* associated GPIO chip
|
||||
* @consumer: a desired consumer label for the selected GPIO lines such as
|
||||
* "my-bitbanged-relay"
|
||||
* @config: requested configuration for the lines.
|
||||
* @num_lines: number of lines requested in this request, i.e. the number
|
||||
* of valid fields in the GPIO_V2_LINES_MAX sized arrays, set to 1 to
|
||||
* request a single line
|
||||
* @event_buffer_size: a suggested minimum number of line events that the
|
||||
* kernel should buffer. This is only relevant if edge detection is
|
||||
* enabled in the configuration. Note that this is only a suggested value
|
||||
* and the kernel may allocate a larger buffer or cap the size of the
|
||||
* buffer. If this field is zero then the buffer size defaults to a minimum
|
||||
* of num_lines*16.
|
||||
* @padding: reserved for future use and must be zero filled
|
||||
* @fd: if successful this field will contain a valid anonymous file handle
|
||||
* after a GPIO_GET_LINE_IOCTL operation, zero or negative value means
|
||||
* error
|
||||
*/
|
||||
struct gpio_v2_line_request {
|
||||
__u32 offsets[GPIO_V2_LINES_MAX];
|
||||
char consumer[GPIO_MAX_NAME_SIZE];
|
||||
struct gpio_v2_line_config config;
|
||||
__u32 num_lines;
|
||||
__u32 event_buffer_size;
|
||||
/* Pad to fill implicit padding and reserve space for future use. */
|
||||
__u32 padding[5];
|
||||
__s32 fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_v2_line_info - Information about a certain GPIO line
|
||||
* @name: the name of this GPIO line, such as the output pin of the line on
|
||||
* the chip, a rail or a pin header name on a board, as specified by the
|
||||
* GPIO chip, may be empty
|
||||
* @consumer: a functional name for the consumer of this GPIO line as set
|
||||
* by whatever is using it, will be empty if there is no current user but
|
||||
* may also be empty if the consumer doesn't set this up
|
||||
* @flags: flags for the GPIO line, such as GPIO_V2_LINE_FLAG_ACTIVE_LOW,
|
||||
* GPIO_V2_LINE_FLAG_OUTPUT etc, OR:ed together
|
||||
* @offset: the local offset on this GPIO chip, fill this in when
|
||||
* requesting the line information from the kernel
|
||||
* @num_attrs: the number of attributes in attrs
|
||||
* @attrs: the configuration attributes associated with the line
|
||||
* @padding: reserved for future use
|
||||
*/
|
||||
struct gpio_v2_line_info {
|
||||
char name[GPIO_MAX_NAME_SIZE];
|
||||
char consumer[GPIO_MAX_NAME_SIZE];
|
||||
__u32 offset;
|
||||
__u32 num_attrs;
|
||||
__aligned_u64 flags;
|
||||
struct gpio_v2_line_attribute attrs[GPIO_V2_LINE_NUM_ATTRS_MAX];
|
||||
/* Space reserved for future use. */
|
||||
__u32 padding[4];
|
||||
};
|
||||
|
||||
/**
|
||||
* enum gpio_v2_line_changed_type - &struct gpio_v2_line_changed.event_type
|
||||
* values
|
||||
* @GPIO_V2_LINE_CHANGED_REQUESTED: line has been requested
|
||||
* @GPIO_V2_LINE_CHANGED_RELEASED: line has been released
|
||||
* @GPIO_V2_LINE_CHANGED_CONFIG: line has been reconfigured
|
||||
*/
|
||||
enum gpio_v2_line_changed_type {
|
||||
GPIO_V2_LINE_CHANGED_REQUESTED = 1,
|
||||
GPIO_V2_LINE_CHANGED_RELEASED = 2,
|
||||
GPIO_V2_LINE_CHANGED_CONFIG = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_v2_line_info_changed - Information about a change in status
|
||||
* of a GPIO line
|
||||
* @info: updated line information
|
||||
* @timestamp_ns: estimate of time of status change occurrence, in nanoseconds
|
||||
* @event_type: the type of change with a value from enum
|
||||
* gpio_v2_line_changed_type
|
||||
* @padding: reserved for future use
|
||||
*/
|
||||
struct gpio_v2_line_info_changed {
|
||||
struct gpio_v2_line_info info;
|
||||
__aligned_u64 timestamp_ns;
|
||||
__u32 event_type;
|
||||
/* Pad struct to 64-bit boundary and reserve space for future use. */
|
||||
__u32 padding[5];
|
||||
};
|
||||
|
||||
/**
|
||||
* enum gpio_v2_line_event_id - &struct gpio_v2_line_event.id values
|
||||
* @GPIO_V2_LINE_EVENT_RISING_EDGE: event triggered by a rising edge
|
||||
* @GPIO_V2_LINE_EVENT_FALLING_EDGE: event triggered by a falling edge
|
||||
*/
|
||||
enum gpio_v2_line_event_id {
|
||||
GPIO_V2_LINE_EVENT_RISING_EDGE = 1,
|
||||
GPIO_V2_LINE_EVENT_FALLING_EDGE = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpio_v2_line_event - The actual event being pushed to userspace
|
||||
* @timestamp_ns: best estimate of time of event occurrence, in nanoseconds.
|
||||
* The timestamp_ns is read from CLOCK_MONOTONIC and is intended to allow the
|
||||
* accurate measurement of the time between events. It does not provide
|
||||
* the wall-clock time.
|
||||
* @id: event identifier with value from enum gpio_v2_line_event_id
|
||||
* @offset: the offset of the line that triggered the event
|
||||
* @seqno: the sequence number for this event in the sequence of events for
|
||||
* all the lines in this line request
|
||||
* @line_seqno: the sequence number for this event in the sequence of
|
||||
* events on this particular line
|
||||
* @padding: reserved for future use
|
||||
*/
|
||||
struct gpio_v2_line_event {
|
||||
__aligned_u64 timestamp_ns;
|
||||
__u32 id;
|
||||
__u32 offset;
|
||||
__u32 seqno;
|
||||
__u32 line_seqno;
|
||||
/* Space reserved for future use. */
|
||||
__u32 padding[6];
|
||||
};
|
||||
|
||||
/*
|
||||
* ABI v1
|
||||
*
|
||||
* This version of the ABI is deprecated.
|
||||
* Use the latest version of the ABI, defined above, instead.
|
||||
*/
|
||||
|
||||
/* Informational flags */
|
||||
#define GPIOLINE_FLAG_KERNEL (1UL << 0) /* Line used by the kernel */
|
||||
#define GPIOLINE_FLAG_IS_OUT (1UL << 1)
|
||||
@@ -48,12 +318,15 @@ struct gpiochip_info {
|
||||
* @consumer: a functional name for the consumer of this GPIO line as set by
|
||||
* whatever is using it, will be empty if there is no current user but may
|
||||
* also be empty if the consumer doesn't set this up
|
||||
*
|
||||
* This struct is part of ABI v1 and is deprecated.
|
||||
* Use struct gpio_v2_line_info instead.
|
||||
*/
|
||||
struct gpioline_info {
|
||||
__u32 line_offset;
|
||||
__u32 flags;
|
||||
char name[32];
|
||||
char consumer[32];
|
||||
char name[GPIO_MAX_NAME_SIZE];
|
||||
char consumer[GPIO_MAX_NAME_SIZE];
|
||||
};
|
||||
|
||||
/* Maximum number of requested handles */
|
||||
@@ -79,6 +352,9 @@ enum {
|
||||
* guarantee there are no implicit holes between it and subsequent members.
|
||||
* The 20-byte padding at the end makes sure we don't add any implicit padding
|
||||
* at the end of the structure on 64-bit architectures.
|
||||
*
|
||||
* This struct is part of ABI v1 and is deprecated.
|
||||
* Use struct gpio_v2_line_info_changed instead.
|
||||
*/
|
||||
struct gpioline_info_changed {
|
||||
struct gpioline_info info;
|
||||
@@ -118,12 +394,15 @@ struct gpioline_info_changed {
|
||||
* @fd: if successful this field will contain a valid anonymous file handle
|
||||
* after a GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value
|
||||
* means error
|
||||
*
|
||||
* This struct is part of ABI v1 and is deprecated.
|
||||
* Use struct gpio_v2_line_request instead.
|
||||
*/
|
||||
struct gpiohandle_request {
|
||||
__u32 lineoffsets[GPIOHANDLES_MAX];
|
||||
__u32 flags;
|
||||
__u8 default_values[GPIOHANDLES_MAX];
|
||||
char consumer_label[32];
|
||||
char consumer_label[GPIO_MAX_NAME_SIZE];
|
||||
__u32 lines;
|
||||
int fd;
|
||||
};
|
||||
@@ -137,6 +416,9 @@ struct gpiohandle_request {
|
||||
* this specifies the default output value, should be 0 (low) or
|
||||
* 1 (high), anything else than 0 or 1 will be interpreted as 1 (high)
|
||||
* @padding: reserved for future use and should be zero filled
|
||||
*
|
||||
* This struct is part of ABI v1 and is deprecated.
|
||||
* Use struct gpio_v2_line_config instead.
|
||||
*/
|
||||
struct gpiohandle_config {
|
||||
__u32 flags;
|
||||
@@ -144,21 +426,19 @@ struct gpiohandle_config {
|
||||
__u32 padding[4]; /* padding for future use */
|
||||
};
|
||||
|
||||
#define GPIOHANDLE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0a, struct gpiohandle_config)
|
||||
|
||||
/**
|
||||
* struct gpiohandle_data - Information of values on a GPIO handle
|
||||
* @values: when getting the state of lines this contains the current
|
||||
* state of a line, when setting the state of lines these should contain
|
||||
* the desired target state
|
||||
*
|
||||
* This struct is part of ABI v1 and is deprecated.
|
||||
* Use struct gpio_v2_line_values instead.
|
||||
*/
|
||||
struct gpiohandle_data {
|
||||
__u8 values[GPIOHANDLES_MAX];
|
||||
};
|
||||
|
||||
#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x08, struct gpiohandle_data)
|
||||
#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x09, struct gpiohandle_data)
|
||||
|
||||
/* Eventrequest flags */
|
||||
#define GPIOEVENT_REQUEST_RISING_EDGE (1UL << 0)
|
||||
#define GPIOEVENT_REQUEST_FALLING_EDGE (1UL << 1)
|
||||
@@ -177,12 +457,15 @@ struct gpiohandle_data {
|
||||
* @fd: if successful this field will contain a valid anonymous file handle
|
||||
* after a GPIO_GET_LINEEVENT_IOCTL operation, zero or negative value
|
||||
* means error
|
||||
*
|
||||
* This struct is part of ABI v1 and is deprecated.
|
||||
* Use struct gpio_v2_line_request instead.
|
||||
*/
|
||||
struct gpioevent_request {
|
||||
__u32 lineoffset;
|
||||
__u32 handleflags;
|
||||
__u32 eventflags;
|
||||
char consumer_label[32];
|
||||
char consumer_label[GPIO_MAX_NAME_SIZE];
|
||||
int fd;
|
||||
};
|
||||
|
||||
@@ -196,17 +479,42 @@ struct gpioevent_request {
|
||||
* struct gpioevent_data - The actual event being pushed to userspace
|
||||
* @timestamp: best estimate of time of event occurrence, in nanoseconds
|
||||
* @id: event identifier
|
||||
*
|
||||
* This struct is part of ABI v1 and is deprecated.
|
||||
* Use struct gpio_v2_line_event instead.
|
||||
*/
|
||||
struct gpioevent_data {
|
||||
__u64 timestamp;
|
||||
__u32 id;
|
||||
};
|
||||
|
||||
/*
|
||||
* v1 and v2 ioctl()s
|
||||
*/
|
||||
#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info)
|
||||
#define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0C, __u32)
|
||||
|
||||
/*
|
||||
* v2 ioctl()s
|
||||
*/
|
||||
#define GPIO_V2_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x05, struct gpio_v2_line_info)
|
||||
#define GPIO_V2_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x06, struct gpio_v2_line_info)
|
||||
#define GPIO_V2_GET_LINE_IOCTL _IOWR(0xB4, 0x07, struct gpio_v2_line_request)
|
||||
#define GPIO_V2_LINE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0D, struct gpio_v2_line_config)
|
||||
#define GPIO_V2_LINE_GET_VALUES_IOCTL _IOWR(0xB4, 0x0E, struct gpio_v2_line_values)
|
||||
#define GPIO_V2_LINE_SET_VALUES_IOCTL _IOWR(0xB4, 0x0F, struct gpio_v2_line_values)
|
||||
|
||||
/*
|
||||
* v1 ioctl()s
|
||||
*
|
||||
* These ioctl()s are deprecated. Use the v2 equivalent instead.
|
||||
*/
|
||||
#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info)
|
||||
#define GPIO_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x0b, struct gpioline_info)
|
||||
#define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0c, __u32)
|
||||
#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request)
|
||||
#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)
|
||||
#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x08, struct gpiohandle_data)
|
||||
#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x09, struct gpiohandle_data)
|
||||
#define GPIOHANDLE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0A, struct gpiohandle_config)
|
||||
#define GPIO_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x0B, struct gpioline_info)
|
||||
|
||||
#endif /* _UAPI_GPIO_H_ */
|
||||
|
||||
@@ -649,3 +649,26 @@ char *kstrdup_quotable_file(struct file *file, gfp_t gfp)
|
||||
return pathname;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kstrdup_quotable_file);
|
||||
|
||||
/**
|
||||
* kfree_strarray - free a number of dynamically allocated strings contained
|
||||
* in an array and the array itself
|
||||
*
|
||||
* @array: Dynamically allocated array of strings to free.
|
||||
* @n: Number of strings (starting from the beginning of the array) to free.
|
||||
*
|
||||
* Passing a non-NULL @array and @n == 0 as well as NULL @array are valid
|
||||
* use-cases. If @array is NULL, the function does nothing.
|
||||
*/
|
||||
void kfree_strarray(char **array, size_t n)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!array)
|
||||
return;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
kfree(array[i]);
|
||||
kfree(array);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kfree_strarray);
|
||||
|
||||
@@ -23,17 +23,17 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/gpio.h>
|
||||
#include "gpio-utils.h"
|
||||
|
||||
int monitor_device(const char *device_name,
|
||||
unsigned int line,
|
||||
uint32_t handleflags,
|
||||
uint32_t eventflags,
|
||||
unsigned int *lines,
|
||||
unsigned int num_lines,
|
||||
struct gpio_v2_line_config *config,
|
||||
unsigned int loops)
|
||||
{
|
||||
struct gpioevent_request req;
|
||||
struct gpiohandle_data data;
|
||||
struct gpio_v2_line_values values;
|
||||
char *chrdev_name;
|
||||
int fd;
|
||||
int cfd, lfd;
|
||||
int ret;
|
||||
int i = 0;
|
||||
|
||||
@@ -41,44 +41,55 @@ int monitor_device(const char *device_name,
|
||||
if (ret < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(chrdev_name, 0);
|
||||
if (fd == -1) {
|
||||
cfd = open(chrdev_name, 0);
|
||||
if (cfd == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to open %s\n", chrdev_name);
|
||||
goto exit_free_name;
|
||||
}
|
||||
|
||||
req.lineoffset = line;
|
||||
req.handleflags = handleflags;
|
||||
req.eventflags = eventflags;
|
||||
strcpy(req.consumer_label, "gpio-event-mon");
|
||||
|
||||
ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GET EVENT "
|
||||
"IOCTL (%d)\n",
|
||||
ret);
|
||||
goto exit_close_error;
|
||||
}
|
||||
ret = gpiotools_request_line(device_name, lines, num_lines, config,
|
||||
"gpio-event-mon");
|
||||
if (ret < 0)
|
||||
goto exit_device_close;
|
||||
else
|
||||
lfd = ret;
|
||||
|
||||
/* Read initial states */
|
||||
ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue GPIOHANDLE GET LINE "
|
||||
"VALUES IOCTL (%d)\n",
|
||||
values.mask = 0;
|
||||
values.bits = 0;
|
||||
for (i = 0; i < num_lines; i++)
|
||||
gpiotools_set_bit(&values.mask, i);
|
||||
ret = gpiotools_get_values(lfd, &values);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to issue GPIO LINE GET VALUES IOCTL (%d)\n",
|
||||
ret);
|
||||
goto exit_close_error;
|
||||
goto exit_line_close;
|
||||
}
|
||||
|
||||
fprintf(stdout, "Monitoring line %d on %s\n", line, device_name);
|
||||
fprintf(stdout, "Initial line value: %d\n", data.values[0]);
|
||||
if (num_lines == 1) {
|
||||
fprintf(stdout, "Monitoring line %d on %s\n", lines[0], device_name);
|
||||
fprintf(stdout, "Initial line value: %d\n",
|
||||
gpiotools_test_bit(values.bits, 0));
|
||||
} else {
|
||||
fprintf(stdout, "Monitoring lines %d", lines[0]);
|
||||
for (i = 1; i < num_lines - 1; i++)
|
||||
fprintf(stdout, ", %d", lines[i]);
|
||||
fprintf(stdout, " and %d on %s\n", lines[i], device_name);
|
||||
fprintf(stdout, "Initial line values: %d",
|
||||
gpiotools_test_bit(values.bits, 0));
|
||||
for (i = 1; i < num_lines - 1; i++)
|
||||
fprintf(stdout, ", %d",
|
||||
gpiotools_test_bit(values.bits, i));
|
||||
fprintf(stdout, " and %d\n",
|
||||
gpiotools_test_bit(values.bits, i));
|
||||
}
|
||||
|
||||
while (1) {
|
||||
struct gpioevent_data event;
|
||||
struct gpio_v2_line_event event;
|
||||
|
||||
ret = read(req.fd, &event, sizeof(event));
|
||||
ret = read(lfd, &event, sizeof(event));
|
||||
if (ret == -1) {
|
||||
if (errno == -EAGAIN) {
|
||||
fprintf(stderr, "nothing available\n");
|
||||
@@ -96,12 +107,14 @@ int monitor_device(const char *device_name,
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
fprintf(stdout, "GPIO EVENT %llu: ", event.timestamp);
|
||||
fprintf(stdout, "GPIO EVENT at %llu on line %d (%d|%d) ",
|
||||
event.timestamp_ns, event.offset, event.line_seqno,
|
||||
event.seqno);
|
||||
switch (event.id) {
|
||||
case GPIOEVENT_EVENT_RISING_EDGE:
|
||||
case GPIO_V2_LINE_EVENT_RISING_EDGE:
|
||||
fprintf(stdout, "rising edge");
|
||||
break;
|
||||
case GPIOEVENT_EVENT_FALLING_EDGE:
|
||||
case GPIO_V2_LINE_EVENT_FALLING_EDGE:
|
||||
fprintf(stdout, "falling edge");
|
||||
break;
|
||||
default:
|
||||
@@ -114,8 +127,11 @@ int monitor_device(const char *device_name,
|
||||
break;
|
||||
}
|
||||
|
||||
exit_close_error:
|
||||
if (close(fd) == -1)
|
||||
exit_line_close:
|
||||
if (close(lfd) == -1)
|
||||
perror("Failed to close line file");
|
||||
exit_device_close:
|
||||
if (close(cfd) == -1)
|
||||
perror("Failed to close GPIO character device file");
|
||||
exit_free_name:
|
||||
free(chrdev_name);
|
||||
@@ -127,29 +143,37 @@ void print_usage(void)
|
||||
fprintf(stderr, "Usage: gpio-event-mon [options]...\n"
|
||||
"Listen to events on GPIO lines, 0->1 1->0\n"
|
||||
" -n <name> Listen on GPIOs on a named device (must be stated)\n"
|
||||
" -o <n> Offset to monitor\n"
|
||||
" -o <n> Offset of line to monitor (may be repeated)\n"
|
||||
" -d Set line as open drain\n"
|
||||
" -s Set line as open source\n"
|
||||
" -r Listen for rising edges\n"
|
||||
" -f Listen for falling edges\n"
|
||||
" -b <n> Debounce the line with period n microseconds\n"
|
||||
" [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n"
|
||||
" -? This helptext\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"gpio-event-mon -n gpiochip0 -o 4 -r -f\n"
|
||||
"gpio-event-mon -n gpiochip0 -o 4 -r -f -b 10000\n"
|
||||
);
|
||||
}
|
||||
|
||||
#define EDGE_FLAGS \
|
||||
(GPIO_V2_LINE_FLAG_EDGE_RISING | \
|
||||
GPIO_V2_LINE_FLAG_EDGE_FALLING)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *device_name = NULL;
|
||||
unsigned int line = -1;
|
||||
unsigned int lines[GPIO_V2_LINES_MAX];
|
||||
unsigned int num_lines = 0;
|
||||
unsigned int loops = 0;
|
||||
uint32_t handleflags = GPIOHANDLE_REQUEST_INPUT;
|
||||
uint32_t eventflags = 0;
|
||||
int c;
|
||||
struct gpio_v2_line_config config;
|
||||
int c, attr, i;
|
||||
unsigned long debounce_period_us = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "c:n:o:dsrf?")) != -1) {
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags = GPIO_V2_LINE_FLAG_INPUT;
|
||||
while ((c = getopt(argc, argv, "c:n:o:b:dsrf?")) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
loops = strtoul(optarg, NULL, 10);
|
||||
@@ -158,19 +182,27 @@ int main(int argc, char **argv)
|
||||
device_name = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
line = strtoul(optarg, NULL, 10);
|
||||
if (num_lines >= GPIO_V2_LINES_MAX) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
lines[num_lines] = strtoul(optarg, NULL, 10);
|
||||
num_lines++;
|
||||
break;
|
||||
case 'b':
|
||||
debounce_period_us = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'd':
|
||||
handleflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
|
||||
config.flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN;
|
||||
break;
|
||||
case 's':
|
||||
handleflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
|
||||
config.flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE;
|
||||
break;
|
||||
case 'r':
|
||||
eventflags |= GPIOEVENT_REQUEST_RISING_EDGE;
|
||||
config.flags |= GPIO_V2_LINE_FLAG_EDGE_RISING;
|
||||
break;
|
||||
case 'f':
|
||||
eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE;
|
||||
config.flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING;
|
||||
break;
|
||||
case '?':
|
||||
print_usage();
|
||||
@@ -178,15 +210,23 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (!device_name || line == -1) {
|
||||
if (debounce_period_us) {
|
||||
attr = config.num_attrs;
|
||||
config.num_attrs++;
|
||||
for (i = 0; i < num_lines; i++)
|
||||
gpiotools_set_bit(&config.attrs[attr].mask, i);
|
||||
config.attrs[attr].attr.id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE;
|
||||
config.attrs[attr].attr.debounce_period_us = debounce_period_us;
|
||||
}
|
||||
|
||||
if (!device_name || num_lines == 0) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
if (!eventflags) {
|
||||
if (!(config.flags & EDGE_FLAGS)) {
|
||||
printf("No flags specified, listening on both rising and "
|
||||
"falling edges\n");
|
||||
eventflags = GPIOEVENT_REQUEST_BOTH_EDGES;
|
||||
config.flags |= EDGE_FLAGS;
|
||||
}
|
||||
return monitor_device(device_name, line, handleflags,
|
||||
eventflags, loops);
|
||||
return monitor_device(device_name, lines, num_lines, &config, loops);
|
||||
}
|
||||
|
||||
@@ -22,39 +22,46 @@
|
||||
#include <linux/gpio.h>
|
||||
#include "gpio-utils.h"
|
||||
|
||||
int hammer_device(const char *device_name, unsigned int *lines, int nlines,
|
||||
int hammer_device(const char *device_name, unsigned int *lines, int num_lines,
|
||||
unsigned int loops)
|
||||
{
|
||||
struct gpiohandle_data data;
|
||||
struct gpio_v2_line_values values;
|
||||
struct gpio_v2_line_config config;
|
||||
char swirr[] = "-\\|/";
|
||||
int fd;
|
||||
int ret;
|
||||
int i, j;
|
||||
unsigned int iteration = 0;
|
||||
|
||||
memset(&data.values, 0, sizeof(data.values));
|
||||
ret = gpiotools_request_linehandle(device_name, lines, nlines,
|
||||
GPIOHANDLE_REQUEST_OUTPUT, &data,
|
||||
"gpio-hammer");
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
|
||||
|
||||
ret = gpiotools_request_line(device_name, lines, num_lines,
|
||||
&config, "gpio-hammer");
|
||||
if (ret < 0)
|
||||
goto exit_error;
|
||||
else
|
||||
fd = ret;
|
||||
|
||||
ret = gpiotools_get_values(fd, &data);
|
||||
values.mask = 0;
|
||||
values.bits = 0;
|
||||
for (i = 0; i < num_lines; i++)
|
||||
gpiotools_set_bit(&values.mask, i);
|
||||
|
||||
ret = gpiotools_get_values(fd, &values);
|
||||
if (ret < 0)
|
||||
goto exit_close_error;
|
||||
|
||||
fprintf(stdout, "Hammer lines [");
|
||||
for (i = 0; i < nlines; i++) {
|
||||
for (i = 0; i < num_lines; i++) {
|
||||
fprintf(stdout, "%d", lines[i]);
|
||||
if (i != (nlines - 1))
|
||||
if (i != (num_lines - 1))
|
||||
fprintf(stdout, ", ");
|
||||
}
|
||||
fprintf(stdout, "] on %s, initial states: [", device_name);
|
||||
for (i = 0; i < nlines; i++) {
|
||||
fprintf(stdout, "%d", data.values[i]);
|
||||
if (i != (nlines - 1))
|
||||
for (i = 0; i < num_lines; i++) {
|
||||
fprintf(stdout, "%d", gpiotools_test_bit(values.bits, i));
|
||||
if (i != (num_lines - 1))
|
||||
fprintf(stdout, ", ");
|
||||
}
|
||||
fprintf(stdout, "]\n");
|
||||
@@ -63,15 +70,15 @@ int hammer_device(const char *device_name, unsigned int *lines, int nlines,
|
||||
j = 0;
|
||||
while (1) {
|
||||
/* Invert all lines so we blink */
|
||||
for (i = 0; i < nlines; i++)
|
||||
data.values[i] = !data.values[i];
|
||||
for (i = 0; i < num_lines; i++)
|
||||
gpiotools_change_bit(&values.bits, i);
|
||||
|
||||
ret = gpiotools_set_values(fd, &data);
|
||||
ret = gpiotools_set_values(fd, &values);
|
||||
if (ret < 0)
|
||||
goto exit_close_error;
|
||||
|
||||
/* Re-read values to get status */
|
||||
ret = gpiotools_get_values(fd, &data);
|
||||
ret = gpiotools_get_values(fd, &values);
|
||||
if (ret < 0)
|
||||
goto exit_close_error;
|
||||
|
||||
@@ -81,9 +88,10 @@ int hammer_device(const char *device_name, unsigned int *lines, int nlines,
|
||||
j = 0;
|
||||
|
||||
fprintf(stdout, "[");
|
||||
for (i = 0; i < nlines; i++) {
|
||||
fprintf(stdout, "%d: %d", lines[i], data.values[i]);
|
||||
if (i != (nlines - 1))
|
||||
for (i = 0; i < num_lines; i++) {
|
||||
fprintf(stdout, "%d: %d", lines[i],
|
||||
gpiotools_test_bit(values.bits, i));
|
||||
if (i != (num_lines - 1))
|
||||
fprintf(stdout, ", ");
|
||||
}
|
||||
fprintf(stdout, "]\r");
|
||||
@@ -97,7 +105,7 @@ int hammer_device(const char *device_name, unsigned int *lines, int nlines,
|
||||
ret = 0;
|
||||
|
||||
exit_close_error:
|
||||
gpiotools_release_linehandle(fd);
|
||||
gpiotools_release_line(fd);
|
||||
exit_error:
|
||||
return ret;
|
||||
}
|
||||
@@ -121,7 +129,7 @@ int main(int argc, char **argv)
|
||||
const char *device_name = NULL;
|
||||
unsigned int lines[GPIOHANDLES_MAX];
|
||||
unsigned int loops = 0;
|
||||
int nlines;
|
||||
int num_lines;
|
||||
int c;
|
||||
int i;
|
||||
|
||||
@@ -158,11 +166,11 @@ int main(int argc, char **argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
nlines = i;
|
||||
num_lines = i;
|
||||
|
||||
if (!device_name || !nlines) {
|
||||
if (!device_name || !num_lines) {
|
||||
print_usage();
|
||||
return -1;
|
||||
}
|
||||
return hammer_device(device_name, lines, nlines, loops);
|
||||
return hammer_device(device_name, lines, num_lines, loops);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
* such as "gpiochip0"
|
||||
* @lines: An array desired lines, specified by offset
|
||||
* index for the associated GPIO device.
|
||||
* @nline: The number of lines to request.
|
||||
* @num_lines: The number of lines to request.
|
||||
* @flag: The new flag for requsted gpio. Reference
|
||||
* "linux/gpio.h" for the meaning of flag.
|
||||
* @data: Default value will be set to gpio when flag is
|
||||
@@ -56,7 +56,7 @@
|
||||
* On failure return the errno.
|
||||
*/
|
||||
int gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
|
||||
unsigned int nlines, unsigned int flag,
|
||||
unsigned int num_lines, unsigned int flag,
|
||||
struct gpiohandle_data *data,
|
||||
const char *consumer_label)
|
||||
{
|
||||
@@ -78,12 +78,12 @@ int gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
|
||||
goto exit_free_name;
|
||||
}
|
||||
|
||||
for (i = 0; i < nlines; i++)
|
||||
for (i = 0; i < num_lines; i++)
|
||||
req.lineoffsets[i] = lines[i];
|
||||
|
||||
req.flags = flag;
|
||||
strcpy(req.consumer_label, consumer_label);
|
||||
req.lines = nlines;
|
||||
req.lines = num_lines;
|
||||
if (flag & GPIOHANDLE_REQUEST_OUTPUT)
|
||||
memcpy(req.default_values, data, sizeof(req.default_values));
|
||||
|
||||
@@ -100,20 +100,87 @@ int gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
|
||||
free(chrdev_name);
|
||||
return ret < 0 ? ret : req.fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* gpiotools_request_line() - request gpio lines in a gpiochip
|
||||
* @device_name: The name of gpiochip without prefix "/dev/",
|
||||
* such as "gpiochip0"
|
||||
* @lines: An array desired lines, specified by offset
|
||||
* index for the associated GPIO device.
|
||||
* @num_lines: The number of lines to request.
|
||||
* @config: The new config for requested gpio. Reference
|
||||
* "linux/gpio.h" for config details.
|
||||
* @consumer: The name of consumer, such as "sysfs",
|
||||
* "powerkey". This is useful for other users to
|
||||
* know who is using.
|
||||
*
|
||||
* Request gpio lines through the ioctl provided by chardev. User
|
||||
* could call gpiotools_set_values() and gpiotools_get_values() to
|
||||
* read and write respectively through the returned fd. Call
|
||||
* gpiotools_release_line() to release these lines after that.
|
||||
*
|
||||
* Return: On success return the fd;
|
||||
* On failure return the errno.
|
||||
*/
|
||||
int gpiotools_request_line(const char *device_name, unsigned int *lines,
|
||||
unsigned int num_lines,
|
||||
struct gpio_v2_line_config *config,
|
||||
const char *consumer)
|
||||
{
|
||||
struct gpio_v2_line_request req;
|
||||
char *chrdev_name;
|
||||
int fd;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = asprintf(&chrdev_name, "/dev/%s", device_name);
|
||||
if (ret < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(chrdev_name, 0);
|
||||
if (fd == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to open %s, %s\n",
|
||||
chrdev_name, strerror(errno));
|
||||
goto exit_free_name;
|
||||
}
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
for (i = 0; i < num_lines; i++)
|
||||
req.offsets[i] = lines[i];
|
||||
|
||||
req.config = *config;
|
||||
strcpy(req.consumer, consumer);
|
||||
req.num_lines = num_lines;
|
||||
|
||||
ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue %s (%d), %s\n",
|
||||
"GPIO_GET_LINE_IOCTL", ret, strerror(errno));
|
||||
}
|
||||
|
||||
if (close(fd) == -1)
|
||||
perror("Failed to close GPIO character device file");
|
||||
exit_free_name:
|
||||
free(chrdev_name);
|
||||
return ret < 0 ? ret : req.fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* gpiotools_set_values(): Set the value of gpio(s)
|
||||
* @fd: The fd returned by
|
||||
* gpiotools_request_linehandle().
|
||||
* @data: The array of values want to set.
|
||||
* gpiotools_request_line().
|
||||
* @values: The array of values want to set.
|
||||
*
|
||||
* Return: On success return 0;
|
||||
* On failure return the errno.
|
||||
*/
|
||||
int gpiotools_set_values(const int fd, struct gpiohandle_data *data)
|
||||
int gpiotools_set_values(const int fd, struct gpio_v2_line_values *values)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, data);
|
||||
ret = ioctl(fd, GPIO_V2_LINE_SET_VALUES_IOCTL, values);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue %s (%d), %s\n",
|
||||
@@ -127,17 +194,17 @@ int gpiotools_set_values(const int fd, struct gpiohandle_data *data)
|
||||
/**
|
||||
* gpiotools_get_values(): Get the value of gpio(s)
|
||||
* @fd: The fd returned by
|
||||
* gpiotools_request_linehandle().
|
||||
* @data: The array of values get from hardware.
|
||||
* gpiotools_request_line().
|
||||
* @values: The array of values get from hardware.
|
||||
*
|
||||
* Return: On success return 0;
|
||||
* On failure return the errno.
|
||||
*/
|
||||
int gpiotools_get_values(const int fd, struct gpiohandle_data *data)
|
||||
int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, data);
|
||||
ret = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, values);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Failed to issue %s (%d), %s\n",
|
||||
@@ -169,6 +236,27 @@ int gpiotools_release_linehandle(const int fd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gpiotools_release_line(): Release the line(s) of gpiochip
|
||||
* @fd: The fd returned by
|
||||
* gpiotools_request_line().
|
||||
*
|
||||
* Return: On success return 0;
|
||||
* On failure return the errno.
|
||||
*/
|
||||
int gpiotools_release_line(const int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = close(fd);
|
||||
if (ret == -1) {
|
||||
perror("Failed to close GPIO LINE device file");
|
||||
ret = -errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gpiotools_get(): Get value from specific line
|
||||
* @device_name: The name of gpiochip without prefix "/dev/",
|
||||
@@ -180,11 +268,14 @@ int gpiotools_release_linehandle(const int fd)
|
||||
*/
|
||||
int gpiotools_get(const char *device_name, unsigned int line)
|
||||
{
|
||||
struct gpiohandle_data data;
|
||||
int ret;
|
||||
unsigned int value;
|
||||
unsigned int lines[] = {line};
|
||||
|
||||
gpiotools_gets(device_name, lines, 1, &data);
|
||||
return data.values[0];
|
||||
ret = gpiotools_gets(device_name, lines, 1, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -194,28 +285,36 @@ int gpiotools_get(const char *device_name, unsigned int line)
|
||||
* such as "gpiochip0".
|
||||
* @lines: An array desired lines, specified by offset
|
||||
* index for the associated GPIO device.
|
||||
* @nline: The number of lines to request.
|
||||
* @data: The array of values get from gpiochip.
|
||||
* @num_lines: The number of lines to request.
|
||||
* @values: The array of values get from gpiochip.
|
||||
*
|
||||
* Return: On success return 0;
|
||||
* On failure return the errno.
|
||||
*/
|
||||
int gpiotools_gets(const char *device_name, unsigned int *lines,
|
||||
unsigned int nlines, struct gpiohandle_data *data)
|
||||
unsigned int num_lines, unsigned int *values)
|
||||
{
|
||||
int fd;
|
||||
int fd, i;
|
||||
int ret;
|
||||
int ret_close;
|
||||
struct gpio_v2_line_config config;
|
||||
struct gpio_v2_line_values lv;
|
||||
|
||||
ret = gpiotools_request_linehandle(device_name, lines, nlines,
|
||||
GPIOHANDLE_REQUEST_INPUT, data,
|
||||
CONSUMER);
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags = GPIO_V2_LINE_FLAG_INPUT;
|
||||
ret = gpiotools_request_line(device_name, lines, num_lines,
|
||||
&config, CONSUMER);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fd = ret;
|
||||
ret = gpiotools_get_values(fd, data);
|
||||
ret_close = gpiotools_release_linehandle(fd);
|
||||
for (i = 0; i < num_lines; i++)
|
||||
gpiotools_set_bit(&lv.mask, i);
|
||||
ret = gpiotools_get_values(fd, &lv);
|
||||
if (!ret)
|
||||
for (i = 0; i < num_lines; i++)
|
||||
values[i] = gpiotools_test_bit(lv.bits, i);
|
||||
ret_close = gpiotools_release_line(fd);
|
||||
return ret < 0 ? ret : ret_close;
|
||||
}
|
||||
|
||||
@@ -232,11 +331,9 @@ int gpiotools_gets(const char *device_name, unsigned int *lines,
|
||||
int gpiotools_set(const char *device_name, unsigned int line,
|
||||
unsigned int value)
|
||||
{
|
||||
struct gpiohandle_data data;
|
||||
unsigned int lines[] = {line};
|
||||
|
||||
data.values[0] = value;
|
||||
return gpiotools_sets(device_name, lines, 1, &data);
|
||||
return gpiotools_sets(device_name, lines, 1, &value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,23 +342,32 @@ int gpiotools_set(const char *device_name, unsigned int line,
|
||||
* such as "gpiochip0".
|
||||
* @lines: An array desired lines, specified by offset
|
||||
* index for the associated GPIO device.
|
||||
* @nline: The number of lines to request.
|
||||
* @data: The array of values set to gpiochip, must be
|
||||
* @num_lines: The number of lines to request.
|
||||
* @value: The array of values set to gpiochip, must be
|
||||
* 0(low) or 1(high).
|
||||
*
|
||||
* Return: On success return 0;
|
||||
* On failure return the errno.
|
||||
*/
|
||||
int gpiotools_sets(const char *device_name, unsigned int *lines,
|
||||
unsigned int nlines, struct gpiohandle_data *data)
|
||||
unsigned int num_lines, unsigned int *values)
|
||||
{
|
||||
int ret;
|
||||
int ret, i;
|
||||
struct gpio_v2_line_config config;
|
||||
|
||||
ret = gpiotools_request_linehandle(device_name, lines, nlines,
|
||||
GPIOHANDLE_REQUEST_OUTPUT, data,
|
||||
CONSUMER);
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
|
||||
config.num_attrs = 1;
|
||||
config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
|
||||
for (i = 0; i < num_lines; i++) {
|
||||
gpiotools_set_bit(&config.attrs[0].mask, i);
|
||||
gpiotools_assign_bit(&config.attrs[0].attr.values,
|
||||
i, values[i]);
|
||||
}
|
||||
ret = gpiotools_request_line(device_name, lines, num_lines,
|
||||
&config, CONSUMER);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return gpiotools_release_linehandle(ret);
|
||||
return gpiotools_release_line(ret);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
#ifndef _GPIO_UTILS_H_
|
||||
#define _GPIO_UTILS_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
@@ -23,19 +25,55 @@ static inline int check_prefix(const char *str, const char *prefix)
|
||||
}
|
||||
|
||||
int gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
|
||||
unsigned int nlines, unsigned int flag,
|
||||
unsigned int num_lines, unsigned int flag,
|
||||
struct gpiohandle_data *data,
|
||||
const char *consumer_label);
|
||||
int gpiotools_set_values(const int fd, struct gpiohandle_data *data);
|
||||
int gpiotools_get_values(const int fd, struct gpiohandle_data *data);
|
||||
int gpiotools_release_linehandle(const int fd);
|
||||
|
||||
int gpiotools_request_line(const char *device_name,
|
||||
unsigned int *lines,
|
||||
unsigned int num_lines,
|
||||
struct gpio_v2_line_config *config,
|
||||
const char *consumer);
|
||||
int gpiotools_set_values(const int fd, struct gpio_v2_line_values *values);
|
||||
int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values);
|
||||
int gpiotools_release_line(const int fd);
|
||||
|
||||
int gpiotools_get(const char *device_name, unsigned int line);
|
||||
int gpiotools_gets(const char *device_name, unsigned int *lines,
|
||||
unsigned int nlines, struct gpiohandle_data *data);
|
||||
unsigned int num_lines, unsigned int *values);
|
||||
int gpiotools_set(const char *device_name, unsigned int line,
|
||||
unsigned int value);
|
||||
int gpiotools_sets(const char *device_name, unsigned int *lines,
|
||||
unsigned int nlines, struct gpiohandle_data *data);
|
||||
unsigned int num_lines, unsigned int *values);
|
||||
|
||||
/* helper functions for gpio_v2_line_values bits */
|
||||
static inline void gpiotools_set_bit(__u64 *b, int n)
|
||||
{
|
||||
*b |= _BITULL(n);
|
||||
}
|
||||
|
||||
static inline void gpiotools_change_bit(__u64 *b, int n)
|
||||
{
|
||||
*b ^= _BITULL(n);
|
||||
}
|
||||
|
||||
static inline void gpiotools_clear_bit(__u64 *b, int n)
|
||||
{
|
||||
*b &= ~_BITULL(n);
|
||||
}
|
||||
|
||||
static inline int gpiotools_test_bit(__u64 b, int n)
|
||||
{
|
||||
return !!(b & _BITULL(n));
|
||||
}
|
||||
|
||||
static inline void gpiotools_assign_bit(__u64 *b, int n, bool value)
|
||||
{
|
||||
if (value)
|
||||
gpiotools_set_bit(b, n);
|
||||
else
|
||||
gpiotools_clear_bit(b, n);
|
||||
}
|
||||
|
||||
#endif /* _GPIO_UTILS_H_ */
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct gpioline_info_changed chg;
|
||||
struct gpioline_info req;
|
||||
struct gpio_v2_line_info_changed chg;
|
||||
struct gpio_v2_line_info req;
|
||||
struct pollfd pfd;
|
||||
int fd, i, j, ret;
|
||||
char *event, *end;
|
||||
@@ -40,11 +40,11 @@ int main(int argc, char **argv)
|
||||
for (i = 0, j = 2; i < argc - 2; i++, j++) {
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.line_offset = strtoul(argv[j], &end, 0);
|
||||
req.offset = strtoul(argv[j], &end, 0);
|
||||
if (*end != '\0')
|
||||
goto err_usage;
|
||||
|
||||
ret = ioctl(fd, GPIO_GET_LINEINFO_WATCH_IOCTL, &req);
|
||||
ret = ioctl(fd, GPIO_V2_GET_LINEINFO_WATCH_IOCTL, &req);
|
||||
if (ret) {
|
||||
perror("unable to set up line watch");
|
||||
return EXIT_FAILURE;
|
||||
@@ -71,13 +71,13 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
switch (chg.event_type) {
|
||||
case GPIOLINE_CHANGED_REQUESTED:
|
||||
case GPIO_V2_LINE_CHANGED_REQUESTED:
|
||||
event = "requested";
|
||||
break;
|
||||
case GPIOLINE_CHANGED_RELEASED:
|
||||
case GPIO_V2_LINE_CHANGED_RELEASED:
|
||||
event = "released";
|
||||
break;
|
||||
case GPIOLINE_CHANGED_CONFIG:
|
||||
case GPIO_V2_LINE_CHANGED_CONFIG:
|
||||
event = "config changed";
|
||||
break;
|
||||
default:
|
||||
@@ -87,7 +87,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
printf("line %u: %s at %llu\n",
|
||||
chg.info.line_offset, event, chg.timestamp);
|
||||
chg.info.offset, event, chg.timestamp_ns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,57 +25,73 @@
|
||||
|
||||
struct gpio_flag {
|
||||
char *name;
|
||||
unsigned long mask;
|
||||
unsigned long long mask;
|
||||
};
|
||||
|
||||
struct gpio_flag flagnames[] = {
|
||||
{
|
||||
.name = "kernel",
|
||||
.mask = GPIOLINE_FLAG_KERNEL,
|
||||
.name = "used",
|
||||
.mask = GPIO_V2_LINE_FLAG_USED,
|
||||
},
|
||||
{
|
||||
.name = "input",
|
||||
.mask = GPIO_V2_LINE_FLAG_INPUT,
|
||||
},
|
||||
{
|
||||
.name = "output",
|
||||
.mask = GPIOLINE_FLAG_IS_OUT,
|
||||
.mask = GPIO_V2_LINE_FLAG_OUTPUT,
|
||||
},
|
||||
{
|
||||
.name = "active-low",
|
||||
.mask = GPIOLINE_FLAG_ACTIVE_LOW,
|
||||
.mask = GPIO_V2_LINE_FLAG_ACTIVE_LOW,
|
||||
},
|
||||
{
|
||||
.name = "open-drain",
|
||||
.mask = GPIOLINE_FLAG_OPEN_DRAIN,
|
||||
.mask = GPIO_V2_LINE_FLAG_OPEN_DRAIN,
|
||||
},
|
||||
{
|
||||
.name = "open-source",
|
||||
.mask = GPIOLINE_FLAG_OPEN_SOURCE,
|
||||
.mask = GPIO_V2_LINE_FLAG_OPEN_SOURCE,
|
||||
},
|
||||
{
|
||||
.name = "pull-up",
|
||||
.mask = GPIOLINE_FLAG_BIAS_PULL_UP,
|
||||
.mask = GPIO_V2_LINE_FLAG_BIAS_PULL_UP,
|
||||
},
|
||||
{
|
||||
.name = "pull-down",
|
||||
.mask = GPIOLINE_FLAG_BIAS_PULL_DOWN,
|
||||
.mask = GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN,
|
||||
},
|
||||
{
|
||||
.name = "bias-disabled",
|
||||
.mask = GPIOLINE_FLAG_BIAS_DISABLE,
|
||||
.mask = GPIO_V2_LINE_FLAG_BIAS_DISABLED,
|
||||
},
|
||||
};
|
||||
|
||||
void print_flags(unsigned long flags)
|
||||
static void print_attributes(struct gpio_v2_line_info *info)
|
||||
{
|
||||
int i;
|
||||
int printed = 0;
|
||||
const char *field_format = "%s";
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(flagnames); i++) {
|
||||
if (flags & flagnames[i].mask) {
|
||||
if (printed)
|
||||
fprintf(stdout, " ");
|
||||
fprintf(stdout, "%s", flagnames[i].name);
|
||||
printed++;
|
||||
if (info->flags & flagnames[i].mask) {
|
||||
fprintf(stdout, field_format, flagnames[i].name);
|
||||
field_format = ", %s";
|
||||
}
|
||||
}
|
||||
|
||||
if ((info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING) &&
|
||||
(info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING))
|
||||
fprintf(stdout, field_format, "both-edges");
|
||||
else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_RISING)
|
||||
fprintf(stdout, field_format, "rising-edge");
|
||||
else if (info->flags & GPIO_V2_LINE_FLAG_EDGE_FALLING)
|
||||
fprintf(stdout, field_format, "falling-edge");
|
||||
|
||||
for (i = 0; i < info->num_attrs; i++) {
|
||||
if (info->attrs[i].id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE)
|
||||
fprintf(stdout, ", debounce_period=%dusec",
|
||||
info->attrs[0].debounce_period_us);
|
||||
}
|
||||
}
|
||||
|
||||
int list_device(const char *device_name)
|
||||
@@ -109,18 +125,18 @@ int list_device(const char *device_name)
|
||||
|
||||
/* Loop over the lines and print info */
|
||||
for (i = 0; i < cinfo.lines; i++) {
|
||||
struct gpioline_info linfo;
|
||||
struct gpio_v2_line_info linfo;
|
||||
|
||||
memset(&linfo, 0, sizeof(linfo));
|
||||
linfo.line_offset = i;
|
||||
linfo.offset = i;
|
||||
|
||||
ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &linfo);
|
||||
ret = ioctl(fd, GPIO_V2_GET_LINEINFO_IOCTL, &linfo);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
perror("Failed to issue LINEINFO IOCTL\n");
|
||||
goto exit_close_error;
|
||||
}
|
||||
fprintf(stdout, "\tline %2d:", linfo.line_offset);
|
||||
fprintf(stdout, "\tline %2d:", linfo.offset);
|
||||
if (linfo.name[0])
|
||||
fprintf(stdout, " \"%s\"", linfo.name);
|
||||
else
|
||||
@@ -131,7 +147,7 @@ int list_device(const char *device_name)
|
||||
fprintf(stdout, " unused");
|
||||
if (linfo.flags) {
|
||||
fprintf(stdout, " [");
|
||||
print_flags(linfo.flags);
|
||||
print_attributes(&linfo);
|
||||
fprintf(stdout, "]");
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
|
||||
Reference in New Issue
Block a user