一,设备驱动模型
1.Linux设备驱动模型的由来
首先回顾设备驱动编写的一般流程
【1】实现入口函数module_init()和模块卸载函数module_exit();
【2】申请设备号,register_chrdev(); -----> (与内核相关)
【3】利用udev/mdev机制创建设备文件结点class_create(),device_create(); ------>(与内核相关)
【4】硬件初始化:1.io资源映射ioremap(),内核提供gpio库函数 ; 2.注册中断。------->(与硬件相关)
【5】构建file_operation结构体-------->(与内核相关)
【6】实现操作硬件的方法xxx_open(),xxx_write()…
结论:整个流程除了第四部分与硬件有关,其他都是相似的操作方法,为了方便的(不重复造轮子)编写设备驱动,节省人力,所以提出了设备驱动模型,简化了设备驱动编写的流程。
2.设备驱动模型框架
可以通过sysfs虚拟文件系统查看总线对象(以usb为例)
二,BUS总线模型编程
概念图
1.总线对象
【1】 struct bus_type:总线对象,描述一个总线,管理device和driver,完匹配。
struct bus_type { //只列举重要成员
const char *name; //总线名字
int (*match)(struct device *dev, struct device_driver *drv); //匹配方法
};
【2】注册和注销总线
int bus_register(struct bus_type *bus);
void bus_unregister(struct bus_type *bus);
【3】构建一个总线
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
//实例化一个bus对象
struct bus_type mybus = {
.name = "mybus",
.match = mybus_match,
};
EXPORT_SYMBOL(mybus); //导出总线对象,让device对象和driver对象使用
static int __init mybus_init(void)
{
printk("------%s---------\n",__FUNCTION__);
int ret;
//构建一个总线 结果:/sys/bus/mybus
ret = bus_register(&mybus);
if(ret != 0)
{
printk("bus_register error\n");
return ret;
}
return 0;
}
static void __exit mybus_exit(void)
{
printk("------%s---------\n",__FUNCTION__);
bus_unregister(&mybus);
}
module_init(mybus_init);
module_exit(mybus_exit);
MODULE_LICENSE("GPL");
2.device对象
【1】device对象:设备对象,描述设备信息,包括地址,中断等其他数据
struct device { //只列举重要成员
struct kobject kobj; //所有对象的父类
const char *init_name; //在/sys/bus/mybus/device的名字,用于在总线中匹配
struct bus_type *bus; //指向该device对象依附的总线对象
void *platform_data; //自定义的数据,指向任何类型的数据
};
【2】注册和注销总线
int device_register(struct device *dev);
void device_unregister(struct device *dev);
【3】编写设备对象
定义一个描述设备的信息的结构体,匹配成功之后让driver对象在总线中拿到device对象的信息,实现分离。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
extern struct bus_type mybus;
struct mydev_desc{
char* name;
int irqno;
unsigned long addr;
};
struct mydev_desc deviofo = {
.name = "hqs",
.irqno = 8877,
.addr = 0x12345678,
};
void mydev_release(struct device* dev)
{
printk("------%s---------\n",__FUNCTION__);
}
//构建一个device对象
struct device mydev = {
.init_name = "fsdev_drv",
.bus = &mybus,
.release = mydev_release,
.platform_data = &deviofo,
};
static int __init mydev_init(void)
{
printk("------%s---------\n",__FUNCTION__);
int ret;
//将device注册到总线中
ret = device_register(&mydev);
if(ret < 0)
{
printk("device_register error\n");
return ret;
}
return 0;
}
static void __exit mydev_exit(void)
{
printk("------%s---------\n",__FUNCTION__);
device_unregister(&mydev);
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
3.driver对象
【1】driver对象:描述设备驱动发的方法
struct device_driver { //只列举重要成员
const char *name; //在/sys/bus/mybus/driver的名字,用于在总线中匹配
struct bus_type *bus; //指向该device对象依附的总线对象
int (*probe) (struct device *dev); //device和driver匹配之后要做的事情
int (*remove) (struct device *dev); //device和driver分离之后要做的事情
};
【2】注册和注销总线
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
【3】编写driver对象
匹配成功之后可以在总线中拿到device对象的数据,具体实现在probe函数里。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/io.h>
extern struct bus_type mybus;
struct mydev_desc{
char* name;
int irqno;
unsigned long addr;
};
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);
unsigned long * paddr = ioremap(pdesc->addr, 8);
return 0;
}
int mydrv_remove (struct device *dev)
{
printk("------%s---------\n",__FUNCTION__);
return 0;
}
//构建一个driver对象
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__);
int ret;
//将device注册到总线中
ret = driver_register(&mydrv);
if(ret < 0)
{
printk("driver_register error\n");
return ret;
}
return 0;
}
static void __exit mydrv_exit(void)
{
printk("------%s---------\n",__FUNCTION__);
driver_unregister(&mydrv);
}
module_init(mydrv_init);
module_exit(mydrv_exit);
MODULE_LICENSE("GPL");
4.device对象和driver对象匹配
【1】实现BUS对象中的match方法(按device对象的名字和driver对象中的名字相匹配)
要注意的是不能直接用device对象中的init_name,而要用device对象中继承的struct kobject kobj;里面的成员name不然会报错
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;
}
//实例化一个bus对象
struct bus_type mybus = {
.name = "mybus",
.match = mybus_match,
};
【2】保证device对象和driver对象的名字一样:比如这里都使用"fsdev_drv"就能保证能匹配成功
//构建一个device对象
struct device mydev = {
.init_name = "fsdev_drv",
.bus = &mybus,
.release = mydev_release,
.platform_data = &deviofo,
};
//构建一个driver对象
struct device_driver mydrv = {
.name = "fsdev_drv",
.bus = &mybus,
.probe = mydrv_probe,
.remove = mydrv_remove,
};