Linux總線驅動設備模型

本文將簡要地介紹Linux總線設備驅動模型及其實現方式,最後,本文將會以平臺總線爲例介紹設備和驅動程序的實現過程。

總線設備驅動模型總體介紹及其實現方式

1.1 總線設備驅動模型總體介紹

  如果cpu和外設或者外設和外設想要進行通訊,需要將通信的雙方掛載到一條總線上,這裏的總線可以是具體的總線,如IIC、CAN總線等,也可以是虛擬的總線,如平臺總線。隨着技術的不斷進步,系統的拓撲結構也越來越複雜,對熱插拔,跨平臺移植性的要求也越來越高,2.4內核已經難以滿足這些需求。爲適應這種形勢的需要,從Linux 2.6內核開始提供了全新的設備模型(當然也可以選擇不用這樣的方式實現)。其示意圖如下所示:

                                                                         

    總線設備驅動模型可分爲總線、設備和驅動三個部分,當將一個設備加入到總線上時,內核會在這條總線上尋找該設備對應的驅動;當將一個驅動加入到一條總線上時,內核會在該總線上尋找與該驅動對應的設備。匹配的規則根據不同類型的總線及設備特徵進行定義。

1.2 總線實現方式

(1):總線的描述

  在 Linux 內核中, 總線由 bus_type 結構表示,定義在 <linux/device.h>中

struct bus_type {
const char *name; /*總線名稱*/
int (*match) (struct device *dev, struct
device_driver *drv); /*驅動與設備的匹配函數*/
………
}

其中,match函數的定義爲:

int (*match)(struct device * dev, struct device_driver * drv)

設備和驅動的匹配規則在此函數中定義,match函數的職責爲:當一個新設備或者新驅動被添加到這個總線時,該函數被調用,用於判斷指定的驅動程序是否能處理指定的設備。若可以,則返回非零,若不可以則返回0值。

(2):總線的註冊和註銷

  總線的註冊使用如下函數

bus_register(struct bus_type *bus)

        若成功,新的總線將被添加進系統,並可在/sys/bus 下看到相應的目錄。

  總線的註銷使用:

void bus_unregister(struct bus_type *bus)

若成功,則總線將會從系統中刪除。

1.3 設備的實現方式

(1):設備的描述方式

  在 Linux內核中, 設備由struct device結構表示。 

struct device {
{
const char *init_name; /*設備的名字*/
struct bus_type *bus; /*設備所在的總線*/
………
}

其中,init_name爲設備的名稱,*bus指向總線的描述結構。

(2):設備的註冊和註銷

  設備的註冊使用如下函數:

int device_register(struct device *dev);

       當一個設備成功地添加到一條總線上時可以在/sys/bus/xxx_bus/目錄下看到相應的設備。

  設備的註銷使用如下函數:

void device_unregister(struct device *dev);

1.4 驅動實現方式

(1):驅動的描述方式

  在 Linux內核中, 驅動由 device_driver結構表示:

struct device_driver {
{
const char *name; /*驅動名稱*/
struct bus_type *bus; /*驅動程序所在的總線*/
int (*probe) (struct device *dev);/*設備匹配成功後調用的函數*/
………
}

其中,*name爲驅動的名稱,可以作爲一種設備和驅動匹配的條件;*bus指向所要加入的總線的描述結構;*probe函數指針指向一個操作函數,該函數會在設備和驅動匹配成功之後(即match函數返回爲1時)被調用,一般用來完成對設備的初始化操作。

(2):驅動的註冊和註銷

  驅動的註冊使用如下函數

int driver_register(struct device_driver *drv);

