1. 程式人生 > >Linux匯流排、裝置、驅動模型

Linux匯流排、裝置、驅動模型

1. 分配一個platform_device結構並呼叫platform_device_register函式進行註冊

int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);		// 初始化platform_device的device成員
	arch_setup_pdev_archdata(pdev);
	return platform_device_add(pdev);		// 向核心新增一個平臺裝置
}

2. 呼叫platform_device_add函式向核心新增一個平臺裝置

int platform_device_add(struct platform_device *pdev)
{
	int i, ret;

	if (!pdev)
		return -EINVAL;

	if (!pdev->dev.parent)	//如果pdev->dev.parent為空則將pdev->dev.parent設定為platform_bus
		pdev->dev.parent = &platform_bus;

	pdev->dev.bus = &platform_bus_type;		// 設定匯流排型別為platform_bus_type

	switch (pdev->id) {				// 分配名字
	default:
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
		break;
	case PLATFORM_DEVID_NONE:
		dev_set_name(&pdev->dev, "%s", pdev->name);
		break;
	case PLATFORM_DEVID_AUTO:
		/*
		 * Automatically allocated device ID. We mark it as such so
		 * that we remember it must be freed, and we append a suffix
		 * to avoid namespace collision with explicit IDs.
		 */
		ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
		if (ret < 0)
			goto err_out;
		pdev->id = ret;
		pdev->id_auto = true;
		dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
		break;
	}

	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)) {
			dev_err(&pdev->dev, "failed to claim resource %d\n", i);
			ret = -EBUSY;
			goto failed;
		}
	}

	pr_debug("Registering platform device '%s'. Parent at %s\n",
		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));

	ret = device_add(&pdev->dev);		// 向核心新增一個device
	if (ret == 0)
		return ret;

 failed:
	if (pdev->id_auto) {
		ida_simple_remove(&platform_devid_ida, pdev->id);
		pdev->id = PLATFORM_DEVID_AUTO;
	}

	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);
	}

 err_out:
	return ret;
}

3. 呼叫device_add函式向核心新增一個device

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);
		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);

	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)
		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);
	if (error)
		goto Error;

	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);

	error = device_create_file(dev, &dev_attr_uevent);
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &dev_attr_dev);
		if (error)
			goto ueventattrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;

		devtmpfs_create_node(dev);
	}

	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	device_pm_add(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);				// 為總線上的裝置尋找驅動
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	if (dev->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);
	}
done:
	put_device(dev);
	return error;
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	if (MAJOR(dev->devt))
		devtmpfs_delete_node(dev);
	if (MAJOR(dev->devt))
		device_remove_sys_dev_entry(dev);
 devtattrError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &dev_attr_dev);
 ueventattrError:
	device_remove_file(dev, &dev_attr_uevent);
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	kobject_del(&dev->kobj);
 Error:
	cleanup_device_parent(dev);
	if (parent)
		put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}

4. 呼叫bus_probe_device函式為總線上的裝置尋找驅動

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) {
		ret = device_attach(dev);		// 呼叫device_attach()進行實際的尋找
		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);
}
int device_attach(struct device *dev)
{
	int ret = 0;

	device_lock(dev);
	if (dev->driver) {
		if (klist_node_attached(&dev->p->knode_driver)) {
			ret = 1;
			goto out_unlock;
		}
		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);	// 遍歷bus的drv連結串列為裝置尋找驅動
		pm_request_idle(dev);
	}
out_unlock:
	device_unlock(dev);
	return ret;
}

(1) 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)	// 遍歷整個drv連結串列
		error = fn(drv, data);		// 尋找該裝置匹配的驅動程式,若匹配則將二者繫結
	klist_iter_exit(&i);
	return error;
}

(2)__device_attach函式原始碼

static int __device_attach(struct device_driver *drv, void *data)
{
	struct device *dev = data;

	if (!driver_match_device(drv, dev))		// 呼叫bus的match函式對裝置和驅動進行匹配,若不匹配driver_match_device函式的返回值為1,則程式立即返回,若匹配則繼續向下執行
		return 0;	

	return driver_probe_device(drv, dev);	// 若裝置和驅動匹配,則將該驅動程式繫結到該裝置
}
static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

