第四章 OS Environment自行開發驅動程序的方法
1. 概述
上文中,我們用大量的篇幅介紹了OS Environment所提供的接口以及OSKit去包裝其它系統的驅動程序的方法。那麼我們是否可以在OS Environment下開發自己的驅動程序集呢?回答是肯定的,原因也是很簡單的,OS Environment為我們提供了足夠強大的接口,讓我們去訪問系統的資源,只要我們去調用這些接口,然後根據硬件設備的說明書去寫驅動程序就可以了。
然而,有一點在這裡是需要考慮的,那就是OS Environment的設計意圖是什麼,因為這個環境的設計意圖將直接影響到在這個環境中去開發驅動程序的效果。按照OSKit設計者的說法,OS Environment並非是為了新寫的驅動程序去設計的,因此,OS Environment中關於驅動程序的思想也就不會是很先進的,同樣,也不會有其它的系統為兼容OS Environment而去重新改進自己,因此如果想重寫很多設備的驅動程序的話,那麼最好的選擇可能不是在OS Environment下開發,可以選擇去兼容驅動程序的DDI/DKI標准或UDI等標准,因為這些標准從設計的思想上來說就是作為通用的驅動程序接口,它們會考慮到更多的系統對它們的兼容性等方面的問題。另外,也可以選擇Linux等系統作為開發平台,因為這些系統內核的實現細節是公開的,更有利於寫某些特殊的設備的驅動程序。
由於在什麼平台上寫驅動程序已經超出了本文要討論的范圍,因此這裡我們不再多說,我們還是仔細討論一下在OS Environment這個環境下,一個驅動程序應當如何去寫。
2. 基本方法
寫驅動程序是一件非常復雜的事情,不僅要對操作系統的知識有所了解,還要了解,還要對計算機的體系結構,以及所要驅動的設備非常的熟悉。一個驅動程序究竟是寫成支持標准的POSIX標准呢,還是僅僅為了一些較為特殊的應用,按照自己的要求去寫,這些都是非常復雜的問題,對於驅動程序的作者來說,是必須要考慮好的。
一個驅動程序,通常是要調用操作系統提供的接口,並且為操作系統使用此設備提供一套接口。由於OS Environment最初的設計目的與設計思路,它是與UNIX類的系統的操作系統關於驅動程序的設計思想保持兼容的。UNIX的一個基本特征就是它抽象了設備的處理,所有的硬件設備都與常規的文件十分相似,可以通過與操作文件完全一樣的標准系統調用去打開、關閉、讀、寫一個設備。如果你希望你的操作系統最終能夠讓用戶按照類UNIX的方式去訪問設備,那麼為了達到這個目的,驅動程序就必須為操作系統提供這樣的功能。但是,如果你希望操作系統的用戶可以用其它的更為簡單或者更加出色的方式去訪問設備,那麼你也可以按照自己的方式去寫。這完全是由目標操作系統及應用來決定的。
如果已經決定了要寫一個驅動程序,希望它所提供的接口是類UNIX的,那麼這個驅動程序所要提供的接口就可以在前面第二章中找到,因為實際上OS Environment對外提供的接口都是要驅動程序來支持的。
但無論你的驅動程序的具體接口按照哪一種方法去實現,都有一些調用時必須要提供給系統的,現在我們就來討論一下這些必須提供的調用的問題。
3. 驅動程序必須實現的部分
3.1 OS Environment中驅動程序的接口及數據結構
3.1.1 基本的驅動程序接口
OS Environment中,所有的設備驅動程序最終都將抽象為一個統一的接口,通過這個接口,通過這個接口,可以獲得設備的基本信息。這個COM接口的名稱是oskit_driver。這個接口的定義如下:
struct oskit_driver_ops {
OSKIT_COMDECL (*query)(oskit_driver_t *drv,
const struct oskit_guid *iid,
void **out_ihandle);
OSKIT_COMDECL_U (*addref)(oskit_driver_t *drv);
OSKIT_COMDECL_U (*release)(oskit_driver_t *drv);
OSKIT_COMDECL (*getinfo)(oskit_driver_t *drv,
oskit_devinfo_t *out_info);
};
這個接口中,除了三個所有COM接口都支持的方法以外,有一個getinfo方法,這個方法可以獲得驅動程序的信息,驅動程序的信息會被存儲在out_info所指向的內存單元中。關於oskit_devinfo_t這個結構,我們會在下一節中介紹。
這個接口是一個基類,當一個驅動程序要去實現這個接口的時候,要繼承這個接口,並且提供一個新的方法probe。probe方法的作用是對驅動程序進行初始化,檢測系統中是否存在此設備,如果存在,則要把設備注冊到系統總線上,把設備的信息注冊到系統COM數據庫中等工作。
3.1.2 表示驅動程序信息的數據結構
在上面的函數中,用到了一個數據結構oskit_devinfo,這個數據結構是用來記錄驅動程序的基本信息的,在OS Environment中經常用到,並且如果編寫一個驅動程序,則也要提供這個結構,因此,在這裡詳細介紹一下,這結構的定義如下:
struct oskit_devinfo {
const char *name;
const char *description;
const char *vendor;
const char *author;
const char *version;
};
這個結構中所有的指針所返回的字符串都應當是常量,並且對於設備節點存在時總能夠使用。除了name以外的其它元素都可以是NULL以表示該信息不詳。
name是設備的一個簡單標識,例如"aha1542",name字符串的長度不能超過OSKIT_DEVNAME_MAX所定義的長度,在OSKit中,這個常量的缺省值是15。此字符串只能包含7位的字符,即字符的ASCII碼值在0-127之間,並且第一位必須是字母。
description: 對於設備的詳細描述,如"Adaptec 1542 SCSI controller"。
vender: 設備生產商的名字,例如"Adaptec"。
author: 設備驅動程序的作者。
version: 驅動程序的來源及版本,例如"Linux 1.3.36"。
3.1.3 基本的設備接口
在OSKit中,基本的設備接口是oskit_device,實現了這個接口的每一個對象對應著一個實際的設備。通常,一個設備驅動程序可以操縱多個設備,但設備驅動程序對象與設備對象之間是沒有關系的,設備對象並非驅動程序對象的一部分。當然,如果你願意,也可以將OS Environment改寫成那樣。設備接口的方法的定義如下:
struct oskit_device_ops {
OSKIT_COMDECL (*query)(oskit_device_t *dev,
const struct oskit_guid *iid,
void **out_ihandle);
OSKIT_COMDECL_U (*addref)(oskit_device_t *dev);
OSKIT_COMDECL_U (*release)(oskit_device_t *dev);
OSKIT_COMDECL (*getinfo)(oskit_device_t *dev,
oskit_devinfo_t *out_info);
OSKIT_COMDECL (*getdriver)(oskit_device_t *dev,
oskit_driver_t **out_driver);
};
出了基本的COM方法以外,此接口有兩個自己的方法,一個是getinfo,通過這個方法可以得到設備的信息。設備接口的getinfo方法和驅動程序接口的getinfo方法基本上是相同的,但是,同一個設備的設備接口和驅動接口所返回的信息也並不一定要完全相同,例如,可以讓驅動程序接口返回"aha15xx",以表示此驅動程序支持一系列的設備,而讓設備接口返回"aha1542"以表示此設備的具體型號。
getdriver方法將一個指向該設備的驅動程序的指針放入out_driver所指向的內存單元中。
3.2 OS Environment中驅動程序的注冊方法
驅動程序的注冊,包括驅動程序本身的注冊和設備的注冊。驅動程序本身的注冊先進行,而設備注冊等程序則應當寫在驅動程序的probe代碼中,在檢測設備的時候進行注冊。
驅動程序本身的注冊可以通過OS Environment所提供的接口來進行,這個接口已經在前面第二章介紹過了,注冊驅動程序的方法是driver_register,注銷驅動程序的方法是driver_unregister。
OS Environment也為注冊設備而提供了接口,它們是device_register和device_unregister。設備除了要注冊到COM對象的數據庫中之外,還應當注冊到系統的總線上,這樣,OS Environment就可以實現一個設備樹。
3.3 OS Environment中驅動程序注冊、自檢的全過程
OS Environment中驅動程序的注冊、自檢的過程,可以說是COM在OSKit中的應用的一個很好的表現。在OSKit中,用來表示驅動程序的COM數據庫有兩個,一個是driver_registery,另一個是device_registery。如上一小節所述,要首先將驅動程序注冊到driver_registery中,然後在對系統中所有設備進行檢測時,再利用驅動程序的probe函數將相應的設備注冊到device_registery中。下圖表現了這一過程,並標明了各個工作分別應當在何時完成。
在上圖中isa_bus_probe()及pci_bus_probe() 的部分是由OS Envrionment提供的,在進行檢測的時候,他們並不知道這個系統支持多少設備(注冊了多少驅動),完全通過驅動程序的COM數據庫,來獲取這個信息。
上圖中,背景色較深的方框表示要由驅動程序的作者實現的部分,而其余則是OS Envrionment的功能。可以看出,驅動程序的作者除了程序本身的功能外,要提供一個初始化驅動和一個檢測設備的方法。這兩個程序的寫法,可以參考OSKit包裝Linux或FreeBSD 的驅動程序時使用的代碼。