      驅動的註銷使用:

void driver_unregister(struct device_driver *drv);

2 平臺設備驅動舉例

上面介紹了總線驅動的相關數據結構,下面以平臺設備驅動爲例進一步分析。有了上面的總線驅動的基本數據結構,linux其他總線驅動都是對上面數據結構的再封裝。

下面先列舉一個簡單按鍵驅動:

2.1 device 註冊

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>

MODULE_LICENSE("GPL");
#define GPFCON 0x56000050
//所用的資源描述
static struct resource key_resource[] = {
    [0] = {
        .start = GPFCON,
        .end   = GPFCON + 8,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_EINT0,
        .end   = IRQ_EINT2,
        .flags = IORESOURCE_IRQ,
    },
};
struct platform_device key_device = {
    .name          = "my-key",
    .id          = 0,
    .num_resources      = ARRAY_SIZE(key_resource),
    .resource      = key_resource,
};
static int button_init()
{
    platform_device_register(&key_device);
     
    return 0;
}
static void button_exit()
{       
    platform_device_unregister(&key_device);
}
module_init(button_init);
module_exit(button_exit);

2.2 driver註冊

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>

MODULE_LICENSE("GPL");

struct work_struct *work;

struct timer_list buttons_timer;

unsigned int key_num = 0;

wait_queue_head_t  key_q;

struct resource *res;
struct resource *res_irq;
unsigned int *key_base;
void work_func(struct work_struct *work)
{
    mod_timer(&buttons_timer, jiffies + (HZ /10));     
}
void buttons_timer_function(unsigned long data)  
{
    unsigned int key_val;
    
    key_val = readw(key_base+1)&0x1; 
    if (key_val == 0)
       key_num = 4;
    
    key_val = readw(key_base+1)&0x4;
    if (key_val == 0)
        key_num = 3;
    
    wake_up(&key_q); 
} 
irqreturn_t key_int(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    //2. 清除已經發生的按鍵中斷
    
    //3. 提交下半部
    schedule_work(work);
  
    //return 0;
    return IRQ_HANDLED;
        
}
void key_hw_init()
{
    unsigned short data;
        
    data = readw(key_base);
    data &= ~0b110011;
    data |= 0b100010;

    writew(data,key_base);
}
int key_open(struct inode *node,struct file *filp)
{
    return 0;    
}
ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{ 
    wait_event(key_q,key_num);
        
    copy_to_user(buf, &key_num, 4);
    
    key_num = 0;
  
    return 4;
}
struct file_operations key_fops = 
{
    .open = key_open,
    .read = key_read,    
};
struct miscdevice key_miscdev = {
    .minor = 200,
    .name = "key",
    .fops = &key_fops,    
};
int key_probe(struct platform_device *pdev)
{
    int ret,size;
    
    
    ret = misc_register(&key_miscdev);
    
    if (ret !=0)
        printk("register fail!\n");
    
    //註冊中斷處理程序
    
    res_irq =  platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    
    request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
    request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);
    
    //按鍵初始化
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    size = (res->end - res->start) + 1;
    key_base = ioremap(res->start, size);
    
    key_hw_init();
    
    //. 創建工作
    work = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work, work_func);
    
    /* 初始化定時器 */  
    init_timer(&buttons_timer);   
    buttons_timer.function  = buttons_timer_function;  
    
    /* 向內核註冊一個定時器 */  
    add_timer(&buttons_timer);  
    
    /*初始化等待隊列*/
    init_waitqueue_head(&key_q);
    
    return 0;
}
int key_remove(struct platform_device *dev)
{
    free_irq(res_irq->start, (void *)4);
    free_irq(res_irq->end, (void *)3);

    iounmap(key_base);
    misc_deregister(&key_miscdev);
    return 0;
}
static struct platform_driver key_driver = {
    .probe        = key_probe,
    .remove        = key_remove,
    .driver        = {
        .owner    = THIS_MODULE,
        .name    = "my-key",
    },
};
static int button_init()
{
    return platform_driver_register(&key_driver);
}
static void button_exit()
{       
    platform_driver_unregister(&key_driver);
}
module_init(button_init);
module_exit(button_exit);

3 平臺設備驅動詳解

3.1 平臺總線初始化

系統初始化的時候,會初始化平臺總線:

kernel_init

     ----------->kernel_init_freeable

          -------------->do_basic_setup

                  ---------------->driver_init

                           ------------------>platform_bus_init

