Linux GPIO子系统代码分析

/* 分析GPIO SUB子系统源码 Linux内核版本4.9.37 */

/****************************************** GPIO对应的数据结构*************************************/
/* 每个引脚都对应于一个引脚描述符 */
struct gpio_desc {

struct gpio_device *gdev; //GPIO设备

unsigned long flags; //引脚对应的标志

/* flag symbols are bit numbers */

#define FLAG_REQUESTED 0 // 引脚被已被请求

#define FLAG_IS_OUT 1 // 引脚是输出状态

#define FLAG_EXPORT 2 /* 被 sysfs_lock保护 */

#define FLAG_SYSFS 3 /* 导入到 /sys/class/gpio/control */

#define FLAG_ACTIVE_LOW 6 /* 输出低电平 */

#define FLAG_OPEN_DRAIN 7 /* 引脚是开漏类型 */

#define FLAG_OPEN_SOURCE 8 /* 引脚是开源类型 */

#define FLAG_USED_AS_IRQ 9 /* 引脚连接到中断 */

#define FLAG_IS_HOGGED 11 /* 引脚被占用 */

const char *label;     //链接标志

const char *name;

};

struct gpio_device {

int id; //GPIO芯片的数字ID号

struct device dev; //对应的设备结构体

struct cdev chrdev; //对应的字符设备

struct device *mockdev;

struct module *owner;

struct gpio_chip *chip; //gpio芯片

struct gpio_desc *descs; //引脚描述符指针

int base; //gpio的起始号码

u16 ngpio; //管理的GPIO数量

char *label; //标签数据

void *data; //驱动程序分配的数据

struct list_head list;

#ifdef CONFIG_PINCTRL

struct list_head pin_ranges; //定义一个引脚范围的链表

#endif

};

struct gpio_chip {

const char *label;

struct gpio_device *gpiodev; //指向GPIO设备

struct device *parent; //GPIO的父设备

struct module *owner;

/*对应的回调函数 */

/* 向内核申请对应的GPIO */

int (*request)(struct gpio_chip *chip,

unsigned offset);

void (*free)(struct gpio_chip *chip,

unsigned offset);

int (*get_direction)(struct gpio_chip *chip,

unsigned offset);

int (*direction_input)(struct gpio_chip *chip,

unsigned offset);

int (*direction_output)(struct gpio_chip *chip,

unsigned offset, int value);

int (*get)(struct gpio_chip *chip,

unsigned offset);

void (*set)(struct gpio_chip *chip,

unsigned offset, int value);

void (*set_multiple)(struct gpio_chip *chip,

unsigned long *mask,

unsigned long *bits);

int (*set_debounce)(struct gpio_chip *chip,

unsigned offset,

unsigned debounce);

int (*set_single_ended)(struct gpio_chip *chip,

unsigned offset,

enum single_ended_mode mode);

int (*to_irq)(struct gpio_chip *chip,

unsigned offset);

void (*dbg_show)(struct seq_file *s,

struct gpio_chip *chip);

int base; //引脚的起始编号

u16 ngpio; //芯片支持的GPIO数量

const char *const *names;//对应的名字

bool can_sleep; //是否睡眠

bool irq_not_threaded;

#if IS_ENABLED(CONFIG_GPIO_GENERIC)

unsigned long (*read_reg)(void __iomem *reg);

void (*write_reg)(void __iomem *reg, unsigned long data);

unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin); //该回调返回正确的GPIO对应掩码

void __iomem *reg_dat; //数据寄存器

void __iomem *reg_set; //置位寄存器

void __iomem *reg_clr; //置零寄存器

void __iomem *reg_dir; //方向寄存器

int bgpio_bits; //对应的bit

spinlock_t bgpio_lock;

unsigned long bgpio_data;

unsigned long bgpio_dir;

#endif

#ifdef CONFIG_GPIOLIB_IRQCHIP //支持中断

struct irq_chip *irqchip; //中断芯片

struct irq_domain *irqdomain; //中断映射的区域

unsigned int irq_base; //中断的起始号

