?? 8.html
字號(hào):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312"> <META NAME="GENERATOR" CONTENT="《良友》v2.1, 作者:安富國,http://winking.126.com"> <TITLE>驅(qū)動(dòng)</TITLE></HEAD><BODY style="font-family: 宋體; font-size: 9pt"> <CENTER><TABLE CELLSPACING=10 CELLPADDING=10 WIDTH="60%" BGCOLOR="#FFB693" ><TR><TD ALIGN=CENTER><FONT SIZE=+2><!--標(biāo)題由此開始-->驅(qū)動(dòng)</TD></TR></TABLE></CENTER><p><h3>目 錄</h3><!--目錄由此開始--><A NAME="Content" ID="Content"></A><OL><LI><A HREF="#I471">驅(qū)動(dòng)</A></LI><OL><LI><A HREF="#I472">I/O端口</A></LI><LI><A HREF="#I473">from smth</A></LI><OL><LI><A HREF="#I474">基本結(jié)構(gòu)</A></LI><LI><A HREF="#I475">驅(qū)動(dòng)程序</A></LI><LI><A HREF="#I476">具體實(shí)現(xiàn)</A></LI></OL><LI><A HREF="#I477">PCI</A></LI><LI><A HREF="#I478">loopback</A></LI><LI><A HREF="#I479">Sis 900</A></LI><LI><A HREF="#I734">ISA總線DMA的實(shí)現(xiàn)</A></LI></OL></OL><hr><br><A NAME="I471" ID="I471"></A><center><b><font size=+2>驅(qū)動(dòng)</font></b></center><br> Linux系統(tǒng)支持三種類型的硬件設(shè)備:字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備。字符設(shè)備是直接讀取的,不必使用緩沖區(qū)。例如,系統(tǒng)的串行口/dev/cua0和/dev/cua1。塊設(shè)備每次只能讀取一定大小的塊的倍數(shù),通常一塊是512或者1024字節(jié)。塊設(shè)備通過緩沖區(qū)讀寫,并且可以隨機(jī)地讀寫。塊設(shè)備可以通過它們的設(shè)備文件存取,但通常是通過文件系統(tǒng)存取。只有塊設(shè)備支持掛接的文件系統(tǒng)。網(wǎng)絡(luò)設(shè)備是通過BSD套接字界面存取的。<p> Linux系統(tǒng)支持多種設(shè)備,這些設(shè)備的驅(qū)動(dòng)程序之間有一些共同的特點(diǎn):<br> * 內(nèi)核代碼:設(shè)備驅(qū)動(dòng)程序是系統(tǒng)內(nèi)核的一部分,所以如果驅(qū)動(dòng)程序出現(xiàn)錯(cuò)誤的話,將可能嚴(yán)重地破壞整個(gè)系統(tǒng)。<br> * 內(nèi)核接口:設(shè)備驅(qū)動(dòng)程序必須為系統(tǒng)內(nèi)核或者它們的子系統(tǒng)提供一個(gè)標(biāo)準(zhǔn)的接口。例如,一個(gè)終端驅(qū)動(dòng)程序必須為Linux內(nèi)核提供一個(gè)文件I/O接口;一個(gè)SCSI設(shè)備驅(qū)動(dòng)程序應(yīng)該為SCSI子系統(tǒng)提供一個(gè)SCSI設(shè)備接口,同時(shí)SCSI子系統(tǒng)也應(yīng)為系統(tǒng)內(nèi)核提供文件I/O和緩沖區(qū)。<br> * 內(nèi)核機(jī)制和服務(wù):設(shè)備驅(qū)動(dòng)程序利用一些標(biāo)準(zhǔn)的內(nèi)核服務(wù),例如內(nèi)存分配等。<br> * 可裝入:大多數(shù)的Linux設(shè)備驅(qū)動(dòng)程序都可以在需要時(shí)裝入內(nèi)核,在不需要時(shí)卸載。<br> * 可設(shè)置:Linux系統(tǒng)設(shè)備驅(qū)動(dòng)程序可以集成為系統(tǒng)內(nèi)核的一部分,至于哪一部分需要集成到內(nèi)核中,可以在系統(tǒng)編譯時(shí)設(shè)置。<p><center><A HREF="#Content">[目錄]</A></center><hr><br><A NAME="I472" ID="I472"></A><center><b><font size=+2>I/O端口</font></b></center><br> 關(guān)鍵詞:設(shè)備管理、驅(qū)動(dòng)程序、I/O端口、資源<p> 申明:這份文檔是按照自由軟件開放源代碼的精神發(fā)布的,任何人可以免費(fèi)獲得、使用和重新發(fā)布,但是你沒有限制別人重新發(fā)布你發(fā)布內(nèi)容的權(quán)利。發(fā)布本文的目的是希望它能對(duì)讀者有用,但沒有任何擔(dān)保,甚至沒有適合特定目的的隱含的擔(dān)保。更詳細(xì)的情況請(qǐng)參閱GNU通用公共許可證(GPL),以及GNU自由文檔協(xié)議(GFDL)。<p> 幾乎每一種外設(shè)都是通過讀寫設(shè)備上的寄存器來進(jìn)行的。外設(shè)寄存器也稱為“I/O端口”,通常包括:控制寄存器、狀態(tài)寄存器和數(shù)據(jù)寄存器三大類,而且一個(gè)外設(shè)的寄存器通常被連續(xù)地編址。CPU對(duì)外設(shè)IO端口物理地址的編址方式有兩種:一種是I/O映射方式(I/O-mapped),另一種是內(nèi)存映射方式(Memory-mapped)。而具體采用哪一種則取決于CPU的體系結(jié)構(gòu)。<p> 有些體系結(jié)構(gòu)的CPU(如,PowerPC、m68k等)通常只實(shí)現(xiàn)一個(gè)物理地址空間(RAM)。在這種情況下,外設(shè)I/O端口的物理地址就被映射到CPU的單一物理地址空間中,而成為內(nèi)存的一部分。此時(shí),CPU可以象訪問一個(gè)內(nèi)存單元那樣訪問外設(shè)I/O端口,而不需要設(shè)立專門的外設(shè)I/O指令。這就是所謂的“內(nèi)存映射方式”(Memory-mapped)。<p> 而另外一些體系結(jié)構(gòu)的CPU(典型地如X86)則為外設(shè)專門實(shí)現(xiàn)了一個(gè)單獨(dú)地地址空間,稱為“I/O地址空間”或者“I/O端口空間”。這是一個(gè)與CPU地RAM物理地址空間不同的地址空間,所有外設(shè)的I/O端口均在這一空間中進(jìn)行編址。CPU通過設(shè)立專門的I/O指令(如X86的IN和OUT指令)來訪問這一空間中的地址單元(也即I/O端口)。這就是所謂的“I/O映射方式”(I/O-mapped)。與RAM物理地址空間相比,I/O地址空間通常都比較小,如x86 CPU的I/O空間就只有64KB(0-0xffff)。這是“I/O映射方式”的一個(gè)主要缺點(diǎn)。<p> Linux將基于I/O映射方式的或內(nèi)存映射方式的I/O端口通稱為“I/O區(qū)域”(I/O region)。在討論對(duì)I/O區(qū)域的管理之前,我們首先來分析一下Linux是如何實(shí)現(xiàn)“I/O資源”這一抽象概念的。<p>3.1 Linux對(duì)I/O資源的描述<p> Linux設(shè)計(jì)了一個(gè)通用的數(shù)據(jù)結(jié)構(gòu)resource來描述各種I/O資源(如:I/O端口、外設(shè)內(nèi)存、DMA和IRQ等)。該結(jié)構(gòu)定義在include/linux/ioport.h頭文件中:<p><br> struct resource {<br> const char *name;<br> unsigned long start, end;<br> unsigned long flags;<br> struct resource *parent, *sibling, *child;<br> };<p> 各成員的含義如下:<p> 1. name指針:指向此資源的名稱。<br> 2. start和end:表示資源的起始物理地址和終止物理地址。它們確定了資源的范圍,也即是一個(gè)閉區(qū)間[start,end]。<br> 3. flags:描述此資源屬性的標(biāo)志(見下面)。<br> 4. 指針parent、sibling和child:分別為指向父親、兄弟和子資源的指針。<p> 屬性flags是一個(gè)unsigned long類型的32位標(biāo)志值,用以描述資源的屬性。比如:資源的類型、是否只讀、是否可緩存,以及是否已被占用等。下面是一部分常用屬性標(biāo)志位的定義(ioport.h):<p><br>/*<br> * IO resources have these defined flags.<br> */<br>#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */<p>#define IORESOURCE_IO 0x00000100 /* Resource type */<br>#define IORESOURCE_MEM 0x00000200<br>#define IORESOURCE_IRQ 0x00000400<br>#define IORESOURCE_DMA 0x00000800<p>#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */<br>#define IORESOURCE_READONLY 0x00002000<br>#define IORESOURCE_CACHEABLE 0x00004000<br>#define IORESOURCE_RANGELENGTH 0x00008000<br>#define IORESOURCE_SHADOWABLE 0x00010000<br>#define IORESOURCE_BUS_HAS_VGA 0x00080000<p>#define IORESOURCE_UNSET 0x20000000<br>#define IORESOURCE_AUTO 0x40000000<br>#define IORESOURCE_BUSY 0x80000000<br> /* Driver has marked this resource busy */<p><p> 指針parent、sibling和child的設(shè)置是為了以一種樹的形式來管理各種I/O資源。<p>3.2 Linux對(duì)I/O資源的管理<p> Linux是以一種倒置的樹形結(jié)構(gòu)來管理每一類I/O資源(如:I/O端口、外設(shè)內(nèi)存、DMA和IRQ)的。每一類I/O資源都對(duì)應(yīng)有一顆倒置的資源樹,樹中的每一個(gè)節(jié)點(diǎn)都是一個(gè)resource結(jié)構(gòu),而樹的根結(jié)點(diǎn)root則描述了該類資源的整個(gè)資源空間。<p> 基于上述這個(gè)思想,Linux在kernel/Resource.c文件中實(shí)現(xiàn)了對(duì)資源的申請(qǐng)、釋放及查找等操作。<p> 3.2.1 I/O資源的申請(qǐng)<p> 假設(shè)某類資源有如下這樣一顆資源樹:<p> 節(jié)點(diǎn)root、r1、r2和r3實(shí)際上都是一個(gè)resource結(jié)構(gòu)類型。子資源r1、r2和r3通過sibling指針鏈接成一條單向非循環(huán)鏈表,其表頭由root節(jié)點(diǎn)中的child指針定義,因此也稱為父資源的子資源鏈表。r1、r2和r3的parent指針均指向他們的父資源節(jié)點(diǎn),在這里也就是圖中的root節(jié)點(diǎn)。<p> 假設(shè)想在root節(jié)點(diǎn)中分配一段I/O資源(由圖中的陰影區(qū)域表示)。函數(shù)request_resource()實(shí)現(xiàn)這一功能。它有兩個(gè)參數(shù):①root指針,表示要在哪個(gè)資源根節(jié)點(diǎn)中進(jìn)行分配;②new指針,指向描述所要分配的資源(即圖中的陰影區(qū)域)的resource結(jié)構(gòu)。該函數(shù)的源代碼如下(kernel/resource.c):<p><br> int request_resource(struct resource *root, struct resource *new)<br> {<br> struct resource *conflict;<p> write_lock(&resource_lock);<br> conflict = __request_resource(root, new);<br> write_unlock(&resource_lock);<br> return conflict ? -EBUSY : 0;<br> }<p><p> 對(duì)上述函數(shù)的NOTE如下:<p> ①資源鎖resource_lock對(duì)所有資源樹進(jìn)行讀寫保護(hù),任何代碼段在訪問某一顆資源樹之前都必須先持有該鎖。其定義如下(kernel/Resource.c):<p> static rwlock_t resource_lock = RW_LOCK_UNLOCKED;<p> ②可以看出,函數(shù)實(shí)際上是通過調(diào)用內(nèi)部靜態(tài)函數(shù)__request_resource()來完成實(shí)際的資源分配工作。如果該函數(shù)返回非空指針,則表示有資源沖突;否則,返回NULL就表示分配成功。<p> ③最后,如果conflict指針為NULL,則request_resource()函數(shù)返回返回值0,表示成功;否則返回-EBUSY表示想要分配的資源已被占用。<p> 函數(shù)__request_resource()完成實(shí)際的資源分配工作。如果參數(shù)new所描述的資源中的一部分或全部已經(jīng)被其它節(jié)點(diǎn)所占用,則函數(shù)返回與new相沖突的resource結(jié)構(gòu)的指針。否則就返回NULL。該函數(shù)的源代碼如下<p><br>(kernel/Resource.c):<br>/* Return the conflict entry if you can't request it */<br>static struct resource * __request_resource<br> (struct resource *root, struct resource *new)<br>{<br> unsigned long start = new->start;<br> unsigned long end = new->end;<br> struct resource *tmp, **p;<p> if (end < start)<br> return root;<br> if (start < root->start)<br> return root;<br> if (end > root->end)<br> return root;<br> p = &root->child;<br> for (;;) {<br> tmp = *p;<br>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -