应用程序调用glibc函数
内核层大致分为两个源文件
1)drv.c (发出硬件操作指令)
包含file operation结构体的设置、注册,结构体中包含了.open(),.read()等驱动函数;
- .open()会调用对应的函数led_drv_open();
- led_drv_open()会找到led_operation(此结构体由底层驱动源文件led_gpio.drv.c提供)结构体中某一个成员比如init,此成员的值实际上是对应硬件寄存器操作函数的地址;
- p_led_opr->init(minor, status);根据次设备号和stasus状态参数执行硬件引脚操作,完成硬件驱动。
2)led_gpio_drv.c(执行硬件操作动作)
包含platform_driver结构体的设置注册,该结构体中包含了
/* 1. 定义platform_driver */
static struct platform_driver chip_demo_gpio_driver = {
.probe = chip_demo_gpio_probe,
.remove = chip_demo_gpio_remove,
.driver = {
.name = "100ask_led",
.of_match_table = ask100_leds,
},
};
.probe()函数,该函数包括了a.从设备节点读取引脚信息并保存,b.调用drv.c文件中的函数创造led设备节点;
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, "100ask_led", &led_drv); /* /dev/led */
led_class = class_create(THIS_MODULE, "100ask_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, "100ask_led%d", 0); /* /dev/100ask_led0 */
return 0;
}
.remove()函数,该函数包括设备的注销和gpio的释放等。
static int chip_demo_gpio_remove(struct platform_device *pdev)
{
device_destroy(led_class, MKDEV(major, 0));
class_destroy(led_class);
unregister_chrdev(major, "100ask_led");
gpiod_put(led_gpio);
return 0;
}
当platform_device中的dev.of_node中的compatible与platform_driver的platform_driver -> driver -> of_match_table->of_device_id中的compatible匹配时,就会调用probe();
获取引脚信息后,硬件寄存器操作函数board_led_init()书写了对不同寄存器的不同操作;
硬件寄存器操作函数board_led_init()的地址被赋值给led_opr结构体中的init成员;使用指针变量将led_opr的地址返回给drv.c文件
注:2文件中用到的硬件资源的来源:设备树文件dts编译所得的dtb二进制文件被uboot传递给内核后解析注册为platform_device中所得。
补充:在引入Pinctrl等子系统之后,硬件资源的获取会变得更加简单,对引脚的设置也更加容易,以上提到的内核层文件内容可以整合到一个驱动程序源文件中。且此源文件适用于所有的开发板,不同的开发板使用不同的设备树文件。