Linux設備模型淺析之驅動篇

Linux設備模型淺析之驅動篇
本文屬本人原創,歡迎轉載,轉載請註明出處。由於個人的見識和能力有限,不可能面
面俱到,也可能存在謬誤,敬請網友指出,本人的郵箱是 yzq.seen@gmail.com,博客是
http://zhiqiang0071.cublog.cn   。   
Linux設備模型,僅僅看理論介紹,比如LDD3的第十四章,會感覺太抽象不易理解,而
通過閱讀內核代碼就更具體更易理解,所以結合理論介紹和內核代碼閱讀能夠更快速的理解掌
握linux設備模型。這一序列的文章的目的就是在於此,看這些文章之前最好能夠仔細閱讀
LDD3的第十四章。大部分device和driver都被包含在一個特定bus中,platform_device和
platform_driver   就是如此,包含在 platform_bus_type中。這裏就以對platform_bus_type的調用
爲主線,淺析platform_driver的註冊過程,從而理解linux設備模型。platform_bus_type用於關
聯SOC   的 platform device   和 platform driver,比如在內核linux-2.6.29中所有S3C2410中的 
platform device都保存在devs.c中。這裏就以S3C2410  RTC的驅動程序rtc-s3c.c爲例來分析
platform_driver_register()例程的調用過程。在文章的最後貼有一張針對本例的device model圖
片,可在閱讀本文章的時候作爲參照。閱讀這篇文章之前,最好先閱讀文章《Linux設備模型
淺析之設備篇》。
一、S3C2410  RTC的 platform_driver定義在drivers/rtc/rtc-s3c.c中,代碼如下:
static struct platform_driver s3c2410_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = __devexit_p(s3c_rtc_remove),
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.driver = {
.name = "s3c2410-rtc",
.owner = THIS_MODULE,
},
};
由於name = "s3c2410-rtc",後面會分析到,它將成爲一個目錄的名字,
即/sys/bus/platform/s3c2410-rtc。s3c_rtc_probe ()的調用後面也會講到,其實在之前《Linux設備
模型淺析之設備篇》中已經分析過了。使用platform_driver_register()註冊s3c2410_rtc_driver ,
下面就分析它。
二、platform_driver_register()例程定義在drivers/base/ platform.c中,代碼如下:
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type; //    設置爲 platform_bus_type
if (drv->probe)
drv->driver.probe = platform_drv_probe;  // 轉存到device_driver中
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);  // 該例程將device_driver註冊到bus,後面將分析
}
代碼中,
1. 設置成platform_bus_type這個很重要,因爲driver和device是通過bus聯繫在一起的,具
體在本例中是通過platform_bus_type中註冊的回調例程和屬性來是實現的,在《Linux設備模型
淺析之設備篇》中講到的driver與device   的匹配就是通過 platform_bus_type註冊的回到例程
mach()來完成的。關於platform_bus_type的分析請參考《Linux設備模型淺析之設備篇》。下面
就分析driver_register()。
三、driver_register()例程定義在drivers/base/ driver.c中,代碼如下:
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
// 做些判斷
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);
/*通過驅動的名字查找driver,如果找到了,說明已經註冊過,返回錯誤代碼,後面會
分析*/
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EEXIST;
}
/* 將driver加入到bus的kset   ,並生成些文件夾和鏈接文件,後面會分析 */
ret = bus_add_driver(drv);
if (ret)
return ret;
/* 添加attribute_group   ,本例中沒有設置 drv->groups */
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
代碼中,
1. 正如該例程的英文註釋所言,大部分工作在bus_add_driver()例程中完成。
bus_add_driver()例程的作用類似於《Linux設備模型淺析之設備篇》分析過的
bus_add_device(),只不過後者將device添得到bus中。下面分析driver_find()例程。2. driver_find()例程通過驅動所屬bus的driver容器drivers_kset來查找,該例程定義在
drivers/base/ driver.c中,代碼如下:
struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);  //    在 drivers_kset容器中查找
struct driver_private *priv;
if (k) {
priv = to_driver(k);
return priv->driver; // 返回找到的driver
}
return NULL;
}
代碼中,
2.1. 通過kset_find_obj(bus->p->drivers_kset, name)查找該driver的kobj,其代碼如下,
struct kobject *kset_find_obj(struct kset *kset, const char *name)
{
struct kobject *k;
struct kobject *ret = NULL;
spin_lock(&kset->list_lock);
list_for_each_entry(k, &kset->list, entry) { //    遍歷 kset->list 列表獲取kobj
if (kobject_name(k) && !strcmp(kobject_name(k), name)) { // 比較name字符
ret = kobject_get(k); // 如果找到就增加引用並返回
break;
}
}
spin_unlock(&kset->list_lock);
return ret;
}
顯然,所有同類型的driver   都註冊到了 一個bus->p->drivers_kset->list中,所以可通過其查找已
經註冊的driver。下面分析bus_add_driver()例程。
3. bus_add_driver()例程將driver註冊到bus中,在本例中是platform_bus_type,該例程定
義在drivers/base/ bus.c中,代碼如下:
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus); // 增加對bus的引用
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);priv = kzalloc(sizeof(*priv), GFP_KERNEL); // 這個結構體中存放着kobj相關的數據
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv; //    反向指向包含其的 drv,以便後續使用
drv->p = priv; //    將 priv保存到device_driver
/* 指向bus的drivers_kset容器,該容器的作用與device_kset容器相同,前者是包含所
有註冊到該bus的driver,後者是包含所有註冊到該bus的device。*/
priv->kobj.kset = bus->p->drivers_kset;
/*    初始化 priv->kobj   ,並將其添加到 bus->p->drivers_kset中,在本例中生
成/sys/bus/platform/drivers/s3c2410-rtc目錄,後面會分析drivers_kset 的初始化
及/sys/bus/platform/drivers/   目錄的生成 */
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
     "%s", drv->name);
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) { //    在 bus_register()例程中已經設置爲1了
error = driver_attach(drv); // 所以會尋找匹配的device,後面分析
if (error)
goto out_unregister;
}
// 將driver   鏈接到 klist_drivers,方便後續快速查找driver
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  
// 將driver添加到module模塊,後面會分析
module_add_driver(drv->owner, drv);
/*  生成/sys//sys/bus/platform/drivers/s3c2410-rtc/uevent屬性文件,其作用與device中的
uevent類似,可參照《Linux設備模型淺析之設備篇》*/
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
/* 添加bus的公有屬性文件到/sys//sys/bus/platform/drivers/s3c2410-rtc/目錄,所有的driver
  都添加 */
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",
__func__, drv->name);
}/*  ” 如果配置了 CONFIG_HOTPLUG" “ ,則生成 bind”和"unbind”屬性文件,可用於手動匹
  配和移除 device與driver   之間的關聯 */
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
// 通過uevent設置幾個環境變量並通知用戶空間,以便調用程序來完成相關設置
kobject_uevent(&priv->kobj, KOBJ_ADD);
return error;
out_unregister:
kobject_put(&priv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
代碼中,
3.1. 老一點的2.6的內核版本如2.6.10直接將kobj嵌入在struct device_driver中,新版的內
核用struct driver_private來存放kobj相關的數據,將struct driver_private嵌入在struct 
device_driver中。struct driver_private定義如下:
struct driver_private {
struct kobject kobj; // kobj
struct klist klist_devices; // 用於鏈接關聯到該driver的device
struct klist_node knode_bus; //    用於鏈接到 bus->p->klist_drivers
struct module_kobject *mkobj; // 模塊的kobj,後面會講到module
struct device_driver *driver; // 反向指向包含其的driver
}
3.2. 使用bus_register()例程註冊bus(本例是platform_bus_type)的時候,除了生成該bus
的kset容器subsys,還生成devices_kset和drivers_kset容器,都包含在struct bus_type_private裏
。當然也先後生成他們的目錄/sys/bus/platform、/sys/bus/platform/devices
、/sys/bus/platform/drivers。先看看struct bus_type_private的定義:
struct bus_type_private {
struct kset subsys; // 代表該bus,裏面的kobj是該bus的主kobj,也就是最頂層
struct kset *drivers_kset; // 包含該bus所有的driver
struct kset *devices_kset; // 包含該bus所有的device
struct klist klist_devices; // 其作用與devices_kset->list作用相同
struct klist klist_drivers; // 其作用與drivers _kset->list作用相同
struct blocking_notifier_head bus_notifier; // 通知bus相關的模塊
unsigned int drivers_autoprobe:1; // 設置是否在driver註冊的時候probe device
struct bus_type *bus; // 回指包含自己的bus
}
其實該結構體的英文註釋寫的很清楚,可到內核的drivers/base/ base.h中查看。
3.3. bus_register()例程定義在drivers/base/bus.c   中 ,部分代碼如下:
int bus_register(struct bus_type *bus){
int retval;
struct bus_type_private *priv;
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;  // 反向指向bus
bus->p = priv;
// 將bus的名字設置爲kobj ” 的名字,本例中是 platform”
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset; // 指向其父kset   , bus_kset在buses_init()例程中添加
priv->subsys.kobj.ktype = &bus_ktype; // 設置讀取屬性文件的默認方法
priv->drivers_autoprobe = 1; // 設置爲註冊時probe device
/* 註冊priv->subsys容器,初試化完後,調用了kobject_add_internal()註冊其kobj到
bus_kset父容器裏,並生成目錄/sys/bus/platfrom */
retval = kset_register(&priv->subsys);
/* 生成devices_kset 容器,命名爲"devices" ,由於其父kobj是priv->subsys.kobj,所以生
成的目錄是/sys/bus/platform/devices  */
priv->devices_kset = kset_create_and_add("devices", NULL,
 &priv->subsys.kobj);
/* 生成drivers _kset 容器,命名爲"drivers" ,由於其父kobj是priv->subsys.kobj,所以生
成的目錄是/sys/bus/platform/ drivers  */
priv->drivers_kset = kset_create_and_add("drivers", NULL,
 &priv->subsys.kobj);

  下面該分析 driver_attach()例程了。
3.4. driver_attach()例程從bus中查找匹配的device,該例程定義在drivers/base/ dd.c中,代
碼如下:
int driver_attach(struct device_driver *drv)
{
/* 遍歷bus的klist_devices列表,對每個device使用回調函數__driver_attach()來鑑別是否
和driver   匹配 */
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
代碼中,
3.4. 1. bus_for_each_dev()定義在在drivers/base/ bus.c中,代碼如下:
int bus_for_each_dev(struct bus_type *bus, struct device *start,     void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus)
return -EINVAL;
// 設置i
klist_iter_init_node(&bus->p->klist_devices, &i,
     (start ? &start->knode_bus : NULL));
// 使用i遍歷
while ((dev = next_device(&i)) && !error)
error = fn(dev, data); // 使用回調例程處理
klist_iter_exit(&i);
return error;
}
接着分析回調例程__driver_attach()。
3.4. 2.  回調例程__driver_attach()定義在drivers/base/ dd.c中,代碼如下:
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.
 */
/* 調用bus的match (),在這裏是platform_bus_type的mach(),即platform_match()例
  程 ,其在《Linux設備模型淺析之設備篇   》中分析過 */
if (drv->bus->match && !drv->bus->match(dev, drv))
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)   //    顯然本例中 s3c_device_rtc在註冊時沒有找到driver,所以這裏會執行
driver_probe_device(drv, dev); // 這裏開始probe
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;}
3.4.2.1.  driver_probe_device()在之前的《Linux設備模型淺析之設備篇》文章已經分析,所
以這裏直接拷貝過來。
driver_probe_device()也是定義在drivers/base/dd.c中,代碼如下:
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))  //    判斷 dev 是否已經註冊
return -ENODEV;
/* 調用bus的match (),在這裏是platform_bus_type的mach(),即platform_match()例
  程 ,其在《Linux設備模型淺析之設備篇   》中分析過 */
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
 drv->bus->name, __func__, dev_name(dev), drv->name);
