Linux Kernel gpio export功能

本文详细介绍了Linux系统中GPIO Sysfs接口的工作原理及其实现方式,包括如何通过gpio_export()函数将GPIO导出到Sysfs文件系统,以及/sys/class/gpio/export功能的使用方法。

一、前言

在正常情况下,假设我们在控制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_lowvalue,并且有对应的 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()

Linux内核中,多个模块共享使用GPIO需要通过合理的配置和管理机制来实现。GPIO共享的核心在于确保多个模块不会同时对同一个引脚进行冲突的操作,同时保持系统的稳定性和可靠性。 ### 使用GPIO请求和释放机制 Linux内核提供了GPIO请求和释放机制,通过`gpio_request()`和`gpio_free()`函数来管理GPIO资源。多个模块可以通过请求GPIO来确保其在使用期间不会被其他模块占用。当模块完成操作后,调用`gpio_free()`释放GPIO资源,以便其他模块可以使用。 例如,一个模块可以通过以下代码请求并使用GPIO: ```c int gpio; int ret; gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, NULL); if (!gpio_is_valid(gpio)) { pr_err("Invalid GPIO\n"); return -EINVAL; } ret = gpio_request(gpio, "reset-gpio"); if (ret) { pr_err("Failed to request GPIO\n"); return ret; } gpio_direction_output(gpio, 1); // 设置GPIO输出高电平 gpio_set_value(gpio, 0); // 设置GPIO输出低电平 gpio_free(gpio); // 使用完成后释放GPIO ``` 上述代码中,`gpio_request()`用于请求GPIO资源,`gpio_direction_output()`和`gpio_set_value()`用于控制GPIO的状态,`gpio_free()`用于释放GPIO资源。这种机制可以确保GPIO资源在多个模块之间安全共享[^3]。 ### 导出GPIO到用户空间 Linux内核还支持将GPIO导出到用户空间,以便用户空间程序可以访问和控制GPIO。通过`gpio_export()`函数,可以将GPIO导出到sysfs文件系统,用户空间程序可以通过文件操作来控制GPIO状态。导出GPIO的代码示例如下: ```c int gpio; int ret; gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, NULL); if (!gpio_is_valid(gpio)) { pr_err("Invalid GPIO\n"); return -EINVAL; } ret = gpio_request(gpio, "reset-gpio"); if (ret) { pr_err("Failed to request GPIO\n"); return ret; } ret = gpio_export(gpio, false); if (ret) { pr_err("Failed to export GPIO\n"); gpio_free(gpio); return ret; } // 用户空间可以通过/sys/class/gpio/gpioX/value访问GPIO状态 ``` 在上述代码中,`gpio_export()`函数用于将GPIO导出到用户空间,用户空间程序可以通过读写`/sys/class/gpio/gpioX/value`文件来控制GPIO的状态。这种机制可以实现内核模块和用户空间程序对GPIO的共享使用[^2]。 ### 多模块共享GPIO的冲突处理 为了避免多个模块共享GPIO时的冲突问题,可以通过以下几种方式来处理: 1. **同步控制**:多个模块在访问GPIO时需要进行同步控制,确保同一时间只有一个模块可以操作GPIO。可以使用互斥锁(mutex)或信号量(semaphore)来实现同步控制。 2. **开漏输出模式**:对于需要多个模块同时输出的GPIO引脚,可以配置为开漏输出模式,这样多个模块可以共享同一个引脚而不会导致短路。开漏输出模式需要外部上拉电阻来提供高电平驱动能力。 3. **中断共享**:如果多个模块需要通过GPIO触发中断,可以使用共享中断机制。在注册中断时,可以通过`IRQF_SHARED`标志来允许多个中断处理程序共享同一个中断线。 ### GPIO复用功能 在某些情况下,GPIO引脚可以被配置为复用功能(Alternate Function),以支持多个外设共享同一个引脚。例如,STM32系列微控制器支持多种复用功能,可以通过寄存器配置将引脚映射到不同的外设上。复用功能的配置可以通过以下代码实现: ```c void configure_gpio_alternate_function(enum gpio_port port, u32 pin, u8 alternate_function) { gpio_set_mode(port, pin, GPIO_MODE_ALTERNATE); gpio_set_alternate_function(port, pin, alternate_function); } ``` 在此示例中,`gpio_set_mode()`函数将GPIO引脚设置为复用功能模式,`gpio_set_alternate_function()`函数指定具体的复用功能编号。通过这种方式,可以将同一个GPIO引脚分配给不同的外设模块。 ### 注意事项 1. **信号完整性**:在共享GPIO时,需要确保信号完整性,避免因外部干扰或负载过大导致信号失真。 2. **驱动能力**:开漏输出模式需要外部上拉电阻来提供高电平驱动能力,选择合适的电阻值可以平衡功耗和响应速度。 3. **同步控制**:多个模块共享GPIO时,应确保控制逻辑的同步性,避免因时序问题导致误操作。 通过合理配置GPIO的工作模式和使用复用功能,可以实现GPIO引脚的共享使用。在实际应用中,应根据具体需求选择合适的配置方法,并注意信号完整性和驱动能力的优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值