天天看點

驅動裝置模型---sys檔案系統

linux混混之牢騷:

 同僚小李要移民到美國,上司問他:“你對你的工資不滿意嗎?”

 小李說:“滿意。”

“對你的住房不滿意?”

“滿意”

“那是上網環境不滿意?”

“也滿意”

“對醫療,孩子上學都不滿意?”

“都滿意!”

“既然你都滿意為什麼還要移民?”

“因為那裡允許有不滿意!”

linux version:2.6.39

什麼是sys檔案系統:

Sysfs檔案系統是一個類似于proc檔案系統的特殊檔案系統,用于将系統中的裝置組織成層次結構,并向使用者模式程式提供詳細的核心資料結構資訊。

其實,就是 在使用者态可以通過對sys檔案系統的通路,來看核心态的一些驅動或者裝置等。

去/sys看一看,

localhost:/sys#ls

/sys/ block/ bus/ class/ devices/ firmware/ kernel/ module/ power/

Block目錄:包含所有的塊裝置,進入到block目錄下,會發現下面全是link檔案,link到sys/device/目錄下的一些裝置。

Devices目錄:包含系統所有的裝置,并根據裝置挂接的總線類型組織成層次結構

Bus目錄:包含系統中所有的總線類型

Drivers目錄:包括核心中所有已注冊的裝置驅動程式

Class目錄:系統中的裝置類型(如網卡裝置,聲霸卡裝置等)。去class目錄中看一下,随便進到一個檔案夾下,會發現該檔案夾下的檔案其實是連接配接檔案,link到/sys/device/.../../...下的一個裝置檔案。 可以說明,其實class目錄并不會建立什麼裝置,隻是将已經注冊的裝置,在class目錄下重新歸類,放在一起。

但是,你可能根本沒有去關心過sysfs的挂載過程,她是這樣被挂載的。

mount -t sysfs sysfs /sys

但是sys檔案是根據什麼依據來建立其内容呢?他的資訊來源是什麼呢?

下面來分析sys的資訊來源。

Linus裝置底層模型

Kobject

應該說每個Kobject結構都對應一個 目錄。for example:/sys/bus/pci/drivers/serial/ 路徑, serial這個目錄就是由一個kobject 結構體 來表示的。由此可見,Kobject是用來表示 直接對應着一個 裝置,或裝置驅動  的目錄。Kobject包含了 這個目錄的一些資訊,如:目錄名,父目錄,裝置名稱等等一些資訊。當然,如果Kobject用來表示一個目錄,那麼他所包含的資訊是差不多了,但是Kobject表示的目錄是用來描述某一個裝置/裝置驅動 的。是以僅僅Kobject這個結構體還不能完全的描述這個裝置/裝置驅動,再是以,Kobject這個結構體不會單獨使用,一般都會包含在另一個結構體中,用網絡上的話說就是包含在一個容器中。這個容器可以是:device結構體,device_drive結構體。現在層次就很明顯了,device/device_drive來表示一個裝置/裝置驅動,當然包含了這個裝置/裝置驅動的資訊,并且還包含了這個驅動所對應的目錄的資訊,Kobject結構。

當然device/device_drive在另外一層的東西了,後面再分析。我們在這裡就先分析Kobject結構。

struct kobject {
	const char		*name;  //目錄的name
	struct list_head	entry;           //Kobject插入到某個連結清單的指針。
	struct kobject		*parent;  //父目錄,剛才所述kobject所表示的是裝置/裝置驅動目錄,但為什麼他的父目錄也用kobject來表示呢?後面講解。
	struct kset		*kset;    //kobject上上級目錄可能是Kset,這個表示。 這個變量和parent有些相似的地方。 可以從kset_register函數中看出些端倪。
	struct kobj_type	*ktype;
	struct sysfs_dirent	*sd;
	struct kref		kref;   //被引用的次數
	unsigned int state_initialized:1;
	unsigned int state_in_sysfs:1;
	unsigned int state_add_uevent_sent:1;
	unsigned int state_remove_uevent_sent:1;
	unsigned int uevent_suppress:1;
};
           