// 這裏真正開始調用用戶在device_driver 中註冊的probe()例程
ret = really_probe(dev, drv);
done:
return ret;
}
下面分析really_probe()例程,顧名思義,其真正開始probe了。之前的《Linux設備模型淺析之
設備篇》文章已經分析,所以這裏直接拷貝過來。
3.4.2.2.  really_probe()定義在drivers/base/dd.c中,代碼如下:
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
 drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;  // 將匹配的driver   指針關聯到 dev,以便後續使用
/*如果設備和驅動已經關聯了,則在dev目錄下,即s3c2410-rtc 目錄下生成名
” 爲 driver"的鏈接文件,指向其關聯的驅動dev->driver的sys目錄,並且在dev->
driver的sys目錄下生成鏈接文件,名字和dev ” 的名字一樣,即 3c2410-wdt ",指
向/sys/devices/platform/s3c2410-rtc 目錄
*/
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;}
//    如果設置了 dev->bus->probe,則調用,在platform_bus_type沒有設置
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
/* 所以,調用驅動註冊在device_driver裏的probe,這個很常用,用於獲得硬件資源,初
始化硬件等,在本例中就是調用註冊到driver的s3c_rtc_probe ()例程。
*/
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
// 將device添加到driver列表中,並通知bus上的設備,表明BOUND_DRIVER。
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
 drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
       "%s: probe of %s failed with error %d\n",
       drv->name, dev_name(dev), ret);
}
/*
 * Ignore errors returned by ->probe so that the next driver can try
 * its luck.
 */
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
代碼中,
3.4.2.2.1.在s3c_rtc_probe ()中獲取了硬件資源和註冊了rtc device,所以會產生相關的文件
夾和文件,並調用rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE)註冊rtc
類設備,在《Linux設備模型淺析之設備篇》中做了具體分析,請參照。3.5. 接着執行 module_add_driver()例程,其定義在drivers/base/module.c中,代碼如下:
void module_add_driver(struct module *mod, struct device_driver *drv)
{
char *driver_name;
int no_warn;
struct module_kobject *mk = NULL;
if (!drv)
return;
if (mod) //  本例中設置爲THIS_MODULE   ,所以執行
mk = &mod->mkobj;
else if (drv->mod_name) { // 如果設置了模塊的名字,則到module_kset容器列表中查找
struct kobject *mkobj;
/* Lookup built-in module entry in /sys/modules */
mkobj = kset_find_obj(module_kset, drv->mod_name); // 根據模塊名查找
if (mkobj) {
mk = container_of(mkobj, struct module_kobject, kobj);
/* remember our module structure */
drv->p->mkobj = mk;
/* kset_find_obj took a reference */
kobject_put(mkobj);
}
}
if (!mk)
return;
/* Don't check return codes; these calls are idempotent */
/* 本例中,假設rtc-s3c.c驅動編譯成模塊,手工在shell中使用insmod命令加載。所以,
會在/sys/bus/platform/drivers/s3c2410-rtc/目錄下生成名爲"module"的鏈接文件,指
向/sys/modules/rtc-s3c目錄,至於/sys/modules/rtc-s3c目錄是如何產生的,稍後將做分析
*/
no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module");
// 本例中,生成的driver_name “ 是 platform:s3c2410-rtc”,你看了該例程的實現就會明白
driver_name = make_driver_name(drv);
if (driver_name) {
// 生成/sys/modules/rtc-s3c/drivers目錄
module_create_drivers_dir(mk);
/*  本例中,在/sys/modules/rtc-s3c/drivers “ 目錄下生成名爲 platform:s3c2410-rtc”的
鏈接文件,指向/sys/bus/platform/drivers/s3c2410-rtc/   目錄 */
no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
    driver_name);
kfree(driver_name);
}
}代碼中,
3.5.1. 看了上面的分析,一定會產生一個疑問,/sys/modules/rtc-s3c目錄是如何產生的呢?
下面就說這個問題。首先說說/sys/modules目錄是如何產生的。在kernel/params.c中有個初始化
例程param_sysfs_init()在系統初始化的時候會調用,在該例程中調用了module_kset = 
kset_create_and_add("module", &module_uevent_ops, NULL),顯然生成了一個kset容器,產生
了/sys/module目錄,該kset容器被賦給了全局指針module_kset,所以我們所有的驅動模塊的
mkobj   都掛在在它的名下。
3.5.2. 再說說/sys/modules/rtc-s3c目錄是如何產生的。rtc-s3c.c驅動程序被編譯成模塊rtc-
s3c.ko。insmod加載時會產生系統調用,調用到的內核入口程序是定義在kernel/module.c中的
init_module()例程(其實是sys_init_module())。該例程會調用在同一個文件中的load_module()
例程,該例程會生成一個struct module並根據記載的模塊進行初始化並將其加入到一個鏈表
中,以便以後進行引用。在init_module()例程中會調用到mod_sysfs_init()例程,其代碼如下:
int mod_sysfs_init(struct module *mod)
{
int err;
struct kobject *kobj;
if (!module_sysfs_initialized) {
printk(KERN_ERR "%s: module sysfs not initialized\n",
       mod->name);
err = -EINVAL;
goto out;
}
//    先在 module_kset 容器的列表中查找,看是否該mod已經加載
kobj = kset_find_obj(module_kset, mod->name); 
if (kobj) {
// 加載過了則打印錯誤信息並返回
printk(KERN_ERR "%s: module is already loaded\n", mod->name);
kobject_put(kobj);
err = -EINVAL;
goto out;
}
mod->mkobj.mod = mod;
memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj));
//    將 kobj.kset   指向 module_kset,也就是包含在它的名下
mod->mkobj.kobj.kset = module_kset;
/* 因爲傳入的parent參數爲NULL   ,所以會使用 module_kset.kobj作爲mkobj.kobj的
partent kobj。mod->name 就是模塊名,根據生成的模塊來獲得,本例中模塊爲rtc-
s3c.ko   ,顯然 mod->name = “rtc-s3c”,所以會產生/sys/module/rtc-s3c目錄。*/
err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL,
   "%s", mod->name);