irq_flow_handler_t irq_handler;//中断处理函数

unsigned int irq_default_type; //中断默认的类型

int irq_parent;

bool irq_need_valid_mask; //中断是否需要中断有效掩码

unsigned long *irq_valid_mask;

struct lock_class_key *lock_key;

#endif

#if defined(CONFIG_OF_GPIO) //对应设备树的操作

/*

* If CONFIG_OF is enabled, then all GPIO controllers described in the

* device tree automatically may have an OF translation

*/

struct device_node *of_node; //设备树的结点

int of_gpio_n_cells;

int (*of_xlate)(struct gpio_chip *gc,

const struct of_phandle_args *gpiospec, u32 *flags);

#endif

};

/*使用此函数向内核注册一个GPIO芯片  并调用相应的API即可*/
static inline int gpiochip_add(struct gpio_chip *chip)
{
  return gpiochip_add_data(chip, NULL);
}
int gpiochip_add_data(struct gpio_chip *chip, void *data)
{

unsigned long flags;

int status = 0;

unsigned i;

int base = chip->base; //芯片对应的起始芯片标号

struct gpio_device *gdev;

/* 向内核空间申请一个gpio_device结构体空间,并填充其中的成员*/

gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);

if (!gdev)

  return -ENOMEM;

gdev->dev.bus = &gpio_bus_type;

gdev->chip = chip;

chip->gpiodev = gdev;

if (chip->parent) {

gdev->dev.parent = chip->parent;

gdev->dev.of_node = chip->parent->of_node; /* 设备树的结点 */

}

#ifdef CONFIG_OF_GPIO

/* If the gpiochip has an assigned OF node this takes precedence */

if (chip->of_node)

  gdev->dev.of_node = chip->of_node;

#endif

/* 申请对应的ID号 */

gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);

if (gdev->id < 0) {

  status = gdev->id;

  goto err_free_gdev;

  }
dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); //设置对应的名字
device_initialize(&gdev->dev);
dev_set_drvdata(&gdev->dev, gdev);
/* 设置OWENER成员 */
if (chip->parent && chip->parent->driver)
  gdev->owner = chip->parent->driver->owner;
else if (chip->owner)
  /* TODO: remove chip->owner */
  gdev->owner = chip->owner;
else
  gdev->owner = THIS_MODULE;

/*向内核申请对应的引脚描述符空间 */
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
if (!gdev->descs) {
  status = -ENOMEM;
  goto err_free_gdev;
}

if (chip->ngpio == 0) {
  chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
  status = -EINVAL;
  goto err_free_descs;
}

/* 设置设备的标签 */
if (chip->label)
  gdev->label = kstrdup(chip->label, GFP_KERNEL);
else
  gdev->label = kstrdup("unknown", GFP_KERNEL);
if (!gdev->label) {
  status = -ENOMEM;
  goto err_free_descs;
}

  gdev->ngpio = chip->ngpio;
  gdev->data = data;

  spin_lock_irqsave(&gpio_lock, flags);

/*
* TODO: this allocates a Linux GPIO number base in the global
* GPIO numberspace for this chip. In the long run we want to
* get *rid* of this numberspace and use only descriptors, but
* it may be a pipe dream. It will not happen before we get rid
* of the sysfs interface anyways.
*/
if (base < 0) {
  base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
  status = base;
  spin_unlock_irqrestore(&gpio_lock, flags);
  goto err_free_label;
}
/*
* TODO: it should not be necessary to reflect the assigned
* base outside of the GPIO subsystem. Go over drivers and
* see if anyone makes use of this, else drop this and assign
* a poison instead.
*/
chip->base = base;
}
gdev->base = base;
/* struct list_head gpio_devices;
* 将gdev添加到gpio_devices全局链表中去
*/
status = gpiodev_add_to_list(gdev);

if (status) {
  spin_unlock_irqrestore(&gpio_lock, flags);
  goto err_free_label;
}

spin_unlock_irqrestore(&gpio_lock, flags);

/* 初始化时设置所有的引脚描述符为输出 */
for (i = 0; i < chip->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];

