按照之前的实现驱动的方法。对于同一类设备,驱动层实现的操作方法是相似的,而设备信息总是不同的,一个产品上有很多这样的同类设备,我们为每一个设备都定制一套驱动程序,代价似乎太“高昂点”;对于一个设别,如果硬件有所改动,也必然要修改取代代码。

为了提高代码的通用性,我们将驱动和设备进行分离,设备对象专门用来描述设备(硬件)的信息。而驱动负责重设备中获取这些硬件描述,主要用来实现操作方法。总线负责将两者配对。

这样可以提高代码的通用性,让驱动可以兼容不同的硬件。至于如何应用,笔者还是不清楚。这里只介绍设备驱动模型,之后会将模型构建出来。

 

自定义总线 mybus

mybus.c实现device和device的匹配。在模块加载后,注册mybus到总线;每调用一次注册函数(driver_register或者device_register)就会匹配一次,匹配就会执行match函数,match函数执行成功之后,代码会往下走,去执行驱动中的probe函数。
模块加载 -->  注册mybus总线  -->  回调 mybus.match  -->  匹配成功

  1. 构建总线对象
  2. 注册和注销总线
  3. 实现回调mybus.match方法
//mubus.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>



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

mydev.c用来描述设备信息。将硬件的信息描述成为一个结构体,添加到mydev.platform_data这个属性当中。在模块加载后,注册device到了mybus总线

  1. 构建device对象
  2. 将device注册到mybus总线/从mybus总线注销
  3. 自定义数据类型,构造结构体描述硬件信息
//mydev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#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

mydrv.c用来实现驱动的方法。在模块加载后,注册driver到了mybus总线。当驱动和设备匹配成功后,系统自动调用mydrv.probe这个方法。另外,驱动也通过总线获取设备的信息,"dev_info.h"头文件中声明的是描述设备的结构体类型,是用来接收另一边设备数据的格式。

  1. 构建driver对象
  2. 将driver对象注册总线/从总线注销
  3. 实现驱动的回调mydrv.probe 和 mydrv.remove方法
//mydrv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/io.h>
#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/目录下查看到这个结点