注意:在kenerl中,如kref,前面講到的 page_reference變量。 都用來表示被引用。 是以 以後看變量的時候要注意看 ref或reference,來表示被引用。

相關函數

void kobject_init(struct kobject * kobj);kobject初始化函數。

int kobject_set_name(struct kobject *kobj, const char *format, ...);設定指定kobject的名稱。

struct kobject *kobject_get(struct kobject *kobj);将kobj 對象的引用計數加1,同時傳回該對象的指針。

void kobject_put(struct kobject * kobj); 将kobj對象的引用計數減1,如果引用計數降為0,則調用kobject release()釋放該kobject對象。

int kobject_add(struct kobject * kobj);将kobj對象加入Linux裝置層次。挂接該kobject對象到kset的list鍊中,增加父目錄各級kobject的引用計數,在其parent指向的目錄下建立檔案節點,并啟動該類型核心對象的hotplug函數。

int kobject_register(struct kobject * kobj);kobject注冊函數。通過調用kobject init()初始化kobj,再調用kobject_add()完成該核心對象的注冊。

void kobject_del(struct kobject * kobj);從Linux裝置層次(hierarchy)中删除kobj對象。

void kobject_unregister(struct kobject * kobj);kobject登出函數。與kobject register()相反,它首先調用kobject del從裝置層次中删除該對象,再調用kobject put()減少該對象的引用計數,如果引用計數降為0,則釋放kobject對象。

kobject下的結構體描述:

struct kobj_type

{

 void (*release)(struct kobject *);

struct sysfs_ops * sysfs_ops;

struct attribute ** default_attrs;

 };

Kobj type資料結構包含三個域:一個release方法用于釋放kobject占用的資源;一個sysfs ops指針指向sysfs操作表和一個sysfs檔案系統預設屬性清單。

Sysfs操作表包括兩個函數store()和show()。當使用者态讀取屬性時,show()函數被調用,該函數編碼指定屬性值存入buffer中傳回給使用者态;而store()函數用于存儲使用者态傳入的屬性值。

attribute struct attribute

 {

char * name;

struct module * owner;

mode_t mode;

};

attribute屬性。它以檔案的形式輸出到sysfs的目錄當中。在kobject對應的目錄下面。檔案 名就是name。檔案讀寫的方法對應于kobj type中的sysfs ops。

Kset

像剛才所說,每個Kobject結構都對應一個 目錄。for example:/sys/bus/pci/drivers/serial/ 路徑, /serial/這個目錄由一個kobject 結構體 來表示的。但是/serial/的上一級目錄/drivers/如何表示呢?那麼就出現了Kset這個結構體。

/**
 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
 *
 * A kset defines a group of kobjects.  They can be individually
 * different "types" but overall these kobjects all want to be grouped
 * together and operated on in the same manner.  ksets are used to
 * define the attribute callbacks and other common events that happen to
 * a kobject.
 *
 * @list: the list of all kobjects for this kset
 * @list_lock: a lock for iterating over the kobjects
 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
 * @uevent_ops: the set of uevent operations for this kset.  These are
 * called whenever a kobject has something happen to it so that the kset
 * can add new environment variables, or filter out the uevents if so
 * desired.
 */
struct kset {
	struct list_head list;  //由于Kset下會有很多個Kobject的目錄,是以使用一個list将他們全部link起來。
	spinlock_t list_lock;   //鎖機制
	struct kobject kobj;    //Kest本質上來說,也是個目錄,是以他也使用了Kobject,來表示他自己的這個目錄
	struct kset_uevent_ops *uevent_ops;    //由于Kset是将很多的有公共特性的Kobject集中到一起,是以這個變量操作,在他的目錄下的一些共性操作。
};
           