desc->gdev = gdev;
/*
* REVISIT: most hardware initializes GPIOs as inputs
* (often with pullups enabled) so power usage is
* minimized. Linux code should set the gpio direction
* first thing; but until it does, and in case
* chip->get_direction is not set, we may expose the
* wrong direction in sysfs.
*/

if (chip->get_direction) {
/*
* If we have .get_direction, set up the initial
* direction flag from the hardware.
*/
  int dir = chip->get_direction(chip, i);

if (!dir)
  set_bit(FLAG_IS_OUT, &desc->flags);
} else if (!chip->direction_input) {
  /*
  * If the chip lacks the .direction_input callback
  * we logically assume all lines are outputs.
  */
  set_bit(FLAG_IS_OUT, &desc->flags);
}
}

#ifdef CONFIG_PINCTRL
  INIT_LIST_HEAD(&gdev->pin_ranges);
#endif

  status = gpiochip_set_desc_names(chip);
  if (status)
    goto err_remove_from_list;

    status = gpiochip_irqchip_init_valid_mask(chip);
  if (status)
    goto err_remove_from_list;

    status = of_gpiochip_add(chip);
  if (status)
    goto err_remove_chip;

    acpi_gpiochip_add(chip);

/*
* By first adding the chardev, and then adding the device,
* we get a device node entry in sysfs under
* /sys/bus/gpio/devices/gpiochipN/dev that can be used for
* coldplug of device nodes and other udev business.
* We can do this only if gpiolib has been initialized.
* Otherwise, defer until later.
*/
if (gpiolib_initialized) {
  status = gpiochip_setup_dev(gdev);
if (status)
  goto err_remove_chip;
}
return 0;

err_remove_chip:
  acpi_gpiochip_remove(chip);
  gpiochip_free_hogs(chip);
  of_gpiochip_remove(chip);
  gpiochip_irqchip_free_valid_mask(chip);
err_remove_from_list:
  spin_lock_irqsave(&gpio_lock, flags);
list_del(&gdev->list);
  spin_unlock_irqrestore(&gpio_lock, flags);
err_free_label:
  kfree(gdev->label);
err_free_descs:
  kfree(gdev->descs);
err_free_gdev:
  ida_simple_remove(&gpio_ida, gdev->id);
/* failures here can mean systems won't boot... */
  pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
    gdev->base, gdev->base + gdev->ngpio - 1,
    chip->label ? : "generic");
  kfree(gdev);
  return status;
}

/*******************************inclue/linux/gpio.h**************************************/
#define GPIOF_DIR_OUT (0 << 0)
#define GPIOF_DIR_IN (1 << 0)

#define GPIOF_INIT_LOW (0 << 1)
#define GPIOF_INIT_HIGH (1 << 1)

#define GPIOF_IN (GPIOF_DIR_IN)
#define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW)
#define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH)

/* Gpio pin is active-low */
#define GPIOF_ACTIVE_LOW (1 << 2)

/* Gpio pin is open drain */
#define GPIOF_OPEN_DRAIN (1 << 3)

/* Gpio pin is open source */
#define GPIOF_OPEN_SOURCE (1 << 4)

#define GPIOF_EXPORT (1 << 5)
#define GPIOF_EXPORT_CHANGEABLE (1 << 6)
#define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT)
#define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE)

/*用来描述GPIO配置的一个结构体 */
struct gpio {
  unsigned gpio; //GPIO引脚号
  unsigned long flags; //使用以上GPIOF_*的宏来标记引脚的属性
  const char *label; //用来描述引脚的字符串
};

/* 在Kconfig中配置了CONFIG_GPIOLIB选项提供以下接口来操作GPIO口 通过以下的接口会调用gpio_chip上对应的回调函数*/
/********************************************************************************/
static inline int gpio_get_value(unsigned int gpio)
{
  return __gpio_get_value(gpio);
}

static inline int __gpio_get_value(unsigned gpio)
{
  return gpiod_get_raw_value(gpio_to_desc(gpio));
}

