切入正题,今天要学习的是驱动的分层/分离概念。
分离分层的目的是将硬件相关的代码和系统中比较稳定的代码分离开,并且按照一定的框架联系到一起。这样我们在写一个驱动的时候能够更加灵活,顶层的应用程序也能更加稳定的调用底层的接口。对驱动开发者而言,这样写驱动程序将更有逻辑性。纯粹时个人理解哈。
以input.c框架为例,从图中可以看到系统把硬件相关的代码放在一起,把纯软件的相对稳定的部分放在一起,如evdev.c 最后他们通过input.c相连接。
除了输入子系统之外,设备总线也遵循这一原则。接下来我们一起分析设备总线的分层分离。
从设备总线的框架中,了解到 硬件相关的部分为device,相对而言比较稳定的部分是driver。 driver 部分通过driver_register 把driver的结构体放入drv链表中, device除了通过device_add 把device结构体放入到总线的drv链表中以外,还会将device从drv中取出来,通过drv中的math函数与driver 进行比较,看driver可不可以支持device, 可以的话就调用probe函数。那怎么知道是不是匹配的呢???? 在平台的platform_match中通过比较device的名字和driver的名字是否匹配,如果匹配,系统就认为是匹配的,就会调用probe函数。
这是一种左右两边建立链接的机制。
1、 把device 放入bus的drv链表 | 1、 把driver 放入bus的drv链表
dev 2、 从bus的drv链表取出每一个driver,用match函数判断是否支持dev | driver 2、 从bus的drv链表取出每一个dev,用match函数判断是否支持driver
3、 若支持,则调用 driver 的probe函数 | 3、 若支持,则调用probe函数
下面进行led驱动的源码分析。
前面我们所说的driver 和device 他们指的是结构体。device的结构体中定义了device 的名字,资源,id,release。我们着重关注的是device的名字。 led_resource 是什么呢?
static struct platform_device led_dev = {
.name = "myled",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
},
};
led_resource结构体
static struct resource led_resource[] = {
[0] = { //寄存器地址
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM, //使用MEM资源
},
[1] = { //第5个引脚,
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ, //使用IRQ资源
}
};
led_release、 入口出口函数
static void led_release(struct device * dev)
{
}
//入口 出口函数
static int led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
}
static void led_dev_exit(void)
{
platform_device_unregister(&led_dev);
}
driver部分: 先建立好基本的框架 入口出口函数+修饰
static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
}
static void led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
建立file_operlation 为访问提供接口
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
创建open、 write 函数
static int led_open(struct inode *inode, struct file *file)
{
//printk("first_drv_openn");
*gpio_con &= ~(0x3< *gpio_con |= (0x1< return 0;
}
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
//printk("first_drv_writen");
copy_from_user(&val, buf, count); // copy_to_user();
if (val == 1)
{
*gpio_dat &= ~(1< else { *gpio_dat |= (1< return 0; } 构造一个平台驱动结构体 struct platform_driver led_drv = { .probe = led_probe, .remove = led_remove, .driver = { .name = "myled", //名字要和之前led_dev的保持一致 } }; 创建probe函数, 调用device提供的资源进行硬件相关的配置 这些配置都是固定不变的,只要改变device的资源就可以改变 probe的硬件配置,这部分时不用进行修改的。 static int led_probe(struct platform_device *pdev) { struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); gpio_con = ioremap(res->start, res->end - res->start + 1); gpio_dat = gpio_con + 1; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); pin = res->start; printk("led_probe, found ledn"); major = register_chrdev(0, "myled", &led_fops); cls = class_create(THIS_MODULE, "myled"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */ return 0; } 最后为卸载驱动提供函数 static int led_remove(struct platform_device *pdev) { /* iounmap */ printk("led_remove, remove ledn"); class_device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "myled"); iounmap(gpio_con); return 0; } S3C2440 驱动分离分层概念到此结束。