5. 呼叫driver_probe_device將裝置與驅動程式進行繫結(呼叫probe函式)

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);			// 呼叫probe
	pm_request_idle(dev);

	return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;
	int local_trigger_count = atomic_read(&deferred_trigger_count);

	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;

	/* If using pinctrl, bind pins now before probing */
	ret = pinctrl_bind_pins(dev);
	if (ret)
		goto probe_failed;

	if (driver_sysfs_add(dev)) {
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}

	if (dev->bus->probe) {					// 如果裝置本身含有probe函式,那麼久呼叫裝置的probe函式
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {					// 如果裝置不含有probe函式,那麼就呼叫驅動程式的probe函式
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

	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;
	dev_set_drvdata(dev, NULL);

	if (ret == -EPROBE_DEFER) {
		/* Driver requested deferred probing */
		dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
		driver_deferred_probe_add(dev);
		/* Did a trigger occur while probing? Need to re-trigger if yes */
		if (local_trigger_count != atomic_read(&deferred_trigger_count))
			driver_deferred_probe_trigger();
	} else 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);
	} else {
		pr_debug("%s: probe of %s rejects match %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;
}
到此Linux核心的匯流排裝置驅動模型分析完畢。從上面的分析過程可以看出,所謂的platform_device並不是與字元裝置、塊裝置和網路裝置並列的概念,而是Linux系統提供的一種附加手段。

相關推薦

linux裝置驅動模型匯流排裝置驅動三者的關係

匯流排、裝置、驅動,也就是bus、device、driver,在核心裡都有對應的結構體,在include/linux/device.h 裡定義。Device.h (linux-3.4.2\inclu

Linux匯流排裝置驅動模型

1. 分配一個platform_device結構並呼叫platform_device_register函式進行註冊 int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->de

Linux裝置模型匯流排裝置驅動程式和類)之一:bus_type

      匯流排是處理器和一個或多個裝置之間的通道,在裝置模型中,所有的裝置都通過匯流排相連,甚至是內部的虛擬"platform"匯流排。可以通過ls -l /sys/bus看到系統載入的所有匯流排。drwxr-xr-x root     root             

Linux匯流排裝置驅動》自己寫的demo

本demo基於Android2.3模擬器開發,核心程式碼(Linux2.6.29)和Android程式碼可以在網上下載、這裡不在說明。 一、驅動 1.匯流排驅動 功能:匯流排驅動;提供設備註冊和裝置驅動註冊以及裝置與裝置驅動匹配等函式功能。 testbus.c #inc

【迅為iTop4412學習筆記】4. 匯流排裝置驅動,註冊流程

宣告 以下都是我剛開始看驅動視訊的個人強行解讀,如果有誤請指出,共同進步。 本節目標 瞭解Linux匯流排、裝置 驅動註冊的流程 基本知識 一般的裝置:接入裝置 -> 註冊裝置 -> 註冊

裝置驅動歸納總結(八):1.匯流排裝置驅動 —— 匯流排的註冊

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 這幾天一直在看裝置模型,核心的程式碼看得我越來越沮喪,特別是kboject、kset和ktype之間的關係。但是,裝置

NodeJS事件驅動模型

接觸nodejs有兩個月,對nodejs的兩大特性一直有點模糊,即非同步IO和事件驅動。通過對《深入淺出nodejs》和幾篇部落格的閱讀以後,有了大致的瞭解,總結一下。 幾個例子 在開始之前,先來看幾個簡單例子,這也是我在使用nodejs時候遇到的幾個比較困惑的例子。 example

核心裝置驅動檔案系統核心空間使用者空間

學習驅動過程中有一些疑問,記錄下來,並希望能在2015-6-30之前能夠有一個明確的答案。 疑問: 一、當裝置插上的時候,核心怎麼發現裝置並更新/sys、/dev、/proc檔案系統、怎麼通知給使用者空間?更具體一點是probe的過程 二、device和driver怎麼配對

linux驅動開發:匯流排裝置驅動三要素

平臺匯流排和IIC,SPI,IIS都是匯流排型別,一般的,匯流排下,掛載對應的裝置。但實際上,裝置要正常運轉,是需要驅動程式來未知提供驅動的。所以linux核心也把驅動掛載在對應的匯流排下。匯流排,驅動,裝置三者缺一不可. 相應的,核心衍生出來的平臺匯流排,那

裝置檔案裝置檔案節點 裝置 裝置驅動 區別

1.裝置Linux下的裝置通常分為三類,字元裝置,塊裝置和網路裝置。見的字元裝置有滑鼠、鍵盤、串列埠、控制檯等。常見的塊裝置有各種硬碟、flash磁碟、RAM磁碟等。在Linux裡一個網路裝置也可以叫做一個網路介面,如eth0,應用程式是通過Socket而不是裝置節點來訪問網

linux裝置驅動歸納總結(九):1.platform匯流排裝置驅動

linux裝置驅動歸納總結(九):1.platform匯流排的裝置和驅動 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 這一節可

【Tensorflow】Tensorflow的圖會話裝置變數核心

前言 基礎知識,前面我們介紹到,Tensorflow的資料流圖是由節點和邊組成的有向無環圖,此外,還涉及一些其他概念,如圖、會話、裝置、變數、核心等。 圖(Graph) import tensorflow as tf # 建立圖 # 建立一個常量運算操作,產生一個1 x 2

linux 下platform裝置驅動註冊的先後順序

我明白了,你說的是另一種情況,象USB 的hcd驅動一樣,先註冊驅動,後註冊裝置。 在註冊裝置時,遍歷裝置所在匯流排,找到與裝置匹配的驅動,再呼叫device_attach()--> driver_probe_device()時的情況。 這時,會呼叫裝置所在匯流排的probe()函式,如果沒有

[Swift通天遁地]五高階擴充套件-(9)顏色裝置UserDefaultsURL等擴充套件方法

本文將演示顏色、裝置、UserDefaults、URL等擴充套件方法。 首先確保在專案中已經安裝了所需的第三方庫。 點選【Podfile】,檢視安裝配置檔案。 1 platform :ios, '12.0' 2 use_frameworks! 3 4 target 'DemoApp' do

linux裝置驅動模型之Kobjectkobj_typekset

一、sysfs檔案系統簡介: 1、sysfs概述 sysfs檔案系統是核心物件(kobject)、屬性(kobj_type)、及它們相互關係的一種表現。 sysfs非常重要的特徵:使用者可以從sysfs中讀出核心資料,也可以將使用者資料寫入核心。 2、核心結構與sysfs對應關係:

Linux I2C核心匯流排裝置驅動

I2C匯流排僅僅使用SCL、 SDA這兩根訊號線就實現了裝置之間的資料互動,極大地簡化了對硬體資源和PCB板佈線空間的佔用。因此, I2C匯流排非常廣泛地應用在EEPROM、實時鐘、小型LCD等裝置與C

裝置驅動匯流排模型簡介

前言         在Linux系統中,有很多的框架提供驅動編寫者使用,比如前面我寫過的一篇文章:input子系統的架構分析及應用。都是將純軟體相關的程式碼和操作硬體相關的程式碼分離開,這樣就使得驅動的編寫者省去編寫大量的、重複的程式碼,只要專注於底層硬體相關的程式碼就可

Linux裝置驅動模型框架分析(三)——LDDM的實體bus_typedevice和device_driver

在Linux裝置模型中,Bus(匯流排)是一類特殊的裝置,它是連線處理器和其它裝置之間的通道(channel)。為了方便裝置模型的實現,核心規定,系統中的每個裝置都要連線在一個Bus上,這個Bus可以是一個內部Bus、虛擬Bus或者Platform Bus。 device

嵌入式Linux驅動筆記(十六)------裝置驅動模型(kobjectksetktype)

你好!這裡是風箏的部落格, 歡迎和我一起交流。 前幾天去面試,被問到Linux裝置驅動模型這個問題,沒答好,回來後惡補知識,找了些資料,希望下次能答出個滿意答案。 Linux早期時候,一個驅動對應一個裝置,也就對應一個硬體地址,那當有兩個一樣的裝置

Linux裝置匯流排驅動之間的關係

(一)、驅動、匯流排和裝置的主要資料結構 (include/linux/device.h) (/driver/base/base.h)   (include/device.h) 匯流排中的那兩條連結串列是怎麼形成的。核心要求每次出現一個裝置就要向匯流排彙報,或者