Commit 60afe31d authored by Linus Walleij's avatar Linus Walleij

Merge branch 'chardev' into devel

parents 42178e2a fe95046e
What: /sys/class/gpio/ What: /sys/class/gpio/
Date: July 2008 Date: July 2008
KernelVersion: 2.6.27 KernelVersion: 2.6.27
Contact: David Brownell <dbrownell@users.sourceforge.net> Contact: Linus Walleij <linusw@kernel.org>
Description: Description:
As a Kconfig option, individual GPIO signals may be accessed from As a Kconfig option, individual GPIO signals may be accessed from
...@@ -26,3 +26,5 @@ Description: ...@@ -26,3 +26,5 @@ Description:
/label ... (r/o) descriptive, not necessarily unique /label ... (r/o) descriptive, not necessarily unique
/ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1) /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
This ABI is deprecated and will be removed after 2020. It is
replaced with the GPIO character device.
What: /dev/gpiochip[0-9]+
Date: November 2015
KernelVersion: 4.4
Contact: linux-gpio@vger.kernel.org
Description:
The character device files /dev/gpiochip* are the interface
between GPIO chips and userspace.
The ioctl(2)-based ABI is defined and documented in
[include/uapi]<linux/gpio.h>.
The following file operations are supported:
open(2)
Currently the only useful flags are O_RDWR.
ioctl(2)
Initiate various actions.
See the inline documentation in [include/uapi]<linux/gpio.h>
for descriptions of all ioctls.
close(2)
Stops and free up the I/O contexts that was associated
with the file descriptor.
Users: TBD
...@@ -4821,10 +4821,14 @@ L: linux-gpio@vger.kernel.org ...@@ -4821,10 +4821,14 @@ L: linux-gpio@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git
S: Maintained S: Maintained
F: Documentation/gpio/ F: Documentation/gpio/
F: Documentation/ABI/testing/gpio-cdev
F: Documentation/ABI/obsolete/sysfs-gpio
F: drivers/gpio/ F: drivers/gpio/
F: include/linux/gpio/ F: include/linux/gpio/
F: include/linux/gpio.h F: include/linux/gpio.h
F: include/asm-generic/gpio.h F: include/asm-generic/gpio.h
F: include/uapi/linux/gpio.h
F: tools/gpio/
GRE DEMULTIPLEXER DRIVER GRE DEMULTIPLEXER DRIVER
M: Dmitry Kozlov <xeb@mail.ru> M: Dmitry Kozlov <xeb@mail.ru>
......
...@@ -547,6 +547,7 @@ static struct class gpio_class = { ...@@ -547,6 +547,7 @@ static struct class gpio_class = {
int gpiod_export(struct gpio_desc *desc, bool direction_may_change) int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{ {
struct gpio_chip *chip; struct gpio_chip *chip;
struct gpio_device *gdev;
struct gpiod_data *data; struct gpiod_data *data;
unsigned long flags; unsigned long flags;
int status; int status;
...@@ -566,6 +567,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ...@@ -566,6 +567,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
} }
chip = desc->chip; chip = desc->chip;
gdev = chip->gpiodev;
mutex_lock(&sysfs_lock); mutex_lock(&sysfs_lock);
...@@ -605,7 +607,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ...@@ -605,7 +607,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
if (chip->names && chip->names[offset]) if (chip->names && chip->names[offset])
ioname = chip->names[offset]; ioname = chip->names[offset];
dev = device_create_with_groups(&gpio_class, chip->parent, dev = device_create_with_groups(&gpio_class, &gdev->dev,
MKDEV(0, 0), data, gpio_groups, MKDEV(0, 0), data, gpio_groups,
ioname ? ioname : "gpio%u", ioname ? ioname : "gpio%u",
desc_to_gpio(desc)); desc_to_gpio(desc));
...@@ -771,7 +773,7 @@ static int __init gpiolib_sysfs_init(void) ...@@ -771,7 +773,7 @@ static int __init gpiolib_sysfs_init(void)
{ {
int status; int status;
unsigned long flags; unsigned long flags;
struct gpio_chip *chip; struct gpio_device *gdev;
status = class_register(&gpio_class); status = class_register(&gpio_class);
if (status < 0) if (status < 0)
...@@ -784,8 +786,8 @@ static int __init gpiolib_sysfs_init(void) ...@@ -784,8 +786,8 @@ static int __init gpiolib_sysfs_init(void)
* registered, and so arch_initcall() can always gpio_export(). * registered, and so arch_initcall() can always gpio_export().
*/ */
spin_lock_irqsave(&gpio_lock, flags); spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(chip, &gpio_chips, list) { list_for_each_entry(gdev, &gpio_devices, list) {
if (chip->cdev) if (gdev->chip->cdev)
continue; continue;
/* /*
...@@ -798,7 +800,7 @@ static int __init gpiolib_sysfs_init(void) ...@@ -798,7 +800,7 @@ static int __init gpiolib_sysfs_init(void)
* gpio_lock prevents us from doing this. * gpio_lock prevents us from doing this.
*/ */
spin_unlock_irqrestore(&gpio_lock, flags); spin_unlock_irqrestore(&gpio_lock, flags);
status = gpiochip_sysfs_register(chip); status = gpiochip_sysfs_register(gdev->chip);
spin_lock_irqsave(&gpio_lock, flags); spin_lock_irqsave(&gpio_lock, flags);
} }
spin_unlock_irqrestore(&gpio_lock, flags); spin_unlock_irqrestore(&gpio_lock, flags);
......
This diff is collapsed.
...@@ -12,13 +12,40 @@ ...@@ -12,13 +12,40 @@
#ifndef GPIOLIB_H #ifndef GPIOLIB_H
#define GPIOLIB_H #define GPIOLIB_H
#include <linux/gpio/driver.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h>
#include <linux/cdev.h>
enum of_gpio_flags; enum of_gpio_flags;
enum gpiod_flags; enum gpiod_flags;
struct acpi_device; struct acpi_device;
/**
* struct gpio_device - internal state container for GPIO devices
* @id: numerical ID number for the GPIO chip
* @dev: the GPIO device struct
* @chrdev: character device for the GPIO device
* @owner: helps prevent removal of modules exporting active GPIOs
* @chip: pointer to the corresponding gpiochip, holding static
* data for this device
* @list: links gpio_device:s together for traversal
*
* This state container holds most of the runtime variable data
* for a GPIO device and can hold references and live on after the
* GPIO chip has been removed, if it is still being used from
* userspace.
*/
struct gpio_device {
int id;
struct device dev;
struct cdev chrdev;
struct module *owner;
struct gpio_chip *chip;
struct list_head list;
};
/** /**
* struct acpi_gpio_info - ACPI GPIO specific information * struct acpi_gpio_info - ACPI GPIO specific information
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
...@@ -90,7 +117,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, ...@@ -90,7 +117,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
extern struct spinlock gpio_lock; extern struct spinlock gpio_lock;
extern struct list_head gpio_chips; extern struct list_head gpio_devices;
struct gpio_desc { struct gpio_desc {
struct gpio_chip *chip; struct gpio_chip *chip;
...@@ -149,17 +176,17 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc) ...@@ -149,17 +176,17 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
/* With chip prefix */ /* With chip prefix */
#define chip_emerg(chip, fmt, ...) \ #define chip_emerg(chip, fmt, ...) \
pr_emerg("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_crit(chip, fmt, ...) \ #define chip_crit(chip, fmt, ...) \
pr_crit("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_err(chip, fmt, ...) \ #define chip_err(chip, fmt, ...) \
pr_err("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_warn(chip, fmt, ...) \ #define chip_warn(chip, fmt, ...) \
pr_warn("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_info(chip, fmt, ...) \ #define chip_info(chip, fmt, ...) \
pr_info("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#define chip_dbg(chip, fmt, ...) \ #define chip_dbg(chip, fmt, ...) \
pr_debug("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__) dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
#ifdef CONFIG_GPIO_SYSFS #ifdef CONFIG_GPIO_SYSFS
......
#ifndef __LINUX_GPIO_DRIVER_H #ifndef __LINUX_GPIO_DRIVER_H
#define __LINUX_GPIO_DRIVER_H #define __LINUX_GPIO_DRIVER_H
#include <linux/device.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/irq.h> #include <linux/irq.h>
...@@ -10,22 +11,22 @@ ...@@ -10,22 +11,22 @@
#include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinctrl.h>
#include <linux/kconfig.h> #include <linux/kconfig.h>
struct device;
struct gpio_desc; struct gpio_desc;
struct of_phandle_args; struct of_phandle_args;
struct device_node; struct device_node;
struct seq_file; struct seq_file;
struct gpio_device;
#ifdef CONFIG_GPIOLIB #ifdef CONFIG_GPIOLIB
/** /**
* struct gpio_chip - abstract a GPIO controller * struct gpio_chip - abstract a GPIO controller
* @label: for diagnostics * @label: for diagnostics
* @gpiodev: the internal state holder, opaque struct
* @parent: optional parent device providing the GPIOs * @parent: optional parent device providing the GPIOs
* @cdev: class device used by sysfs interface (may be NULL) * @cdev: class device used by sysfs interface (may be NULL)
* @owner: helps prevent removal of modules exporting active GPIOs * @owner: helps prevent removal of modules exporting active GPIOs
* @data: per-instance data assigned by the driver * @data: per-instance data assigned by the driver
* @list: links gpio_chips together for traversal
* @request: optional hook for chip-specific activation, such as * @request: optional hook for chip-specific activation, such as
* enabling module power and clock; may sleep * enabling module power and clock; may sleep
* @free: optional hook for chip-specific deactivation, such as * @free: optional hook for chip-specific deactivation, such as
...@@ -107,11 +108,11 @@ struct seq_file; ...@@ -107,11 +108,11 @@ struct seq_file;
*/ */
struct gpio_chip { struct gpio_chip {
const char *label; const char *label;
struct gpio_device *gpiodev;
struct device *parent; struct device *parent;
struct device *cdev; struct device *cdev;
struct module *owner; struct module *owner;
void *data; void *data;
struct list_head list;
int (*request)(struct gpio_chip *chip, int (*request)(struct gpio_chip *chip,
unsigned offset); unsigned offset);
......
...@@ -138,6 +138,7 @@ header-y += genetlink.h ...@@ -138,6 +138,7 @@ header-y += genetlink.h
header-y += gen_stats.h header-y += gen_stats.h
header-y += gfs2_ondisk.h header-y += gfs2_ondisk.h
header-y += gigaset_dev.h header-y += gigaset_dev.h
header-y += gpio.h
header-y += gsmmux.h header-y += gsmmux.h
header-y += hdlcdrv.h header-y += hdlcdrv.h
header-y += hdlc.h header-y += hdlc.h
......
/*
* <linux/gpio.h> - userspace ABI for the GPIO character devices
*
* Copyright (C) 2015 Linus Walleij
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _UAPI_GPIO_H_
#define _UAPI_GPIO_H_
#include <linux/ioctl.h>
#include <linux/types.h>
/**
* struct gpiochip_info - Information about a certain GPIO chip
* @name: the name of this GPIO chip
* @lines: number of GPIO lines on this chip
*/
struct gpiochip_info {
char name[32];
__u32 lines;
};
#define GPIO_GET_CHIPINFO_IOCTL _IOR('o', 0x01, struct gpiochip_info)
#endif /* _UAPI_GPIO_H_ */
...@@ -13,6 +13,7 @@ help: ...@@ -13,6 +13,7 @@ help:
@echo ' cpupower - a tool for all things x86 CPU power' @echo ' cpupower - a tool for all things x86 CPU power'
@echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
@echo ' freefall - laptop accelerometer program for disk protection' @echo ' freefall - laptop accelerometer program for disk protection'
@echo ' gpio - GPIO tools'
@echo ' hv - tools used when in Hyper-V clients' @echo ' hv - tools used when in Hyper-V clients'
@echo ' iio - IIO tools' @echo ' iio - IIO tools'
@echo ' lguest - a minimal 32-bit x86 hypervisor' @echo ' lguest - a minimal 32-bit x86 hypervisor'
...@@ -53,7 +54,7 @@ acpi: FORCE ...@@ -53,7 +54,7 @@ acpi: FORCE
cpupower: FORCE cpupower: FORCE
$(call descend,power/$@) $(call descend,power/$@)
cgroup firewire hv guest spi usb virtio vm net iio: FORCE cgroup firewire hv guest spi usb virtio vm net iio gpio: FORCE
$(call descend,$@) $(call descend,$@)
liblockdep: FORCE liblockdep: FORCE
...@@ -119,7 +120,7 @@ acpi_clean: ...@@ -119,7 +120,7 @@ acpi_clean:
cpupower_clean: cpupower_clean:
$(call descend,power/cpupower,clean) $(call descend,power/cpupower,clean)
cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean: cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean gpio_clean:
$(call descend,$(@:_clean=),clean) $(call descend,$(@:_clean=),clean)
liblockdep_clean: liblockdep_clean:
...@@ -155,6 +156,7 @@ build_clean: ...@@ -155,6 +156,7 @@ build_clean:
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
gpio_clean
.PHONY: FORCE .PHONY: FORCE
CC = $(CROSS_COMPILE)gcc
CFLAGS += -Wall -g -D_GNU_SOURCE
all: lsgpio
lsgpio: lsgpio.o gpio-utils.o
%.o: %.c gpio-utils.h
.PHONY: clean
clean:
rm -f *.o lsgpio
/*
* GPIO tools - helpers library for the GPIO tools
*
* Copyright (C) 2015 Linus Walleij
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include "gpio-utils.h"
/*
* GPIO tools - utility helpers library for the GPIO tools
*
* Copyright (C) 2015 Linus Walleij
*
* Portions copied from iio_utils and lssio:
* Copyright (c) 2010 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
* Copyright (c) 2008 Jonathan Cameron
* *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#ifndef _GPIO_UTILS_H_
#define _GPIO_UTILS_H_
#include <string.h>
static inline int check_prefix(const char *str, const char *prefix)
{
return strlen(str) > strlen(prefix) &&
strncmp(str, prefix, strlen(prefix)) == 0;
}
#endif /* _GPIO_UTILS_H_ */
/*
* lsgpio - example on how to list the GPIO lines on a system
*
* Copyright (C) 2015 Linus Walleij
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* Usage:
* lsgpio <-n device-name>
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <poll.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <linux/gpio.h>
#include "gpio-utils.h"
int list_device(const char *device_name)
{
struct gpiochip_info cinfo;
char *chrdev_name;
int fd;
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\n", chrdev_name);
goto free_chrdev_name;
}
/* Inspect this GPIO chip */
ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &cinfo);
if (ret == -1) {
ret = -errno;
fprintf(stderr, "Failed to retrieve GPIO fd\n");
if (close(fd) == -1)
perror("Failed to close GPIO character device file");
goto free_chrdev_name;
}
fprintf(stdout, "GPIO chip: %s, %u GPIO lines\n",
cinfo.name, cinfo.lines);
if (close(fd) == -1) {
ret = -errno;
goto free_chrdev_name;
}
free_chrdev_name:
free(chrdev_name);
return ret;
}
void print_usage(void)
{
fprintf(stderr, "Usage: lsgpio [options]...\n"
"List GPIO chips, lines and states\n"
" -n <name> List GPIOs on a named device\n"
" -? This helptext\n"
);
}
int main(int argc, char **argv)
{
const char *device_name;
int ret;
int c;
while ((c = getopt(argc, argv, "n:")) != -1) {
switch (c) {
case 'n':
device_name = optarg;
break;
case '?':
print_usage();
return -1;
}
}
if (device_name)
ret = list_device(device_name);
else {
const struct dirent *ent;
DIR *dp;
/* List all GPIO devices one at a time */
dp = opendir("/dev");
if (!dp) {
ret = -errno;
goto error_out;
}
ret = -ENOENT;
while (ent = readdir(dp), ent) {
if (check_prefix(ent->d_name, "gpiochip")) {
ret = list_device(ent->d_name);
if (ret)
break;
}
}
ret = 0;
if (closedir(dp) == -1) {
perror("scanning devices: Failed to close directory");
ret = -errno;
}
}
error_out:
return ret;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment