Linux 创建gpiochip 流程和使用流程

创建gpiochip的流程

定义gpio_chip结构: 在GPIO控制器驱动中,首先要定义一个gpio_chip结构体,并初始化其各个字段。

struct gpio_chip my_gpio_chip = {
    .label = "my_gpio_chip",
    .base = -1,  // 让内核自动分配基地址
    .ngpio = 32,  // 比如该控制器管理32个GPIO引脚
    .owner = THIS_MODULE,
    .set = my_gpio_set_value,
    .get = my_gpio_get_value,
    .direction_input = my_gpio_direction_input,
    .direction_output = my_gpio_direction_output,
    // 其他必需的操作函数指针
};
struct gpio_chip my_gpio_chip = {
    .label = "my_gpio_chip",
    .base = -1,  // 让内核自动分配基地址
    .ngpio = 32,  // 比如该控制器管理32个GPIO引脚
    .owner = THIS_MODULE,
    .set = my_gpio_set_value,
    .get = my_gpio_get_value,
    .direction_input = my_gpio_direction_input,
    .direction_output = my_gpio_direction_output,
    // 其他必需的操作函数指针
};

实现操作函数: 实现上面结构体中的各个函数,比如设置输入输出方向、设置和获取引脚值。

static void my_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value) {
    // 设置GPIO引脚的值
}

static int my_gpio_get_value(struct gpio_chip *chip, unsigned offset) {
    // 获取GPIO引脚的值
}

static int my_gpio_direction_input(struct gpio_chip *chip, unsigned offset) {
    // 设置GPIO引脚为输入
}

static int my_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) {
    // 设置GPIO引脚为输出并设置初始值
}
static void my_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value) {
    // 设置GPIO引脚的值
}

static int my_gpio_get_value(struct gpio_chip *chip, unsigned offset) {
    // 获取GPIO引脚的值
}

static int my_gpio_direction_input(struct gpio_chip *chip, unsigned offset) {
    // 设置GPIO引脚为输入
}

static int my_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) {
    // 设置GPIO引脚为输出并设置初始值
}

注册gpiochip: 在驱动的初始化函数中,使用gpiochip_add函数注册这个gpio_chip。

static int __init my_gpio_driver_init(void) {
    int ret;
    ret = gpiochip_add(&my_gpio_chip);
    if (ret) {
        pr_err("Failed to add gpiochip\n");
        return ret;
    }
    pr_info("gpiochip added successfully\n");
    return 0;
}

static void __exit my_gpio_driver_exit(void) {
    gpiochip_remove(&my_gpio_chip);
    pr_info("gpiochip removed successfully\n");
}

module_init(my_gpio_driver_init);
module_exit(my_gpio_driver_exit);
static int __init my_gpio_driver_init(void) {
    int ret;
    ret = gpiochip_add(&my_gpio_chip);
    if (ret) {
        pr_err("Failed to add gpiochip\n");
        return ret;
    }
    pr_info("gpiochip added successfully\n");
    return 0;
}

static void __exit my_gpio_driver_exit(void) {
    gpiochip_remove(&my_gpio_chip);
    pr_info("gpiochip removed successfully\n");
}

module_init(my_gpio_driver_init);
module_exit(my_gpio_driver_exit);

设备树支持(可选): 对于设备树支持的驱动,还需要在设备树中添加GPIO控制器的描述,并在驱动中解析这些节点。

设备树节点示例:
dts文件:

gpio@12340000 {
    compatible = "my,gpio";
    reg = <0x12340000 0x1000>;
    gpio-controller;
    #gpio-cells = <2>;
};
gpio@12340000 {
    compatible = "my,gpio";
    reg = <0x12340000 0x1000>;
    gpio-controller;
    #gpio-cells = <2>;
};

内核解析设备树示例:

static int my_gpio_probe(struct platform_device *pdev) {
    struct resource *res;
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    // 解析资源,映射寄存器等
    return gpiochip_add(&my_gpio_chip);
}

