按照之前的实现驱动的方法。对于同一类设备,驱动层实现的操作方法是相似的,而设备信息总是不同的,一个产品上有很多这样的同类设备,我们为每一个设备都定制一套驱动程序,代价似乎太“高昂点”;对于一个设别,如果硬件有所改动,也必然要修改取代代码。
为了提高代码的通用性,我们将驱动和设备进行分离,设备对象专门用来描述设备(硬件)的信息。而驱动负责重设备中获取这些硬件描述,主要用来实现操作方法。总线负责将两者配对。
这样可以提高代码的通用性,让驱动可以兼容不同的硬件。至于如何应用,笔者还是不清楚。这里只介绍设备驱动模型,之后会将模型构建出来。
mybus.c实现device和device的匹配。在模块加载后,注册mybus到总线;每调用一次注册函数(driver_register或者device_register)就会匹配一次,匹配就会执行match函数,match函数执行成功之后,代码会往下走,去执行驱动中的probe函数。
模块加载 --> 注册mybus总线 --> 回调 mybus.match --> 匹配成功
//mubus.c
#include
#include
#include
int mybus_match(struct device *dev, struct device_driver *drv);
struct bus_type mybus = {//实例化一个bus对象
.name = "mybus",
.match = mybus_match,
};
EXPORT_SYMBOL(mybus);
static int __init mybus_init(void)
{
printk("----------%s-------------\n", __FUNCTION__);
int ret;
ret = bus_register(&mybus); //注册bus
if(ret != 0)
{
printk("bus_register error\n");
return ret;
}
return ret;
}
static void __exit mybus_exit(void)
{
printk("----------%s-------------\n", __FUNCTION__);
//注销bus
bus_unregister(&mybus);
}
int mybus_match(struct device *dev, struct device_driver *drv)
{
//如果匹配成功,match方法一定要返回一个1, 失败返回0
if(!strncmp(drv->name, dev->kobj.name, strlen(drv->name)))
{
printk("match ok\n");
return 1;
}else{
printk("match failed\n");
return 0;
}
return 0;
}
module_init(mybus_init);
module_exit(mybus_exit);
MODULE_LICENSE("GPL");
注册成功后在系统sys/目录下查看到这个结点
mydev.c用来描述设备信息。将硬件的信息描述成为一个结构体,添加到mydev.platform_data这个属性当中。在模块加载后,注册device到了mybus总线
//mydev.c
#include
#include
#include
#include "dev_info.h"
void mydev_release(struct device *dev);
extern struct bus_type mybus;
struct mydev_desc devinfo = {
.name = "testdev",
.irqno = 9999,
.addr = 0x30008000,
};
struct device mydev = {
.init_name = "fsdev_drv",
.bus = &mybus,
.release = mydev_release,
.platform_data = &devinfo,
};
static int __init mydev_init(void)
{
printk("----------%s-------------\n", __FUNCTION__);
//将device注册到总线中
int ret;
ret = device_register(&mydev);
if(ret < 0)
{
printk("device_register error\n");
return ret;
}
return ret;
}
static void __exit mydev_exit(void)
{
printk("----------%s-------------\n", __FUNCTION__);
device_unregister(&mydev);//注销dev
}
void mydev_release(struct device *dev)
{
printk("----------%s-------------\n", __FUNCTION__);
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
注册成功后在系统sys/目录下查看到这个设备结点
mydrv.c用来实现驱动的方法。在模块加载后,注册driver到了mybus总线。当驱动和设备匹配成功后,系统自动调用mydrv.probe这个方法。另外,驱动也通过总线获取设备的信息,"dev_info.h"头文件中声明的是描述设备的结构体类型,是用来接收另一边设备数据的格式。
//mydrv.c
#include
#include
#include
#include
#include "dev_info.h"
int mydrv_probe(struct device *dev);
int mydrv_remove(struct device *dev);
extern struct bus_type mybus;
struct device_driver mydrv = {
.name = "fsdev_drv",
.bus = &mybus,
.probe = mydrv_probe,
.remove = mydrv_remove,
};
static int __init mydrv_init(void)
{
printk("----------%s-------------\n", __FUNCTION__);
//将driver注册到总线中
int ret;
ret = driver_register(&mydrv);
if(ret < 0)
{
printk("device_register error\n");
return ret;
}
return ret;
}
static void __exit mydrv_exit(void)
{
printk("----------%s-------------\n", __FUNCTION__);
driver_unregister(&mydrv);
}
struct mydev_desc *pdesc;
int mydrv_probe(struct device *dev)
{
printk("----------%s-------------\n", __FUNCTION__);
pdesc = (struct mydev_desc *)dev->platform_data;
printk("name = %s\n", pdesc->name);
printk("irqno = %d\n", pdesc->irqno);
return 0;
}
int mydrv_remove(struct device *dev)
{
printk("----------%s-------------\n", __FUNCTION__);
return 0;
}
module_init(mydrv_init);
module_exit(mydrv_exit);
MODULE_LICENSE("GPL");
注册成功后在系统sys/目录下查看到这个结点