subsystem

在以前的版本中,還有subsystem結構,但 是在現在的版本中都已經去掉了,用Kset來代替

struct subsystem {
struct kset kset;

struct rw semaphore rwsem; 

};

           

由上面聲明可以看出,完全可以讓Kset來代替subsystem結構。

總結:

1,在sys下,表示一個目錄使用的結構體是 Kobject,但是在linux的核心中,有硬體的裝置 和 軟體的驅動,在sys下都需要用一個目錄來表示。 單純的一個Kobject結構無法表示完全,增加了容器,來封裝Kobject。 即下面要将的:device和drive_device結構。

2, 最底層驅動目錄的上一層目錄,從sys角度上來說,他依然是個目錄,是以他也有Kobjec這個變量。但是從他的意義上講,他将 一些有公共特性Kobjec  的 device/driver_device結構組織到了一起,是以除了有Kobject這個變量外,他又添加了一些變量,組成了Kset這個結構來表示這一級的目錄。但是僅僅是用Kset來表示了這一級的目錄,和1,一樣,僅僅表示一個目錄是不夠的,在linux核心中,需要他在軟體上有個映射。是以,也将Kset進行了封裝,形成了  bus_type這個結構。

3, 從1 ,2,的解釋可以看出,應為kobject在Kset的目錄下,那麼 device/device_driver 就在 bus_type結構下。是以,linux驅動模型中,驅動和裝置都是挂在總線下面的。

4, 如上所述,Kset的意義:表示一個目錄(由結構體下的Kobject來完成),并且這個目錄下的所有目錄有共同的特性(是以說,Kset表示的目錄下,不一定非要是Kobject街頭的,也可以是Kset結構的。即:Kset嵌套Kset)。是以使用Kset來代替了以前的 subsystem結構。

貼兩張圖來形象了解一下:

1, Kset和Kobject的連接配接圖(from linux那些事之我是sys)

驅動裝置模型---sys檔案系統

2,整個sys目錄的結構體表示圖:(from ULK--當然,在這裡subsystem結構要換成Kset了,但我個人認為,以前的subsystem結構上會更清晰,不是嗎?)

(但這邊有個問題。。。Kobject通過下面的attribute來建立目錄下的檔案,但我看到目錄下有好幾個檔案,難道是根據一個attribute來建立好幾個檔案?疑惑ing,好像attribute是個指針,還能當數組首位址?bus_add_attrs函數中如是說)

驅動裝置模型---sys檔案系統

裝置模型的上層容器

剛才講了Kset和Kobject結構體,都是用來表示 sys下的目錄結構的。下面來講驅動中封裝這些結構的容器。

總線bus

bus_type結構: 剛才上面已經将的夠多的了,閑話少說,直接上code。

struct bus_type {
	const char		*name;       //總線的名稱,這個名字理論上并不是sys/bus/下的那些目錄的目錄名。那些目錄的目錄名應該是在下面變量  subsys_private p.sbusys的name變量中。但是往往那個name是由這個name指派的,是以就一樣的。但這裡要明白的是(還是上面的老生常談),表示目錄是由Kset.Kobject這個東西來表示的。
	struct bus_attribute	*bus_attrs;  //根據後面的bus_add_attrs函數分析,這些個屬性可能是數組
	struct device_attribute	*dev_attrs;
	struct driver_attribute	*drv_attrs;  //bus device driver的屬性,一些操作導入導出的屬性,等後面再分析。

	int (*match)(struct device *dev, struct device_driver *drv);
	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 (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);       //總線的操作

	const struct dev_pm_ops *pm;           //power manage 的operations

	struct subsys_private *p;  見下面:
};
           
struct subsys_private {  //為了保持和上面的代碼的連貫,我将這個結構體的注釋部分放到下面了。注釋還是比較清楚的,不解釋
 struct kset subsys;      
 struct kset *devices_kset;