struct device platform_bus = {
	.init_name	= "platform",
};
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();
-------------------------------------------------------------(1)
	error = device_register(&platform_bus);//平臺總線也被抽象成一個設備,所以註冊平臺總線設備
	if (error)
		return error;
-------------------------------------------------------------(2)
	error =  bus_register(&platform_bus_type);//註冊平臺總線
	if (error)
		device_unregister(&platform_bus);
	return error;
}

(1)平臺總線也被抽象成一個設備,device_register的詳細說明,可以參考這篇文章:

https://blog.csdn.net/oqqYuJi12345678/article/details/103057226

該函數會在/sys/devices/下面註冊platform文件夾,後面註冊的平臺設備會放到該platform文件夾下

(2)向內核總線系統註冊平臺總線

內核通過 bus_register 進行 Bus  註冊,註冊到了 bus_kest

系統初始化的時候有如下代碼:

int __init buses_init(void)
{
	// /sys/bus 目錄 這裏創建的
	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
	if (!bus_kset)
		return -ENOMEM;
	return 0;
}

下面看一下bus_register具體做了什麼:

int bus_register(struct bus_type *bus)
{
	int retval;
	struct bus_type_private *priv;
 
	priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
/* 1. bus 與 prv 相互建立聯繫 */
	// 私有數據 .bus ->  bus 本身
	priv->bus = bus;
	// bus->p 指向 priv
	bus->p = priv;
	// 內核通知鏈
	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
 
/* 設置 bus->prv->subsys->kobj */
	// 設置 priv->subsys.kobj.name = bus->name  對應於/sys/ 目錄下的目錄名
	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
	// 所有的 priv->subsys.kobj.kset 指向 bus_kse 對應於圖中④與六的關係
	priv->subsys.kobj.kset = bus_kset;
	// 所有的priv->subsys.kobj.ktype 等於 bus_ktype
	priv->subsys.kobj.ktype = &bus_ktype;
	
	priv->drivers_autoprobe = 1;//device和driver註冊的時候會檢查該位
	
/* 註冊 kset (bus->prv->subsys priv->devices_kset priv->drivers_kset) */	
	// 註冊 priv->subsys ,由於 priv->subsys.kobj.kset = bus_kset,所以會在 /sys/bus/目錄下創建 目錄 如/sys/bus/platform
	retval = kset_register(&priv->subsys);
	// sysfs_create_file(&bus->p->subsys.kobj, &bus_attr_uevent->attr);
	retval = bus_create_file(bus, &bus_attr_uevent);
 
	// 由於 priv->subsys.kobj.kset = bus_kset ,因此會創建 /sys/bus/XXX/devices 目錄 如 /sys/bus/platform/devices
	priv->devices_kset = kset_create_and_add("devices", NULL,
						 &priv->subsys.kobj);
	// 同理 創建 /sys/bus/XXX/devices 目錄 如 /sys/bus/platform/drivers
	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);
	// 初始化 klist_devices 並設置get put 函數  初始化 klist_drivers 不知爲何沒有get put ?
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&priv->klist_drivers, NULL, NULL);
 
	retval = add_probe_files(bus);  // static inline int add_probe_files(struct bus_type *bus) { return 0; }
	// 添加 bus->attrs 屬性文件
	retval = bus_add_attrs(bus);
 
	return 0;
 
}

上面函數主要是在/sys/bus/下面註冊了platform目錄,然後在/sys/bus/platform/目錄下面創建devices和drivers目錄,以及其他的一些屬性文件。

看一下/sys/bus/platform/具體有哪些文件和目錄:

                                          

3.2 平臺設備註冊

上面的平臺設備驅動調用了platform_device_register函數註冊device:

int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);
	arch_setup_pdev_archdata(pdev);
	return platform_device_add(pdev);
}

platform_device_register函數先通過device_initialize函數初始化platform_device的device成員,然後調用platform_device_add向內核添加一個平臺設備。

int platform_device_add(struct platform_device *pdev)
{
	int i, ret = 0;
 
	if (!pdev)	/* 如果pdev爲空則返回EINVAL */
		return -EINVAL;
 
	/* 如果pdev->dev.parent爲空則將pdev->dev.parent設置爲platform_bus */
	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;
 
	pdev->dev.bus = &platform_bus_type;	/* 設置總線類型 */
 
	if (pdev->id != -1)		/* 如果id = -1則表示自動分配name */
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
	else
		dev_set_name(&pdev->dev, pdev->name);
 
	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];	/* 獲取資源 */
 
		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);
 
		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM) /* 設置資源類型 */
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}
 
		if (p && insert_resource(p, r)) {
			printk(KERN_ERR
			       "%s: failed to claim resource %d\n",
			       dev_name(&pdev->dev), i);
			ret = -EBUSY;
			goto failed;
		}
	}
 
	pr_debug("Registering platform device '%s'. Parent at %s\n",
		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));
 
	/* 向內核添加一個device */
	ret = device_add(&pdev->dev);
	if (ret == 0)
		return ret;
 
 failed:
	while (--i >= 0) {
		struct resource *r = &pdev->resource[i];
		unsigned long type = resource_type(r);
 
		if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
			release_resource(r);
	}
 
	return ret;
}

可以看到platform_device_register函數就是對device_add的封裝。已經討論過幾回了,現在重點關注device_add中的幾個部分:

int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;
..........................................................
	//在上面函數中dev->parent被設置爲platform_bus,所以後面會在/sys/devices/platform/下面創建設備文件
	parent = get_device(dev->parent);
	kobj = get_device_parent(dev, parent);
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent)
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
//在/sys/devices/platform/下面創建設備文件
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;

..............................................................
--------------------------------------------------------(1)
	error = bus_add_device(dev);
.................................................
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);

	kobject_uevent(&dev->kobj, KOBJ_ADD);
-----------------------------------------------------------(2)
	bus_probe_device(dev);
	if (parent)//添加到bus的鏈表上
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}

(1)bus_add_device的最重要工作是把device添加到bus的鏈表中:

int bus_add_device(struct device *dev)
{
	struct bus_type *bus = bus_get(dev->bus);
	int error = 0;

	if (bus) {
		pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
		error = device_add_attrs(bus, dev);
		if (error)
			goto out_put;
		error = sysfs_create_link(&bus->p->devices_kset->kobj,
						&dev->kobj, dev_name(dev));
		if (error)
			goto out_id;
		error = sysfs_create_link(&dev->kobj,
				&dev->bus->p->subsys.kobj, "subsystem");
		if (error)
			goto out_subsys;
		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);//把device添加到bus鏈表中
	}
	return 0;

out_subsys:
	sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_id:
	device_remove_attrs(bus, dev);
out_put:
	bus_put(dev->bus);
	return error;
}

(2)bus_probe_device遍歷bus上面的driver鏈表,看看是否有匹配的drvier要probe:

void bus_probe_device(struct device *dev)
{
	struct bus_type *bus = dev->bus;
	struct subsys_interface *sif;
	int ret;

	if (!bus)
		return;

	if (bus->p->drivers_autoprobe) { //bus在之前註冊時候設置了drivers_autoprobe
		ret = device_attach(dev);//執行該函數
		WARN_ON(ret < 0);
	}

	mutex_lock(&bus->p->mutex);
	list_for_each_entry(sif, &bus->p->interfaces, node)
		if (sif->add_dev)
			sif->add_dev(dev, sif);
	mutex_unlock(&bus->p->mutex);
}

調用device_attach進行匹配:

int device_attach(struct device *dev)
{
	int ret = 0;

	device_lock(dev);
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
		pm_request_idle(dev);
	
out_unlock:
	device_unlock(dev);
	return ret;
}

主要函數爲bus_for_each_drv:

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
		     void *data, int (*fn)(struct device_driver *, void *))
{
	struct klist_iter i;
	struct device_driver *drv;
	int error = 0;

	if (!bus)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_drivers, &i,
			     start ? &start->p->knode_bus : NULL);
	while ((drv = next_driver(&i)) && !error)//遍歷bus->p->klist_drivers鏈表,調用__device_attach進行匹配
		error = fn(drv, data);
	klist_iter_exit(&i);
	return error;
}

