什么是总线设备驱动模型
用一个流程图来概况:Bus/Dev/Drv模型
该设计模型将硬件资源和驱动程序分离开,这样的程序方便扩展。
伪代码
以编写LED驱动程序为例理解总线设备驱动模型
第一步: 实现platform_device结构体。
将外设的一些引脚信息,单独放到一个同板卡相关同芯片不相关的.c文件中。
// 分配设置注册platform_device 结构体。在这里面定义资源,指定设备名字。
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include "led_resource.h"
// 该函数在卸载 platform_device 时会被调用,如果不提供的话内核会打印警告信息。
static void led_dev_release(struct device *dev)
{
}
static struct resource resources[] = {
{
.start = GROUP_PIN(3,1),
.flags = IORESOURCE_IRQ,
.name = "100ask_led_pin",
},
{
.start = GROUP_PIN(5,8),
.flags = IORESOURCE_IRQ,
.name = "100ask_led_pin",
},
};
static struct platform_device board_A_led_dev = {
.name = "100ask_led",
.num_resources = ARRAY_SIZE(resources),
.resource = resources,
.dev = {
.release = led_dev_release,
},
};
/* 该.c文件作为一个可加载模块,里面也有入口函数、出口函数。
将硬件信息注册到内核中
*/
static int __init led_dev_init(void)
{
int err;
// 向内核注册platform_device结构体
err = platform_device_register(&board_A_led_dev);
return 0;
}
static void __exit led_dev_exit(void)
{
platform_device_unregister(&board_A_led_dev);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
第二步:实现platform_driver结构体
同一个芯片对外设的初始化操作是类似的,比如LED0 LED1这些GPIO引脚、寄存器操作的操作同芯片的关系十分紧密,因此要将外设的初始化、控制等操作方法定义在同芯片相关同板卡不相关的.c文件中,同时platform_driver结构体也在这个.c中进行实现。
// 定义platform_driver结构体
static struct platform_driver chip_demo_gpio_driver = {
.probe = chip_demo_gpio_probe, // 当有匹配的platform_device时,它的probe函数会调用
.remove = chip_demo_gpio_remove,
.driver = {
.name = "100ask_led",
},
};
/*
function:
1、从匹配的platform_device中获取资源,确定GPIO引脚。
2、把引脚记录下来,在操作硬件时使用。
3、新发现一个GPIO引脚,就调用上册驱动的代码创建设备节点。
*/
static int chip_demo_gpio_probe(struct platform_device *pdev)
{
struct resource *res;
int i = 0;
while (1)
{
res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
if (!res)
break;
g_ledpins[g_ledcnt] = res->start;
led_class_create_device(g_ledcnt);
g_ledcnt++;
}
return 0;
}
/*
function:向内核注册platform_driver结构体,以及向上层驱动程序注册硬件操作方法的函数。
*/
static int __init chip_demo_gpio_drv_init(void)
{
int err;
err = platform_driver_register(&chip_demo_gpio_driver);
register_led_operations(&board_demo_led_opr);// 告知上层驱动程序,封装硬件操作方法的结构体
return 0;
}
static void __exit lchip_demo_gpio_drv_exit(void)
{
platform_driver_unregister(&chip_demo_gpio_driver);
}
// 硬件操作代码
static int board_demo_led_init (int which) /* 初始化 LED, which-哪个 LED */
static int board_demo_led_ctl (int which, char status) /* 控制 LED, which-哪个 LED, status:1-亮,0-灭 */
/*
将操作硬件相关的代码放入结构体封装
*/
static struct led_operations board_demo_led_opr = {
.init = board_demo_led_init,
.ctl = board_demo_led_ctl,
};
module_init(chip_demo_gpio_drv_init);
module_exit(lchip_demo_gpio_drv_exit);
MODULE_LICENSE("GPL");
第三步:定义并实现file_operation结构体
/* 定义自己的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,
};
/*
将file_operation结构体告诉内核。入口函数:安装驱动程序时,就回去调用这个入口函数。
*/
static int __init led_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/led */
led_class = class_create(THIS_MODULE, "100ask_led_class");
err = PTR_ERR(led_class);
if (IS_ERR(led_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "led");
return -1;
}
return 0;
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 */
static void __exit led_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
class_destroy(led_class);
unregister_chrdev(major, "100ask_led");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");