static const struct of_device_id my_gpio_of_match[] = {
    { .compatible = "my,gpio", },
    {},
};
MODULE_DEVICE_TABLE(of, my_gpio_of_match);

static struct platform_driver my_gpio_driver = {
    .probe = my_gpio_probe,
    .remove = my_gpio_remove,
    .driver = {
        .name = "my_gpio",
        .of_match_table = of_match_ptr(my_gpio_of_match),
    },
};

module_platform_driver(my_gpio_driver);
static int my_gpio_probe(struct platform_device *pdev) {
    struct resource *res;
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    // 解析资源,映射寄存器等
    return gpiochip_add(&my_gpio_chip);
}

static const struct of_device_id my_gpio_of_match[] = {
    { .compatible = "my,gpio", },
    {},
};
MODULE_DEVICE_TABLE(of, my_gpio_of_match);

static struct platform_driver my_gpio_driver = {
    .probe = my_gpio_probe,
    .remove = my_gpio_remove,
    .driver = {
        .name = "my_gpio",
        .of_match_table = of_match_ptr(my_gpio_of_match),
    },
};

module_platform_driver(my_gpio_driver);

使用gpiochip的流程

  • 请求和释放GPIO引脚:

在驱动或应用程序中,可以使用gpio_request和gpio_free来请求和释放一个GPIO引脚。

int gpio = 20;  // 想要使用的GPIO引脚编号
int ret;

ret = gpio_request(gpio, "my_gpio_user");
if (ret) {
    printk(KERN_ERR "Failed to request GPIO %d\n", gpio);
    return ret;
}

// 使用完毕后释放GPIO
gpio_free(gpio);
int gpio = 20;  // 想要使用的GPIO引脚编号
int ret;

ret = gpio_request(gpio, "my_gpio_user");
if (ret) {
    printk(KERN_ERR "Failed to request GPIO %d\n", gpio);
    return ret;
}

// 使用完毕后释放GPIO
gpio_free(gpio);
  • 设置和读取GPIO引脚值:

使用gpio_set_value和gpio_get_value函数来设置和读取GPIO引脚的值。

gpio_set_value(gpio, 1);  // 将引脚设置为高电平
int value = gpio_get_value(gpio);  // 读取引脚的当前值
gpio_set_value(gpio, 1);  // 将引脚设置为高电平
int value = gpio_get_value(gpio);  // 读取引脚的当前值
  • 设置GPIO方向:

使用gpio_direction_input和gpio_direction_output函数来设置GPIO引脚的方向。

ret = gpio_direction_input(gpio);  // 将引脚设置为输入
if (ret) {
    printk(KERN_ERR "Failed to set GPIO %d as input\n", gpio);
    return ret;
}

ret = gpio_direction_output(gpio, 1);  // 将引脚设置为输出,并初始化为高电平
if (ret) {
    printk(KERN_ERR "Failed to set GPIO %d as output\n", gpio);
    return ret;
}
ret = gpio_direction_input(gpio);  // 将引脚设置为输入
if (ret) {
    printk(KERN_ERR "Failed to set GPIO %d as input\n", gpio);
    return ret;
}

ret = gpio_direction_output(gpio, 1);  // 将引脚设置为输出,并初始化为高电平
if (ret) {
    printk(KERN_ERR "Failed to set GPIO %d as output\n", gpio);
    return ret;
}

用户空间操作:

在用户空间,可以通过sysfs接口或者/dev/gpiochip字符设备来操作GPIO引脚。

  • 使用sysfs接口:
echo 20 > /sys/class/gpio/export  # 请求GPIO 20引脚
echo out > /sys/class/gpio/gpio20/direction  # 设置方向为输出
echo 1 > /sys/class/gpio/gpio20/value  # 设置值为高电平
cat /sys/class/gpio/gpio20/value  # 读取值
echo 20 > /sys/class/gpio/unexport  # 释放GPIO 20引脚
echo 20 > /sys/class/gpio/export  # 请求GPIO 20引脚
echo out > /sys/class/gpio/gpio20/direction  # 设置方向为输出
echo 1 > /sys/class/gpio/gpio20/value  # 设置值为高电平
cat /sys/class/gpio/gpio20/value  # 读取值
echo 20 > /sys/class/gpio/unexport  # 释放GPIO 20引脚