nt gpiod_get_raw_value(const struct gpio_desc *desc)
{
  VALIDATE_DESC(desc);
  /* Should be using gpio_get_value_cansleep() */
  WARN_ON(desc->gdev->chip->can_sleep);
  return _gpiod_get_raw_value(desc);
}

static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
{
  return desc - &desc->gdev->descs[0];
}

static int _gpiod_get_raw_value(const struct gpio_desc *desc)

{

struct gpio_chip *chip;
int offset;
int value;

chip = desc->gdev->chip; //从描述符找到GPIO芯片

offset = gpio_chip_hwgpio(desc); //对应引脚在该芯片上的引脚号偏移

value = chip->get ? chip->get(chip, offset) : -EIO;//调用回调函数获取GPIO的引脚值
value = value < 0 ? value : !!value;
trace_gpio_value(desc_to_gpio(desc), 1, value);
return value;
}
/********************************************************************************/

static inline void gpio_set_value(unsigned int gpio, int value)
{
  __gpio_set_value(gpio, value);
}

void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{
VALIDATE_DESC_VOID(desc);
/* Should be using gpiod_set_value_cansleep() */
WARN_ON(desc->gdev->chip->can_sleep);
_gpiod_set_raw_value(desc, value);
}

static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
{
struct gpio_chip *chip;

chip = desc->gdev->chip;
trace_gpio_value(desc_to_gpio(desc), 0, value);
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
_gpio_set_open_drain_value(desc, value);
else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
_gpio_set_open_source_value(desc, value);
else
chip->set(chip, gpio_chip_hwgpio(desc), value); //设置对应的输出值
}
/********************************************************************************/
static inline int gpio_cansleep(unsigned int gpio)
{
return __gpio_cansleep(gpio);
}

static inline int gpio_to_irq(unsigned int gpio)
{
return __gpio_to_irq(gpio);
}

static inline int irq_to_gpio(unsigned int irq)
{
return -EINVAL;
}

#endif /* ! CONFIG_ARCH_HAVE_CUSTOM_GPIO_H */

/* CONFIG_GPIOLIB: bindings for managed devices that want to request gpios */

struct device;

int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label);
void devm_gpio_free(struct device *dev, unsigned int gpio);

 

/*******************************************************include/asm-generic**********************************/

/* Platforms may implement their GPIO interface with library code,
* at a small performance cost for non-inlined operations and some
* extra memory (for code and for per-GPIO table entries).
*
* While the GPIO programming interface defines valid GPIO numbers
* to be in the range 0..MAX_INT, this library restricts them to the
* smaller range 0..ARCH_NR_GPIOS-1.
*
* ARCH_NR_GPIOS is somewhat arbitrary; it usually reflects the sum of
* builtin/SoC GPIOs plus a number of GPIOs on expanders; the latter is
* actually an estimate of a board-specific value.
*/

#ifndef ARCH_NR_GPIOS
#if defined(CONFIG_ARCH_NR_GPIO) && CONFIG_ARCH_NR_GPIO > 0
#define ARCH_NR_GPIOS CONFIG_ARCH_NR_GPIO
#else
#define ARCH_NR_GPIOS 512
#endif
#endif

/*
* "valid" GPIO numbers are nonnegative and may be passed to
* setup routines like gpio_request(). only some valid numbers
* can successfully be requested and used.
*
* Invalid GPIO numbers are useful for indicating no-such-GPIO in
* platform data and other tables.
*/

static inline bool gpio_is_valid(int number)
{
return number >= 0 && number < ARCH_NR_GPIOS;
}

struct device;
struct gpio;
struct seq_file;
struct module;
struct device_node;
struct gpio_desc;

/* caller holds gpio_lock *OR* gpio is marked as requested */
static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
{
return gpiod_to_chip(gpio_to_desc(gpio));
}

/* Always use the library code for GPIO management calls,
* or when sleeping may be involved.
*/
extern int gpio_request(unsigned gpio, const char *label);
extern void gpio_free(unsigned gpio);

static inline int gpio_direction_input(unsigned gpio)
{
return gpiod_direction_input(gpio_to_desc(gpio));
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}

static inline int gpio_set_debounce(unsigned gpio, unsigned debounce)
{
return gpiod_set_debounce(gpio_to_desc(gpio), debounce);
}

static inline int gpio_get_value_cansleep(unsigned gpio)
{
return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio));
}
static inline void gpio_set_value_cansleep(unsigned gpio, int value)
{
return gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value);
}


/* A platform's <asm/gpio.h> code may want to inline the I/O calls when
* the GPIO is constant and refers to some always-present controller,
* giving direct access to chip registers and tight bitbanging loops.
*/
static inline int __gpio_get_value(unsigned gpio)
{
return gpiod_get_raw_value(gpio_to_desc(gpio));
}
static inline void __gpio_set_value(unsigned gpio, int value)
{
return gpiod_set_raw_value(gpio_to_desc(gpio), value);
}

static inline int __gpio_cansleep(unsigned gpio)
{
return gpiod_cansleep(gpio_to_desc(gpio));
}

static inline int __gpio_to_irq(unsigned gpio)
{
return gpiod_to_irq(gpio_to_desc(gpio));
}

extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
extern int gpio_request_array(const struct gpio *array, size_t num);
extern void gpio_free_array(const struct gpio *array, size_t num);

/*将对应的引脚导入的文件系统中 */
static inline int gpio_export(unsigned gpio, bool direction_may_change)
{
return gpiod_export(gpio_to_desc(gpio), direction_may_change);
}
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
struct gpio_chip *chip;
struct gpio_device *gdev;
struct gpiod_data *data;
unsigned long flags;
int status;
const char *ioname = NULL;
struct device *dev;
int offset;

/* can't export until sysfs is available ... */
if (!gpio_class.p) {
pr_debug("%s: called too early!\n", __func__);
return -ENOENT;
}

if (!desc) {
pr_debug("%s: invalid gpio descriptor\n", __func__);
return -EINVAL;
}

gdev = desc->gdev;
chip = gdev->chip;

mutex_lock(&sysfs_lock);

/* check if chip is being removed */
if (!chip || !gdev->mockdev) {
status = -ENODEV;
goto err_unlock;
}

spin_lock_irqsave(&gpio_lock, flags);
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
test_bit(FLAG_EXPORT, &desc->flags)) {
spin_unlock_irqrestore(&gpio_lock, flags);
gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
__func__,
test_bit(FLAG_REQUESTED, &desc->flags),
test_bit(FLAG_EXPORT, &desc->flags));
status = -EPERM;
goto err_unlock;
}
spin_unlock_irqrestore(&gpio_lock, flags);

/*gpiod_data 分配空间*/
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
status = -ENOMEM;
goto err_unlock;
}

data->desc = desc;
mutex_init(&data->mutex);
if (chip->direction_input && chip->direction_output)
data->direction_can_change = direction_may_change;
else
data->direction_can_change = false;

offset = gpio_chip_hwgpio(desc);
if (chip->names && chip->names[offset])
ioname = chip->names[offset];

/* 创建一个device 并且在文件系统中注册 */
dev = device_create_with_groups(&gpio_class, &gdev->dev,
MKDEV(0, 0), data, gpio_groups,
ioname ? ioname : "gpio%u",
desc_to_gpio(desc));
if (IS_ERR(dev)) {
status = PTR_ERR(dev);
goto err_free_data;
}
/* 设置导入的标志 */
set_bit(FLAG_EXPORT, &desc->flags);
mutex_unlock(&sysfs_lock);
return 0;

err_free_data:
kfree(data);
err_unlock:
mutex_unlock(&sysfs_lock);
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}

static inline int gpio_export_link(struct device *dev, const char *name,
unsigned gpio)
{
return gpiod_export_link(dev, name, gpio_to_desc(gpio));
}

static inline void gpio_unexport(unsigned gpio)
{
gpiod_unexport(gpio_to_desc(gpio));
}

 

上一篇:clickhouse条件函数


下一篇:记php-mysql分页查询出现重复数据