总线平台驱动程序总结:只为自己肤浅的理解 针对2.6.32内核 mini2440 建议用Notepad个软件查看
修改部分主要为:my_bus.c和my_bus_device.c中的代码,也修改了一点见解部分都在这两块里面
以platform平台为例子的理解
Linux的驱动程序,模型一般在总线、设备、驱动3个模块
其结构为:
总线:bus_type 核心成员如下
struct bus_type {
const char *name; //总线名字
struct bus_attribute *bus_attrs; //总线属性
struct device_attribute *dev_attrs; //每一条总线也是一个设备,所以会有设备属性
struct driver_attribute *drv_attrs; //驱动属性
int (*match)(struct device *dev, struct device_driver *drv); 这个函数相当的重要,主要是匹配驱动程序和设备是否匹配该函数
在2.6.32以前(具体多少我也不知道)会匹配device_driver(驱动程序)的name成员和
device(设备)的id_table,2.6.32的时候就不是匹配这个了,在device成员里面有个
init_name,最终将会和这个匹配。具体的注册过程你可以追踪device_register()这个函数,这个函数里面
有两个成员:如下
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}不知道为什么在2.6.32里面用这个函数注册要失败,可能是某些地方没有初始化。后面就用
device_add(dev);这个函数来注册,结果就成功了。具体为什么同样你可以跟踪内核代码
int (*probe)(struct device *dev);
struct bus_type_private *p;
}; match函数必须要有
设备:核心成员如下
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj; 与驱动匹配的东西
const char *init_name; 注册设备的时候,会把这个东西填入到kobj.name里面,初始化设备的时候要用到这个。是最终匹配驱动
的关键
struct device_type *type;
struct bus_type *bus; 设备所在的总线,非常重要的一个成员
struct device_driver *driver;
void *platform_data;
....
}
驱动:struct device_driver,其核心成员如下
struct device_driver {
const char *name; //驱动程序的名字,当insmod后可以在sys/bus/bus<驱动所在的总线>/drivers下面看到这个名字
struct bus_type *bus; //驱动程序所在的总线
struct module *owner;
const char *mod_name;
bool suppress_bind_attrs;
int (*probe) (struct device *dev); (总线来完成匹配工作的match函数)当驱动程序和设备匹配成功的时候,由总线驱动来调用驱动程序的probe函数
probe函数也是整个驱动程序的入口,在这个入口里面你可以注册很多东西,如字符设备驱动,混杂设备驱动
块设备驱动,网络设备驱动等等。
int (*remove) (struct device *dev); //善后工作,针对probe函数的善后工作
.....
struct driver_private *p; //这个成员也比较重要
};
后面介绍的平台驱动--struct platform_driver 留心的朋友你会发现,这个结构体只是对device_driver结构体的一个封装。里面增加了一点东西。
工作流程:
这个模型:其实需要三个.KO模块。即bus.ko、device.ko、driver.ko稍后会把这个简单的代码给贴出来
这样的好处有驱动程序只能通过设备区分才能得到需要的资源,具体体现在platform平台中。还有很多好处!我的理解哈
假如你的驱动程序是以这种模型的话,如果你要加载你的驱动程序,首先你得保证你的驱动程序所在的总线要再内核中,也就是说你得写加载bus.ko模块,然后才能加载
device.ko、driver.ko这两个模块,这两个模块只要被加载上,总线驱动(bus.ko)会干这样一件事情:eg:如果你是加载的device.ko(设备,现实中如你给pc机插上
一个U盘等等)这个模块,内核会调用你这个设备对应的总线驱动程序,然后将处理这个设备的工作交给总线驱动,总线驱动会在遍历自己的驱动程序链表中每一个驱动程序,调用自己的match函数来检查自己的驱动程序是否支持这个新加入的设备,如果支持的话,就调用支持这个新设备的驱动程序的probe函数(具体要把这个新加入的设备看做是字符设备还是块设备
以及其他设备,要看这个probe函数里面怎么来注册的了),看括号的注释你应该清楚,probe函数就是驱动程序的核心部分。然后后续的工作就和一般的书写字符设备,块设备步骤
一样了,在驱动程序的remove函数里面一般会做在与probe函数里面对应的善后工作(必须注销驱动程序,释放中断啊等等)。
简单的测试程序:针对2.6.32的内核
1、总线部分:
#include
#include
#include
#include
#include
#include
static char *Version = "$Rversion:1.0 $";
static int my_match(struct device *dev,struct device_driver *driver)
{
return !strncmp(dev_name(dev), driver->name, strlen(driver->name));
}
static void my_bus_release(struct device *dev)
{
printk(KERN_DEBUG"my bus realse\n");
}
struct device my_bus = {
.init_name = "my_bus0",
.release = my_bus_release,
};
struct bus_type my_bus_type= {
.name = "my_bus",
.match = my_match,
};
static ssize_t show_bus_version(struct bus_type *bus,char *buf)
{
return snprintf(buf,PAGE_SIZE,"%s\n",Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
static __init int my_bus_init(void)
{
int ret;
ret = bus_register(&my_bus_type);
if(ret)
return ret;
ret = device_register(&my_bus);
if(ret)
return ret;
device_create_file(&my_bus, &dev_attr_mybus);
if(bus_create_file(&my_bus_type,&bus_attr_version))
printk(KERN_NOTICE"Fail to create version attribute!\n");
return 0;
}
static void my_bus_exit(void)
{
bus_unregister(&my_bus_type);
device_unregister(&my_bus);
}
EXPORT_SYMBOL(my_bus);
EXPORT_SYMBOL(my_bus_type);
module_init(my_bus_init);
module_exit(my_bus_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TboyARM Inc.");
2、设备部分:
#include
#include
#include
#include
#include
#include
extern struct device my_bus;
extern struct bus_type my_bus_type;
//static char * ptr_name = "my_dev" ;
static void my_dev_release(struct device *dev)
{
printk("my_dev_release!\n");
}
struct device my_dev = {
.init_name = "my_dev",
.bus = &my_bus_type,
.parent = &my_bus,
.release = my_dev_release,
};
static ssize_t mydev_show(struct device *dev,char *buf)
{
return sprintf(buf,"%s\n","This is my device!");
}
static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);
static int __init my_bus_dev_init(void)
{
int ret = 0;
//dev_set_name(&my_dev, "my_dev");
device_register(&my_dev);
device_create_file(&my_dev, &dev_attr_dev);
return ret;
}
static void my_bus_dev_exit(void)
{
device_unregister(&my_dev);
}
module_init(my_bus_dev_init);
module_exit(my_bus_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TboyARM Inc.");
3、驱动程序部分:
#include
#include
#include
#include
#include
#include
extern struct bus_type my_bus_type;
static int my_probe(struct device *dev )
{
printk("Driver found device which my driver can handle!\n");
return 0;
}
static int my_remove(struct device *dev)
{
printk("Driver found device unpluged!\n");
return 0;
}
struct device_driver my_driver = {
.name = "my_dev",
.bus = &my_bus_type,
.probe = my_probe,
.remove = my_remove,
};
static ssize_t mydriver_show(struct device_driver *driver,char *buf)
{
return sprintf(buf,"%s\n","This is my driver!");
}
static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);
static int __init my_driver_init(void)
{
int ret = 0;
driver_register(&my_driver);
driver_create_file(&my_driver, &driver_attr_drv);
return ret;
}
static void my_driver_exit(void)
{
driver_unregister(&my_driver);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TboyARM Inc.");
以上是简单的总线、驱动、设备模型的实例代码。接下来就来分析platform平台驱动。
platform平台驱动浅析
一、两个重要成员:
platform_device
platform_driver
根据前面一天的学习总线、驱动、设备模型应该还有一个platform_bus成员!查看内核源码发现
struct device platform_bus = {
.init_name = "platform",
};这个东西内核已经给我们弄好了,这个也体现出来了总线也是一个设备,所以我们不需要写总线部分的驱动
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs, 属性
.match = platform_match, 匹配函数
.uevent = platform_uevent, 事件函数,相当的重要
.pm = &platform_dev_pm_ops,
};
二、工作流程:
1、定义platform_device 定义一个设备
2、注册platform_device 注册一个设备
3、定义platform_driver
4、注册platform_driver
三、平台设备的描述
1、结构体定义
struct platform_device {
const char * name; 设备名
int id; 设备编号,配合设备名使用
struct device dev; 前面一天中的device结构,其实platform_device只是对device结构进行了一个封装
u32 num_resources;
struct resource * resource; 设备资源 非常重要,什么基地址,中断号等等这些都在这个里面
struct platform_device_id *id_entry;
struct pdev_archdata archdata;
};
2、platform_device的分配和使用
struct platform_device *platform_device_alloc(const char *name, int id)
name: 设备名
id: 设备id,一般为-1
3、注册平台设备
platform_device_register这个函数注册不成功。我也不知道为什么?就用下面一个函数
int platform_device_add(struct platform_device *pdev)在platform_bus总线里面里面去注册一个设备,(用这个函数注册的时候,其撤销函数要用platform_device_del)
bus体现在哪里?
pdev->dev.bus这个成员,在调用这个函数之前,要先把这个成员给填充 填充这个:platform_bus_type(填充的函数就是platform_device_alloc这个里面)
4、设备资源的讲解:
struct resource {
resource_size_t start; 资源的起始物理地址
resource_size_t end; 资源的结束物理地址
const char *name; 资源的名称
unsigned long flags; 资源的类型,必须MEM,IO,IRQ类型等等
struct resource *parent, *sibling, *child; 资源的链表指针
};
eg:
static struct resource s3c_wdt_resource1 = {
.start = 0x44100000,
.end = 0x44200000,
.flags = IORESOURCE_MEM, 内存资源
};
static struct resource s3c_wdt_resource2 = {
.start = 20,
.end = 20, 有的设备有多个中断号
.flags = IORESOURCE_IRQ, 中断资源
};
5、有资源,肯定就要能够获取资源。获取资源的函数
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
dev: 资源所属的设备
type: 获取资源的类型
num: 获取的资源数
eg:
platform_get_resource(pdev,IORESOURCE_IRQ,0) 获取中断号
四、平台驱动的描述:platform_driver
1、platform_driver结构体定义
struct platform_driver {
int (*probe)(struct