static int __init gpiolib_sysfs_init(void)
{
......
status = class_register(&gpio_class);
......
for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
......
status = gpiochip_export(chip);
......
}
......
}
gpio_class的name定义为gpio,所以我们可以在/sys/class/下面看到gpio的目录。class_attr被定义为gpio_class_attrs,里面定义了class的两个属性export和unexport,该属性最终会在用户空间生成对应的文件。class_attribute包含一个attribute变量和show/sotre两个方法,其中attribute用来标示该属性的名字和操作权限,show/store是对该属性进行操作的函数,对应用户空间的cat/echo操作。所以在/sys/class/gpio目录下面可以看到两个文件分别为export和unexport,同时可以看到这两个文件的属性为所有者可写,即对应创建时定义的0200。所以这两个文件只能用echo向文件写内容,而不能通过cat显示文件的内容。从export和unexport的定义也可以验证这点,它们的show都定义为NULL,shore分别定义export_store和unexport_store,由此可以得知这两个函数就是用户空间操作的最终执行函数。先跳过这两个函数,继续看gpiolib_sysfs_init的第二部分工作。
static struct class_attribute gpio_class_attrs[] = {
__ATTR(export, 0200, NULL, export_store),
__ATTR(unexport, 0200, NULL, unexport_store),
__ATTR_NULL,
};
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode },\
.show = _show,
.store = _store,
}
第二部分的工作就是遍历每一个注册的gpio pin,然后把注册到gpio lib的GPIO控制器导出到用户空间。首先在创建一个名为gpiochip*的设备,所以可以在/sys/class/gpio目录下面看到对应的gpiochip*目录,以及该目录下面的subsystem和uevent两个属性文件。然后再在该目录下面创建定义的属性文件,从属性数组gpiochip_attrs可以看到总共定义了三个属性,分别为base,label和ngpio,属性的权限为所有用户可读。最后把该控制器的exported标志设置为1,表示已经导出到用户空间。
static int gpiochip_export(struct gpio_chip *chip)
{
......
dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,"gpiochip%d", chip->base);
status = sysfs_create_group(&dev->kobj,&gpiochip_attr_group);
......
}
回到第一部分来看export_store函数,该函数同样分成gpio_request()和gpio_export()两个阶段。在gpio_request中,首先判断该pin的有效性,即该pin的pin num不能超过最大的GPIO pin脚数,然后把FLAG_REQUESTED标记设为1,表示该pin已被申请使用,同时把label属性设置为sysfs,最后调用gpio chip注册时定义的request函数,由前面的硬件驱动分析可知,该函数直接返回一个0值。gpio_export()的执行过程和gpiochip_export比较类似,先创建一个gpio*的设备,该过程会在用户空间的/sys/class/gpio目录下面导出gpio*的目录和该目录下面的subsytem/uevent属性文件。
int gpio_export(unsigned gpio, bool direction_may_change)
{
......
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),desc, ioname ? ioname : "gpio%u", gpio);
status = sysfs_create_group(&dev->kobj,&gpio_attr_group);
if (!status && direction_may_change)
status = device_create_file(dev,&dev_attr_direction);
f (!status && gpio_to_irq(gpio) >= 0&& (direction_may_change|| !test_bit(FLAG_IS_OUT,&desc->flags)))
status = device_create_file(dev,&dev_attr_edge);
......
set_bit(FLAG_EXPORT, &desc->flags);
......
}
在/sys/class/gpio/目录下面导出gpio*目录后,再在该目录下生成各种属性文件。首先创建的是gpio_attr_group属性组,该组里面包含value和active_low两个属性,这两个属性用DEVICE_ATTR来定义,从定义可以看出,属性文件的权限是0644,即所有者可读可写可执行,用户组和其他用户是可读。在用户空间可以通过value查看和设置GPIO pin的值,通过active_low来查看和设置pin的active_low的标志位。如果注册的GPIO chip有定义GPIO方向操作的函数,则创建direction属性,该属性同样是用DEVICE_ATTR宏来创建,属性的权限也为0644,用户可以利用/sys/class/gpio/gpio*/direction文件来控制gpio pin的操作方向。如果要导出的Pin可以作中断使用,同时该pin没有设置FLAG_IS_OUT位,则用创建edge属性;当该pin作为中断pin使用时,用户可以利用/sys/class/gpio/gpio*/edge文件设置中断触发的方式。
static const DEVICE_ATTR(active_low, 0644,gpio_active_low_show, gpio_active_low_store);
static const DEVICE_ATTR(value, 0644,gpio_value_show, gpio_value_store);
static /* const */ DEVICE_ATTR(direction, 0644,gpio_direction_show, gpio_direction_store);
static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);
以value属性来分析属性文件的具体操作,当用户在用户空间用cat打开value文件查看gpio pin的值时,gpio_value_show()会被调用。该函数的主要调用路径为 gpio_get_value_cansleep()->gpio_to_chip()->chip->get,即通过gpio pin找到该pin对应的gpio chip,然后利用该chip的get函数从GPIO控制器的寄存器里面读到具体的值。
当用户在用户空间用ehco把值写到value文件时,gpio_value_store会被调用,该函数的作用跟gpio_value_show相反,是把用户空间传递过来的值写到GPIO控制器的寄存器。调用路径为gpio_set_value_cansleep()->gpio_to_chip()->chip->set。