arm公司发布的arm坂本从arm9的2440,2410,cortexA8的S5pv210再到A9的4412,其实有些驱动已经做得很好了,没有必要再重复写,我们把以前的驱动继承然后拿过来用就好了,但是现在涉及到一个问题,不同的平台寄存器的地址是不一样的,但是操作是一样的,我们将相同的操作写成一个driver驱动,不同的device中写入不同的资源,这样我们的一个驱动drv就可以去同时适应多个平台的device了
一个抽象的模型如下
device和driver之间有一个Bus,我们把bus比作一条高速公路,drv可以通过这个bus可以快速匹配dev,通过名字去匹配(在接下来的代码中会体现出来)
在开发板开机时候,其实已经有很多的总线了,是系统已经定义好了的
cd sys/bus/ //进入到这个目录可以看到
我们随便进入一个总线中可以看到对应的devices和drivers
现在我们自己通过代码构造自己的总线bus,device,driver就更容易理解什么是总线
首先我们要将mybus,mydevice和mydrv驱动编译进内核,当三个都编译进内核的时候驱动mydrv就可以通过mybus获取到mydevice里面的资源,通过名字匹配
if(!strncmp(drv->name,dev->kobj.name,strlen(drv->name))) //匹配已经注册到总线中的名字
如果匹配成功就会自动调用reprove函数
现在我们来实现这个模型
直接上代码:
mybus.c
#include<linux/init.h>
#include<linux/module.h>
#include<linux/device.h>
int my_match(struct device *dev, struct device_driver *drv)//将device和drive中的名字进行匹配
{
if(!strncmp(drv->name,dev->kobj.name,strlen(drv->name)))
{
printk("\n match ok!!!!!!!!!!!!!!!!!\n");
return 1;
}
else
{
printk("\n match fail!!!!!!!!!!!!!!!!!\n");
return 0;
}
return 0;
}
struct bus_type mybus={
.name="mybus",
.match=my_match,
};
EXPORT_SYMBOL(mybus);//要将这个变量到导出才能在其他驱动源代码中识别
static int __init qinbus_init(void)
{
int ret=bus_register(&mybus);//总线注册
if(ret!=0)
{
printk("\nbus_register error!!\n");
return -1;
}
printk("\n qinbus_init!! \n");
return 0;
}
static void __exit qinbus_exit(void)
{
bus_unregister(&mybus);//总线注销
printk("\n bus_unregister!! \n");
}
module_init(qinbus_init);
module_exit(qinbus_exit);
MODULE_LICENSE("GPL");
mydevice.c
#include<linux/init.h>
#include<linux/module.h>
#include<linux/device.h>
extern struct bus_type mybus;
struct desc_info{
char *name;
long int num;
};
struct desc_info my_desc={
.name="qinyifei",
.num=1234,
};
void close_dev(struct device *dev)
{
}
struct device mydev={
.init_name="my_drv",
.bus=&mybus,
.release=close_dev,
.platform_data=&my_desc,
};
static int __init qindevice_init(void)
{
int ret=device_register(&mydev);
if(ret<0)
{
printk("\n device_register error!! \n");
return -1;
}
return 0;
}
static void __exit qindevice_exit(void)
{
device_unregister(&mydev);
printk("\n device_unregister!! \n");
}
module_init(qindevice_init);
module_exit(qindevice_exit);
MODULE_LICENSE("GPL");
mydrv.c
#include<linux/init.h>
#include<linux/module.h>
#include<linux/device.h>
extern struct bus_type mybus;//定义外部变量,从总线驱动代码中拿到那个变量
struct desc_info{
char name;
long int num;
};
struct desc_info *my_desc;
//如果匹配成功,内核就会自动调用这个函数
int myprobe(struct device *dev){
my_desc=(struct desc_info *)dev->platform_data;
printk("\n\nnum is %d\n\n",my_desc->num);
printk("\n\nmyprobe!!!\n\n");
return 0;
}
//驱动从总线中移除时候将自动调用这个函数
int myremove(struct device *dev){
printk(" myremove !!!");
return 0;
}
struct device_driver mydrv={
.name="my_drv",
.bus=&mybus,
.probe=myprobe,
.remove=myremove,
};
static int __init qindrv_init(void)
{
int ret=driver_register(&mydrv);
if(ret<0)
{
printk("\n drv_register error!! \n");
return -1;
}
printk("\n drv_register success!! \n");
return 0;
}
static void __exit qindrv_exit(void)
{
driver_unregister(&mydrv);
printk("\n drv_unregister!! \n");
}
module_init(qindrv_init);
module_exit(qindrv_exit);
MODULE_LICENSE("GPL");
Makefile
KERN_DIR =/home/george/Desktop/linux-3.5
all:
make -C $(KERN_DIR) M=`pwd` modules
prog:
arm-linux-gcc app.c -o app
clean:
rm -rf modules.order
rm *.ko *.mod.c *.o *.symvers
rm_app:
rm app
copy_to:
cp *.ko /mnt/hgfs/linux_4412
obj-m += mybus.o
obj-m += mydevice.o
obj-m += mydrv.o
将驱动编译好了以后传输到开发板
安装完mybus.ko以后我们可以发现 /sys/bus/中多了一个我们自己创建的mybus
现在我们进入mybus中的devices和drivers中我们可以发现是空的
接下来我们安装mydevice.ko和mydriver.ko
安装完以后可以发现已经有了我们自己创建的dev和drv,并且在安装完mydrv.ko以后自己去匹配名字,匹配成功就自动调用了reprove函数
这样我们就完成了这个模型