在.dts中定义一个led_gpio(client device)节点,子节点包含pinctrl系统,整个节点会在内核中注册成一个platform_device。对应的要注册一个platform_driver,当insmod drv.ko时,device和driver匹配之后,就执行probe()获得引脚,注册file_operations,设置方向、读值/写值。???在file_opretions中进行读写???
不同之处,之前用board_A_led.c注册的platform_device,用chip_demo_gpio.c注册platform_driver。现在只剩下leddrv.c文件了,其他文件都没有了啊!看来pinctrl+gpio可以省好多代码!
7.10.3.1.ledtest.c
没有改变。
7.10.3.2.leddrv.c
用代码对比看了下改动挺大的,逐一分析。
1.增加了2个.h文件,
#include <linux/gpio/consumer.h> //包含GPIO获取和使用的函数
#include <linux/platform_device.h>
2.新增一个结构体指针变量,static struct gpio_desc *led_gpio,看下原定义:
struct gpio_desc {
struct gpio_device *gdev;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
……
/* Connection label */
const char *label;
/* Name of the GPIO */
const char *name;
};
搜索了一下,有一个博客有一个很好的说明,借用下:https://blog.csdn.net/u010388659/article/details/81286821,
“如上图所示,右上方部分为GPIO驱动对其它驱动提供的GPIO操作接口,其对应的右下方部分为GPIO硬件操作接口,也就是说对外提供的接口最终会一一对应的对硬件GPIO进行操作。再来看左边部分,左上方部分为一全局数组,记录各个GPIO的描述符,即对应左下方的gpio_desc结构体,其中gpio_chip指向硬件层的GPIO,flags为一标志位,用来指示当前GPIO是否已经占用,当用gpio_request申请GPIO资源时,flags位就会置位,当调用gpio_free释放GPIO资源时,flags就会清零。label是一个字符串指针,用来作说明。”(暂时不理解)
3.led_init()/led_exit(),看驱动程序先看入口函数和出口函数,入口函数用来向内核注册platform_driver,以前的函数是向内核注册一个chrdev然后创建dev class,出口函数也对应只是卸载platform_driver即可。另外,定义了platform_driver结构体,之前是在chip_demo_gpio.c中定义的,结构体内容不多说了之前都说过。
134: /* 4.定义匹配platform_device的compatible变量 */
135: static const struct of_device_id ask100_leds[] = {
136: { .compatible = "100ask,leddrv" },
137: { },
138: };
126: /* 1. 定义platform_driver */
127: static struct platform_driver chip_demo_gpio_driver = {
128: .probe = chip_demo_gpio_probe,
129: .remove = chip_demo_gpio_remove,
130: .driver = {
131: .name = "100ask_led",
132: .of_match_table = ask100_leds,
133: },
134: };
135:
136: /* 2. 注册platform_driver */
137: static int __init led_init(void)
138: {
139: int err;
141: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
142:
143: /* 2.1 注册platform_driver */
144: err = platform_driver_register(&chip_demo_gpio_driver);
145:
146: return err;
147: }
148:
149: /* 3. 卸载platform_driver */
150: static void __exit led_exit(void)
151: {
152: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
153:
154: /* 3.1 卸载platform_driver */
155: platform_driver_unregister(&chip_demo_gpio_driver);
156: }
看下platform_driver_register(),可以依次追踪一下driver是如何和device macthing的以及如何调用probe函数的。
如果probe和remove没有内容的话,那最简单的leddrv是不是可以就这样了,编译,insmod之后platform_driver和经设备树转化的platform_driver匹配之后就可以什么都不做了!不行,比如执行测试程序,./ledtest /dev/led0 on,需要先open这个/dev/led0次设备,问题是此时内核中没都没这个次设备呢!所以还是得需要file_operations注册一个设备,之前都是在入口函数中注册,现在转移到probe中注册了。
4.probe(),主要有4点功能:
24: /* 6.0定义gpio_dsec结构体 */
25: static struct gpio_desc *led_gpio;
83: /* 6. 定义probe函数
84: * 功能:
85: * a.从platform_device获得GPIO节点;
86: * b.注册file_operations结构体,创建主设备;
87: * c.创建设备类;
88: * d.创建次设备;
89: */
90: static int chip_demo_gpio_probe(struct platform_device *pdev)
91: {
92: //int err;
93:
94: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
95:
96: /* 6.1获取设备节点led-gpios=<...>; */
97: led_gpio = gpiod_get(&pdev->dev, "led", 0);
98: if (IS_ERR(led_gpio)) {
99: dev_err(&pdev->dev, "Failed to get GPIO for led\n");
100: return PTR_ERR(led_gpio);
101: }
102:
103: /* 6.2注册file_operations结构体,创建100ask_led设备 */
104: major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/100ask_led */
105:
106: /* 6.3创建100ask_led_class */
107: led_class = class_create(THIS_MODULE, "100ask_led_class");
108: if (IS_ERR(led_class)) {
109: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
110: unregister_chrdev(major, "led");
111: gpiod_put(led_gpio);
112: return PTR_ERR(led_class);
113: }
114:
115: /* 6.4创建100ask_ledx次设备 */
116: device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0);
116: /* /dev/100ask_led0 */
117:
118: return 0;
120: } « end chip_demo_gpio_probe »
122: /* 8实现remove函数 */
123: static int chip_demo_gpio_remove(struct platform_device *pdev)
124: {
125: /* 依次卸载次设备、设备类、主设备和释放gpio节点 */
126: device_destroy(led_class, MKDEV(major, 0));
127: class_destroy(led_class);
128: unregister_chrdev(major, "100ask_led");
129: gpiod_put(led_gpio);
130:
131: return 0;
132: }
看下gpiod_get()
/**
* gpiod_get - obtain a GPIO for a given GPIO function
* @dev: GPIO consumer, can be NULL for system-global GPIOs
* @con_id: function within the GPIO consumer
* @flags: optional GPIO initialization flags
*
* Return the GPIO descriptor corresponding to the function con_id of device
* dev, -ENOENT if no GPIO has been assigned to the requested function, or
* another IS_ERR() code if an error occurred while trying to acquire the GPIO.
*/
struct gpio_desc *__must_check gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
return gpiod_get_index(dev, con_id, 0, flags);
}
然后gpiod_get->gpiod_get_index->of_find_gpio->
of_get_named_gpiod_flags依次找到dts led-gpio node,那它又是怎么去匹配的pinctrl呢?自己找了下:
platform_driver_register
__platform_driver_register
driver_register
bus_add_driver
driver_attach
bus_for_each_dev
__driver_attach
driver_probe_device
really_probe
pinctrl_bind_pins
pinctrl_lookup_state
ret = drv->probe(dev);
5.定义file_operations结构体,结构体成员没有变化,open函数和write有所改变,可以直接对gpio子系统操作。
28: /* 7.实现open/read/write/close等函数 */
29:
30: /* 7.1实现open函数 */
31: static int led_drv_open (struct inode *node, struct file *file)
32: {
33: //int minor = iminor(node);
34:
35: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
36: /* 根据次设备号初始化LED */
37: gpiod_direction_output(led_gpio, 0);
38:
39: return 0;
40: }
41:
48:
49: /* 7.3实现write函数 */
50: /* write(fd, &val, 1); */
51: static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t
51: *offset)
52: {
53: int err;
54: char status;
58: printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
59: err = copy_from_user(&status, buf, 1);
60:
61: /* 根据次设备号和status控制LED */
62: gpiod_set_value(led_gpio, status);
63:
64: return 1;
65: }
/**
* gpiod_direction_output - set the GPIO direction to output
* @desc: GPIO to set to output
* @value: initial output value of the GPIO
*
* Set the direction of the passed GPIO to output, such as gpiod_set_value() can
* be called safely on it. The initial value of the output must be specified
* as the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
* account.
*
* Return 0 in case of success, else an error code.
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
VALIDATE_DESC(desc);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
return _gpiod_direction_output_raw(desc, value);
}
_gpiod_direction_output_raw
gpiod_direction_input
gpio_chip_hwgpio
*********************************************************************
/**
* gpiod_set_value() - assign a gpio's value
* @desc: gpio whose value will be assigned
* @value: value to assign
*
* Set the logical value of the GPIO, i.e. taking its ACTIVE_LOW status into
* account
*
* This function should be called from contexts where we cannot sleep, and will
* complain if the GPIO chip functions potentially sleep.
*/
void gpiod_set_value(struct gpio_desc *desc, int value)
{
VALIDATE_DESC_VOID(desc);
/* Should be using gpiod_set_value_cansleep() */
WARN_ON(desc->gdev->chip->can_sleep);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
_gpiod_set_raw_value(desc, value);
}
_gpiod_set_raw_value
_gpio_set_open_drain_value
_gpio_set_open_source_value
小结:内核通过gpio_get()获知led-gpios,进而获取到IO的复用配置,通过gpiod_direction_output设置IO为output,通过gpiod_set_value设置IO逻辑电平,进而控制LED。