if (err)
kobject_put(&mod->mkobj.kobj);/* delay uevent until full sysfs population */
out:
return err;
}
3.5.3.  順便說說本例中platform_driver_register(&s3c2410_rtc_driver)是被如何調用的,該例
  程被 模塊初始化例程s3c_rtc_init(void)調用。module_init(s3c_rtc_init)使得其會被賦給 
__this_module.init,而該init函數指針會在init_module()例程中被調用。這樣
platform_driver_register(&s3c2410_rtc_driver)就被調用了,呵呵。
至此,platform_driver_register()   例程完成調用。由於 platform_device   s3c_device_rtc是在
系統初始化的時候註冊的,所以driver能夠找到匹配的device,併產生一系列的文件夾和文
件,可看附圖。
作個小結,從上面的分析可以看出,sys文件系統中devices、bus、class和dev目錄裏的內
容之間的關聯是通過調用device_register()、driver_register()和init_module()例程來完成的。很顯
然,linux設備模型就這樣建立起來了。
附圖:
注:
1. 其中黑色字體的橢圓形表示是個文件夾;
2. 其中青色字體的橢圓形表示是個鏈接文件;
3. 用箭頭表示文件夾之間的隸屬關係和鏈接文件與文件夾之間的鏈接關係。
發佈了16 篇原創文章 · 獲贊 1 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章