至今, 我們已經(jīng)看到大量低級(jí)框架和一個(gè)相對(duì)少的例子. 我們?cè)噲D在本章剩下部分中補(bǔ)充, 隨著我們進(jìn)入 Linux 設(shè)備模型的更高級(jí). 為此, 我們介紹一個(gè)新的虛擬總線, 我們稱為 lddbus, [46]并且修改 scullp 驅(qū)動(dòng)來(lái) "接入" 到這個(gè)總線.
再一次, 許多驅(qū)動(dòng)作者將不會(huì)需要這里涉及的材料. 這個(gè)水平的細(xì)節(jié)通常在總線級(jí)別處理, 并且很少作者需要添加一個(gè)新總線類型. 這個(gè)信息是有用的, 但是, 對(duì)任何人好奇在 PCI, USB 等層面的里面發(fā)生了什么或者誰(shuí)需要在那個(gè)級(jí)別做改變.
一個(gè)總線是處理器和一個(gè)或多個(gè)設(shè)備之間的通道. 為設(shè)備模型的目的, 所有的設(shè)備都通過(guò)一個(gè)總線連接, 甚至當(dāng)它是一個(gè)內(nèi)部的虛擬的,"平臺(tái)"總線. 總線可以插入另一個(gè) - 一個(gè) USB 控制器常常是一個(gè) PCI 設(shè)備, 例如. 設(shè)備模型表示在總線和它們控制的設(shè)備之間的實(shí)際連接.
在 Linux 設(shè)備模型中, 一個(gè)總線由 bus_type 結(jié)構(gòu)代表, 定義在 <linux/device.h>. 這個(gè)結(jié)構(gòu)看來(lái)象:
struct bus_type {
char *name;
struct subsystem subsys;
struct kset drivers;
struct kset devices;
int (*match)(struct device *dev, struct device_driver *drv);
struct device *(*add)(struct device * parent, char * bus_id);
int (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
/* Some fields omitted */
};
name 成員是總線的名子, 有些同 pci. 你可從這個(gè)結(jié)構(gòu)中見(jiàn)到每個(gè)總線是它自己的子系統(tǒng); 這個(gè)子系統(tǒng)不位于 sysfs 的頂層, 但是. 相反, 它們?cè)诳偩€子系統(tǒng)下面. 一個(gè)總線包含 2 個(gè) ksets, 代表已知的總線的驅(qū)動(dòng)和所有插入總線的設(shè)備. 所以, 有一套方法我們馬上將涉及.
如同我們提過(guò)的, 例子源碼包含一個(gè)虛擬總線實(shí)現(xiàn)稱為 lddbus. 這個(gè)總線建立它的 bus_type 結(jié)構(gòu), 如下:
struct bus_type ldd_bus_type = { .name = "ldd", .match = ldd_match, .hotplug = ldd_hotplug, };
注意很少 bus_type 成員要求初始化; 大部分由設(shè)備模型核心處理. 但是, 我們確實(shí)不得不指定總線的名子, 以及任何伴隨它的方法.
不可避免地, 一個(gè)新總線必須注冊(cè)到系統(tǒng), 通過(guò)一個(gè)對(duì) bus_register 的調(diào)用. lddbus 代碼這樣做以這樣的方式:
ret = bus_register(&ldd_bus_type);
if (ret)
return ret;
這個(gè)調(diào)用可能失敗, 當(dāng)然, 因此返回值必須一直檢查. 如果它成功, 新總線子系統(tǒng)已被添加到系統(tǒng); 在 sysfs 中 /sys/bus 的下面可以見(jiàn)到, 并且可能啟動(dòng)添加設(shè)備.
如果有必要從系統(tǒng)中去除一個(gè)總線(當(dāng)關(guān)聯(lián)模塊被去除, 例如), 調(diào)用調(diào)用 bus_unregister:
void bus_unregister(struct bus_type *bus);
有幾個(gè)給 bus_type 結(jié)構(gòu)定義的方法; 它們?cè)试S總線代碼作為一個(gè)設(shè)備核心和單獨(dú)驅(qū)動(dòng)之間的中介. 在 2.6.10 內(nèi)核中定義的方法是:
int (match)(struct device device, struct device_driver *driver);
這個(gè)方法被調(diào)用, 大概多次, 無(wú)論何時(shí)一個(gè)新設(shè)備或者驅(qū)動(dòng)被添加給這個(gè)總線. 它應(yīng)當(dāng)返回一個(gè)非零值如果給定的設(shè)備可被給定的驅(qū)動(dòng)處理. (我們馬上進(jìn)入設(shè)備和 device_driver 結(jié)構(gòu)的細(xì)節(jié)). 這個(gè)函數(shù)必須在總線級(jí)別處理, 因?yàn)槟鞘呛线m的邏輯存在的地方; 核心內(nèi)核不能知道如何匹配每個(gè)可能總線類型的設(shè)備和驅(qū)動(dòng).
int (hotplug) (struct device device, char *envp, int num_envp, char buffer, int buffer_size);
這個(gè)模塊允許總線添加變量到環(huán)境中, 在產(chǎn)生一個(gè)熱插拔事件在用戶空間之前. 參數(shù)和 kset 熱插拔方法相同( 在前面的 "熱插拔事件產(chǎn)生" 一節(jié)中描述 ).
lddbus 驅(qū)動(dòng)有一個(gè)非常簡(jiǎn)單的匹配函數(shù), 它僅僅比較驅(qū)動(dòng)和設(shè)備的名子:
static int ldd_match(struct device *dev, struct device_driver *driver)
{
return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}
當(dāng)涉及到真實(shí)硬件, match 函數(shù)常常在有設(shè)備自身提供的硬件 ID 和驅(qū)動(dòng)提供的 ID 之間, 做一些比較.
lddbus 熱插拔方法看來(lái)象這樣:
static int ldd_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
{
envp[0] = buffer;
if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s",
Version) >= buffer_size)
return -ENOMEM;
envp[1] = NULL;
return 0;
}
這里, 我們加入 lddbus 源碼的當(dāng)前版本號(hào), 只是以防有人好奇.
如果你在編寫總線級(jí)別的代碼, 你可能不得不對(duì)所有已經(jīng)注冊(cè)到你的總線的設(shè)備或驅(qū)動(dòng)進(jìn)行一些操作. 它可能會(huì)誘惑人直接進(jìn)入 bus_type 結(jié)構(gòu)中的各種結(jié)構(gòu), 但是最好使用已經(jīng)提供的幫助函數(shù).
為操作每個(gè)對(duì)總線已知的設(shè)備, 使用:
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *));
這個(gè)函數(shù)列舉總線上的每個(gè)設(shè)備, 傳遞關(guān)聯(lián)的設(shè)備結(jié)構(gòu)給 fn, 連同作為 data 來(lái)傳遞的值. 如果 start 是 NULL, 列舉從總線的第一個(gè)設(shè)備開始; 否則列舉從 start 之后的第一個(gè)設(shè)備開始. 如果 fn 返回一個(gè)非零值, 列舉停止并且那個(gè)值從 bus_for_each_dev 返回.
有一個(gè)類似的函數(shù)來(lái)列舉驅(qū)動(dòng):
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));
這個(gè)函數(shù)就像 buf_for_each_dev, 除了, 當(dāng)然, 它替之作用于驅(qū)動(dòng).
應(yīng)當(dāng)注意, 這 2 個(gè)函數(shù)持有總線子系統(tǒng)的讀者/寫者旗標(biāo)在工作期間. 因此試圖一起使用這 2 個(gè)會(huì)死鎖 -- 每個(gè)將試圖獲取同一個(gè)旗標(biāo). 修改總線的操作( 例如注銷設(shè)備 )也將鎖住. 因此, 小心使用 bus_for_each 函數(shù).
幾乎 Linux 驅(qū)動(dòng)模型中的每一層都提供一個(gè)添加屬性的接口, 并且總線層不例外. bus_attribute 類型定義在 <linux/device.h> 如下:
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf,
size_t count);
};
我們已經(jīng)見(jiàn)到 struct attribute 在 "缺省屬性" 一節(jié). bus_attribute 類型也包含 2 個(gè)方法來(lái)顯示和設(shè)置屬性值. 大部分在 kobject 之上的設(shè)備模型層以這種方式工作.
已經(jīng)提供了一個(gè)方便的宏為在編譯時(shí)間創(chuàng)建和初始化 bus_attribute 結(jié)構(gòu):
BUS_ATTR(name, mode, show, store);
這個(gè)宏聲明一個(gè)結(jié)構(gòu), 產(chǎn)生它的名子通過(guò)前綴字符串 busattr 到給定的名子.
任何屬于一個(gè)總線的屬性應(yīng)當(dāng)明確使用 bus_create_file 來(lái)創(chuàng)建:
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
屬性也可被去除, 使用:
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
lddbus 驅(qū)動(dòng)創(chuàng)建一個(gè)簡(jiǎn)單屬性文件, 再次, 包含源碼版本號(hào). show 方法和 bus_attribute 結(jié)構(gòu)設(shè)置如下:
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
創(chuàng)建屬性文件在模塊加載時(shí)間完成:
if (bus_create_file(&ldd_bus_type, &bus_attr_version))
printk(KERN_NOTICE "Unable to create version attribute\n");
這個(gè)調(diào)用創(chuàng)建一個(gè)屬性文件(/sys/busldd/version) 包含 lddbus 代碼的版本號(hào).
在最低層, Linux 系統(tǒng)中的每個(gè)設(shè)備由一個(gè) struct device 代表:
struct device { struct device parent; struct kobject kobj; char bus_id[BUS_ID_SIZE]; struct bus_type bus; struct device_driver driver; void driver_data; void (release)(struct device dev); / Several fields omitted /};
有許多其他的 struct device 成員只對(duì)設(shè)備核心代碼感興趣. 但是, 這些成員值得了解:
struct device *parent
設(shè)備的 "parent" 設(shè)備 -- 它所附著到的設(shè)備. 在大部分情況, 一個(gè)父設(shè)備是某種總線或者主控制器. 如果 parent 是 NULL, 設(shè)備是一個(gè)頂層設(shè)備, 這常常不是你所要的.
struct kobject kobj;
代表這個(gè)設(shè)備并且連接它到層次中的 kobject. 注意, 作為一個(gè)通用的規(guī)則, device->kobj->parent 等同于 device->parent->kobj.
char bus_id[BUS_ID_SIZE];
唯一確定這個(gè)總線上的設(shè)備的字符串. PCI 設(shè)備, 例如, 使用標(biāo)準(zhǔn)的 PCI ID 格式, 包含域, 總線, 設(shè)備, 和功能號(hào).
struct bus_type *bus;
確定設(shè)備位于哪種總線.
struct device_driver *driver;
管理這個(gè)設(shè)備的驅(qū)動(dòng); 我們查看 struct device_driver 在下一節(jié).
void *driver_data;
一個(gè)可能被設(shè)備驅(qū)動(dòng)使用的私有數(shù)據(jù)成員.
void (release)(struct device dev);
當(dāng)對(duì)這個(gè)設(shè)備的最后引用被去除時(shí)調(diào)用的方法; 它從被嵌入的 kobject 的 release 方法被調(diào)用. 注冊(cè)到核心的所有的設(shè)備結(jié)構(gòu)必須有一個(gè) release 方法, 否則內(nèi)核打印出慌亂的抱怨.
最少, parent, bus_id, bus, 和 release 成員必須在設(shè)備結(jié)構(gòu)被注冊(cè)前設(shè)置.
通常的注冊(cè)和注銷函數(shù)在:
int device_register(struct device *dev);
void device_unregister(struct device *dev);
我們已經(jīng)見(jiàn)到 lddbus 代碼如何注冊(cè)它的總線類型. 但是, 一個(gè)實(shí)際的總線是一個(gè)設(shè)備并且必須單獨(dú)注冊(cè). 為簡(jiǎn)單起見(jiàn), lddbus 模塊只支持一個(gè)單個(gè)虛擬總線, 因此這個(gè)驅(qū)動(dòng)在編譯時(shí)建立它的設(shè)備:
static void ldd_bus_release(struct device *dev)
{
printk(KERN_DEBUG "lddbus release\n");
}
struct device ldd_bus = {
.bus_id = "ldd0",
.release = ldd_bus_release
};
這是頂級(jí)總線, 因此 parent 和 bus 成員留為 NULL. 我們有一個(gè)簡(jiǎn)單的, no-op release 方法, 并且, 作為第一個(gè)(并且唯一)總線, 它的名子時(shí) ldd0. 這個(gè)總線設(shè)備被注冊(cè), 使用:
ret = device_register(&ldd_bus);
if (ret)
printk(KERN_NOTICE "Unable to register ldd0\n");
一旦調(diào)用完成, 新總線可在 sysfs 中 /sys/devices 下面見(jiàn)到. 任何加到這個(gè)總線的設(shè)備接著在 /sys/devices/ldd0 下顯示.
sysfs 中的設(shè)備入口可有屬性. 相關(guān)的結(jié)構(gòu)是:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, char *buf);
ssize_t (*store)(struct device *dev, const char *buf,
size_t count);
};
這些屬性結(jié)構(gòu)可在編譯時(shí)建立, 使用這些宏:
DEVICE_ATTR(name, mode, show, store);
結(jié)果結(jié)構(gòu)通過(guò)前綴 devattr 到給定名子上來(lái)命名. 屬性文件的實(shí)際管理使用通常的函數(shù)對(duì)來(lái)處理:
int device_create_file(struct device *device, struct device_attribute *entry);
void device_remove_file(struct device *dev, struct device_attribute *attr);
struct bus_type 的 dev_attrs 成員指向一個(gè)缺省的屬性列表, 這些屬性給添加到總線的每個(gè)設(shè)備創(chuàng)建.
設(shè)備結(jié)構(gòu)包含設(shè)備模型核心需要的來(lái)模型化系統(tǒng)的信息. 大部分子系統(tǒng), 但是, 跟蹤關(guān)于它們駐留的設(shè)備的額外信息. 結(jié)果, 對(duì)設(shè)備很少由空設(shè)備結(jié)構(gòu)所代表; 相反, 這個(gè)結(jié)構(gòu), 如同 kobject 結(jié)構(gòu), 常常是嵌入一個(gè)更高級(jí)的設(shè)備表示中. 如果你查看 struct pci_dev 的定義或者 struct usb_device 的定義, 你會(huì)發(fā)現(xiàn)一個(gè) struct device 埋在其中. 常常地, 低層驅(qū)動(dòng)甚至不知道 struct device, 但是有例外.
lddbus 驅(qū)動(dòng)創(chuàng)建它自己的設(shè)備類型( struct ldd_device ) 并且期望單獨(dú)的設(shè)備驅(qū)動(dòng)來(lái)注冊(cè)它們的設(shè)備使用這個(gè)類型. 它是一個(gè)簡(jiǎn)單結(jié)構(gòu):
struct ldd_device {
char *name;
struct ldd_driver *driver;
struct device dev;
};
#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);
這個(gè)結(jié)構(gòu)允許驅(qū)動(dòng)提供一個(gè)實(shí)際的名子給設(shè)備( 這可以清楚地不同于它的總線 ID, 存儲(chǔ)于設(shè)備結(jié)構(gòu)) 以及一個(gè)這些驅(qū)動(dòng)信息的指針. 給真實(shí)設(shè)備的結(jié)構(gòu)常常還包含關(guān)于供應(yīng)者信息, 設(shè)備型號(hào), 設(shè)備配置, 使用的資源, 等等. 可以在 struct pci_dev (<linux/pci.h>) 或者 struct usb_device (<linux/usb.h>) 中找到好的例子. 一個(gè)方便的宏( to_ldd_device ) 也為 struct ldd_device 定義, 使得容易轉(zhuǎn)換指向被嵌入的結(jié)構(gòu)的指針為 ldd_device 指針.
lddbus 輸出的注冊(cè)接口看來(lái)如此:
int register_ldd_device(struct ldd_device *ldddev)
{
ldddev->dev.bus = &ldd_bus_type;
ldddev->dev.parent = &ldd_bus;
ldddev->dev.release = ldd_dev_release;
strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);
return device_register(&ldddev->dev);
}
EXPORT_SYMBOL(register_ldd_device);
這里, 我們簡(jiǎn)單地填充一些嵌入的設(shè)備結(jié)構(gòu)成員( 單個(gè)驅(qū)動(dòng)不應(yīng)當(dāng)需要知道這個(gè) ), 并且注冊(cè)這個(gè)設(shè)備到驅(qū)動(dòng)核心. 如果我們想添加總線特定的屬性到設(shè)備, 我們可在這里做.
為顯示這個(gè)接口如何使用, 我們介紹另一個(gè)例子驅(qū)動(dòng), 我們稱為 sculld. 它是在第 8 章介紹的 scullp 驅(qū)動(dòng)上的另一個(gè)變體. 它實(shí)現(xiàn)通用的內(nèi)存區(qū)設(shè)備, 但是 sculld 也使用 Linux 設(shè)備模型, 通過(guò) lddbus 接口.
sculld 驅(qū)動(dòng)添加一個(gè)它自己的屬性到它的設(shè)備入口; 這個(gè)屬性, 稱為 dev, 僅僅包含關(guān)聯(lián)的設(shè)備號(hào). 這個(gè)屬性可被一個(gè)模塊用來(lái)加載腳本或者熱插拔子系統(tǒng), 來(lái)自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn), 當(dāng)設(shè)備被添加到系統(tǒng)時(shí). 這個(gè)屬性的設(shè)置遵循常用模式:
static ssize_t sculld_show_dev(struct device *ddev, char *buf)
{
struct sculld_dev *dev = ddev->driver_data;
return print_dev_t(buf, dev->cdev.dev);
}
static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);
接著, 在初始化時(shí)間, 設(shè)備被注冊(cè), 并且 dev 屬性被創(chuàng)建通過(guò)下面的函數(shù):
static void sculld_register_dev(struct sculld_dev *dev, int index)
{
sprintf(dev->devname, "sculld%d", index);
dev->ldev.name = dev->devname;
dev->ldev.driver = &sculld_driver;
dev->ldev.dev.driver_data = dev;
register_ldd_device(&dev->ldev);
device_create_file(&dev->ldev.dev, &dev_attr_dev);
}
注意, 我們使用 driver_data 成員來(lái)存儲(chǔ)指向我們自己的內(nèi)部的設(shè)備結(jié)構(gòu)的指針.
設(shè)備模型跟蹤所有對(duì)系統(tǒng)已知的驅(qū)動(dòng). 這個(gè)跟蹤的主要原因是使驅(qū)動(dòng)核心能匹配驅(qū)動(dòng)和新設(shè)備. 一旦驅(qū)動(dòng)在系統(tǒng)中是已知的對(duì)象, 但是, 許多其他的事情變得有可能. 設(shè)備驅(qū)動(dòng)可輸出和任何特定設(shè)備無(wú)關(guān)的信息和配置變量, 例如:
驅(qū)動(dòng)由下列結(jié)構(gòu)定義:
struct device_driver {
char *name;
struct bus_type *bus;
struct kobject kobj;
struct list_head devices;
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown) (struct device *dev);
};
再一次, 幾個(gè)結(jié)構(gòu)成員被忽略( 全部?jī)?nèi)容見(jiàn) <linux/device.h> ). 這里, name 是驅(qū)動(dòng)的名子( 它在 sysfs 中出現(xiàn) ), bus 是這個(gè)驅(qū)動(dòng)使用的總線類型, kobj 是必然的 kobject, devices 是當(dāng)前綁定到這個(gè)驅(qū)動(dòng)的所有設(shè)備的列表, probe 是一個(gè)函數(shù)被調(diào)用來(lái)查詢一個(gè)特定設(shè)備的存在(以及這個(gè)驅(qū)動(dòng)是否可以使用它), remove 當(dāng)設(shè)備從系統(tǒng)中去除時(shí)被調(diào)用, shutdown 在關(guān)閉時(shí)被調(diào)用來(lái)關(guān)閉設(shè)備.
使用 device_driver 結(jié)構(gòu)的函數(shù)的形式, 現(xiàn)在應(yīng)當(dāng)看來(lái)是類似的(因此我們快速涵蓋它們). 注冊(cè)函數(shù)是:
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
通常的屬性結(jié)構(gòu)在:
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *drv, char *buf);
ssize_t (*store)(struct device_driver *drv, const char *buf,
size_t count);
};
DRIVER_ATTR(name, mode, show, store);
以及屬性文件以通常的方法創(chuàng)建:
int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);
void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);
bus_type 結(jié)構(gòu)含有一個(gè)成員( drv_attrs ) 指向一套缺省屬性, 對(duì)所有關(guān)聯(lián)到這個(gè)總線的驅(qū)動(dòng)都創(chuàng)建.
如同大部分驅(qū)動(dòng)核心結(jié)構(gòu)的情形, device_driver 結(jié)構(gòu)常常被發(fā)現(xiàn)嵌到一個(gè)更高級(jí)的, 總線特定的結(jié)構(gòu). lddbus 子系統(tǒng)不會(huì)和這樣的趨勢(shì)相反, 因此它已定義了它自己的 ldd_driver 結(jié)構(gòu):
struct ldd_driver {
char *version;
struct module *module;
struct device_driver driver;
struct driver_attribute version_attr;
};
#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);
這里, 我們要求每個(gè)驅(qū)動(dòng)提供特定當(dāng)前軟件版本, 并且 lddbus 輸出這個(gè)版本字串為它知道的每個(gè)驅(qū)動(dòng). 總線特定的驅(qū)動(dòng)注冊(cè)函數(shù)是:
int register_ldd_driver(struct ldd_driver *driver)
{
int ret;
driver->driver.bus = &ldd_bus_type;
ret = driver_register(&driver->driver);
if (ret)
return ret;
driver->version_attr.attr.name = "version";
driver->version_attr.attr.owner = driver->module;
driver->version_attr.attr.mode = S_IRUGO;
driver->version_attr.show = show_version;
driver->version_attr.store = NULL;
return driver_create_file(&driver->driver, &driver->version_attr);
}
這個(gè)函數(shù)的第一部分只注冊(cè)低級(jí)的 device_driver 結(jié)構(gòu)到核心; 剩下的建立版本屬性. 因?yàn)檫@個(gè)屬性在運(yùn)行時(shí)被創(chuàng)建, 我們不能使用 DRIVER_ATTR 宏; 反之, driver_attribute 結(jié)構(gòu)必須手工填充. 注意我們?cè)O(shè)定屬性的擁有者為驅(qū)動(dòng)模塊, 不是 lddbus 模塊; 這樣做的理由是可以在為這個(gè)屬性的 show 函數(shù)的實(shí)現(xiàn)中見(jiàn)到:
static ssize_t show_version(struct device_driver *driver, char *buf)
{
struct ldd_driver *ldriver = to_ldd_driver(driver);
sprintf(buf, "%s\n", ldriver->version);
return strlen(buf);
}
有人可能認(rèn)為屬性擁有者應(yīng)當(dāng)是 lddbus 模塊, 因?yàn)閷?shí)現(xiàn)這個(gè)屬性的函數(shù)在那里定義. 這個(gè)函數(shù), 但是, 是使用驅(qū)動(dòng)自身所創(chuàng)建的 ldd_driver 結(jié)構(gòu). 如果那個(gè)結(jié)構(gòu)在一個(gè)用戶空間進(jìn)程試圖讀取版本號(hào)時(shí)要消失, 事情會(huì)變得麻煩. 指定驅(qū)動(dòng)模塊作為屬性的擁有者阻止了模塊被卸載, 在用戶空間保持屬性文件打開時(shí). 因?yàn)槊總€(gè)驅(qū)動(dòng)模塊創(chuàng)建一個(gè)對(duì) lddbus 模塊的引用, 我們能確信 lddbus 不會(huì)在一個(gè)不合適的時(shí)間被卸載.
為完整起見(jiàn), sculld 創(chuàng)建它的 ldd_driver 結(jié)構(gòu)如下:
static struct ldd_driver sculld_driver = { .version = "$Revision: 1.1 $", .module = THIS_MODULE, .driver = { .name = "sculld", }, };
一個(gè)簡(jiǎn)單的對(duì) register_ldd_driver 的調(diào)用添加它到系統(tǒng)中. 一旦完成初始化, 驅(qū)動(dòng)信息可在 sysfs 中見(jiàn)到:
$ tree /sys/bus/ldd/drivers
/sys/bus/ldd/drivers
`-- sculld
|-- sculld0 -> ../../../../devices/ldd0/sculld0
|-- sculld1 -> ../../../../devices/ldd0/sculld1
|-- sculld2 -> ../../../../devices/ldd0/sculld2
|-- sculld3 -> ../../../../devices/ldd0/sculld3
`-- version
[46] 這個(gè)總線的邏輯名子, 當(dāng)然, 應(yīng)當(dāng)是"sbus", 但是這個(gè)名子已經(jīng)被一個(gè)真實(shí)的, 物理總線采用.
更多建議: