?? (ldd) ch15-外圍總線概覽(轉載).htm
字號:
<DIV align=center><FONT color=#ffffff>|</FONT></DIV></TD>
<TD width="8%" height=4>
<DIV align=center><A href="mailto:joyfire@sina.com"><FONT
color=#ffffff>聯系</FONT></A></DIV></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE borderColor=#666666 cellPadding=2 width="90%" align=center border=2>
<TBODY>
<TR>
<TD bgColor=#000000>
<P align=center><A
href="http://211.71.69.201/joyfire/lsdp/index.htm"><FONT color=#ffffff
size=2>目錄頁</FONT></A> | <A
href="http://211.71.69.201/joyfire/lsdp/18.htm"><FONT color=#ffffff
size=2>上一頁</FONT></A> | <A
href="http://211.71.69.201/joyfire/lsdp/20.htm"><FONT color=#ffffff
size=2>下一頁</FONT></A></P>
<P align=center><FONT face=黑體 color=#ffffff size=6>(LDD) Ch15-外圍總線概覽(轉載)
</FONT></P><SPAN style="LINE-HEIGHT: 1; LETTER-SPACING: 0pt"><FONT
color=#ffffff size=3>
<P>發信人: Altmayer (alt), 信區: GNULinux<BR>標 題: (LDD) Ch15-外圍總線概覽(轉載)<BR>發信站: 飲水思源 (2001年12月13日08:58:01 星期四), 站內信件<BR> <BR>【 以下文字轉載自 <FONT
color=#00ff00>UNIXpost </FONT>討論區 】<BR>【 原文由<FONT
color=#00ff00> altmayer.bbs@bbs.nju.edu.cn,</FONT> 所發表 】<BR> <BR>【 以下文字轉載自 <FONT
color=#00ff00>altmayer </FONT>的信箱 】<BR> <BR> <BR>第十五章 外圍總線概覽<BR> <BR> <BR> <BR>在第八章“硬件管理”中,我們介紹了最低級的硬件控制,本章提供一個較高級的總線<BR>體系結構的概覽。總線由電氣接口和編程接口組成。在這一章,我打算介紹編程接口。<BR> <BR>本章覆蓋了幾種總線體系結構。不過,基本重點是訪問PCI外圍的核心功能,因為近來,<BR>PCI總線是最常用的外圍總線,也是核心支持最好的總線。<BR> <BR> <BR> <BR>PCI接口<BR></P></FONT><FONT
color=#ffffff size=3>
<P>PCI接口<BR> <BR> <BR> <BR>盡管很多計算機用戶認為PCI(外圍部件互連,Peripheral Component Interconnect)<BR>是布局電氣線路的一種方法,但實際上,它是一組完全的規范,定義了計算機的不同部<BR>分是如何交互的。<BR> <BR>PCI規范覆蓋了與計算機接口相關的絕大多數方面。我不打算在這里全部介紹,在本節中<BR>,我主要關心一個PCI驅動程序是如何找到它的硬件,并獲得對它的訪問的。在第二章“<BR>構造和運行模塊”的“自動和手工配置”一節,及在第九章“中斷處理”的“自動檢測<BR>中斷號”一節中討論過的探測技術同樣可以應用于PCI設備,但規范還提供了探測的另外<BR>辦法。<BR> <BR>PCI結構被設計來替代ISA標準,由三個主要目標:在計算機和其外圍之間傳送數據時有<BR>更高的性能,盡可能地做到平臺無關性,使在系統中增減外圍設備得到簡化。<BR> <BR>PCI通過使用比ISA高的時鐘頻率來獲得更高的性能;它的時鐘運行在25或33MHZ(實際時<BR>鐘是系統時鐘的幾分之一的整數倍),而且馬上就會游66MHZ的擴展。另外,它被裝配在<BR>32位的數據總線上,64位的擴展正在規范中。平臺無關性一直是計算機總線的一個設計<BR>目標,這是PCI的尤其重要的一個特征,因為PC世界一直以來總是被處理器特定的標準所<BR>主宰。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>不過對驅動程序作者來說,最要緊的是對接口板自動檢測的支持。PCI設備是無跳線的(<BR>與大多數ISA外圍不同),并且在引導時被自動配置。因此,設備驅動程序必須能訪問設<BR>備上的配置信息來完成初始化。這些情形都不需要任何探測。<BR> <BR> <BR> <BR>PCI尋址<BR> <BR>每個外圍由一個總線號、一個設備號、和一個功能號確定。雖然PCI規范允許一個系統最<BR>多擁有256條總線,但PC只有一條。每條總線最多帶32個設備,但每個設備可以是最多個<BR>功能的多功能板(如一個音頻設備帶一個CD-ROM驅動器)。每個功能可以由一個16位的<BR>鍵或兩個8位的鍵確定。Linux核心采用后一種方法。<BR> <BR>每個外圍板子的硬件電路回答與三個地址空間相關的詢問:內存位置,I/O端口,和配置<BR>寄存器。前兩個地址空間由PCI總線上的所有設備共享(也就是說,當你訪問一個內存位<BR>置,所有的設備都將同時看到這個總線周期)。而配置空間則利用“地理尋址”,每個<BR>槽有一個配置事務的私用使能線,PCI控制器一次訪問一個板子,不會有地址沖突。考慮<BR>到驅動程序,內存和I/O是以通常的inb,memcpy等來訪問。而配置事務則通過調用特定的<BR>核心函數訪問配置寄存器來完成。至于中斷,每個PCI設備有4個中斷管腳,它們到處理<BR>器中斷線的路由是主板的任務;PCI中斷可以設計為共享的,這樣即使是一個有限中斷線<BR>的處理器也能帶很多PCI接口板。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>PCI總線的I/O空間使用32位的地址總線(這樣就是4GB的I/O端口),而內存空間則可以<BR>用32位或64位地址訪問。地址對每個設備來說應該是唯一的,但也有可能有兩個設備錯<BR>誤地映射到同一個地址,使得哪個都不能被訪問。一個好消息是接口卡提供的每個內存<BR>和I/O地址區段都可以通過配置事務重映射。這就是設備可以在引導時被初始化從而避免<BR>地址沖突的機制這些區段當前映射到的地址可以從配置空間讀出,因此Linux驅動程序可<BR>以不通過探測就訪問其設備。一旦配置寄存器被讀出,驅動程序就可以安全的訪問它的<BR>硬件。<BR> <BR>PCI配置空間由每個設備函數256個字節構成,配置寄存器的布局是標準化的。配置空間<BR>有四個字節含有一個唯一的函數ID,因此驅動程序可以通過在外圍查找特定的ID來B確定<BR>它的設備*。總之,每個設備板子被地理尋址以取得它的配置寄存器;這個信息可以用來<BR>確定這個板子或采取進一步動作。<BR> <BR>從前面的描述,應該清楚PCI接口標準比ISA的主要創新是配置地址空間。因此,除了通<BR>常的驅動程序代碼外,PCI驅動程序還需要訪問配置空間的能力。<BR> <BR>在本章的其余部分,我將使用單詞“設備”來指一個設備功能,因為多功能板上的每個<BR>功能均是一個獨立的實體。當我提到一個設備,我是指元組“總線號,設備號,功能號<BR>”。如前所述,每個元組在Linux中由兩個8位數字表示。<BR> <BR> <BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>引導時<BR> <BR>讓我們看一下PCI是如何工作的,從系統引導開始,因為那時設備被配置。<BR> <BR>當PCI設備被加電時,硬件關閉。或者說,設備只對配置事務響應。加電時,設備沒有映<BR>射到計算機地址空間的內存和I/O端口;所有其它的設備特定的特征,象中斷線,也都被<BR>關閉。<BR> <BR>幸運的是,每個PCI母板都裝有懂得PCI 的固件,根據平臺的不同被稱做BIOS、NVRAM、<BR>或PROM。固件提供對設備配置地址空間的訪問,即使處理器的指令集不提供這樣的能力<BR>。<BR> <BR>在系統引導時,固件對每個PCI外圍執行配置事務,從而為它提供的任何地址區段分配一<BR>個安全的地方。到設備驅動程序訪問設備時,它的內存和I/O區段已經被映射到處理器的<BR>地址空間。驅動程序可以改變這個缺省的分配,但它通常并不這樣做,除非有一些設備<BR>相關的原因要求這樣。<BR> <BR>在Linux中,用戶可以通過讀/proc/pci來查看PCI 設備,這是個文本文件,系統中每個P<BR>CI板子有一項。下面是/proc/pci中一項的例子:<BR> <BR>(代碼344)<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>/proc/pci中每一項是一個設備的設備無關特征的概述,如它的配置寄存器所描述的。例<BR>如,上面這一項告訴我們這個設備有板上內存,已被映射到地址0xf1000000。一些古怪<BR>的細節的含義以后在我介紹過配置寄存器后將會清楚。<BR> <BR> <BR> <BR>檢測設備<BR> <BR>如前面提到的,配置空間的布局是設備無關的。在這一節,我們將看看用來確定外圍的<BR>配置寄存器。<BR> <BR>PCI設備有一個256字節的地址空間。前64個字節是標準化的,而其余的則是設備相關的<BR>。圖15-1顯示了設備無關配置空間的布局。<BR> <BR>如圖所示,有些PCI的配置寄存器是要求的,而有些則是可選的。每個PCI設備必須在必<BR>要寄存器中包含有意義的值,而可選寄存器的內容則以來與實際外圍的能力。可選域并<BR>不使用,除非必要域的內容表明它們是有效的。這樣,必要域斷言了板子的能力,包括<BR>其它域可用與否。<BR> <BR>有意思的是注意到PCI寄存器總是小印地安字節順序的。盡管標準要設計為體系結構無關<BR>的,PCI的設計者有時還是顯示出對PC環境的偏見。驅動程序的作者應該留神字節順序,<BR>特別是訪問多字節的配置寄存器時;在PC上工作的代碼可能在別的平臺上就不行。Linux<BR></P></FONT><FONT
color=#ffffff size=3>
<P>特別是訪問多字節的配置寄存器時;在PC上工作的代碼可能在別的平臺上就不行。Linux<BR>的開發者已經注意了字節排序問題(見下一節“訪問配置空間”),但這個問題還是要<BR>牢記在心。不幸的是,標準函數ntohs和ntohl都不能用,因為網絡字節順序與PCI順序相<BR>反;在Linux2.0中沒有標準函數將PCI字節順序轉換為主機字節順序,每個用單個字節構<BR>成多字節值的驅動程序都應該特別小心地正確處理印地安字節序。核心版本2.1.10引入<BR>了幾個函數來處理這些字節順序問題,它們在第十七章“最近的發展”中“轉換函數”<BR>一節介紹。<BR> <BR>(圖15-1:標準化的PCI配置寄存器)<BR> <BR>描述所有的配置項超出了本書的范圍。通常,與設備一起發布的技術文檔會描述它支持<BR>的寄存器。我們感興趣的是驅動程序如何找到它的設備,以及它如何訪問設備的配置空<BR>間。<BR> <BR>三個PCI寄存器確定一個設備:銷售商,設備ID,和類。每個PCI外圍把它自己的值放入<BR>這些只讀寄存器,驅動程序可以用它們來查找設備。讓我們更仔細地看看這些寄存器:<BR> <BR>銷售商<BR> <BR>這個16位的寄存器確定硬件的生產商。例如,每個Intel的設備都會標上同樣的銷售商號<BR>,8086 hex(是個隨即值?)。這樣的號碼有一個全球的注冊,生產商必須申請一個唯<BR>一的號。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>設備ID<BR> <BR>這是另一個16位寄存器,由生產商選擇;不需要有官方的注冊。這個ID通常與銷售商ID<BR>成對出現,形成一個硬件設備的唯一的32位標志符。我將用單詞“簽名”來指銷售商/設<BR>備ID對。一個設備驅動程序經常以來于簽名來確定它的設備;驅動程序的作者從硬件文<BR>檔中知道要尋找什么值。<BR> <BR>類<BR> <BR>每個外圍設備都屬于一個類。類寄存器是個16位的值,它的高八位確定 “基類”(或組<BR>)。例如,“以太網”和“令牌環”是屬于“網絡”組的兩類,而“串行”和“并行”<BR>類屬于“通信”組。有些驅動程序可以支持幾種類似的設備,它們雖然有不同的簽名,<BR>卻屬于同一類;這些驅動程序可以依賴于類寄存器來確定它們的外圍,如以后所示。<BR> <BR> <BR> <BR>下面的頭文件,宏,以及函數都將被PCI驅動程序用來尋找它的硬件設備:<BR> <BR>#include <linux/config.h><BR> <BR>驅動程序需要知道是否PCI函數在核心是可用的。通過包含這個頭文件,驅動程序獲得了<BR>對CONFIG_宏的訪問,包括CONFIG_PCI(將在下面介紹)。從1.3.73以來,這個頭文件包含<BR></P></FONT><FONT
color=#ffffff size=3>
<P>對CONFIG_宏的訪問,包括CONFIG_PCI(將在下面介紹)。從1.3.73以來,這個頭文件包含<BR>在<linux/fs.h>中;如果想向后兼容,你必須把它顯式地包含。<BR> <BR>CONFIG_PCI<BR> <BR>如果核心包括對PCI BIOS調用的支持,那么這個宏被定義。并不是每個計算機都有PCI總<BR>線,所以核心的開發者應該把 PCI的支持做成編譯時選項,從而在無PCI的計算機上運行<BR>Linux時節省內存。如果CONFIG_PCI沒有定義,那么這個列表中其它的函數都不可用,驅<BR>動程序應使用預編譯的條件語句將PCI支持全都排除在外,以避免加載時的“未定義符號<BR>”錯。<BR> <BR>#include <linux/bios32.h><BR> <BR>這個頭文件聲明了本節介紹的所有的原型,因此一定要被包含。這個頭文件還定義了函<BR>數返回的錯誤代碼的符號值。它在1.2和2.0之間沒有改變,因此沒有可移植性問題。<BR> <BR>int pcibios_present(void)<BR> <BR>由于PCI相關的函數在無PCI的計算機上毫無意義,pcibios_present函數就是告訴驅動程<BR>序計算機是否支持PCI;如果BIOS懂得PCI,它返回一個為真布爾值。即使CONFIG_PCI被<BR>定義了,PCI功能仍是一個運行時選項。因此,你在調用下面介紹的函數之前要檢查一下<BR>pcibios_present,保證計算機支持PCI。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>#include <linux/pci.h><BR> <BR>這個頭文件定義了下面函數使用的所有數值的符號名。并不是所有的設備ID都在這個文<BR>件中列出了,但你在為你的ID,銷售商,類定義宏之前,最好還是看看這個文件。注意<BR>這個文件一直在變大,因為不斷有新設備的符號定義被加入。<BR> <BR>int pcibios_find_device(unsigned short vendor, unsigned short id, unsigned<BR>short index,<BR> <BR> unsigned char *bus, unsigned char *function);<BR> <BR>如果CONFIG_PCI被定義了,并且pcibios_present也是真,這個函數被用來從BIOS請求關<BR>于設備的信息。銷售商/ID對確定設備。index用來支持具有同樣的銷售商/ID對的幾個設<BR>備,下面將會解釋。對這個函數的調用返回設備在總線上的位置以及函數指針。返回代<BR>碼為0表示成功,非0表示失敗。<BR> <BR>int pcibios_find_class(unsigned int class_code, unsigned short index,<BR> <BR> unsigned char *bus, unsigned char *function);<BR> <BR>這個函數和上一個類似,但它尋找屬于特定類的設備。參數class_code傳遞的形式為:1<BR>6位的類寄存器左移八位,這與BIOS接口使用類寄存器的方式有關。這次還是,返回代碼<BR></P></FONT><FONT
color=#ffffff size=3>
<P>6位的類寄存器左移八位,這與BIOS接口使用類寄存器的方式有關。這次還是,返回代碼<BR>為0表示成功,非0表示有錯。<BR> <BR>char *pcibios_strerror(int error);<BR> <BR>這個函數用來翻譯一個PCI錯誤代碼(象pcibios_find_device返回的)為一個字符串。<BR>你也許在查找函數返回的即不是PCIBIOS_SUCCESSFUL(0),也不是PCIBIOS_DEVICE_NOT_F<BR>OUND時(這是當所有的設備都被找過以后所期望返回的錯誤代碼),希望打印一條錯誤<BR>信息。<BR> <BR> <BR> <BR>下面的代碼是驅動程序在加載時檢測設備所使用的典型代碼。如上面所提到的,這個查<BR>找可以基于簽名或者設備類。不管是哪種情況,驅動程序不許存儲bus和function值,它<BR>們在后面確定設備時要用到。function的前五位確定設備,后三位確定函數。<BR> <BR>下面的代碼中,每個設備特定的符號加前綴jail_(另一個指令列表),大寫或小寫依賴<BR>于符號的種類。<BR> <BR>如果驅動程序可以依賴于唯一的銷售商/ID對,下面的循環可以用來初始化驅動程序:<BR> <BR>(代碼347)<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>(代碼348)<BR> <BR>如果這個代碼段只處理由JAIL_VENDOR和JAIL_ID確定的一類PCI設備,那么它是正確的。<BR> <BR> <BR>不過,很多驅動程序非常靈活,能夠同時處理PCI和ISA板子。在這種情況下,驅動程序<BR>僅在沒有檢測到PCI板子或CONFIG_PCIBIOS沒有定義時才探測ISA設備。<BR> <BR>使用pcibios_find_class要求jail_init_dev完成比例子中要多的工作。只要它找到了一<BR>個屬于指定類的設備,這個函數就成功返回,但驅動程序還要確認其簽名也是被支持的<BR>。這個任務通過一系列的條件語句完成,結果是拋棄很多不期望的設備。<BR> <BR>有些PCI外圍包含通用目的的PCI接口芯片和設備特定的電路。所有使用同樣接口芯片的<BR>外圍板子都有同樣的簽名,驅動程序必須進行額外的探測以保證它在處理正確的外圍設<BR>備。因此,有時象jail_init_dev之類的函數必須準備好做一些設備特定的額外的檢測,<BR>以拋棄那些可能有正確簽名的設備。<BR> <BR> <BR> <BR>訪問配置空間<BR> <BR>在驅動程序檢測到設備后,它通常要對三個地址空間讀或寫:內存、端口和配置。特別<BR></P></FONT><FONT
color=#ffffff size=3>
<P>在驅動程序檢測到設備后,它通常要對三個地址空間讀或寫:內存、端口和配置。特別<BR>地,訪問配置空間對驅動程序來說極為重要,以呢這是它發現設備被映射到內存和I/O空<BR>間什么地方的唯一的辦法。<BR> <BR>由于微處理器無法直接訪問配置空間,計算機銷售商必須提供一個辦法來完成它。準確<BR>的實現因此是銷售商相關的,與我們這里的討論無關。幸運的是,這個事務的軟件接口<BR>(下面描述)是標準化的,驅動程序或Linux核心都不需要知道它的細節。<BR> <BR>至于驅動程序,配置空間可以通過8位、16位、32位的數據傳送來訪問。相關函數的原型<BR>在<linux/bios32.h>:<BR> <BR>int pcibios_read_config_byte(unsigned char bus, unsigned char function,<BR> <BR> unsigned char where, unsigned char *ptr);<BR> <BR>int pcibios_read_config_word(unsigned char bus, unsigned char function,<BR> <BR> unsigned char where, unsigned char *ptr);<BR> <BR>int pcibios_read_config_dword(unsigned char bus, unsigned char function,<BR> <BR> unsigned char where, unsigned char *ptr);<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR> 從由bus和function確定的設備的配置空間讀取1,2,4個字節。參數where是從配置<BR>空間開始處的字節偏移。 從配置空間取出的值通過ptr返回,這些函數的返回值是錯誤<BR>代碼。字和雙字函數將剛從小印地安字節序讀出的值轉換為處理器本身的字節序,因此<BR>你并不需要處理字節序。<BR> <BR>int pcibios_write_config_byte(unsigned char bus, unsigned char function,<BR> <BR> unsigned char where, unsigned char val);<BR> <BR>int pcibios_write_config_word(unsigned char bus, unsigned char function,<BR> <BR> unsigned char where, unsigned short val);<BR> <BR>int pcibios_write_config_dword(unsigned char bus, unsigned char function,<BR> <BR> unsigned char where, unsigned int val);<BR> <BR> 向配置空間里寫1,2,4個字節。設備仍由bus和function確定,要寫的值由val傳遞<BR>。字和雙字函數在向外圍設備寫之前將數值轉換為小印地安字節序。<BR> <BR>訪問配置變量的最好辦法是使用在<linux/pci.h>中定義的符號名。例如,下面的兩行程<BR>序通過給pcibios_read_config_byte的where傳遞符號名來獲取一個設備的修正ID。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>序通過給pcibios_read_config_byte的where傳遞符號名來獲取一個設備的修正ID。<BR> <BR>Unsigned char jail_get_revision(unsigned char bus, unsigned char fn)<BR> <BR>{<BR> <BR> unsigned char *revision;<BR> <BR> <BR> <BR> pcibios_read_config_byte(bus,fn, PCI_REVISION_ID,&revision);<BR> <BR> return revision;<BR> <BR>}<BR> <BR>當訪問多字節值時,程序遠一定要記住字節序的問題。<BR> <BR> <BR> <BR>看看一個配置快照<BR> <BR>如果你向瀏覽你系統上PCI設備的配置空間,你可以編譯并加載模塊pci/pcidata.c,它<BR></P></FONT><FONT
color=#ffffff size=3>
<P>如果你向瀏覽你系統上PCI設備的配置空間,你可以編譯并加載模塊pci/pcidata.c,它<BR>在O’Reilly FTP站點上提供的源文件中。<BR> <BR>這個模塊生成一個動態的/proc/pcidata文件,包含有你的PCI設備配置空間的二進制快<BR>照。這個快照在文件每次被讀時更新。/proc/pcidata的大小被限制為PAGE_SIZE字節(這<BR>是動態/proc文件的限制,在第四章“調試技術”中“使用/proc文件系統”一節介紹過)<BR>。這樣,它只列出前PAGESIZE/256個設備的配置內存,意味著16或32個設備(也許對你<BR>的系統已經夠了)。我選擇把/proc/pcidata作成二進制文件,而不是象其它/proc文件<BR>那樣是文本的,就是因為這個大小限制。<BR> <BR>pcidata的另一個限制是它只掃描系統的第一條PCI總線。如果你的系統有到其它PCI總線<BR>的橋,pcidata將忽略它們。<BR> <BR>在/proc/pcidata中設備出現的順序與/proc/pci中相反。這是因為/proc/pci讀的是一個<BR>從頭部生長的鏈表,而/proc/pcidata則是一個簡單的查找循環,它按照取到的順序將所<BR>有的東西輸出。<BR> <BR>例如,我的抓圖器在/proc/pcidata的第二個出現,(目前)有下面的配置寄存器:<BR> <BR>morgana% dd bs=256 skip=1 count=1 if=/proc/pcidata | od –Ax –t x1<BR> <BR>1+0 records in<BR> <BR></P></FONT><FONT
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -