一、前言
在正常情况下,假设我们在控制usb使能相关gpio脚的时候,代码中通常我们会做如下调用:
#define USB_EN_GPIO 11
gpio_request(USB_EN_GPIO, "usb_en");
gpio_direction_output(USB_EN_GPIO, 1);
gpio_export(USB_EN_GPIO, false);
上述执行完毕之后就可以给 GPIO_11 这个口上电,并且会生成 sysfs相关的控制节点。这里是/sys/class/gpio/gpio11/
的目录,该目录下的内容为:
-rw-r--r-- 0 0 4096 1970-01-01 01:38 active_low
-rw-r--r-- 0 0 4096 1970-01-01 01:38 direction
drwxr-xr-x 0 0 1970-01-01 00:00 power
lrwxrwxrwx 0 0 1970-01-01 01:38 subsystem -> ../../../../class/gpio
-rw-r--r-- 0 0 4096 1970-01-01 00:00 uevent
-rw-r--r-- 0 0 4096 1970-01-01 01:38 value
调用 gpio_export()
就可以生成该GPIO相关的sysfs控制节点。
二、gpio_export()
1、函数定义:
// include/asm-generic/gpio.h
/*
* A sysfs interface can be exported by individual drivers if they want,
* but more typically is configured entirely from userspace.
*/
static inline int gpio_export(unsigned gpio, bool direction_may_change)
{
return gpiod_export(gpio_to_desc(gpio), direction_may_change);
}
2、gpiod_export()
定义
drivers/gpio/gpiolib.c
/**
* gpiod_export - export a GPIO through sysfs
* @gpio: gpio to make available, already requested
* @direction_may_change: true if userspace may change gpio direction
* Context: arch_initcall or later
*
* When drivers want to make a GPIO accessible to userspace after they
* have requested it -- perhaps while debugging, or as part of their
* public interface -- they may use this routine. If the GPIO can
* change direction (some can't) and the caller allows it, userspace
* will see "direction" sysfs attribute which may be used to change
* the gpio's direction. A "value" attribute will always be provided.
*
* Returns zero on success, else an error.
*/
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
参数 @gpio
用于指定是哪个gpio;
参数 @direction_may_change
用来标记这个gpio的输入输出方向是否可以改变;
3、gpiod_export()
实现
- 如果该gpio已经设置了输入或者输出,那么它的
direction_may_change
为false。
if (!desc->chip->direction_input || !desc->chip->direction_output)
direction_may_change = false;
- 创建目录
/sys/class/gpio/gpiox/
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
desc, ioname ? ioname : "gpio%u",
desc_to_gpio(desc));
- 创建目录
/sys/class/gpio/gpio11/
的相关控制属性
status = sysfs_create_group(&dev->kobj, &gpio_attr_group);
if (status)
goto fail_unregister_device;
static const struct attribute *gpio_attrs[] = {
&dev_attr_value.attr,
&dev_attr_active_low.attr,
NULL,
};
static const struct attribute_group gpio_attr_group = {
.attrs = (struct attribute **) gpio_attrs,
};
这里面固定两个属性:active_low
和 value
,并且有对应的 show() 和 store() 方法。
- 如果这个gpio可以设置输入输出方向,那么要创建
direction()
的控制属性
if (direction_may_change) {
status = device_create_file(dev, &dev_attr_direction);
if (status)
goto fail_unregister_device;
}
- 如果这是一个中断的gpio,那么要创建中断触发边沿 edge() 的控制属性
if (gpiod_to_irq(desc) >= 0 && (direction_may_change ||
!test_bit(FLAG_IS_OUT, &desc->flags))) {
status = device_create_file(dev, &dev_attr_edge);
if (status)
goto fail_unregister_device;
}
三、/sys/class/gpio/export
比如说我们有个gpio12是用于测试的引脚,比如说用来测量代码的执行时间,在release版本中根本不会像在前言中的代码去request和export它。但是我们又要用使用它,怎么办呢?
- 方法一就是像前面的那样,在代码中去调用。不过这比较麻烦,还要编译烧录。
- 另外一个办法就是使用
/sys/class/gpio/export
的功能(也有与之相反的unexport),可以使用#echo 12 > /sys/class/gpio/export
将gpio12 export出来。这样以后在/sys/class/gpio/
目录下就会创建gpio12/
的目录,并且该目录有相关的对gpio12的操作了。
这个强大的功能由 export_store()
实现。这个是 /sys/class/gpio/
下 export 的控制属性。
/*
* /sys/class/gpio/export ... write-only
* integer N ... number of GPIO to export (full access)
* /sys/class/gpio/unexport ... write-only
* integer N ... number of GPIO to unexport
*/
static ssize_t export_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t len)
在该函数中分别会调用 gpiod_request()
和 gpiod_export()
为参数 @buf 指定的gpio口申请并且export出来。
status = gpiod_request(desc, "sysfs");
if (status < 0) {
if (status == -EPROBE_DEFER)
status = -ENODEV;
goto done;
}
status = gpiod_export(desc, true);
if (status < 0)
gpiod_free(desc);
else
set_bit(FLAG_SYSFS, &desc->flags);
四、gpio sysfs
所有关于gpio sysfs的功能都在 drivers/gpio/gpiolib.c
中实现,入口函数是:gpiolib_sysfs_init()
。