分类: LINUX
2009-06-07 19:30:57
在前面一篇linux设备模型深探(1)我们详细了解了底层元素kset,kobject,ktype之间的关系后,本节讲解下驱动模型中另外几个概念(bus、driver、device)为后面具体分析特定驱动(platform,pci)模型打个基础。
BUS
在设备模型中,所有的device都是通过总线bus 连接,这里的bus包括通常意义的总线如usb,pci,也包括虚拟的platform总线。
[root@wangp bus]#
pwd
/sys/bus
[root@wangp bus]#
ls
ac97 acpi
bluetooth gameport i2c
ide pci pci_express
pcmcia platform pnp scsi serio usb
[root@wangp
platform]# pwd
/sys/bus/platform
[root@wangp
platform]# ls
devices drivers
struct bus_type {
const
char * name;
struct
module * owner;
struct
kset subsys;
struct kset drivers;
struct
kset devices;
struct
klist klist_devices;
struct
klist klist_drivers;
struct
blocking_notifier_head bus_notifier;
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);
unsigned
int drivers_autoprobe:1;
};
name是总线的名字,每个总线下都有自己的子系统,其中包含2个kset,deviece和driver,分别代表已知总线的驱动和插入总线的设备
如platform总线的声明如下:
struct bus_type
platform_bus_type = {
.name =
"platform",
.dev_attrs = platform_dev_attrs,
.match =
platform_match,
.uevent =
platform_uevent,
.suspend =
platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume =
platform_resume,
};
只有很少的bus_type成员需要初始化,大部分交给kernel来处理
关于总线的操作常用的如下:
int
bus_register(struct bus_type * bus);
void
bus_unregister(struct bus_type * bus);
/* iterator helpers
for buses */
列举总线上从start之后的每个设备,并进行fn操作,通常用途是对bus上的设备和驱动进行绑定
int
bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, int
(*fn)(struct device *, void *));
int
driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus,
NULL, drv, __driver_attach);
}
static int
__driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = data;
/*
*
Lock device and try to bind to it. We drop the error
*
here and always return 0, because we need to keep trying
*
to bind to devices and some drivers will return an error
*
simply if it didn't support the device.
*
*
driver_probe_device() will spit a warning if there
*
is an error.
*/
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
几乎linux设备模型的每一层都提供了添加属性的函数,总线也不例外
struct bus_attribute
{
struct attribute attr;
ssize_t (*show)(struct bus_type *, char *
buf); //显示属性
ssize_t (*store)(struct bus_type *, const
char * buf, size_t count); //设置属性
};
创建属于一个总线的属性使用(在模块的加载时间完成)
int bus_create_file(struct bus_type *,struct
bus_attribute *);
void
bus_remove_file(struct bus_type *, struct bus_attribute *);
在说明下bus在sysfs里面的结构,刚才已经讲过,bus_type中有2个kset结构对应于device和driver,也就是说每个bus下面都会有device何driver2个文件夹。
首先在总线上注册的驱动会得到一个文件夹driver,如platform驱动
[root@wangp
platform]# pwd
/sys/bus/platform
[root@wangp
platform]# ls
devices drivers
[root@wangp
drivers]# pwd
/sys/bus/platform/drivers
[root@wangp
drivers]# ls
i8042 pcspkr
serial8250 vesafb
而任何在总线/sys/bus/xxx/上发现的设备会得到一个symlink(符号链接)即/sys/bus/xxx/device指向/sys/device/xxx下面的文件夹
[root@wangp
devices]# pwd
/sys/bus/platform/devices
[root@wangp
devices]# ls -l
total 0
lrwxrwxrwx 1 root
root 0 Jun 6 10:37 bluetooth ->
../../../devices/platform/bluetooth
lrwxrwxrwx 1 root
root 0 Jun 6 10:37 floppy.0 ->
../../../devices/platform/floppy.0
lrwxrwxrwx 1 root
root 0 Jun 6 10:37 i8042 ->
../../../devices/platform/i8042
lrwxrwxrwx 1 root
root 0 Jun 6 10:37 pcspkr ->
../../../devices/platform/pcspkr
lrwxrwxrwx 1 root
root 0 Jun 6 10:37 serial8250 ->
../../../devices/platform/serial8250
lrwxrwxrwx 1 root
root 0 Jun 6 10:37 vesafb.0 ->
../../../devices/platform/vesafb.0
DEVICE
struct device {
struct klist klist_children;
struct klist_node knode_parent; /*
node in sibling list */
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent; //该设备所属的设备
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct device_type *type;
unsigned is_registered:1;
unsigned uevent_suppress:1;
struct semaphore sem; /* semaphore to
synchronize calls to
* its driver.
*/
struct bus_type * bus; /* type
of bus device is on */
struct device_driver *driver; /* which driver has allocated this device */
void *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data, device core doesn't touch it */
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/*
Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct list_head dma_pools; /* dma
pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */
/* arch specific additions */
struct dev_archdata archdata;
spinlock_t devres_lock;
struct list_head devres_head;
/* class_device migration path */
struct list_head node;
struct class *class;
dev_t devt; /* dev_t, creates the sysfs
"dev" */
struct attribute_group **groups; /*
optional groups */
void (*release)(struct
device * dev);
};
这个结构相当于一个基类,对于基于特定总线的设备,会派生出特定的device结构(linux的驱动模型有很多结构都可以基于类来看待)
struct
platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
一个总线设备用如下函数注册(注册总线类型)
int
device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
以完成parent name bus_id bus几个成员的初始化,注册后可以在/sys/devices下面看到
void
device_unregister(struct device *dev);
同时和其相关的属性为
struct
device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev,
struct device_attribute *attr,char *buf);
ssize_t (*store)(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
};
int
device_create_file(struct device *device,struct device_attribute * entry);
device_remove_file(struct
device * dev, struct device_attribute * attr);
以下完成一个总线设备的注册:
static void
simple_bus_release(struct device *dev)
{
printk("simple bus release\n");
}
struct device
simple_bus = {
.bus_id ="simple_bus",
.release = simple_bus_release
}
ret =
device_register(&simple_bus);
if(ret)
pritnk("unable
to register simple_bus\n");
完成注册后,simple_bus就可以再sysfs中/sys/devices下面看见,任何挂载这个bus上的device都会在/sys/devices/simple_bus下看到
DEVICE_DRIVER
struct device_driver
{
const char * name;//在sysfs下显示
struct bus_type * bus;
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module * owner;
const char * mod_name; /* used
for built-in modules */
struct module_kobject * mkobj;
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 (*resume) (struct device * dev);
};
由于大多数驱动都会带有特有的针对某种特定总线的信息,因此一般都是基于device_driver来派生出自己
特有的驱动,如
struct
platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device
*);
int (*suspend)(struct platform_device *,
pm_message_t state);
int (*suspend_late)(struct
platform_device *, pm_message_t state);
int (*resume_early)(struct
platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
比较xxx_driver 和device_driver我们可以发现,结构体所带的方法基本相同,在具体应该用的时候是可以转换的。
驱动的注册
int
driver_register(struct device_driver * drv);
void
driver_unregister(struct device_driver * drv);
而大多数驱动会调用针对特定总线的诸如platform_driver_register,pci_driver_register之类的函数去注册
总线(bus)可以挂接一类设备(device)
驱动(driver)可以驱动一类设备(device)
因此和bus一样,device_driver也有一个函数为某个驱动来遍历所有设备
int
driver_for_each_dev(struct device_driver *drv, void *data,int
(*callback)(struct device *dev,void *data);
所有device_driver完成注册后,会在/sys/bus/xxx/driver目录下看到驱动信息
同时相应的属性内容
struct
driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *,
char * buf);
ssize_t (*store)(struct device_driver *,
const char * buf, size_t count);
};
int driver_create_file(struct device_driver
*,struct driver_attribute *);
void
driver_remove_file(struct device_driver *, struct driver_attribute *);
说了这么多,现在来理一理kobject kset,subsys,sysfs,bus之间的关系
上图反映了继承体系的一个基本结构,kset是一组相同的kobject的集合,kernel可以通过跟踪kset来跟踪所用的特定类型设备,platform、pci、i2c等,kset起到连接作用将设备模型和sysfs联系在一起。每个kset自身都包含一个kobject,这个kobject将作为很多其他的kobject的父类,从sys上看,某个kobject的父类是某个目录,那么它就是那个目录的子目录,parent指针可以代表目录层次,这样典型的设备模型层次就建立起来了,从面向对象的观点看,kset是顶层的容器类,kset继承他自己的kobject,并且可以当做kobject来处理
如图:kset把它的子类kobject放在链表里面,kset子类链表里面那些kobject的kset指针指向上面的kset,parent指向父类。
struct kobject {
const char * k_name;
struct kref kref;
struct list_head entry;
struct kobject * parent;
struct kset * kset;
struct kobj_type * ktype;
struct sysfs_dirent * sd;
};
struct kset {
struct kobj_type *ktype;
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
有了这些基本单元,就可以派生出许多其他的新类别