linux裝置驅動模型 - device/bus/driver
阿新 • • 發佈:2018-11-03
在linux驅動模型中,為了便於管理各種裝置,我們把不同裝置分別掛在他們對應的總線上,裝置對應的驅動程式也在總線上找,這樣就提出了deivce-bus-driver的模型,硬體上有許多裝置匯流排,那麼我們就在裝置模型上抽象出bus概念,相應的device就代表裝置,driver表示驅動,在程式碼中它們對應的結構體下面介紹,對於實際的裝置及匯流排,這些結構體就可以嵌入到實際總線上。
1. bus
瞭解bus,就要先介紹下bus的結構體,一條匯流排定義完後要註冊到系統中,第二節介紹註冊函式,最後再介紹下其他一些相關API
1.1 struct bus_type
struct bus_type {
const char *name;--------------------------------------------匯流排名字
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);-------匹配函式(用於匹配device&driver)
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 (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);-----------PM相關
int (*resume)(struct device *dev);--------------------------------PM相關
const struct dev_pm_ops *pm;--------------------------------------PM相關
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
1.2 註冊匯流排
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
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;
retval = kset_register(&priv->subsys);--------------------在/sys/bus目錄下建立當前匯流排目錄
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent);----------在當前匯流排目錄下建立檔案uevent
if (retval)
goto bus_uevent_fail;
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);-----------------在當前匯流排目錄下建立devices目錄
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);----------------在當前匯流排目錄下建立drivers目錄
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus);-------------------------在當前匯流排目錄下建立probe相關檔案
if (retval)
goto bus_probe_files_fail;
retval = bus_add_groups(bus, bus->bus_groups);
if (retval)
goto bus_groups_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
。。。。。。
}
1.3 其他API
匯流排解除安裝函式
extern void bus_unregister(struct bus_type *bus);
2. device
先介紹devices結構體,在介紹註冊函式
2.1 struct device
struct device {
struct device *parent;-------------------------父裝置
struct device_private *p;
struct kobject kobj;--------------------------------嵌入的kobject
const char *init_name; /* initial name of the device */
const struct device_type *type;---------------------所屬的device型別
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */---所屬的bus
struct device_driver *driver; /* which driver has allocated this
device */---------------------------------對應的驅動driver
void *platform_data; /* Platform specific data, device
core doesn't touch it */------------------私有platform資料
void *driver_data; /* Driver data, set and get with
dev_set/get_drvdata */--------------------私有driver資料
struct dev_pm_info power;-----------------------------------PM相關
struct dev_pm_domain *pm_domain;--------------------------PM相關
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
#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. */
unsigned long dma_pfn_offset;
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 */
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */------裝置樹相關
struct fwnode_handle *fwnode; /* firmware device node */
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock;
struct list_head devres_head;-----------------------------------devres相關
struct klist_node knode_class;
struct class *class;----------------------------------------所屬的class
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
bool offline_disabled:1;
bool offline:1;
};
2.2 設備註冊函式
device的註冊函式device_register分兩步,先初始化device_initialize,主要是初始化所屬的kset為/sys/devices目錄,及其他(如PM相關),然後再註冊,函式為device_add
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);------------------有初始name就設定
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);---沒有初始name就設定預設的
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);---在/sys/devices目錄下建立當前裝置目錄
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &dev_attr_uevent);--------在當前裝置目錄下建立檔案uevent
if (error)
goto attrError;
error = device_add_class_symlinks(dev);-------------------建立連結檔案
if (error)
goto SymlinkError;
error = device_add_attrs(dev);----------------------------建立其他檔案,如在class目錄下
if (error)
goto AttrsError;
error = bus_add_device(dev);------------------------------device加入到bus-device的連結串列中
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
if (error)
goto DevAttrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto SysEntryError;
devtmpfs_create_node(dev);
}
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);--------__device_attach-------呼叫此函式來和driver進行匹配
if (parent)-----------------------------------------加入到父裝置連結串列中
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {-----------------------------------和class相關的操作
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
。。。。。。
}
具體device如何匹配到對應的driver在函式__device_attach中進行,加入開始device沒有driver,那麼會在bus中找到對應的driver,看下函式__device_attach
static int __device_attach(struct device *dev, bool allow_async)
{
int ret = 0;
device_lock(dev);
if (dev->driver) {
if (device_is_bound(dev)) {
ret = 1;
goto out_unlock;
}
ret = device_bind_driver(dev);---------如果已經有driver,那麼就繫結到device並進行probe
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
struct device_attach_data data = {
.dev = dev,
.check_async = allow_async,
.want_async = false,
};
if (dev->parent)
pm_runtime_get_sync(dev->parent);
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);----如果沒有driver,就遍歷總線上的driver,直到找到並進行probe
if (!ret && allow_async && data.have_async) {
/*
* If we could not find appropriate driver
* synchronously and we are allowed to do
* async probes and there are drivers that
* want to probe asynchronously, we'll
* try them.
*/
dev_dbg(dev, "scheduling asynchronous probe\n");
get_device(dev);
async_schedule(__device_attach_async_helper, dev);
} else {
pm_request_idle(dev);
}
if (dev->parent)
pm_runtime_put(dev->parent);
}
out_unlock:
device_unlock(dev);
return ret;
}
2.3 其他API
device解除安裝函式
extern void device_unregister(struct device *dev);
3. driver
先介紹driver的結構體,再介紹註冊函式
3.1 struct device_driver
struct device_driver {
const char *name;-----------------------------driver名字
struct bus_type *bus;--------------------------所屬匯流排
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
enum probe_type probe_type;
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);----------------探測初始化函式
int (*remove) (struct device *dev);---------------刪除函式
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);---PM相關
int (*resume) (struct device *dev);------------------------PM相關
const struct attribute_group **groups;
const struct dev_pm_ops *pm;-------------------------------PM相關
struct driver_private *p;
};
3.2 註冊函式
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
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);
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);--------------------主要在這裡進行註冊
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
看下函式bus_add_driver
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);-----------------------得到所屬的匯流排
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;-------設定所屬匯流排的drivers
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);------------在所屬匯流排的drivers目錄下建立本驅動目錄
if (error)
goto out_unregister;
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);---把本驅動加入到匯流排驅動連結串列中
if (drv->bus->p->drivers_autoprobe) {
if (driver_allows_async_probing(drv)) {
pr_debug("bus: '%s': probing driver %s asynchronously\n",
drv->bus->name, drv->name);
async_schedule(driver_attach_async, drv);
} else {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
}------------------__driver_attach--------------呼叫此函式來進行探測初始化
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",
__func__, drv->name);
}
error = driver_add_groups(drv, bus->drv_groups);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
。。。。。。。。
}
那麼驅動是如何匹配到對應的device的,繼續探索函式__driver_attach
(drivers/base/dd.c)
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
int ret;
/*
* 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.
*/
ret = driver_match_device(drv, dev);--------呼叫匯流排的match函式來進行匹配
if (ret == 0) {
/* no match */
return 0;
} else if (ret == -EPROBE_DEFER) {
dev_dbg(dev, "Device match requests probe deferral\n");
driver_deferred_probe_add(dev);
} else if (ret < 0) {
dev_dbg(dev, "Bus failed to match device: %d", ret);
return ret;
} /* ret > 0 means positive match */
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);------沒有driver就設定此driver為device的driver,然後呼叫驅動probe初始化
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
3.3 其他API
解除安裝函式
extern void driver_unregister(struct device_driver *drv);