设备驱动模型之总线浅析
,理解设备驱动模型对linux驱动的编写是大有裨益的,下面会结合一个简单的实例对总线这一块作一些分析。(基于TCC8900 + linux-2.6.28)
二,实例代码如下: #include<linux/device.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/string.h>
#define COMMENT_SIZE 1024
staticchar comment[COMMENT_SIZE]="this bus is for test";
staticint bus_match(struct device *dev,struct device_driver * driver)
{
return!strncmp(dev->bus_id,driver->name,strlen(driver->name));
}
struct bus_type test_bus_type ={
/*这个名字会出现在/sys/bus/目录下*/
.name ="test_bus",
.match = bus_match,
};
static ssize_t show_bus_comment(struct bus_type * bus,char* buf)
{
return snprintf(buf, COMMENT_SIZE,"%s\n", comment);
}
static ssize_t store_bus_comment(struct bus_type *bus,
constchar*buf,size_tcount)
{
memset(comment, 0, COMMENT_SIZE);
if(count>COMMENT_SIZE){
strncpy(comment,buf,COMMENT_SIZE-1);
return COMMENT_SIZE-1;
}else{
strncpy(comment,buf,count);
returncount;
}
}
static BUS_ATTR(comment, S_IWUSR | S_IRUGO, show_bus_comment, store_bus_comment);
staticint __init test_bus_init(void)
{
int ret;
/*注册总线*/
ret=bus_register(&test_bus_type);
if(ret){
printk(KERN_WARNING "bus_register error: %d\n", ret);
return ret;
}
/*创建属性文件*/
if(bus_create_file(&test_bus_type,&bus_attr_comment))
printk(KERN_INFO "cannot create comment attribute!\n");
return ret;
}
staticvoid __exit test_bus_exit(void)
{
bus_unregister(&test_bus_type);
}
module_init(test_bus_init);
module_exit(test_bus_exit);
MODULE_AUTHOR("J.H.Luo<sparkle-cliz@sohu.com>");
MODULE_LICENSE("Dual BSD/GPL");
1,bus_type结构体 struct bus_type {
constchar *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);
/*热插拔事件*/
int(*uevent)(struct device *dev,struct kobj_uevent_env *env);
int(*probe)(struct device *dev);
int(*remove)(struct device *dev);
void(*shutdown)(struct device *dev);
int(*suspend)(struct device *dev, pm_message_t state);
int(*suspend_late)(struct device *dev, pm_message_t state);
int(*resume_early)(struct device *dev);
int(*resume)(struct device *dev);
struct pm_ext_ops *pm;
/*下面这个结构体可以说是一个容器,内嵌了sysfs文件系统最基本的结构体kobject*/
struct bus_type_private *p;
};
在本例中,我们只定义了bus_type的name和match成员,其中match函数只是简单地匹配device和device_driver的ID是不是一样。如果一样则返回非零,说明匹配成功。 2, int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;
/* 申请一个bus_type_private结构体,就是上面我们提到的“容器”*/
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if(!priv)
return-ENOMEM;
/*我中有你,你中有我的思想在linux内核中随处可见*/
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
/*把总线的名称设置为kobject的名称,这就是为什么我们在/sys/bus/下可以看到一个目录项跟新注册的总线名称一样的原因*/
retval = kobject_set_name(&priv->subsys.kobj,"%s", bus->name);
if(retval)
goto out;
/*初始化kset,并注册它*/
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype =&bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
if(retval)
goto out;
/*创建属性文件uevent ,支持热插拔之用*/
retval = bus_create_file(bus,&bus_attr_uevent);
if(retval)
goto bus_uevent_fail;
/*在每条bus总线下创建两个目录项devices和drivers
以存放挂在这条总线上的设备和驱动*/
priv->devices_kset = kset_create_and_add("devices",NULL,
&priv->subsys.kobj);
if(!priv->devices_kset){
retval =-ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers",NULL,
&priv->subsys.kobj);
if(!priv->drivers_kset){
retval =-ENOMEM;
goto bus_drivers_fail;
}
/*初始化klist, klist跟引用计数有关系,
klist_devices_get和klist_devices_put分别是
引用计数增加和减小的操作函数*/
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers,NULL,NULL);
/*创建两个属性:drivers_probe,drivers_autoprobe*/
retval = add_probe_files(bus);
if(retval)
goto bus_probe_files_fail;
/*如果总线中还有其它属性列表,则在这里创建*/
retval = bus_add_attrs(bus);
if(retval)
goto bus_attrs_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus,&bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
kfree(bus->p);
out:
return retval;
} 整个总线的注册过程大部分工作都是在处理bus_type_private这个结构体,最终的结果是在/sys/bus/下产生一个新的总线分支。
3,在本例中,用bus_create_file()创建一个名为comment的属性,bus_attr_comment由宏BUS_ATTR定义,读写这个属性时分别会调用这个属性的函数:show_bus_comment和store_bus_comment。
其实,由上面注册bus的过程可知,我们也可以直接把这个属性bus_attr_comment放在bus_type的bus_attrs的成员里,这样bus_register的时候它自会为我们创建这个属性。
驱动代码稍作修改即可,如下:
#include<linux/device.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/string.h>
#define COMMENT_SIZE 1024
staticchar comment[COMMENT_SIZE]="this bus is for test";
staticint bus_match(struct device *dev,struct device_driver * driver)
{
return!strncmp(dev->bus_id,driver->name,strlen(driver->name));
}
static ssize_t show_bus_comment(struct bus_type * bus,char* buf)
{
return snprintf(buf, COMMENT_SIZE,"%s\n", comment);
}
static ssize_t store_bus_comment(struct bus_type *bus,
constchar*buf,size_tcount)
{
memset(comment, 0, COMMENT_SIZE);
if(count>COMMENT_SIZE){
strncpy(comment,buf,COMMENT_SIZE-1);
return COMMENT_SIZE-1;
}else{
strncpy(comment,buf,count);
returncount;
}
}
static BUS_ATTR(comment, S_IWUSR | S_IRUGO, show_bus_comment, store_bus_comment);
struct bus_type test_bus_type ={
.name ="test_bus",
.match = bus_match,
.bus_attrs =&bus_attr_comment,
};
staticint __init test_bus_init(void)
{
int ret;
ret=bus_register(&test_bus_type);
if(ret){
printk(KERN_WARNING "bus_register error: %d\n", ret);
return ret;
}
return ret;
}
staticvoid __exit test_bus_exit(void)
{
bus_unregister(&test_bus_type);
}
module_init(test_bus_init);
module_exit(test_bus_exit);
MODULE_AUTHOR("J.H.Luo<sparkle-cliz@sohu.com>");
MODULE_LICENSE("Dual BSD/GPL"); 三,最后给出这个实例的测试过程: /nand2 # insmod bus_test.ko
/nand2 # cd /sys/bus/test_bus/
/sys/bus/test_bus # ls
comment drivers drivers_probe
devices drivers_autoprobe uevent
/sys/bus/test_bus # cat comment
this bus is fortest
/sys/bus/test_bus # cat > comment
hey,guys
^C
/sys/bus/test_bus # cat comment