依次調用bus上面掛載的driver,利用__device_attach函數進行匹配:

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
static int __device_attach(struct device_driver *drv, void *data)
{
	struct device *dev = data;

	if (!driver_match_device(drv, dev))
		return 0;

	return driver_probe_device(drv, dev);
}

匹配成功以後,driver_match_device函數返回不爲0,進一步調用driver_probe_device函數,進行driver probe。

從前面知道platform總線的match函數爲platform_match:

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

可以看到較新版的內核除了匹配設備和驅動名字,還有其他多種匹配手段了。再看一下driver_probe_device函數:

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;
//device已經註冊過,則不再probe
	if (!device_is_registered(dev))
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	pm_runtime_barrier(dev);
	ret = really_probe(dev, drv);
	pm_request_idle(dev);

	return ret;
}

static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;
    dev->driver = drv;  //device 指向driver
。。。。。。。。。。。。。。。。。。。

		ret = drv->probe(dev);//調用driver的probe函數
。。。。。。。。。。。。。。。。。。。。

	driver_bound(dev);//driver和device綁定
	ret = 1;


}
static void driver_bound(struct device *dev)
{
。。。。。。。。。。。。。。。。。。。。
//把device 掛入driver鏈表,一個driver可以匹配多個device
	klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

	/*
	 * Make sure the device is no longer in one of the deferred lists and
	 * kick off retrying all pending devices
	 */
	driver_deferred_probe_del(dev);
	driver_deferred_probe_trigger();

	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_BOUND_DRIVER, dev);
}

可以看到最後會調用driver的probe函數,並且把device掛入driver的鏈表中。一個driver可以對應多個device。需要注意的是,上面really_probe裏面調用的probe函數還不是真正平臺驅動的probe函數,他是平臺設備驅動框架提供的probe函數platform_drv_probe:

static int platform_drv_probe(struct device *_dev)
{
	struct platform_driver *drv = to_platform_driver(_dev->driver);//獲取平臺驅動結構
	struct platform_device *dev = to_platform_device(_dev);
	int ret;

	if (ACPI_HANDLE(_dev))
		acpi_dev_pm_attach(_dev, true);

	ret = drv->probe(dev);//調用平臺驅動的probe
	if (ret && ACPI_HANDLE(_dev))
		acpi_dev_pm_detach(_dev, true);

	return ret;
}

3.4 平臺驅動註冊

上面的例子中調用platform_driver_register來向內核註冊平臺驅動:

int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;
	if (drv->probe)//platform_driver 設置了probe函數,則device_driver需要設置platform_drv_probe,匹配的時候先會進入通用驅動框架,調用device_driver的probe
		drv->driver.probe = platform_drv_probe;
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

driver_register的核心函數爲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);
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
	// 在/sys/bus/platform/drivers 目錄下創建目錄
	priv->kobj.kset = bus->p->drivers_kset;
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	// 匹配 dev
	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
	}
	// 將driver 加入 Bus->p->kist_drivers鏈表
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	// 如果設置了drv->mod_name 根據名字尋找模塊
	module_add_driver(drv->owner, drv);
	// 在/sys/bus/platform/drivers/創建屬性文件
	error = driver_create_file(drv, &driver_attr_uevent);
	error = driver_add_attrs(bus, drv);
	if (!drv->suppress_bind_attrs) {
		error = add_bind_files(drv);
	}
	kobject_uevent(&priv->kobj, KOBJ_ADD);
	return 0;
}

在driver註冊時候會在總線上搜索匹配的device來進行配對:

int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
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 || !bus->p)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)) && !error)
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}
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 (!driver_match_device(drv, dev))
		return 0;

	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver)
		driver_probe_device(drv, dev);
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

	return 0;
}
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

	if (!device_is_registered(dev))
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	pm_runtime_barrier(dev);
	ret = really_probe(dev, drv);
	pm_request_idle(dev);

	return ret;
}

可以看到和device匹配driver的過程是類似的,不再展開細說了。

該文有些內容整理自:https://www.cnblogs.com/blackeyes/p/5066948.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章