GPIO子系统架构分析(一)
GPIO子系统架构分析(二)
通过sysf操作gpio
在gpio-sysfs.c驱动文件的入口函数:
static struct class_attribute gpio_class_attrs[] = {
__ATTR(export, 0200, NULL, export_store),
__ATTR(unexport, 0200, NULL, unexport_store),
__ATTR_NULL,
};
static struct class gpio_class = {
.name = "gpio",
.owner = THIS_MODULE,
.class_attrs = gpio_class_attrs,
};
static int __init gpiolib_sysfs_init(void)
{
int status;
unsigned long flags;
struct gpio_device *gdev;
/* 在/sys/class目录下创建一个gpio目录,
并在/sys/class/gpio目录下创建两个属性文件export和unexport
*/
status = class_register(&gpio_class);
if (status < 0)
return status;
spin_lock_irqsave(&gpio_lock, flags);
/* 遍历gpio_devices链表,调用gpiochip_sysfs_register,
即在/sys/class/gpio目录下,为每个gpio控制器创建一个sysfs目录
*/
list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->mockdev)
continue;
spin_unlock_irqrestore(&gpio_lock, flags);
status = gpiochip_sysfs_register(gdev);
spin_lock_irqsave(&gpio_lock, flags);
}
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}
那么在/sys/class/gpio目录下:
export属性文件用于导出控制的GPIO引脚,unexport用于取消导出。
如下使用方法:
echo 131 > export //将131号的gpio导出到用户空间
导出gpio引脚后,如下图:
进入gpio131目录:
有direction 、value等属性文件。
echo out > direction //可以将gpio引脚设为输出模式
echo in > direction //可以将gpio引脚设为输入模式
echo 1 > value //设置设为高电平
echo 0 > value //设置设为低电平
源码分析
那么执行命令:echo 131 > export,究竟发生了什么?
//echo 131 > export 最终调用到export_store函数,buf指向"131"这个字符串
static ssize_t export_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t len)
{
long gpio;
struct gpio_desc *desc;
int status;
//把字符串转换为long型数据(131)
status = kstrtol(buf, 0, &gpio);
......
//通过gpio号获得对应的gpio_desc
desc = gpio_to_desc(gpio);
......
//申请gpio
status = gpiod_request(desc, "sysfs");
if (status < 0) {
if (status == -EPROBE_DEFER)
status = -ENODEV;
goto done;
}
//导出gpio,创建gpio131目录等
status = gpiod_export(desc, true);
if (status < 0)
gpiod_free(desc);
else
set_bit(FLAG_SYSFS, &desc->flags);
......
}
gpiod_export:
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;
......
gdev = desc->gdev;
chip = gdev->chip;
mutex_lock(&sysfs_lock);
......
//如果已经该gpio引脚已申请或者已导出,则返回错误
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);
......
status = -EPERM;
goto err_unlock;
}
......
//创建一个struct gpiod_data
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
status = -ENOMEM;
goto err_unlock;
}
//设置gpiod_data
data->desc = desc;
mutex_init(&data->mutex);
......
/* 在/sys/class/gpio/目录下创建导出的gpio引脚的sysfs目录
并在该目录下创建属性文件,属性文件name、store/show函数在属性组gpio_groups里描述
*/
dev = device_create_with_groups(&gpio_class, &gdev->dev,
MKDEV(0, 0), data, gpio_groups,
ioname ? ioname : "gpio%u",
desc_to_gpio(desc));
......
//设置已导出标志位
set_bit(FLAG_EXPORT, &desc->flags);
......
}
重点在于这个属性组,gpio_groups,定义了direction、value等属性及操作函数:
static struct attribute *gpio_attrs[] = {
&dev_attr_direction.attr, //direction属性
&dev_attr_edge.attr, //edge属性
&dev_attr_value.attr, //value属性
&dev_attr_active_low.attr, //active属性
NULL,
};
static const struct attribute_group gpio_group = {
.attrs = gpio_attrs,
.is_visible = gpio_is_visible,
};
static const struct attribute_group *gpio_groups[] = {
&gpio_group,
NULL
};
以direction属性文件为例,打开direction属性文件会调用direction_show函数:
/* drivers/gpio/gpiolib-sysfs.c */
static ssize_t direction_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
ssize_t status;
mutex_lock(&data->mutex);
//获取方向,返回状态
gpiod_get_direction(desc);
status = sprintf(buf, "%s\n",
test_bit(FLAG_IS_OUT, &desc->flags)
? "out" : "in");
mutex_unlock(&data->mutex);
return status;
}
打开direction属性文件如下图:
对direction属性文件写,调用direction_store函数:
/* drivers/gpio/gpiolib-sysfs.c */
static ssize_t direction_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
ssize_t status;
mutex_lock(&data->mutex);
if (sysfs_streq(buf, "high"))
status = gpiod_direction_output_raw(desc, 1);
else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low"))
//设置为输出模式
status = gpiod_direction_output_raw(desc, 0);
else if (sysfs_streq(buf, "in"))
//设置为输入模式
status = gpiod_direction_input(desc);
else
status = -EINVAL;
mutex_unlock(&data->mutex);
return status ? : size;
}