- 为什么要引入pinctrl和gpio子系统?
我认为有这么一个根本出发点——让你的程序更有生命力,不要为了目标板的一点小小的变更而忙得焦头烂额。所以,
1)当你的按键驱动程序要去适配不同的SOC时,要做到不改变驱动程序,就要把驱动程序分为上层的按键驱动程序“空壳”(即没有实际对硬件 *** 作的框架)和下层的具体SOC的驱动程序(实现实际所需硬件驱动,并注册给上层以便于其调用)的上下两层,这样一来,当目标SOC变更时换掉下层的SOC驱动程序即可,这就是分层;但,更进一步地,
2)当目标SOC一致而外围电路发生变化时,如果按照上面第1)条更改下层的驱动程序,你仍然会为写一些重复的代码而觉得焦头烂额。这就引入了分离的思想——即将下层的驱动程序分为目标板资源程序文件和对应的SOC驱动程序文件,这样,只要目标板的SOC不变更,只需要修改目标板资源文件即可,这就是分离;但,更进一步地,
3)既然要将资源文件与驱动文件分离,就会存在驱动对于资源文件的依懒或资源文件对驱动文件的依懒(要看工程师的编程习惯),依懒总会带来麻烦——不要依懒我,我也不要依懒你,我们都依懒“接口”吧。于是就有了上层的platform平台设备总线接口,当需要修改驱动程序或设备(资源)文件时修改就好了,platform平台设备总线bus会自动匹配对两者,即当注册platform_device时会自动搜索平台总线下匹配的platform_driver,当注册platform_driver时自动搜索平台总线下匹配的platform_device,然后将资源用于驱动的对象,这就是platform平台设备驱动总线接口;但更进一步地,
4)因为linux使用了宏内核,随着arm处理器的迅猛发展,使用arm处理器的产品千变万化层出不穷,而这又造成了大量的资源文件(被Linus称为“垃圾”的东西)使得内核变得雍肿不堪。为了不将这些大量的重复的资源文件放入内核,于是就有了基于open firmware的设备树,资源文件以设备树文件形式存在而不再放入内核,而在内核启动时就将其转换为platform_device文件,内核雍肿的问题得以解决;但更进一步地,
5)设备树的属性定义自由度非常大——为了匹配platform_driver驱动程序可以设置各种属性,驱动开发人员认识水平和经验各不相同,编写出来的驱动程序也多种多样,这些变化往往影响驱动程序的复用。于是,pinctrl子系统、gpio子系统、input子系统等设备树的标准化属性便应运而生。简单而言,pinctrl子系统用于选择某芯片引脚用于什么作用,比如将imx6ull的GPIO1.IO18用作IO功能,则可以这样定义设备树:
pinctrl_key:keygrp { fsl,pins = < MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080 >; }; key { #address-cells = <1>; #size-cells = <1>; compatible = "glen-key"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_key>; ... };
在内核启动后,imx6ull的GPIO1.IO18就用作IO功能了,这个过程不需要普通驱动开发人员来处理,而需要芯片厂商的驱动开发人员设计对应的pinctrl底层驱动程序,在此按住不表。说回gpio子系统,其用于设置GPIO引脚方向(输入还是输出)、读值──获得电平状态,写值──输出高低电平。有了gpio子系统,普通驱动开发人员只需要调用其接口即可,同样地,具体到如何 *** 作寄存器、读写什么值这些工作由芯片厂商的驱动开发人员设计——因为芯片原厂对这些最熟悉。这里面内核上层提供了pinctrl、gpio接口函数,也就是一堆“空壳”(框架),具体SOC的pinctrl、gpio函数的实现要芯片厂商的驱动开发工程师设计好后注册给这些“空壳”(框架)调用。这就是pinctrl、gpio子系统。但更进一步地,
5)基于RISC-V指令集的SOC发展迅猛,可以猜测在未来不远的时间会得到广泛地应用,那么发展一种跨SOC的程序框架成为非常大的可能,拭目以待吧。
扯远了,本文基于pinctrl和gpio子系统的按键驱动仍保留了button_drv.c、imx6_io_drv.c、button_drv.h文件。
2. 按键设备驱动(框架)文件 与上文无差异
button_drv.c文件实现与硬件有关的按键驱动,主要实现struct file_operations类型相关函数及其实例的注册/解除注册。其中的.read、.open、.release成员要调用下层驱动:
#include#include #include #include #include #include #include #include #include #include #include #include #include "button_drv.h" struct button_drv btn_drv = { .hw_ops = NULL, .name = "gbtn", .major = 0, }; ssize_t button_drv_read (struct file *filp, char __user *buf, size_t size, loff_t *pos) { int ret; int minor = iminor(filp->f_inode); struct btn_hw_ops *ops = (struct btn_hw_ops *)filp->private_data; //struct btn_hw_ops *ops = btn_drv.hw_ops; char kval; size = (size >= 1) ? 1 : 0; if (ops == NULL) { printk("Please register button hardware operations instance %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EIO; } if (minor >= ops->num) { printk("Reading button value error %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EINVAL; } if (ops->read(minor, &kval) == 0) { ret = copy_to_user(buf, &kval, size); printk("Read button value successfully:"); return size; } return 0; } int button_drv_open (struct inode *nd, struct file *filp) { int minor = iminor(nd); struct btn_hw_ops *ops = btn_drv.hw_ops; filp->private_data = btn_drv.hw_ops; if (ops == NULL) { printk("Please register button hardware operations instance %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EIO; } if (minor >= ops->num) { printk("Openning button driver error %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EINVAL; } if (ops->open(minor) == 0) printk("Open button driver successfully:"); return 0; } int button_drv_release (struct inode *nd, struct file *filp) { int minor = iminor(nd); struct btn_hw_ops *ops = (struct btn_hw_ops *)filp->private_data; if (ops == NULL) { printk("Please register button hardware operations instance %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EIO; } if (minor >= ops->num) { printk("Closing button driver error %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EINVAL; } if (ops->close(minor) == 0) { printk("Close button driver successfully:"); filp->private_data = NULL; } return 0; } unsigned int button_drv_poll (struct file *filp, struct poll_table_struct * wait) { return 0; } static struct file_operations button_drv_ops = { .owner = THIS_MODULE, .read = button_drv_read, .open = button_drv_open, .release = button_drv_release, .poll = button_drv_poll, }; static int __init button_drv_init(void) { btn_drv.major = register_chrdev(btn_drv.major, btn_drv.name, &button_drv_ops); btn_drv.class = class_create(THIS_MODULE, btn_drv.name); if (IS_ERR(btn_drv.class)) { printk("Class create error!n"); unregister_chrdev(btn_drv.major, btn_drv.name); } printk(" %s %s line %dn", __FILE__, __FUNCTION__, __LINE__); return 0; } module_init(button_drv_init); static void __exit button_drv_exit(void) { class_destroy(btn_drv.class); unregister_chrdev(btn_drv.major, btn_drv.name); printk(" %s %s line %dn", __FILE__, __FUNCTION__, __LINE__); } module_exit(button_drv_exit); int button_hw_ops_register(struct btn_hw_ops *ops) { if (btn_drv.hw_ops) return -EIO; btn_drv.hw_ops = ops; printk("Register button hardware operations instance successfullyn"); return 0; } EXPORT_SYMBOL(button_hw_ops_register); void button_hw_ops_unregister(struct btn_hw_ops *ops) { if (btn_drv.hw_ops) btn_drv.hw_ops = NULL; printk("Unregister button hardware operations instance successfullyn"); } EXPORT_SYMBOL(button_hw_ops_unregister); void button_device_create(int idx) { device_create(btn_drv.class, NULL, MKDEV(btn_drv.major, idx), NULL, "gbtn%d", idx); } EXPORT_SYMBOL(button_device_create); void button_device_destroy(int idx) { device_destroy(btn_drv.class, MKDEV(btn_drv.major, idx)); } EXPORT_SYMBOL(button_device_destroy); MODULE_AUTHOR("glen"); MODULE_LICENSE("GPL");
其中包含的button_drv.h文件提供了与下层soc驱动共用的结构体,注册/销毁soc驱动等接口函数,以便在button_drv.c文件中调用——驱动上层和下层都依懒于接口。
#ifndef __BUTTON_DRV_H__ #define __BUTTON_DRV_H__ struct btn_hw_ops { u32 num; int (* read) (int idx, char *kval); int (* open) (int idx); int (* close) (int idx); }; struct button_drv { struct btn_hw_ops *hw_ops; struct class *class; char *name; int major; }; int button_hw_ops_register(struct btn_hw_ops *ops); void button_hw_ops_unregister(struct btn_hw_ops *ops); void button_device_create(int idx); void button_device_destroy(int idx); #endif // !__BUTTON_DRV_H__
button_hw_ops_register/button_hw_ops_unregister函数用于注册、解注册实际SOC驱动,button_device_create/button_device_destroy函数用于创建、销毁次设备号。由于这些用于SOC的驱动工作已由芯片厂商驱动工程师完成,这里看起来有些多余,不过我还是想过渡一下。
3. SOC相关的IO驱动文件
imx6_io_drv.c获取IO资源(这里获取的是按键的IO信息),并将资源用于实现按键的open、read、close等 *** 作:
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include "button_drv.h" // #include "imx6_io_drv.h" // static struct btn_dev_res __btn_dev_res[16]; static struct gpio_descs *p_gbtn = NULL; static int imx6_io_read (int idx, char *kval); static int imx6_io_open (int idx); static int imx6_io_close (int idx); static struct btn_hw_ops btn_hw_oper = { .open = imx6_io_open, .read = imx6_io_read, .close = imx6_io_close, }; static int imx6_io_read (int idx, char *kval) { if (idx >= btn_hw_oper.num) return -EINVAL; *kval = gpiod_get_value(p_gbtn->desc[idx]); return 0; } static int imx6_io_open (int idx) { int ret; if (idx >= btn_hw_oper.num) return -EINVAL; ret = gpiod_direction_input(p_gbtn->desc[idx]); if (ret) printk("Set the button pin as input error %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); else printk("Set the button%d pin as input successfully!n", idx); return ret; } static int imx6_io_close (int idx) { if (idx >= btn_hw_oper.num) return -EINVAL; return 0; } int btn_hw_drv_probe (struct platform_device *pdev) { int i; p_gbtn = gpiod_get_array_optional(&pdev->dev, "gbtn", GPIOD_ASIS); if (p_gbtn == NULL) { printk("Get "gbtn-gpios" property failed! n"); return -EIO; } printk("Get %d gpio members from the "gbtn gpios" property!n", p_gbtn->ndescs); btn_hw_oper.num = p_gbtn->ndescs; for (i = 0; i < p_gbtn->ndescs; i++) button_device_create(i); return 0; } int btn_hw_drv_remove(struct platform_device *pdev) { button_hw_ops_unregister(NULL); gpiod_put_array(p_gbtn); button_device_destroy(btn_hw_oper.num); btn_hw_oper.num = 0; return 0; } static const struct of_device_id gbtns_id[] = { {.compatible = "glen,gbtn"}, { }, }; static struct platform_driver btn_hw_drv = { .driver = { .name = "gbtn", .of_match_table = gbtns_id, }, .probe = btn_hw_drv_probe, .remove = btn_hw_drv_remove, }; static int __init imx6_io_drv_init(void) { int ret; ret = platform_driver_register(&btn_hw_drv); if (ret) pr_err("Unable to initialize imx6 io drivern"); else pr_info("The imx6 io driver is registered.n"); button_hw_ops_register(&btn_hw_oper); return ret; } module_init(imx6_io_drv_init); static void __exit imx6_io_drv_exit(void) { platform_driver_unregister(&btn_hw_drv); pr_info("The imx6 io driver is unregistered.n"); } module_exit(imx6_io_drv_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("glen");
可以看出,用于实现按键的open、read、close等 *** 作已经非常简单。
4. 更改设备树文件按键相关资源
pinctrl_key:keygrp { fsl,pins = < MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xF080 >; }; gbtns { compatible = "glen,gbtn"; #address-cells = <1>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_key>; gpio-controller; #gpio-cells = <2>; gbtn-gpios = <&gpio1 18 GPIO_ACTIVE_LOW>, <&gpio1 3 GPIO_ACTIVE_LOW>; };
与上一篇文章相比,这里用了pinctrl和gpio,使得设备树变得更简单。
5. 应用程序(不作修改)
#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" int main(int argc, char *argv[]) { int fd, ret; char *filename; char kval; if (argc != 2) { printf("Error Usage!rn"); return -1; } filename = argv[1]; fd = open(filename, O_RDWR); if (fd < 0) { printf("Can't open file %srn", filename); return -1; } while (1) { ret = read(fd, &kval, 1); if (ret <= 0) { printf("Read glen button value failed!rn"); close(fd); return -1; } else { printf("The glen button value is: %d!rn", kval); } sleep(1); } ret = close(fd); if (ret < 0) { printf("file %s close failed!rn", argv[1]); return -1; } return 0; }
- 在alientek_linux_alpha开发板实测验证如下
/ # cd drv_module/ /drv_module # ls btn_drv_test button_drv.ko imx6_io_drv.ko /drv_module # insmod button_drv.ko /home/glen/linux/imx6ull/glen/3_3_button_pinctrl/button_drv.c button_drv_init line 120 /drv_module # insmod imx6_io_drv.ko Get 2 gpio members from the "gbtn gpios" property! The imx6 io driver is registered. Register button hardware operations instance successfully /drv_module # ./random: nonblocking pool is initialized /drv_module # ./btn_drv_test /dev/gbtn0 Set the button0 pin as input successfully! Open button driver successfully:Read button value successfully:The glen button value is: 0! Read button value successfully:The glen button value is: 0! Read button value successfully:The glen button value is: 0! Read button value successfully:The glen button value is: 0! Read button value successfully:The glen button value is: 0! Read button value successfully:The glen button value is: 0! Read button value successfully:The glen button value is: 1! Read button value successfully:The glen button value is: 1! Read button value successfully:The glen button value is: 1! Read button value successfully:The glen button value is: 1! Read button value successfully:The glen button value is: 1! Read button value successfully:The glen button value is: 0! Read button value successfully:The glen button value is: 0! ^CClose button driver successfully: /drv_module # rmmod imx6_io_drv.ko Register button hardware operations instance successfully The imx6 io driver is unregistered. /drv_module # rmmod button_drv.ko /home/glen/linux/imx6ull/glen/3_3_button_pinctrl/button_drv.c button_drv_exit line 130
- 下面将imx6_io_drv.c当中的实现并入到button_drv.c文件,与上面验证的效果相同
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include "button_drv.h" struct button_drv btn_drv = { .p_gbtn = NULL, .name = "gbtn", .major = 0, }; ssize_t button_drv_read (struct file *filp, char __user *buf, size_t size, loff_t *pos) { int minor = iminor(filp->f_inode); struct gpio_descs *ops = (struct gpio_descs *)filp->private_data; char kval; size = (size >= 1) ? 1 : 0; if (ops == NULL) { printk("Please register button instance %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EIO; } if (minor >= ops->ndescs) { printk("Reading button value error %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EINVAL; } kval = gpiod_get_value(ops->desc[minor]); if (copy_to_user(buf, &kval, size)) return -EFAULT; printk("Read button%d value successfully:", minor); return size; } int button_drv_open (struct inode *nd, struct file *filp) { int ret; int minor = iminor(nd); struct gpio_descs *ops = btn_drv.p_gbtn; if (ops == NULL) { printk("Please register button instance %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EIO; } if (minor >= ops->ndescs) { printk("Openning button driver error %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EINVAL; } ret = gpiod_direction_input(ops->desc[minor]); if (ret) printk("Set the button pin as input error %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); else printk("Set the button%d pin as input successfully!n", minor); filp->private_data = btn_drv.p_gbtn; return 0; } int button_drv_release (struct inode *nd, struct file *filp) { int minor = iminor(nd); struct gpio_descs *ops = (struct gpio_descs *)filp->private_data; if (ops == NULL) { printk("Please register button instance %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EIO; } if (minor >= ops->ndescs) { printk("Closing button driver error %s %s, %dn", __FILE__, __FUNCTION__, __LINE__); return -EINVAL; } filp->private_data = NULL; return 0; } unsigned int button_drv_poll (struct file *filp, struct poll_table_struct * wait) { return 0; } static struct file_operations button_drv_ops = { .owner = THIS_MODULE, .read = button_drv_read, .open = button_drv_open, .release = button_drv_release, .poll = button_drv_poll, }; int btn_hw_drv_probe (struct platform_device *pdev) { int i; btn_drv.p_gbtn = gpiod_get_array_optional(&pdev->dev, "gbtn", GPIOD_ASIS); if (btn_drv.p_gbtn == NULL) { printk("Get "gbtn-gpios" property failed! n"); return -EIO; } printk("Get %d gpio members from the "gbtn gpios" property!n", btn_drv.p_gbtn->ndescs); btn_drv.major = register_chrdev(btn_drv.major, btn_drv.name, &button_drv_ops); btn_drv.class = class_create(THIS_MODULE, btn_drv.name); if (IS_ERR(btn_drv.class)) { printk("Class create error!n"); unregister_chrdev(btn_drv.major, btn_drv.name); } printk(" %s %s line %dn", __FILE__, __FUNCTION__, __LINE__); for (i = 0; i < btn_drv.p_gbtn->ndescs; i++) device_create(btn_drv.class, NULL, MKDEV(btn_drv.major, i), NULL, "gbtn%d", i); return 0; } int btn_hw_drv_remove(struct platform_device *pdev) { int i; for (i = 0; i < btn_drv.p_gbtn->ndescs; i++) device_destroy(btn_drv.class, MKDEV(btn_drv.major, i)); gpiod_put_array(btn_drv.p_gbtn); return 0; } static const struct of_device_id gbtns_id[] = { {.compatible = "glen,gbtn"}, { }, }; static struct platform_driver btn_hw_drv = { .driver = { .name = "gbtn", .of_match_table = gbtns_id, }, .probe = btn_hw_drv_probe, .remove = btn_hw_drv_remove, }; static int __init button_drv_init(void) { int ret; ret = platform_driver_register(&btn_hw_drv); if (ret) pr_err("Unable to initialize button drivern"); else pr_info("The button driver is registered.n"); return 0; } module_init(button_drv_init); static void __exit button_drv_exit(void) { platform_driver_unregister(&btn_hw_drv); class_destroy(btn_drv.class); unregister_chrdev(btn_drv.major, btn_drv.name); printk(" %s %s line %dn", __FILE__, __FUNCTION__, __LINE__); } module_exit(button_drv_exit); MODULE_AUTHOR("glen"); MODULE_LICENSE("GPL");
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)