举报网站建设情况网站图标下载
2026/5/21 19:39:27 网站建设 项目流程
举报网站建设情况,网站图标下载,大学生asp网站开发的实训周,php网站留言板漏洞一、GPIO子系统概述 1、什么是GPIO子系统 GPIO#xff08;General Purpose Input/Output#xff0c;通用输入输出#xff09;子系统是 Linux 内核中用于统一管理 GPIO 引脚的框架。它提供了标准的 API 接口#xff0c;使得驱动程序可以独立于具体硬件平台操作 GPIO。 2、…一、GPIO子系统概述1、什么是GPIO子系统GPIOGeneral Purpose Input/Output通用输入输出子系统是 Linux 内核中用于统一管理 GPIO 引脚的框架。它提供了标准的 API 接口使得驱动程序可以独立于具体硬件平台操作 GPIO。2、使用GPIO子系统的好处硬件无关性驱动代码不再直接操作寄存器而是调用标准接口。设备树配置引脚信息在设备树中描述便于移植和修改。资源管理支持自动资源释放devm_* 系列函数。sysfs 接口用户空间可通过文件系统操作 GPIO。二、GPIO子系统的组成与使用流程1、两大组成部分a、Pinctrl 子系统负责引脚的复用功能配置将某个引脚配置为 GPIO 功能。b、GPIO 子系统负责 GPIO 的方向设置、读写操作。2、使用流程设备树中指定GPIO引脚 → 驱动中获取GPIO → 设置方向 → 读写值 → 释放资源三、设备树中如何描述GPIO1、GPIO控制器节点由芯片厂商在 .dtsi 文件中定义关键属性gpio-controller; // 表示这是一个GPIO控制器 #gpio-cells 2; // 描述一个引脚需要2个cell2、引用GPIO引脚在自己的设备节点中使用 gpios 或 name-gpios 属性led-gpios gpio1 5 GPIO_ACTIVE_HIGH; key-gpios gpio2 3 GPIO_ACTIVE_LOW;第一个 cell引脚在组内的编号从0开始。第二个 cell标志位如电平有效性。四、驱动中如何操作GPIO1、两大API体系体系头文件代表结构函数前缀说明描述符体系linux/gpio/consumer.hgpio_descgpiod_推荐使用与设备树结合好传统体系linux/gpio.h整数引脚号gpio_需要知道具体引脚号移植性差2、常用函数对照表描述符体系功能函数说明获取GPIOgpiod_get()/devm_gpiod_get()获取单个GPIO获取多个GPIOgpiod_get_index()/gpiod_get_array()按索引或数组获取设置方向gpiod_direction_input()/gpiod_direction_output()输入/输出读/写值gpiod_get_value()/gpiod_set_value()读写逻辑值释放GPIOgpiod_put()/devm_gpiod_put()释放资源3、推荐使用 devm_ 系列函数自动资源管理当设备卸载或驱动退出时自动释放 GPIO避免资源泄漏。例如devm_gpiod_get()、devm_gpiod_put()。4、逻辑值与物理值gpiod_set_value() 设置的是逻辑值。如果引脚在设备树中定义为 GPIO_ACTIVE_LOW则逻辑值1对应物理低电平。逻辑值 实际期望的电平效果考虑极性。5、用户驱动使用GPIO代码实例/* 确定主设备号 */ static int major 0; static struct class *led_class; static struct gpio_desc *led_gpio; /* 实现对应的open/read/write等函数填入file_operations结构体 */ static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset) { printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); return 0; } /* write(fd, val, 1); */ static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) { int err; char status; //struct inode *inode file_inode(file); //int minor iminor(inode); printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); err copy_from_user(status, buf, 1); /* 根据次设备号和status控制LED */ gpiod_set_value(led_gpio, status); return 1; } static int led_drv_open (struct inode *node, struct file *file) { //int minor iminor(node); printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); /* 根据次设备号初始化LED */ gpiod_direction_output(led_gpio, 0); return 0; } static int led_drv_close (struct inode *node, struct file *file) { printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); return 0; } /* 定义自己的file_operations结构体 */ static struct file_operations led_drv { .owner THIS_MODULE, .open led_drv_open, .read led_drv_read, .write led_drv_write, .release led_drv_close, }; /* 从platform_device获得GPIO * 把file_operations结构体告诉内核注册驱动程序 */ static int chip_demo_gpio_probe(struct platform_device *pdev) { //int err; printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); /* 4.1 设备树中定义有: led-gpios...; */ led_gpio gpiod_get(pdev-dev, led, 0); if (IS_ERR(led_gpio)) { dev_err(pdev-dev, Failed to get GPIO for led\n); return PTR_ERR(led_gpio); } /* 4.2 注册file_operations */ major register_chrdev(0, myled, led_drv); /* /dev/led */ led_class class_create(THIS_MODULE, my_led_class); if (IS_ERR(led_class)) { printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); unregister_chrdev(major, led); gpiod_put(led_gpio); return PTR_ERR(led_class); } device_create(led_class, NULL, MKDEV(major, 0), NULL, myled%d, 0); /* /dev/myled0 */ return 0; } static int chip_demo_gpio_remove(struct platform_device *pdev) { device_destroy(led_class, MKDEV(major, 0)); class_destroy(led_class); unregister_chrdev(major, myled); gpiod_put(led_gpio); return 0; } static const struct of_device_id leds[] { { .compatible my-leddrv }, { }, }; /* 定义platform_driver */ static struct platform_driver chip_demo_gpio_driver { .probe chip_demo_gpio_probe, .remove chip_demo_gpio_remove, .driver { .name myled, .of_match_table leds, }, }; /* 在入口函数注册platform_driver */ static int __init led_init(void) { int err; printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); err platform_driver_register(chip_demo_gpio_driver); return err; } /* 有入口函数就应该有出口函数卸载驱动程序时就会去调用这个出口函数 * 卸载platform_driver */ static void __exit led_exit(void) { printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__); platform_driver_unregister(chip_demo_gpio_driver); } /* 其他完善提供设备信息自动创建设备节点 */ module_init(led_init); module_exit(led_exit); MODULE_LICENSE(GPL);五、GPIO子系统架构与重要数据结构1、GPIO子系统层次架构a、三层结构模型┌─────────────────────────────────────────┐ │ 用户驱动 │ │ 使用GPIO API │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ GPIO子系统核心层GPIOLIB │ │ 提供标准APIgpiod_* 和 gpio_* 函数族 │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ GPIO控制器驱动层gpio_chip │ │ 芯片厂商实现注册具体的GPIO控制器 │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 硬件GPIO控制器 │ │ 实际物理引脚寄存器操作 │ └─────────────────────────────────────────┘b、GPIOLIB向上提供的接口面向驱动开发者核心功能包括获取/释放GPIO设置方向输入/输出读写引脚值c、GPIOLIB向下提供的接口面向芯片厂商核心函数gpiochip_add_data() 芯片厂商通过该函数向内核注册自己的GPIO控制器。2、三大核心数据结构详解a、gpio_deviceGPIO控制器的内核表示// 概念结构非实际代码 struct gpio_device { int id; // 系统中第几个GPIO控制器 struct gpio_chip *chip; // 指向具体的GPIO芯片控制结构含有各类操作函数 struct gpio_desc *descs; // GPIO描述符数组每个引脚对应一个 int base; // 该控制器在全局GPIO编号空间中的起始编号 int ngpio; // 该控制器包含的GPIO引脚数量 // ... 其他内部管理字段 };关键点每个注册的GPIO控制器会获得一个base编号该控制器内的第n个GPIO的全局编号为base n每个GPIO控制器对应一个 gpio_device内核自动创建驱动开发者一般不直接操作包含该控制器所有引脚的 gpio_desc 数组b、gpio_chipGPIO控制器的功能抽象// 关键成员示意 struct gpio_chip { const char *label; // 控制器标签用于标识 struct device *parent; // 父设备通常为platform_device // 各类操作函数 int (*direction_input)(struct gpio_chip *chip, unsigned offset); int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value); int (*get)(struct gpio_chip *chip, unsigned offset); void (*set)(struct gpio_chip *chip, unsigned offset, int value); int (*to_irq)(struct gpio_chip *chip, unsigned offset); // GPIO转中断 int base; // 起始GPIO编号动态分配 u16 ngpio; // GPIO数量 const char *const *names; // 可选每个GPIO的名称 // ... 其他成员 };关键点驱动开发者的核心编写对象芯片厂商需要实现这个结构体必须实现的回调函数方向设置、数值读写可选实现的中断转换函数通过 gpiochip_add_data() 注册后内核会创建对应的 gpio_devicec、gpio_desc单个GPIO引脚的描述符// 关键成员示意 struct gpio_desc { struct gpio_device *gdev; // 所属的GPIO控制器 unsigned long flags; // 标志位方向、使用状态等 // ... 其他内部管理字段 };关键点每个GPIO引脚对应一个 gpio_desc上层驱动通过 gpiod_get() 等函数获取的就是这个结构体的指针包含引脚的当前状态和配置信息六、GPIO控制器驱动开发流程1、定义并初始化 gpio_chip 结构体2、设置实现必要的回调函数direction_input / direction_output方向控制get / set数值读写to_irq可选中断支持3、注册GPIO控制器调用 gpiochip_add_data()4、在设备树中描述控制器现代Linux驱动标准做法5、实现probe/remove函数管理驱动生命周期七、GPIO子系统和Pinctrl子系统的交互机制1、为什么需要交互——引脚复用的必要性a、物理现实一个物理引脚可能有多种用途 ├── GPIO功能通用输入输出 ├── I2C功能数据线SDA ├── SPI功能片选CS └── UART功能发送线TXb、工作流程使用GPIO的正确流程 1. 通过Pinctrl将引脚配置为GPIO功能 2. 通过GPIO子系统操作该引脚2、交互的两种方式a、显式配置推荐在设备树中明确指定Pinctrl配置// 1. Pinctrl控制器定义 pinctrl: pin-controller { compatible vendor,pinctrl; // LED引脚配置 led_pins: led_pins { pins GPIOA5; function gpio; bias-pull-up; drive-strength 20; // 20mA驱动能力 }; }; // 2. GPIO控制器定义 gpio1: gpio { compatible vendor,gpio; gpio-controller; #gpio-cells 2; ngpios 16; }; // 3. 设备节点 led_device { compatible vendor,led; // GPIO引用 led-gpios gpio1 5 GPIO_ACTIVE_HIGH; // 可选的显式Pinctrl配置 pinctrl-names default; pinctrl-0 led_pins; };b、隐式自动配置GPIO后门机制某些引脚默认使用GPIO功能许多现代芯片驱动实现了自动配置当调用 gpiod_get() 获取GPIO时GPIO子系统自动调用Pinctrl将引脚复用为GPIO功能优点简化设备树配置缺点可读性降低依赖驱动实现// 1. Pinctrl控制器定义 pinctrl: pin-controller { compatible vendor,pinctrl; }; // 2. GPIO控制器定义 gpio1: gpio { compatible vendor,gpio; gpio-controller; #gpio-cells 2; ngpios 16; // 建立映射GPIO1的0-15引脚对应Pinctrl的0-15引脚 gpio-ranges pinctrl 0 0 16; }; // 3. 设备节点 led_device { compatible vendor,led; // GPIO引用 led-gpios gpio1 5 GPIO_ACTIVE_HIGH; };3、GPIO与Pinctrl的映射关系a、两个编号空间Pinctrl编号空间芯片视角 GPIO编号空间内核视角 0 ── 引脚0 100 ── GPIO组0引脚0 1 ── 引脚1 101 ── GPIO组0引脚1 2 ── 引脚2 102 ── GPIO组0引脚2 ... ... 6 ── 引脚6 106 ── GPIO组1引脚2 7 ── 引脚7 107 ── GPIO组1引脚3b、映射建立机制通过设备树的gpio-ranges属性建立映射gpio_virt: virtual_gpiocontroller { compatible vendor,gpio; gpio-controller; #gpio-cells 2; ngpios 12; /* * gpio-ranges格式 * pinctrl_handle GPIO起始号 Pinctrl起始号 引脚数量 */ gpio-ranges pinctrlA 0 128 12; };含义该GPIO控制器的0号引脚 ←→ PinctrlA的128号引脚共映射12个引脚c、内核自动解析映射在GPIO驱动注册时内核会自动解析 gpio-ranges 属性建立GPIO引脚号与Pinctrl引脚号之间的映射关系。具体来说在 gpiochip_add_data 函数中会调用 of_gpiochip_add_pin_range 来处理这些映射。4、内核交互流程详解a、GPIO申请时的自动配置dev驱动调用gpiod_get() ↓ GPIO子系统gpiod_request_commit() ↓ 调用gpio_chip-request回调通常为gpiochip_generic_request ↓ Pinctrl子系统pinctrl_request_gpio() ↓ 转换为Pinctrl引脚号gpio_to_pin() ↓ 调用Pinctrl操作pinmux_request_gpio() → pin_request() ↓ 调用Pinctrl驱动的回调pmxops-gpio_request_enable() 或 pmxops-request ↓ 硬件配置将引脚复用为GPIO功能b、关键代码路径// GPIO申请时调用链 gpiod_get() → gpiod_get_index() → of_find_gpio() // 从设备树找到GPIO → gpiod_request() // 申请GPIO → gpiod_request_commit() → chip-request() // GPIO芯片的request回调通常设置为gpiochip_generic_request → pinctrl_request_gpio() → pinmux_request_gpio() → pin_request() → ops-gpio_request_enable() / pmxops-request // Pinctrl驱动配置硬件c、Pinctrl驱动的实现Pinctrl驱动需要实现以下回调之一struct pinmux_ops { // 方式1专门用于GPIO的使能函数 int (*gpio_request_enable)(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset); // 方式2通用请求函数 int (*request)(struct pinctrl_dev *pctldev, unsigned offset); };八、GPIO控制器驱动框架/* 定义私有数据结构用于保存驱动私有信息如寄存器基地址、中断等 */ struct virtual_gpio_priv { struct gpio_chip gc; /* 可以添加其他私有成员例如 * void __iomem *base; * struct regmap *regmap; * int irq; * spinlock_t lock; */ }; /* 设备树匹配表 */ static const struct of_device_id virtual_gpio_of_match[] { { .compatible your_compatible_string, }, // 修改为实际的兼容字符串 { }, }; MODULE_DEVICE_TABLE(of, virtual_gpio_of_match); /* 设置GPIO方向为输出 */ static int virt_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int val) { /* 1. 根据偏移量offset将对应的GPIO配置为输出模式 * 2. 根据val设置初始输出电平 * 3. 通常需要操作硬件寄存器这里只是打印信息 */ printk(set pin %d as output %s\n, offset, val ? high : low); return 0; } /* 设置GPIO方向为输入 */ static int virt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { /* 根据偏移量offset将对应的GPIO配置为输入模式 */ printk(set pin %d as input\n, offset); return 0; } /* 获取GPIO的值输入或输出 */ static int virt_gpio_get_value(struct gpio_chip *gc, unsigned offset) { /* 从硬件读取偏移量为offset的GPIO的值并返回 */ int val 0; // 这里应该从硬件读取示例中返回0 printk(get pin %d, its val %d\n, offset, val); return val; } /* 设置GPIO的值输出 */ static void virt_gpio_set_value(struct gpio_chip *gc, unsigned offset, int val) { /* 根据偏移量offset和val设置GPIO的输出电平 */ printk(set pin %d as %d\n, offset, val); // 实际硬件操作将val写入对应寄存器的对应位 } /* 将GPIO转换为中断号如果需要支持中断 */ static int virt_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { /* 返回对应GPIO的中断号如果支持中断的话 */ // return irq_create_mapping(NULL, offset); // 示例实际需要根据硬件映射 return -ENXIO; // 默认不支持中断 } /* probe函数当设备与驱动匹配时被调用 */ static int virtual_gpio_probe(struct platform_device *pdev) { int ret; struct virtual_gpio_priv *priv; struct device_node *np pdev-dev.of_node; printk(%s %s %d\n, __FILE__, __FUNCTION__, __LINE__); /* 1. 分配私有数据结构 */ priv devm_kzalloc(pdev-dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* 2. 设置gpio_chip结构体 */ priv-gc.label pdev-name; // 使用设备名作为标签 priv-gc.parent pdev-dev; priv-gc.owner THIS_MODULE; /* 2.1 设置GPIO数量可以从设备树中读取也可以直接指定 */ ret of_property_read_u32(np, ngpios, priv-gc.ngpio); if (ret) { /* 如果设备树中没有ngpios属性则默认设置为一个值例如32 */ priv-gc.ngpio 32; } /* 2.2 设置GPIO基编号通常设为-1表示动态分配 */ priv-gc.base -1; /* 2.3 设置回调函数 */ priv-gc.direction_output virt_gpio_direction_output; priv-gc.direction_input virt_gpio_direction_input; priv-gc.get virt_gpio_get_value; priv-gc.set virt_gpio_set_value; priv-gc.request gpiochip_generic_request; // 通过提供此函数最终使得Pinctrl将引脚复用为GPIO功能 priv-gc.to_irq virt_gpio_to_irq; // 可选如果支持中断 /* 2.4 其他可选设置例如 * priv-gc.can_sleep false; // 如果操作可能会睡眠则设为true * priv-gc.names ...; // 每个GPIO的名字 */ /* 3. 获取硬件资源例如寄存器基地址并映射 */ /* priv-base devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv-base)) { return PTR_ERR(priv-base); } */ /* 4. 注册gpio_chip */ ret devm_gpiochip_add_data(pdev-dev, priv-gc, priv); if (ret) { dev_err(pdev-dev, Failed to register GPIO controller\n); return ret; } /* 5. 将私有数据保存到platform设备中以便remove等函数使用 */ platform_set_drvdata(pdev, priv); return 0; } /* remove函数当设备被移除时被调用 */ static int virtual_gpio_remove(struct platform_device *pdev) { struct virtual_gpio_priv *priv platform_get_drvdata(pdev); printk(%s %s %d\n, __FILE__, __FUNCTION__, __LINE__); /* 如果有需要清理的资源在这里进行 */ /* 注意由于使用了devm系列函数大部分资源会自动释放 */ return 0; } /* platform驱动结构体 */ static struct platform_driver virtual_gpio_driver { .probe virtual_gpio_probe, .remove virtual_gpio_remove, .driver { .name your_driver_name, // 修改为实际的驱动名 .of_match_table of_match_ptr(virtual_gpio_of_match), } }; /* 模块入口函数 */ static int __init virtual_gpio_init(void) { printk(%s %s %d\n, __FILE__, __FUNCTION__, __LINE__); return platform_driver_register(virtual_gpio_driver); } /* 模块出口函数 */ static void __exit virtual_gpio_exit(void) { printk(%s %s %d\n, __FILE__, __FUNCTION__, __LINE__); platform_driver_unregister(virtual_gpio_driver); } module_init(virtual_gpio_init); module_exit(virtual_gpio_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Your Name); MODULE_DESCRIPTION(A generic GPIO controller driver);

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询