在前面一篇 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;
};
有了这些基本单元,就可以派生出许多其他的新类别
了解了驱动模型的构架后,我们就可以分析具体的驱动(platform,pci)。