 struct kset *drivers_kset;
 struct klist klist_devices;
 struct klist klist_drivers;
 struct blocking_notifier_head bus_notifier;
 unsigned int drivers_autoprobe:1;
 struct bus_type *bus;


 struct list_head class_interfaces;
 struct kset glue_dirs;
 struct mutex class_mutex;
 struct class *class;
};


/**
 * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
 *
 * @subsys - the struct kset that defines this subsystem
 * @devices_kset - the list of devices associated
 *
 * @drivers_kset - the list of drivers associated
 * @klist_devices - the klist to iterate over the @devices_kset
 * @klist_drivers - the klist to iterate over the @drivers_kset
 * @bus_notifier - the bus notifier list for anything that cares about things
 *                 on this bus.
 * @bus - pointer back to the struct bus_type that this structure is associated
 *        with.
 *
 * @class_interfaces - list of class_interfaces associated
 * @glue_dirs - "glue" directory to put in-between the parent device to
 *              avoid namespace conflicts
 * @class_mutex - mutex to protect the children, devices, and interfaces lists.
 * @class - pointer back to the struct class that this structure is associated
 *          with.
 *
 * This structure is the one that is the actual kobject allowing struct
 * bus_type/class to be statically allocated safely.  Nothing outside of the
 * driver core should ever touch these fields.
 */

           

這個結構體用來描述比如:/sys/bus/pci pci總線,/sys/bus/platform platform總線等。

另外:從這個結構體分析下來,整個bus的目錄結構都很清楚了eg:

1,可以找到總線下的裝置目錄: bus_type bus ---> subsys_private p---->Kest devices_kset

2,可以找到總線下的裝置驅動目錄: bus_type bus ---> subsys_private p---->Kest driver_kset

另外,找到的也隻是目錄,因為找到的僅僅是Kset結構。

于此看來,這個subsys_private p這個變量挺有用的。 哥預言,此子日後必成大器。當然,條件是 日後!!哥邪惡的笑了。。。。。

裝置device

首先明白,device這個結構并不是直接挂在bus下的,可以到/sys/bus/platform/device下随便看一下,發現裡面的都是link檔案,link到/sys/device/下。是以真正的device結構體的在/sys/device下的。

struct device {
	struct device		*parent;  //裝置的父裝置指針,那麼就是說device的目錄也是可以嵌套的?到/sys/device/platform/serial8250目錄下看看,竟然還存在着 tty/ 目錄,是不是這樣嵌套的呢??天知道。。。。。

	struct device_private	*p;

	struct kobject kobj;        //這個就是說了好久的 Kobject
	const char		*init_name; /* initial name of the device */
	struct device_type	*type;

	struct mutex		mutex;	/* mutex 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;
	struct dev_power_domain	*pwr_domain;

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

	struct device_node	*of_node; /* associated device tree node */

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

裝置driver

struct device_driver {
	const char		*name;
	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 */

	const struct of_device_id	*of_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);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;
};
           

無語。。。。。。。。。。。。。

終于經過了一大段偷工減料之後,能開始分析代碼了

裝置模型的注冊等操作:

總線的操作:

使用者可以自己注冊一個總線,然後将自己喜歡的裝置和驅動挂載到下面。但是linux 2.6中,有個預設的總線,platform總線。我們就分析一下這個總線。

小記:随手在Source insight裡敲了個 platform_bus_init,結果的真的有這個函數,再看一下誰調用他了吧? 竟然是drive_init。啊。。終于找到組織了,在start_kernel的最後一步後調用這個drive_init了。

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();  //清除platform總線上的裝置?不确定,,,好像就是将early_platform_device_list這個裡的内容清空。

	error = device_register(&platform_bus);  //裝置注冊。哦,linux将platform也當成了一個裝置,他在/sys/device目錄下。當然,以後會在platform這個裝置下再建立其他的裝置,回顧剛才介紹device結構體時候有個parent變量,應該就是用在這裡的。具體device_register這個函數,後面再介紹
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);  //總線的注冊。
	if (error)
		device_unregister(&platform_bus);
	return error;
}
           