使用/dev/gpiochip字符设备(需要编写对应的用户空间程序):

  • 用户可以编写程序使用ioctl来操作GPIO引脚,这提供了比sysfs更高效的方式。
// 示例代码需要包含 <linux/gpio.h><fcntl.h>
int fd = open("/dev/gpiochip0", O_RDONLY);
struct gpiochip_info info;
ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);  // 获取芯片信息

// 设置和读取的更多示例可以参考相应的用户空间库或文档
// 示例代码需要包含 <linux/gpio.h><fcntl.h>
int fd = open("/dev/gpiochip0", O_RDONLY);
struct gpiochip_info info;
ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);  // 获取芯片信息

// 设置和读取的更多示例可以参考相应的用户空间库或文档

总结来说,创建和使用gpiochip涉及到内核和用户空间的多层次交互,主要通过定义结构体、实现必要的操作函数、注册芯片、请求和释放GPIO引脚、以及设置和读取引脚状态等步骤来完成。设备树的支持还需要解析设备树节点,并在驱动中根据这些描述进行相应的初始化工作。

<think>好的,我现在需要回答用户关于GPIO中断处理中两个函数gpiochip_irqchip_add_nestedirq_domain_add_legacy的区别的问题。首先,我得回忆一下这两个函数的作用它们在Linux内核中的使用场景。 首先,用户提到这两个函数都是用来设置IRQ中断域的,但具体区别是什么。我记得在Linux内核中,GPIO控制器通常使用irqchip来处理中断,而这两个函数可能属于不同的层次或者用途。 让我先想一下gpiochip_irqchip_add_nested。这个函数应该是用于将GPIO控制器的中断控制器(irqchip)添加到系统中,特别是当这个GPIO控制器是一个嵌套的中断控制器时。嵌套的中断控制器意味着它位于另一个中断控制器的下游,比如级联的中断。这种情况下,中断处理可能需要特殊的处理,比如线程化处理或者嵌套的锁机制。这时候,使用nested类型的irqchip可以确保中断处理程序正确执行,避免死锁等问题。 然后是irq_domain_add_legacy。这个函数属于irqdomain的API,用于创建传统的中断域。传统的中断映射通常是指固定、静态的中断号分配,比如在设备树或ACPI表中已经定义好的中断号。当硬件的中断线是静态配置且不需要动态分配时,可以使用legacy方式。例如,一些老旧的硬件或者某些需要保持向后兼容性的情况会用到这种方法。 接下来,我需要明确它们的区别。首先,应用场景不同。gpiochip_irqchip_add_nested用于GPIO控制器作为嵌套的中断控制器的情况,而irq_domain_add_legacy则是用于传统的中断映射方式。其次,结构层次不同:gpiochip的函数属于GPIO子系统,而irq_domain属于中断子系统更底层的部分。再者,用途不同:nested处理的是中断控制器的层次结构,而legacy处理的是中断号的静态映射。 可能还需要考虑它们的参数使用方式。gpiochip_irqchip_add_nested需要关联到具体的gpio_chip结构,设置父中断控制器,处理函数等,而irq_domain_add_legacy需要指定中断号的范围、操作函数以及设备节点等信息。 另外,错误处理方面可能也有区别。嵌套的中断控制器需要确保父控制器的正确设置,而legacy域则可能涉及更多的静态配置,比如固定的中断号映射,不需要动态分配。 需要验证一下是否这两个函数在代码中的调用位置参数确实如我所想。例如,gpiochip的函数可能在GPIO驱动初始化时被调用,而irq_domain的函数可能在更早的中断控制器注册阶段使用。同时,是否legacy方法已经被逐渐淘汰,或者在某些特定硬件中仍然必需。 总结来说,主要区别在于应用场景、所属子系统中断处理机制的不同。一个是处理嵌套中断的GPIO控制器注册,另一个是创建传统静态中断映射的域。可能需要进一步查阅内核代码或文档来确认这些细节,但根据现有的知识,这样的区分是合理的。</think>在Linux内核中断子系统中,`gpiochip_irqchip_add_nested``irq_domain_add_legacy`虽然都涉及中断域设置,但设计目标应用场景有本质区别: --- ### 一、定位层级不同 1. **gpiochip_irqchip_add_nested** - **所属子系统**:GPIO子系统 - **作用**:将GPIO控制器注册为**嵌套中断控制器**(Nested IRQ Chip),通常用于GPIO引脚级联到父中断控制器(如GIC)的场景。 - **典型场景**:GPIO引脚作为中断源,通过父中断控制器(如`irq-gic`)传递中断信号。 - **关键特征**: - 需要显式指定父中断控制器(`parent_handler`) - 支持嵌套中断处理(避免中断重入导致的死锁) - 通过`gpiochip_set_nested_irqchip()`启用嵌套模式 2. **irq_domain_add_legacy** - **所属子系统**:通用中断子系统(Generic IRQ) - **作用**:创建**传统静态映射**的中断域,用于固定硬件中断号的旧设备。 - **典型场景**:硬件中断号与Linux虚拟中断号(`virq`)是1:1固定映射(如x86 PIC)。 - **关键特征**: - 不涉及硬件级联关系 - 中断号预定义,不可动态分配 - 常用于兼容旧驱动程序或固定中断拓扑的硬件 --- ### 二、中断处理流程差异 1. **gpiochip_irqchip_add_nested** - 中断触发流程: ```text HW中断 → 父中断控制器 → GPIO子控制器 → 具体GPIO引脚的中断处理 ``` - 需要实现`irq_chip`操作(如`.irq_mask`, `.irq_unmask`) - 通过`gpiochip_to_irq()`将GPIO编号转换为虚拟中断号 2. **irq_domain_add_legacy** - 中断触发流程: ```text HW中断 → 直接映射到固定virq → 调用对应中断处理函数 ``` - 不需要级联关系,中断号由`irq_domain`静态分配 --- ### 三、代码结构对比 #### 1. `gpiochip_irqchip_add_nested` 示例 ```c struct gpio_chip *gc; struct irq_chip *irqchip; // 初始化GPIO控制器的irqchip gpiochip_irqchip_add_nested(gc, irqchip, 0, handle_edge_irq, IRQ_TYPE_NONE); // 设置嵌套模式 gpiochip_set_nested_irqchip(gc, irqchip, parent_irq); // 为每个GPIO引脚设置中断 for (i = 0; i < gc->ngpio; i++) irq_set_chip_data(virq + i, gpiochip_get_data(gc)); ``` #### 2. `irq_domain_add_legacy` 示例 ```c struct device_node *np; struct irq_domain *domain; // 创建传统中断域 domain = irq_domain_add_legacy(np, NUM_IRQS, 0, 0, &irq_domain_simple_ops, NULL); // 直接通过固定偏移获取virq int virq = irq_create_mapping(domain, hwirq); ``` --- ### 四、选择依据 | **场景** | **推荐函数** | |---------------------------|------------------------------| | GPIO引脚作为级联中断源 | `gpiochip_irqchip_add_nested` | | 旧设备固定中断号映射 | `irq_domain_add_legacy` | --- ### 五、总结 - **`gpiochip_irqchip_add_nested`**:面向GPIO控制器作为子中断控制器的级联场景,强调嵌套安全父子中断协作。 - **`irq_domain_add_legacy`**:面向传统静态中断映射,适用于无需动态分配中断号的旧硬件。 两者在中断拓扑中的位置作用层级完全不同,需根据硬件中断设计选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天夏已微凉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值