第七章 驱动程序开发-LED驱动-7.10.3.代码分析

    在.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。

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值