/**
 * bus_register - register a bus with the system.
 * @bus: bus.
 *
 * Once we have that, we registered the bus with the kobject
 * infrastructure, then register the children subsystems it has:
 * the devices and drivers that belong to the bus.
 */
int bus_register(struct bus_type *bus)
{
	int retval;
	struct subsys_private *priv;

	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);       //bus_notifier就是個讀寫信号量,和RCU機制,這裡進行初始化

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  //設定name,這個name會顯示在sys/bus/下
	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);               //這個應該是注冊bus,但看函數名是ket_register,是以可能會根據剛才對subsys.kobj.kset的指派來判定是bus,并注冊。後面分析。
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent);       //在對應的bus目錄下,根據attribute來建立一個檔案
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL,                  //這就函數應該是建立目錄,是以在每個bus下會有 device和driver 兩個目錄。
						 &priv->subsys.kobj);
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	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);       //klist還是沒搞明白怎麼用,以後再說吧

	retval = add_probe_files(bus);       //這個也是在對應的總線目錄下,建立bus_attr_drivers_probe 和 bus_attr_drivers_autoprobe檔案。應該是probe的時候使用。
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus);  //循環将所有的bus的屬性都建立成一個檔案。
	if (retval)
		goto bus_attrs_fail;

	pr_debug("bus: '%s': registered\n", bus->name);
	return 0;

bus_attrs_fail:
	remove_probe_files(bus);
bus_probe_files_fail:
	kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
	kset_unregister(bus->p->devices_kset);
bus_devices_fail:
	bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
	kset_unregister(&bus->p->subsys);
out:
	kfree(bus->p);
	bus->p = NULL;
	return retval;
}
           

bus_register-->kset_register

/**
 * kset_register - initialize and add a kset.
 * @k: kset.
 */
int kset_register(struct kset *k)
{
	int err;

	if (!k)
		return -EINVAL;

	kset_init(k);          //初始化,沒什麼東西
	err = kobject_add_internal(&k->kobj);  //下面分析
	if (err)
		return err;
	kobject_uevent(&k->kobj, KOBJ_ADD);    //通過這個函數的注釋可知,向usrspace發送信号。
	return 0;
}
           
static int kobject_add_internal(struct kobject *kobj)
{
 int error = 0;
 struct kobject *parent;


 if (!kobj)
  return -ENOENT;


 if (!kobj->name || !kobj->name[0]) {
  WARN(1, "kobject: (%p): attempted to be registered with empty "
    "name!\n", kobj);
  return -EINVAL;
 }


 parent = kobject_get(kobj->parent);


 /* join kset if set, use it as parent if we do not already have one */
 if (kobj->kset) {
  if (!parent)
   parent = kobject_get(&kobj->kset->kobj);   //get kobject->kset, 判斷與parent對比。      
obj_kset_join(kobj);         //這個函數,是将kobject的entry這個變量 添加到 他的 上一級的kset結構的 list中。
  kobj->parent = parent;
 }


 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
   kobject_name(kobj), kobj, __func__,
   parent ? kobject_name(parent) : "<NULL>",
   kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");


 error = create_dir(kobj);   //建立目錄。比如:/sys/bus 下的 platform, pci等目錄。
 if (error) {
  kobj_kset_leave(kobj);
  kobject_put(parent);
  kobj->parent = NULL;


  /* be noisy on error issues */
  if (error == -EEXIST)
   printk(KERN_ERR "%s failed for %s with "
          "-EEXIST, don't try to register things with "
          "the same name in the same directory.\n",
          __func__, kobject_name(kobj));
  else
   printk(KERN_ERR "%s failed for %s (%d)\n",
          __func__, kobject_name(kobj), error);
  dump_stack();
 } else
  kobj->state_in_sysfs = 1;


 return error;
}

           

