1. 前言
device和device driver是Linux驅(qū)動(dòng)開(kāi)發(fā)的基本概念。Linux kernel的思路很簡(jiǎn)單:驅(qū)動(dòng)開(kāi)發(fā),就是要開(kāi)發(fā)指定的軟件(driver)以驅(qū)動(dòng)指定的設(shè)備,所以kernel就為設(shè)備和驅(qū)動(dòng)它的driver定義了兩個(gè)數(shù)據(jù)結(jié)構(gòu),分別是device和device_driver。因此本文將會(huì)圍繞這兩個(gè)數(shù)據(jù)結(jié)構(gòu),介紹Linux設(shè)備模型的核心邏輯,包括:
設(shè)備及設(shè)備驅(qū)動(dòng)在kernel中的抽象、使用和維護(hù);
設(shè)備及設(shè)備驅(qū)動(dòng)的注冊(cè)、加載、初始化原理;
設(shè)備模型在實(shí)際驅(qū)動(dòng)開(kāi)發(fā)過(guò)程中的使用方法。
注:在介紹device和device_driver的過(guò)程中,會(huì)遇到很多額外的知識(shí)點(diǎn),如Class、Bus、DMA、電源管理等等,這些知識(shí)點(diǎn)都很復(fù)雜,任何一個(gè)都可以作為一個(gè)單獨(dú)的專(zhuān)題區(qū)闡述,因此本文不會(huì)深入解析它們,而會(huì)在后續(xù)的文章中專(zhuān)門(mén)描述。
2. struct device和struct device_driver
在閱讀Linux內(nèi)核源代碼時(shí),通過(guò)核心數(shù)據(jù)結(jié)構(gòu),即可理解某個(gè)模塊60%以上的邏輯,設(shè)備模型部分尤為明顯。
在include/linux/device.h中,Linux內(nèi)核定義了設(shè)備模型中最重要的兩個(gè)數(shù)據(jù)結(jié)構(gòu),struct device和struct device_driver。
struct device
1: /* include/linux/device.h, line 660 */
2: struct device {
3: ????struct device *parent;
4:
5: ????struct device_private *p;
6:
7: ????struct kobject kobj;
8: ????const char *init_name; /* initial name of the device */
9: ????const struct device_type *type;
10:
11:????struct mutex mutex; /* mutex to synchronize calls to
12: ????????????????????????????* its driver.
13: ????????????????????????????*/
14:
15:????struct bus_type *bus; /* type of bus device is on */
16:????struct device_driver *driver; /* which driver has allocated this
17: ????????????????????????????????device */
18:????void *platform_data; /* Platform specific data, device
19: ????????????????????????core doesn't touch it */
20:????struct dev_pm_info power;
21:????struct dev_pm_domain *pm_domain;
22:
23: #ifdef CONFIG_PINCTRL
24:????struct dev_pin_info *pins;
25: #endif
26:
27: #ifdef CONFIG_NUMA
28:????int numa_node; /* NUMA node this device is close to */
29: #endif
30:????u64 *dma_mask; /* dma mask (if dma'able device) */
31:????u64 coherent_dma_mask;/* Like dma_mask, but for
32: ????????????????????????????alloc_coherent mappings as
33: ????????????????????????????not all hardware supports
34: ????????????????????????????64 bit addresses for consistent
35: ????????????????????????????allocations such descriptors. */
36:
37:????struct device_dma_parameters *dma_parms;
38:
39:????struct list_head dma_pools; /* dma pools (if dma'ble) */
40:
41:????struct dma_coherent_mem *dma_mem; /* internal for coherent mem
42:????????????????????????????override */
43: #ifdef CONFIG_CMA
44:????struct cma *cma_area; /* contiguous memory area for dma
45:????????????????????????????allocations */
46: #endif
47:????/* arch specific additions */
48:????struct dev_archdata archdata;
49:
50:????struct device_node *of_node; /* associated device tree node */
51:????struct acpi_dev_node acpi_node; /* associated ACPI device node */
52:
53:????dev_t devt; /* dev_t, creates the sysfs "dev" */
54:????u32 id; /* device instance */
55:
56:????spinlock_t devres_lock;
57:????struct list_head devres_head;
58:
59:????struct klist_node knode_class;
60:????struct class *class;
61:????const struct attribute_group **groups; /* optional groups */
62:
63:????void (*release)(struct device *dev);
64:????struct iommu_group *iommu_group;
65: };
device結(jié)構(gòu)很復(fù)雜(不過(guò)linux內(nèi)核的開(kāi)發(fā)人員素質(zhì)是很高的,該接口的注釋寫(xiě)的非常詳細(xì),感興趣的同學(xué)可以參考內(nèi)核源代碼),這里將會(huì)選一些對(duì)理解設(shè)備模型非常關(guān)鍵的字段進(jìn)行說(shuō)明。
parent,該設(shè)備的父設(shè)備,一般是該設(shè)備所從屬的bus、controller等設(shè)備。
p,一個(gè)用于struct device的私有數(shù)據(jù)結(jié)構(gòu)指針,該指針中會(huì)保存子設(shè)備鏈表、用于添加到bus/driver/prent等設(shè)備中的鏈表頭等等,具體可查看源代碼。
kobj,該數(shù)據(jù)結(jié)構(gòu)對(duì)應(yīng)的struct kobject。
init_name,該設(shè)備的名稱(chēng)。
注1:在設(shè)備模型中,名稱(chēng)是一個(gè)非常重要的變量,任何注冊(cè)到內(nèi)核中的設(shè)備,都必須有一個(gè)合法的名稱(chēng),可以在初始化時(shí)給出,也可以由內(nèi)核根據(jù)“bus name + device ID”的方式創(chuàng)造。
type,struct device_type結(jié)構(gòu)是新版本內(nèi)核新引入的一個(gè)結(jié)構(gòu),它和struct device關(guān)系,非常類(lèi)似stuct kobj_type和struct kobject之間的關(guān)系,后續(xù)會(huì)再詳細(xì)說(shuō)明。
bus,該device屬于哪個(gè)總線(后續(xù)會(huì)詳細(xì)描述)。
driver,該device對(duì)應(yīng)的device driver。
platform_data,一個(gè)指針,用于保存具體的平臺(tái)相關(guān)的數(shù)據(jù)。具體的driver模塊,可以將一些私有的數(shù)據(jù),暫存在這里,需要使用的時(shí)候,再拿出來(lái),因此設(shè)備模型并不關(guān)心該指針得實(shí)際含義。
power、pm_domain,電源管理相關(guān)的邏輯,后續(xù)會(huì)由電源管理專(zhuān)題講解。
pins,"PINCTRL”功能,暫不描述。
numa_node,"NUMA”功能,暫不描述。
dma_mask~archdata,DMA相關(guān)的功能,暫不描述。
devt,dev_t是一個(gè)32位的整數(shù),它由兩個(gè)部分(Major和Minor)組成,在需要以設(shè)備節(jié)點(diǎn)的形式(字符設(shè)備和塊設(shè)備)向用戶(hù)空間提供接口的設(shè)備中,當(dāng)作設(shè)備號(hào)使用。在這里,該變量主要用于在sys文件系統(tǒng)中,為每個(gè)具有設(shè)備號(hào)的device,創(chuàng)建/sys/dev/* 下的對(duì)應(yīng)目錄,如下:
1|root@android:/storage/sdcard0 #ls /sys/dev/char/1\:??????????????????????????????????????????????????????????????????????
1:1/? 1:11/ 1:13/ 1:14/ 1:2/? 1:3/? 1:5/? 1:7/? 1:8/? 1:9/??
1|root@android:/storage/sdcard0 #ls /sys/dev/char/1:1?????????????????????????????????????????????????????????????????????
1:1/? 1:11/ 1:13/ 1:14/?
1|root@android:/storage/sdcard0 # ls /sys/dev/char/1\:1??
/sys/dev/char/1:1??
class,該設(shè)備屬于哪個(gè)class。
groups,該設(shè)備的默認(rèn)attribute集合。將會(huì)在設(shè)備注冊(cè)時(shí)自動(dòng)在sysfs中創(chuàng)建對(duì)應(yīng)的文件。
struct device_driver
1: /* include/linux/device.h, line 213 */
2: struct device_driver {
3: ????const char *name;
4: ????struct bus_type *bus;
5:
6: ????struct module *owner;
7: ????const char *mod_name; /* used for built-in modules */
8:
9: ????bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
10:
11:????const struct of_device_id *of_match_table;
12:????const struct acpi_device_id *acpi_match_table;
13:
14:????int (*probe) (struct device *dev);
15:????int (*remove) (struct device *dev);
16:????void (*shutdown) (struct device *dev);
17:????int (*suspend) (struct device *dev, pm_message_t state);
18:????int (*resume) (struct device *dev);
19:????const struct attribute_group **groups;
20:
21:????const struct dev_pm_ops *pm;
22:
23:????struct driver_private *p;
24: };
device_driver就簡(jiǎn)單多了(在早期的內(nèi)核版本中driver的數(shù)據(jù)結(jié)構(gòu)為"struct driver”,不知道從哪個(gè)版本開(kāi)始,就改成device_driver了):
name,該driver的名稱(chēng)。和device結(jié)構(gòu)一樣,該名稱(chēng)非常重要,后面會(huì)再詳細(xì)說(shuō)明。
bus,該driver所驅(qū)動(dòng)設(shè)備的總線設(shè)備。為什么driver需要記錄總線設(shè)備的指針呢?因?yàn)閮?nèi)核要保證在driver運(yùn)行前,設(shè)備所依賴(lài)的總線能夠正確初始化。
owner、mod_name,內(nèi)核module相關(guān)的變量,暫不描述。
suppress_bind_attrs,是不在sysfs中啟用bind和unbind attribute,如下:root@android:/storage/sdcard0?# ls /sys/bus/platform/drivers/switch-gpio/???????????????????????????????????????????????????
bind?? uevent unbind?
在kernel中,bind/unbind是從用戶(hù)空間手動(dòng)的為driver綁定/解綁定指定的設(shè)備的機(jī)制。這種機(jī)制是在bus.c中完成的,后面會(huì)詳細(xì)解釋。
probe、remove,這兩個(gè)接口函數(shù)用于實(shí)現(xiàn)driver邏輯的開(kāi)始和結(jié)束。Driver是一段軟件code,因此會(huì)有開(kāi)始和結(jié)束兩個(gè)代碼邏輯,就像PC程序,會(huì)有一個(gè)main函數(shù),main函數(shù)的開(kāi)始就是開(kāi)始,return的地方就是結(jié)束。而內(nèi)核driver卻有其特殊性:在設(shè)備模型的結(jié)構(gòu)下,只有driver和device同時(shí)存在時(shí),才需要開(kāi)始執(zhí)行driver的代碼邏輯。這也是probe和remove兩個(gè)接口名稱(chēng)的由來(lái):檢測(cè)到了設(shè)備和移除了設(shè)備(就是為熱拔插起的!)。
shutdown、suspend、resume、pm,電源管理相關(guān)的內(nèi)容,會(huì)在電源管理專(zhuān)題中詳細(xì)說(shuō)明。
groups,和struct device結(jié)構(gòu)中的同名變量類(lèi)似,driver也可以定義一些默認(rèn)attribute,這樣在將driver注冊(cè)到內(nèi)核中時(shí),內(nèi)核設(shè)備模型部分的代碼(driver/base/driver.c)會(huì)自動(dòng)將這些attribute添加到sysfs中。
p,私有數(shù)據(jù)的指針,具體的driver代碼可以把任何需要的內(nèi)容放在這里,反正設(shè)備模型代碼不關(guān)心。
3. 設(shè)備模型框架下驅(qū)動(dòng)開(kāi)發(fā)的基本步驟
在設(shè)備模型框架下,設(shè)備驅(qū)動(dòng)的開(kāi)發(fā)是一件很簡(jiǎn)單的事情,主要包括2個(gè)步驟:
步驟1:分配一個(gè)struct device類(lèi)型的變量,填充必要的信息后,把它注冊(cè)到內(nèi)核中。
步驟2:分配一個(gè)struct device_driver類(lèi)型的變量,填充必要的信息后,把它注冊(cè)到內(nèi)核中。
這兩步完成后,內(nèi)核會(huì)在合適的時(shí)機(jī)(后面會(huì)講),調(diào)用struct device_driver變量中的probe、remove、suspend、resume等回調(diào)函數(shù),從而觸發(fā)或者終結(jié)設(shè)備驅(qū)動(dòng)的執(zhí)行。而所有的驅(qū)動(dòng)程序邏輯,都會(huì)由這些回調(diào)函數(shù)實(shí)現(xiàn),此時(shí),驅(qū)動(dòng)開(kāi)發(fā)者眼中便不再有“設(shè)備模型”,轉(zhuǎn)而只關(guān)心驅(qū)動(dòng)本身的實(shí)現(xiàn)。
以上兩個(gè)步驟的補(bǔ)充說(shuō)明:
1. 一般情況下,Linux驅(qū)動(dòng)開(kāi)發(fā)很少直接使用device和device_driver,因?yàn)閮?nèi)核在它們之上又封裝了一層,如soc device、platform device等等,而這些層次提供的接口更為簡(jiǎn)單、易用(也正是因?yàn)檫@個(gè)原因,本文并不會(huì)過(guò)多涉及device、device_driver等模塊的實(shí)現(xiàn)細(xì)節(jié))。
2. 內(nèi)核提供很多struct device結(jié)構(gòu)的操作接口(具體可以參考include/linux/device.h和drivers/base/core.c的代碼),主要包括初始化(device_initialize)、注冊(cè)到內(nèi)核(device_register)、分配存儲(chǔ)空間+初始化+注冊(cè)到內(nèi)核(device_create)等等,可以根據(jù)需要使用。
3. device和device_driver必須具備相同的名稱(chēng),內(nèi)核才能完成匹配操作,進(jìn)而調(diào)用device_driver中的相應(yīng)接口。這里的同名,作用范圍是同一個(gè)bus下的所有device和device_driver。
4. device和device_driver必須掛載在一個(gè)bus之下,該bus可以是實(shí)際存在的,也可以是虛擬的。
5. driver開(kāi)發(fā)者可以在struct device變量中,保存描述設(shè)備特征的信息,如尋址空間、依賴(lài)的GPIOs等,因?yàn)閐evice指針會(huì)在執(zhí)行probe等接口時(shí)傳入,這時(shí)driver就可以根據(jù)這些信息,執(zhí)行相應(yīng)的邏輯操作了。
4. 設(shè)備驅(qū)動(dòng)probe的時(shí)機(jī)
所謂的"probe”,是指在Linux內(nèi)核中,如果存在相同名稱(chēng)的device和device_driver(注:還存在其它方式,我們先不關(guān)注了),內(nèi)核就會(huì)執(zhí)行device_driver中的probe回調(diào)函數(shù),而該函數(shù)就是所有driver的入口,可以執(zhí)行諸如硬件設(shè)備初始化、字符設(shè)備注冊(cè)、設(shè)備文件操作ops注冊(cè)等動(dòng)作("remove”是它的反操作,發(fā)生在device或者device_driver任何一方從內(nèi)核注銷(xiāo)時(shí),其原理類(lèi)似,就不再單獨(dú)說(shuō)明了)。
設(shè)備驅(qū)動(dòng)prove的時(shí)機(jī)有如下幾種(分為自動(dòng)觸發(fā)和手動(dòng)觸發(fā)):
將struct device類(lèi)型的變量注冊(cè)到內(nèi)核中時(shí)自動(dòng)觸發(fā)(device_register,device_add,device_create_vargs,device_create)
將struct device_driver類(lèi)型的變量注冊(cè)到內(nèi)核中時(shí)自動(dòng)觸發(fā)(driver_register)
手動(dòng)查找同一bus下的所有device_driver,如果有和指定device同名的driver,執(zhí)行probe操作(device_attach)
手動(dòng)查找同一bus下的所有device,如果有和指定driver同名的device,執(zhí)行probe操作(driver_attach)
自行調(diào)用driver的probe接口,并在該接口中將該driver綁定到某個(gè)device結(jié)構(gòu)中----即設(shè)置dev->driver(device_bind_driver)
注2:probe動(dòng)作實(shí)際是由bus模塊(會(huì)在下一篇文章講解)實(shí)現(xiàn)的,這不難理解:device和device_driver都是掛載在bus這根線上,因此只有bus最清楚應(yīng)該為哪些device、哪些driver配對(duì)。
注3:每個(gè)bus都有一個(gè)drivers_autoprobe變量,用于控制是否在device或者driver注冊(cè)時(shí),自動(dòng)probe。該變量默認(rèn)為1(即自動(dòng)probe),bus模塊將它開(kāi)放到sysfs中了,因而可在用戶(hù)空間修改,進(jìn)而控制probe行為。
5. 其它雜項(xiàng)
5.1 device_attribute和driver_attribute
在"Linux設(shè)備模型(4)_sysfs”中,我們有講到,大多數(shù)時(shí)候,attribute文件的讀寫(xiě)數(shù)據(jù)流為:vfs---->sysfs---->kobject---->attibute---->kobj_type---->sysfs_ops---->xxx_attribute,其中kobj_type、sysfs_ops和xxx_attribute都是由包含kobject的上層數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)。
Linux內(nèi)核中關(guān)于該內(nèi)容的例證到處都是,device也不無(wú)例外的提供了這種例子,如下:
1: /* driver/base/core.c, line 118 */
2: static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
3: char *buf)
4: {
5: ????struct device_attribute *dev_attr = to_dev_attr(attr);
6: ????struct device *dev = kobj_to_dev(kobj);
7: ????ssize_t ret = -EIO;
8:
9: ????if (dev_attr->show)
10:????????ret = dev_attr->show(dev, dev_attr, buf);
11:????????if (ret >= (ssize_t)PAGE_SIZE) {
12:????????????print_symbol("dev_attr_show: %s returned bad count\n",
13:????????????????????????(unsigned long)dev_attr->show);
14:????}
15:????return ret;
16: }
17:
18: static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
19: const char *buf, size_t count)
20: {
21:????struct device_attribute *dev_attr = to_dev_attr(attr);
22:????struct device *dev = kobj_to_dev(kobj);
23:????ssize_t ret = -EIO;
24:
25:????if (dev_attr->store)
26:????????ret = dev_attr->store(dev, dev_attr, buf, count);
27:????return ret;
28: }
29:
30: static const struct sysfs_ops dev_sysfs_ops = {
31:????.show = dev_attr_show,
32:????.store = dev_attr_store,
33: };
34:
35: /* driver/base/core.c, line 243 */
36: static struct kobj_type device_ktype = {
37:????.release = device_release,
38:????.sysfs_ops = &dev_sysfs_ops,
39:????.namespace = device_namespace,
40: };
41:
42: /* include/linux/device.h, line 478 */
43: /* interface for exporting device attributes */
44: struct device_attribute {
45:????struct attribute attr;
46:????ssize_t (*show)(struct device *dev, struct device_attribute *attr,
47:????????????????????char *buf);
48:????ssize_t (*store)(struct device *dev, struct device_attribute *attr,
49:????????????????????const char *buf, size_t count);
50: };
至于driver的attribute,則要簡(jiǎn)單的多,其數(shù)據(jù)流為:vfs---->sysfs---->kobject---->attribute---->driver_attribute,如下:
1: /* include/linux/device.h, line 247 */
2: /* sysfs interface for exporting driver attributes */
3:
4: struct driver_attribute {
5: ????struct attribute attr;
6: ????ssize_t (*show)(struct device_driver *driver, char *buf);
7: ????ssize_t (*store)(struct device_driver *driver, const char *buf,
8: ????????????????????size_t count);
9: };
10:
11: #define DRIVER_ATTR(_name, _mode, _show, _store) \
12: struct driver_attribute driver_attr_##_name = \
13:????__ATTR(_name, _mode, _show, _store)
5.2 device_type
device_type是內(nèi)嵌在struct device結(jié)構(gòu)中的一個(gè)數(shù)據(jù)結(jié)構(gòu),用于指明設(shè)備的類(lèi)型,并提供一些額外的輔助功能。它的的形式如下:
1: /* include/linux/device.h, line 467 */
2: struct device_type {
3: ????const char *name;
4: ????const struct attribute_group **groups;
5: ????int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
6: ????char *(*devnode)(struct device *dev, umode_t *mode,
7: ????????????????????kuid_t *uid, kgid_t *gid);
8: ????void (*release)(struct device *dev);
9:
10:????const struct dev_pm_ops *pm;
11: };
device_type的功能包括:
name表示該類(lèi)型的名稱(chēng),當(dāng)該類(lèi)型的設(shè)備添加到內(nèi)核時(shí),內(nèi)核會(huì)發(fā)出"DEVTYPE=‘name’”類(lèi)型的uevent,告知用戶(hù)空間某個(gè)類(lèi)型的設(shè)備available了
groups,該類(lèi)型設(shè)備的公共attribute集合。設(shè)備注冊(cè)時(shí),會(huì)同時(shí)注冊(cè)這些attribute。這就是面向?qū)ο笾小袄^承”的概念
uevent,同理,所有相同類(lèi)型的設(shè)備,會(huì)有一些共有的uevent需要發(fā)送,由該接口實(shí)現(xiàn)
devnode,devtmpfs有關(guān)的內(nèi)容,暫不說(shuō)明
release,如果device結(jié)構(gòu)沒(méi)有提供release接口,就要查詢(xún)它所屬的type是否提供。用于釋放device變量所占的空間
5.3 root device
在sysfs中有這樣一個(gè)目錄:/sys/devices,系統(tǒng)中所有的設(shè)備,都?xì)w集在該目錄下。有些設(shè)備,是通過(guò)device_register注冊(cè)到Kernel并體現(xiàn)在/sys/devices/xxx/下。但有時(shí)候我們僅僅需要在/sys/devices/下注冊(cè)一個(gè)目錄,該目錄不代表任何的實(shí)體設(shè)備,這時(shí)可以使用下面的接口:
1: /* include/linux/device.h, line 859 */
2: /*
3: * Root device objects for grouping under /sys/devices
4: */
5: extern struct device *__root_device_register(const char *name,
6: struct module *owner);
7:
8: /*
9: * This is a macro to avoid include problems with THIS_MODULE,
10: * just as per what is done for device_schedule_callback() above.
11: */
12: #define root_device_register(name) \
13: __root_device_register(name, THIS_MODULE)
14:
15: extern void root_device_unregister(struct device *root);
該接口會(huì)調(diào)用device_register函數(shù),向內(nèi)核中注冊(cè)一個(gè)設(shè)備,但是(你也想到了),沒(méi)必要注冊(cè)與之對(duì)應(yīng)的driver(順便提一下,內(nèi)核中有很多不需要driver的設(shè)備,這是之一)。
?
評(píng)論
查看更多