簡介
作者:hjlin
內核版本:2.6.29
設備驅動模型框架是linux驅動編程的基礎。它通過kobject,kset,ktype等底層數據結構將bus_type, device, device_driver 等高層數據結構組織起來,形成一個層次、分類清晰的驅動模型。優點如下:
1. 代碼重用。將對象抽象爲總線、驅動、設備三種,各司其職。同一總線的多個驅動使用相同的總線對象。同一驅動可以關聯驅動多個設備。
2. 通過sysfs文件系統,清晰了展示內核驅動模型中的層次關係。同時sysfs文件系統還提供了方便的同用戶控件交互的接口。
框架
數據結構
Kobject
Kobject是代表驅動模型中的一個對象。總線、驅動、設備都繼承了它。(在結構體中包含kobject)。每個kobject在sysfs中表現爲一個目錄。
每個kobject都有一個parent kobject和所屬的kset。Kset就是kobject所屬的kset,通過kset的鏈表可以找到所有屬於它的kobject。這些kobject進行uevent操作時,都會調用所屬的kset的uevent_ops方法。父kobj,用於表示kobject之間或者kobject和kset,kset之間的在sysfs中的目錄結構關係。如果父kobj不存在,並且所屬的kset存在的話,則父kobj就是設置爲所屬的kset的內嵌kobj。因此,註冊設備、驅動或者總線的時候如果不指定parent kobj的話,父kobj則會設置爲所屬的kset的kobj。(todo:最好畫圖表示關係)
struct kobject { const char * k_name; struct kref kref; struct list_head entry; //連入到所屬的kset的list struct kobject * parent; //父kobj,如果沒有父kobj,則使用所屬的kset內嵌kobj struct kset * kset; //所屬的kset(如果有的話) struct kobj_type * ktype; //所屬的ktype,如果所屬的kset也有ktype,則用kset的ktype。 struct sysfs_dirent * sd; };
|
Kest
通過kset可以將kobject組織成一顆層次樹。
struct kset { struct list_head list; //屬於該kset的kobj鏈表 spinlock_t list_lock; struct kobject kobj; //內嵌的kobject struct kset_uevent_ops *uevent_ops; //uevent方法,對所有該kset下的kobj進行uevent操作時的方法。 }; struct kset_uevent_ops { int (*filter)(struct kset *kset, struct kobject *kobj); const char *(*name)(struct kset *kset, struct kobject *kobj); int (*uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env); }; |
kobj_type
struct kobj_type { void (*release)(struct kobject *kobj); //kobject釋放時調用 struct sysfs_ops *sysfs_ops; //操縱默認屬性的讀寫方法 struct attribute **default_attrs; //相應的kobject(kset)的默認屬性。對應於sysfs下的文件。 }; struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *,char *); ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); }; struct attribute { const char *name; struct module *owner; mode_t mode; }; |
bus_type
代表一個總線。對應/sys/bus下的一個目錄。管理相應總線下的所有驅動和設備。
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); //驅動模型進行驅動和設備的匹配時調用 int (*uevent)(struct device *dev, struct kobj_uevent_env *env); //uevent方法 int (*probe)(struct device *dev); //match成功之後會調用。一般該probe方法內部會調用相應的device_driver的probe方法 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 dev_pm_ops *pm;
struct bus_type_private *p; }; struct bus_type_private { struct kset subsys; //總線本身目錄 struct kset *drivers_kset; //驅動目錄 struct kset *devices_kset; //設備目錄 struct klist klist_devices; struct klist klist_drivers; struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1; struct bus_type *bus; };
|
Device
struct device { struct device *parent; //父設備
struct device_private *p;
struct kobject kobj; char bus_id[BUS_ID_SIZE]; const char *init_name; /* initial name of the device */ struct device_type *type;
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 *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 device_dma_parameters *dma_parms;
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;
dev_t devt; /* dev_t, creates the sysfs "dev" */
spinlock_t devres_lock; struct list_head devres_head;
struct klist_node knode_class; struct class *class; const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev); }; struct device_private { struct klist klist_children; //子設備的鏈表 struct klist_node knode_parent; //作爲父設備的字設備鏈表(klist_children)的一個節點 struct klist_node knode_driver; //作爲所屬驅動的設備鏈表(klist_devices)的一個節點 struct klist_node knode_bus; //作爲所屬總線的設備鏈表(klist_devices)的一個節點 void *driver_data; struct device *device; };
|
device_driver
struct device_driver { const char *name; struct bus_type *bus;
struct module *owner; const char *mod_name; /* used for built-in modules */
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); struct attribute_group **groups;
struct dev_pm_ops *pm;
struct driver_private *p; }; struct driver_private { struct kobject kobj; struct klist klist_devices; //所驅動的設備的鏈表 struct klist_node knode_bus; //作爲所屬總線的驅動鏈表(klist_drivers)的一個節點 struct module_kobject *mkobj; struct device_driver *driver; };
|
基礎方法
kobject_add_internal
static int kobject_add_internal(struct kobject *kobj)
將kobject添加到設備驅動模型中。主要設置父kobj以及將自己添加到所屬的kset的list中,並且在sysfs中創建目錄。
kobject_uevent
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
send an uevent to userspace
核心方法
註冊總線
bus_register(struct bus_type *bus)
註冊一個總線到驅動模型中。在sysfs中的結構是:
/sys/bus/platform/
|-- devices
| |-- …
|-- drivers
| |-- …
|-- drivers_autoprobe
|-- drivers_probe
`-- uevent
int bus_register(struct bus_type *bus) { int retval; struct bus_type_private *priv; //初始化private數據結構 priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); if (!priv) return -ENOMEM;
priv->bus = bus; bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); //設置kobject名稱。即/bus/sys/下的目錄名稱 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out;
priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; //註冊內嵌kset retval = kset_register(&priv->subsys); if (retval) goto out;
retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; //創建設備kset並且註冊 priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } //創建驅動kset並且註冊 priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; }
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL);
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; 。。。 out: return retval; }
|
註冊設備
主要有兩個工作,初始化設備,將設備掛接到設備驅動模型中並且處理uevent事件和自動匹配設備驅動。
在platform.c的platform_bus_init方法中使用了device_register(&platform_bus),註冊了一個設備,該設備的sys目錄爲/sys/devices/platform/。但是一般情況下,內核對於不同的總線會提供不同的封裝方法在內部調用device_register來註冊設備。比如說platform_register_device,並且在platform_register_device方法中設置了device的父kobj爲platform_bus,導致所有通過的platform_register_device註冊的設備的目錄都會在/sys/devices/platform/下。
在sys文件系統中的結構是:todo
int device_register(struct device *dev) { device_initialize(dev); return device_add(dev); } void device_initialize(struct device *dev) { kobj_set_kset_s(dev, devices_subsys); kobject_init(&dev->kobj); klist_init(&dev->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); INIT_LIST_HEAD(&dev->node); init_MUTEX(&dev->sem); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); device_init_wakeup(dev, 0); set_dev_node(dev, -1); } int device_add(struct device *dev) { struct device *parent = NULL; struct class_interface *class_intf; int error = -EINVAL;
dev = get_device(dev); if (!dev || !strlen(dev->bus_id)) goto Error;
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
parent = get_device(dev->parent); error = setup_parent(dev, parent); if (error) goto Error;
/* first, register with generic layer. */ kobject_set_name(&dev->kobj, "%s", dev->bus_id); error = kobject_add(&dev->kobj); if (error) goto Error;
/* notify platform of device entry */ if (platform_notify) platform_notify(dev);
/* notify clients of device entry (new way) */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); //創建uevent屬性文件。可以通過該文件發送事件 error = device_create_file(dev, &uevent_attr); if (error) goto attrError;
//創建dev的屬性文件。 if (MAJOR(dev->devt)) { error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; } error = device_add_class_symlinks(dev); if (error) goto SymlinkError; //創建所屬class,kobject_type, 以及device本身自帶的屬性文件 error = device_add_attrs(dev); if (error) goto AttrsError; //創建電源管理子目錄 power error = dpm_sysfs_add(dev); if (error) goto PMError; //將該設備掛接到電源鏈表下 device_pm_add(dev); //將該dev添加到bus error = bus_add_device(dev); if (error) goto BusError; kobject_uevent(&dev->kobj, KOBJ_ADD); //發送一個uevent事件到用戶空間,實現熱插拔 bus_attach_device(dev); //自動匹配設備和驅動 if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) { down(&dev->class->sem); /* tie the class to the device */ list_add_tail(&dev->node, &dev->class->devices);
/* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); up(&dev->class->sem); } Done: put_device(dev); return error; BusError: device_pm_remove(dev); dpm_sysfs_remove(dev); PMError: if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: if (MAJOR(dev->devt)) device_remove_file(dev, &devt_attr);
if (dev->class) { sysfs_remove_link(&dev->kobj, "subsystem"); /* If this is not a "fake" compatible device, remove the * symlink from the class to the device. */ if (dev->kobj.parent != &dev->class->subsys.kobj) sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); if (parent) { #ifdef CONFIG_SYSFS_DEPRECATED char *class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_remove_link(&dev->parent->kobj, class_name); kfree(class_name); #endif sysfs_remove_link(&dev->kobj, "device"); } } ueventattrError: device_remove_file(dev, &uevent_attr); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: if (parent) put_device(parent); goto Done; }
void bus_attach_device(struct device * dev) { struct bus_type *bus = dev->bus; int ret = 0;
if (bus) { dev->is_registered = 1; //如果總線自動探測設備,則進行設備驅動匹配。(可以通過bus下的autoprobe屬性文件查看和修改是否支持自動探測) if (bus->drivers_autoprobe) ret = device_attach(dev); WARN_ON(ret < 0); if (ret >= 0) klist_add_tail(&dev->knode_bus, &bus->klist_devices); else dev->is_registered = 0; } } int device_attach(struct device * dev) { int ret = 0;
down(&dev->sem); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) ret = 1; else { dev->driver = NULL; ret = 0; } } else { //將設備和該總線上的所有驅動進行匹配 ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); } up(&dev->sem); return ret; } static int __device_attach(struct device_driver * drv, void * data) { struct device * dev = data; return driver_probe_device(drv, dev); } int driver_probe_device(struct device_driver * drv, struct device * dev) { int ret = 0;
if (!device_is_registered(dev)) return -ENODEV; //首先調用總線的match方法進行匹配 if (drv->bus->match && !drv->bus->match(dev, drv)) goto done;
pr_debug("%s: Matched Device %s with Driver %s\n", drv->bus->name, dev->bus_id, drv->name); //調用總線或者驅動的probe方法繼續進一步的匹配。 ret = really_probe(dev, drv);
done: return ret; } |
註冊驅動
int driver_register(struct device_driver *drv)
註冊驅動到設備驅動模型,在sysfs創建相應的目錄結構,以及自動匹配設備驅動。一般在內核中會包裝在其他的方法中在內部調用driver_register方法。在sysfs中的結構是:
/sys/bus/platform/drivers/serial8250/
|-- bind
|-- serial8250 -> ../../../../devices/platform/serial8250
|-- uevent
`-- unbind
/sys/devices/platform/serial8250
|-- driver -> ../../../bus/platform/drivers/serial8250
int driver_register(struct device_driver * drv) { if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) { printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); } klist_init(&drv->klist_devices, NULL, NULL); return bus_add_driver(drv); } int bus_add_driver(struct device_driver *drv) { struct bus_type * bus = bus_get(drv->bus); int error = 0;
if (!bus) return -EINVAL;
pr_debug("bus %s: add driver %s\n", bus->name, drv->name); error = kobject_set_name(&drv->kobj, "%s", drv->name); if (error) goto out_put_bus; drv->kobj.kset = &bus->drivers; error = kobject_register(&drv->kobj); if (error) goto out_put_bus; //自動匹配設備和驅動 if (drv->bus->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent); if (error) { printk(KERN_ERR "%s: uevent attr (%s) failed\n", __FUNCTION__, drv->name); } error = driver_add_attrs(bus, drv); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __FUNCTION__, drv->name); } error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed\n", __FUNCTION__, drv->name); }
return error; out_unregister: kobject_unregister(&drv->kobj); out_put_bus: bus_put(bus); return error; } |