到此,bus_register解釋完成。

/**
 * device_register - register a device with the system.
 * @dev: pointer to the device structure
 *
 * This happens in two clean steps - initialize the device
 * and add it to the system. The two steps can be called
 * separately, but this is the easiest and most common.
 * I.e. you should only call the two helpers separately if
 * have a clearly defined need to use and refcount the device
 * before it is added to the hierarchy.
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an error! Always use put_device() to give up the
 * reference initialized in this function instead.
 */
int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}
           
/**
 * device_initialize - init device structure.
 * @dev: device.
 *
 * This prepares the device for use by other layers by initializing
 * its fields.
 * It is the first half of device_register(), if called by
 * that function, though it can also be called separately, so one
 * may use @dev's fields. In particular, get_device()/put_device()
 * may be used for reference counting of @dev after calling this
 * function.
 *
 * NOTE: Use put_device() to give up your reference instead of freeing
 * @dev directly once you have called this function.
 */
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	mutex_init(&dev->mutex);
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_pm_init(dev);
	set_dev_node(dev, -1);
}
           

dev_initialize,不解釋。

這裡有個疑問:在bus_register的時候,有條語句:priv->subsys.kobj.kset = bus_kset;。在dev_initialize的時候也有條dev->kobj.kset = devices_kset;語句。 剛才以為是上級目錄的kset結構。但是如此看來好像不是很對,因為dev的上級目錄是不定的,可能在/sys/device/platform下,也可能在其他。但是都指派成devices_kset顯然不對。  那麼有可能在一個标志。所有的bus的subsys.kobj.kset 這個變量都是bus_kset, 所有dev->kobj.kset的變量都是devices_kset。具體為什麼?

天空中深沉的傳來一句話:1+1=幾?

我說:2

啪,一道雷劈死我了。答曰:你知道的太多了。  為了留條命,就不解釋了。

/**
 * device_add - add device to device hierarchy.
 * @dev: device.
 *
 * This is part 2 of device_register(), though may be called
 * separately _iff_ device_initialize() has been called separately.
 *
 * This adds @dev to the kobject hierarchy via kobject_add(), adds it
 * to the global and sibling lists for the device, then
 * adds it to the other relevant subsystems of the driver model.
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an error! Always use put_device() to give up your
 * reference instead.
 */
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	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;
	}

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);
	setup_parent(dev, parent);

	/* use parent numa_node */
	if (parent)
		set_dev_node(dev, dev_to_node(parent));
	//以上是對device進行初始化,包括name,private,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);   //device添加,根據他的parent等,當然還會根據他的attribute built一些檔案。
	if (error)
		goto Error;

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

	error = device_create_file(dev, &uevent_attr);  //built attr file
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &devt_attr);
		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);         //在其他檔案夾 建立link檔案,這就是為什麼在class目錄下也能看到device的目錄和檔案了
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);      //在bus目錄下 建立link檔案,是以在/sys/bus/platform/device下回看到n多個link檔案。
	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_sysf_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);         //進行probe,看有沒和device相對應的driver檔案。
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	if (dev->class) {
		mutex_lock(&dev->class->p->class_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->class_interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->class_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, &devt_attr);
 ueventattrError:
	device_remove_file(dev, &uevent_attr);
 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;
}
           

當然還有 drive_register的函數,其實和device_register差不多,另外,driver_register也會在最後進行probe,看有沒有相應的裝置。driver_register會先check這個drvier所在的bus上有沒有probe函數,如果有就運作這個函數進行probe,如果沒有,就運作自己的probe進行probe,這就是我們在驅動中經常看到的probe函數。

是以,在驅動中,先運作drive_register和先運作device_register都是一樣的。

 喝了,累了,喝紅牛也不管用了,怎麼辦?直接點選 發表文章的 按鈕吧。

驅動裝置模型---sys檔案系統

繼續閱讀