• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            PDF全文下載地址:http://download.csdn.net/source/2320280
            http://bbs.driverdevelop.com/read.php?tid-120461.html

            《USB 軟件結(jié)構(gòu) 》

             

            軟件結(jié)構(gòu)比硬件來(lái)的復(fù)雜很多。因?yàn)樗嗽S多從表面上看不到的層次。比如總線驅(qū)動(dòng)、功能驅(qū)動(dòng)、過(guò)濾驅(qū)動(dòng)等。套用社會(huì)學(xué)的話,這體現(xiàn)了功能應(yīng)用中的分工和統(tǒng)籌。下面我們逐層來(lái)看它們。

              總線驅(qū)動(dòng)
            總線驅(qū)動(dòng)位于驅(qū)動(dòng)棧的最低層,處理復(fù)雜的任務(wù),必須資源分配,子設(shè)備管理。作為下層驅(qū)動(dòng),負(fù)責(zé)處理上層驅(qū)動(dòng)發(fā)下來(lái)的請(qǐng)求。 USB 設(shè)備中的總線驅(qū)動(dòng)主要有兩類:控制器驅(qū)動(dòng)、 HUB 驅(qū)動(dòng);另外還有一個(gè)端口驅(qū)動(dòng)。

            1 ) 控制器驅(qū)動(dòng): Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 。

            首先解釋一下 HCI ,它是主機(jī)控制接口( Host Control Interface )的縮寫。前后一共有三種 HCI 協(xié)議出現(xiàn): USB 1.1 時(shí)代,有 OHCI (開發(fā) HCI )協(xié)議和 UHCI (通用 HCI )協(xié)議; USB2.0 時(shí)代,有 EHCI (擴(kuò)展 HCI )協(xié)議。三個(gè)協(xié)議分別對(duì)應(yīng)了上面的三個(gè)驅(qū)動(dòng)程序。因?yàn)?USB 是向后兼容的,所以 UsbEHC.sys 中也包含了 UsbOhci 和 UsbUhci 的功能。請(qǐng)看下圖。

             


            圖 1 總線設(shè)備 

            上圖中設(shè)備 1 、 3 是控制器設(shè),從名稱上就可以區(qū)別它們的不同: Universal Host Controller 和 Enhanced Host Controller 。所以設(shè)備 1 的驅(qū)動(dòng)程序是 USBUhci.sys ,設(shè)備 3 的驅(qū)動(dòng)程序是 USBEhci.sys 。

            這說(shuō)明同一臺(tái)主機(jī)特別是筆記本電腦中, 1.1 和 2.0 的控制器并存,像筆記本電腦中的鍵盤通過(guò)內(nèi)置 USB 接口與系統(tǒng)連接,其數(shù)據(jù)吞吐量小,只需要 1.1 的控制器就能滿足;而暴露在外供用戶使用的接口則需要 2.0 。

            2) Hub 驅(qū)動(dòng): UsbHub.sys 。 Hub 驅(qū)動(dòng)是所有 USB 設(shè)備的父驅(qū)動(dòng)。下圖描繪了這一景況:


            圖2


            上圖中看到, Hub 驅(qū)動(dòng)的子設(shè)備要不是獨(dú)立設(shè)備,如左側(cè)圖;要么是一個(gè)含有多個(gè)子設(shè)備的父設(shè)備,如右側(cè)圖。 Hub 驅(qū)動(dòng)只為直系子設(shè)備創(chuàng)建唯一的物理設(shè)備對(duì)象。上圖中, Hub 驅(qū)動(dòng)為設(shè)備 1 和設(shè)備 2 創(chuàng)建物理設(shè)備對(duì)象,但并不為設(shè)備 2 的子設(shè)備創(chuàng)建物理設(shè)備對(duì)象。

            3 ) Port 驅(qū)動(dòng): UsbPort.sys 。這是個(gè)框架驅(qū)動(dòng),比較復(fù)雜,很少人會(huì)用到。而上面的 Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 其實(shí)都是他的微端口驅(qū)動(dòng)。對(duì)于這么偏門的框架,不提也罷。

              系統(tǒng)類驅(qū)動(dòng)
            所以出現(xiàn)類驅(qū)動(dòng),體現(xiàn)了 USB 總線在應(yīng)用上的繁榮景象。只有用得多了,才有被歸類的可能。就像人類社會(huì)中有幾百個(gè)國(guó)家,幾千個(gè)民族,正是體現(xiàn)了人類這個(gè)團(tuán)體的多樣性與繁榮。只有在“多”的基礎(chǔ)上,分類才是有必要的;少數(shù)人的小群體,再怎么獨(dú)立特行,也都不足以被分類,甚至定義為“ XX 民族”。

            USB 設(shè)備包含很多的通用的功能類,比如: USB 集線器設(shè)備, USB HID 設(shè)備, USB 音頻設(shè)備, USB MIDI 設(shè)備, USB 存儲(chǔ)設(shè)備。為了讓開發(fā)工作變得更加簡(jiǎn)單, Windows 操作系統(tǒng)為他們提供了系統(tǒng)驅(qū)動(dòng)程序。

            大部分時(shí)候,系統(tǒng)類驅(qū)動(dòng)就是功能驅(qū)動(dòng)。下表是系統(tǒng)提供的 USB 類驅(qū)動(dòng)。

             

            USB 類名稱
             類代碼
             驅(qū)動(dòng)名稱
             系統(tǒng)支持
             
            藍(lán)牙設(shè)備
             0xE0
             Bthusb.sys
             Vista 、 XP
             
            USB 芯片智能卡接口設(shè)備
            (CCID)
             0x0B
             Usbccid.sys
             2K 、 XP 、 2K3 、 Vista 、 2K8
             
            Hub 設(shè)備
             0x09
             Usbhub.sys
             2K 、 XP 、 2K3 、 Vista 、 2K8
             
            HID 設(shè)備
             0x03
             Hidusb.sys
             2K 、 XP 、 2K3 、 Vista 、 2K8
             
            USB 大容量存儲(chǔ)設(shè)備
             0x08
             Usbstor.sys
             2K 、 XP 、 2K3 、 Vista 、 2K8
             
            打印設(shè)備
             0x07
             Usbprint.sys
             2K 、 XP 、 2K3 、 Vista 、 2K8
             
            掃描設(shè)備
             0x06
             WpdUsb.sys Usbscan.sys
             2K 、 XP 、 2K3 、 Vista 、 2K8
             
            媒體傳輸設(shè)備 (MTP)
             0x06
             WpdUsb.sys
             XP 、 2K3 、 Vista 、 2K8
             
            音頻設(shè)備
             0x01
             Usbaudio.sys
             2K 、 XP 、 2K3 、 Vista 、 2K8
             
            Modem 設(shè)備 (CDC)
             0x02
             Usbser.sys
             2K 、 XP 、 2K3 、 Vista 、 2K8
             
            視頻設(shè)備 (UVC)
             0x0E
             Usbvideo.sys
             XP 、 Vista
             

             

            表 1   系統(tǒng)提供的 USB 類驅(qū)動(dòng)

             

            功能驅(qū)動(dòng)
             

            不是所有的 USB 設(shè)備都有類驅(qū)動(dòng),但功能驅(qū)動(dòng)卻是它們唯一的身份證。沒(méi)有功能驅(qū)動(dòng),設(shè)備就不足以在系統(tǒng)中存在。它的作用是為設(shè)備創(chuàng)造一個(gè)獨(dú)一無(wú)二的內(nèi)核設(shè)備對(duì)象( DEVICE_OBJCET ),并因此而在需要的時(shí)候,系統(tǒng)能夠通過(guò)此內(nèi)核設(shè)備對(duì)象找到它。

            如果要讓用戶層也能夠知道并使用 USB 設(shè)備,功能驅(qū)動(dòng)更加不可少。它為設(shè)備在用戶程序可見(jiàn)的名字空間中,為它起一個(gè)別名,這個(gè)別名可以是一個(gè)符號(hào)鏈接,也可以是一個(gè)由 GUID 定義的設(shè)備 Interface 。通過(guò)對(duì)這個(gè)別名進(jìn)行操作,也就是對(duì)設(shè)備本身進(jìn)行操作。

            OK ,上面的話說(shuō)得太滿了,有例外的!唯一的例外是以 RAW 模式驅(qū)動(dòng)的設(shè)備。這種設(shè)備直接由總線驅(qū)動(dòng)來(lái)驅(qū)動(dòng)其工作,不需要功能驅(qū)動(dòng)。這種例子真的不多見(jiàn),也許只有很很底層的控制器設(shè)備、 Hub 設(shè)備之類,才會(huì)這樣做。對(duì)于 RAW 模式驅(qū)動(dòng)的設(shè)備,當(dāng)收到 IRP_MN_QUERY_CAPABILITIES 查詢請(qǐng)求的時(shí)候,在返回的 DEVICE_CAPABILITIES 結(jié)構(gòu)體中,必須將 RawDeviceOK 位設(shè)置為 TRUE 。

            建議讀者在需要的時(shí)候使用 WinOBJ.exe 工具查看系統(tǒng)空間中的設(shè)備與別名。

             

            父驅(qū)動(dòng)與混合設(shè)備
             

            通過(guò)一定的設(shè)置, USB 設(shè)備中的每個(gè)接口可以擁有不同的 Class 和 Protocol 定義,從而實(shí)現(xiàn):一個(gè)設(shè)備,多個(gè)功能。這種設(shè)備被稱為混合設(shè)備。混合設(shè)備的前提是擁有多個(gè)接口,對(duì)單接口設(shè)備談 “ 混合 ” 是沒(méi)有意義的。

            滿足了如下兩個(gè)條件的多接口 USB 設(shè)備,被系統(tǒng)認(rèn)為是混合設(shè)備:

            1.         設(shè)備描述符中,設(shè)備類的值為 0 : (bDeviceClass , bDeviceSubClass, bDeviceProtocol ) = (0, 0, 0);

            2.         只有唯一的配置描述符,即設(shè)備描述符中: (bNumConfigurations ) = (1)

             

            從 WinXP SP2 以后,還支持另外一種混合設(shè)備的判別方式,稱作: Interface Association Descriptor ( IAD )。其實(shí) IAD 描述符是用來(lái)組織 “ 接口組( Interfaces Group ) ” 的。配置描述符中可以有多個(gè) IAD 存在,如果將某兩個(gè)接口將組成接口組,那么首先這兩個(gè)接口必須是緊挨著的,其次,必須有一個(gè) IAD 描述符位于這兩個(gè)接口描述符的前面,也必須是緊挨著的, IAD 描述符中的 bFirstInterface 用來(lái)描述接口組中的第一個(gè)接口 ID , bInterfaceCount 用來(lái)描述接口組中包含多少個(gè)接口。這樣接口集合 : [bFirstInterface, bFirstInterface+bInterfaceCount) 為一個(gè)接口組。

            當(dāng)然,能夠用上 IAD 的設(shè)備,一定是有多接口存在了,否則就是多此一舉了。 IAD 描述符的識(shí)別和實(shí)現(xiàn)是通用父驅(qū)動(dòng)完成的,所以有 IAD 支持的設(shè)備,都被認(rèn)作混合設(shè)備。而識(shí)別設(shè)備是否有 IAD 支持,是通過(guò)設(shè)備描述符中的如下值判斷的:

            (bDeviceClass, bDeviceSubClass, bDeviceProtocol) = (0xEF, 0x02, 0x01)

            IAD 普及率不是很廣,一個(gè)原因就是 XP sp2 以后的操作系統(tǒng)版本才對(duì)它支持,這樣如果把設(shè)備插入到 Win 2000 甚至 XP SP1 上,都不能被正確識(shí)別。這樣,廠商可能必須為不同的系統(tǒng)寫兩套驅(qū)動(dòng)程序。

            我們下面來(lái)說(shuō)說(shuō)當(dāng)一個(gè)多接口混合設(shè)備插入電腦后,系統(tǒng)是如何識(shí)別它,并為它加載驅(qū)動(dòng)的。這里大家要注意到一點(diǎn),就是系統(tǒng)是如何把一個(gè)設(shè)備,通過(guò)多接口,識(shí)別為多個(gè)物理設(shè)備的。

            一開始,系統(tǒng) PNP 管理器安裝常規(guī),讀取并分析 USB 設(shè)備描述符,然后為它分配如下設(shè)備 ID :

             

            USB\VID_vvvv&PID_pppp

            USB\VID_vvvv&PID_pppp&REV_rrrr

            (vvvv, pppp, rrrr: 4 位 16 進(jìn)制數(shù),分別代表了廠商 ID ,產(chǎn)品 ID , USB 版本。對(duì)應(yīng)于設(shè)備描述符中的這些值: idVendor/ idProduct/ bcdDevice)

             

            和兼容 ID :

             

            USB\COMPOSITE

             

            PNP 管理器首先按照常規(guī),根據(jù)上述的設(shè)備 ID 為設(shè)備尋找合適的驅(qū)動(dòng)程序:根據(jù)設(shè)備 ID 到注冊(cè)表的設(shè)備安裝信息庫(kù)(對(duì)應(yīng)于 Enum 和 Class 兩個(gè)鍵)中進(jìn)行搜索,如果找到了安裝記錄,就根據(jù)記錄中的信息加載驅(qū)動(dòng)程序。

            問(wèn)題是如果找不到合法的記錄怎么辦?這時(shí)候就用的著兼容 ID 了,兼容 ID “ USB\COMPOSITE ”是在系統(tǒng)中有注冊(cè)記錄的,并且就對(duì)應(yīng)著通用父設(shè)備驅(qū)動(dòng)( USBCCGP.sys) 。于是,系統(tǒng)為混合設(shè)備加載通用父設(shè)備驅(qū)動(dòng)。讀者可到目錄 Windows\Inf 下查看 usb.inf 文件,此文件中包含了通用父設(shè)備驅(qū)動(dòng)的安裝信息。

            通用父設(shè)備驅(qū)動(dòng)通過(guò)分析配置描述符,完成兩個(gè)動(dòng)作:首先為每個(gè) USB 接口分配一個(gè)的設(shè)備 ID 和兼容 ID ;然后為每個(gè) USB 接口創(chuàng)建一個(gè)物理設(shè)備對(duì)象( Physical Device Object )。設(shè)備 ID 形式如下:

             

            USB\VID_vvvv&PID_pppp&MI_mm

            USB\VID_vvvv&PID_pppp&REV_rrrr&MI_mm

            (mm: 兩個(gè) 16 位數(shù)字表示的接口號(hào) )

             

            根據(jù)接口描述符中的 clsss 類型,分配兼容 ID ,其形式如下:

             

            USB\CLASS_cc

            USB\CLASS_cc&SUBCLASS_ss

            USB\CLASS_cc&SUBCLASS_ss&PROT_pp

            (cc/ ss / pp: 兩位 16 進(jìn)制數(shù)。分別對(duì)應(yīng)于接口描述符中的: bInterfaceClass/ bInterfaceSubClass/ bInterfaceProtocol)

             

            通用父設(shè)備驅(qū)動(dòng)為每個(gè)接口創(chuàng)建了物理設(shè)備對(duì)象后,將接口的設(shè)備 ID 、兼容 ID 信息提交給 PNP 管理器, PNP 管理器就有責(zé)任為這些“虛假的”物理設(shè)備安裝驅(qū)動(dòng)。仍然重復(fù)上面的過(guò)程:根據(jù)每個(gè)接口的設(shè)備 ID 和兼容 ID 在注冊(cè)表的設(shè)備安裝信息庫(kù)中搜索,試圖找到相關(guān)的安裝記錄,如果找到了,就為接口加載相應(yīng)的驅(qū)動(dòng)程序。如果找不到,系統(tǒng)就會(huì)彈出“發(fā)現(xiàn)新設(shè)備”的對(duì)話框,啟動(dòng)驅(qū)動(dòng)安裝向?qū)?。此時(shí)用戶需為這些接口手動(dòng)安裝驅(qū)動(dòng)。

            所以,對(duì)于多接口的混合設(shè)備( composite device ),其設(shè)備驅(qū)動(dòng)的安裝分為兩個(gè)過(guò)程,先嘗試安裝混合驅(qū)動(dòng),如果找不到,就默認(rèn)安裝通用父設(shè)備驅(qū)動(dòng);接下來(lái)通用父設(shè)備驅(qū)動(dòng)為每個(gè)接口分配設(shè)備 ID ,并要求系統(tǒng)為每個(gè)接口啟動(dòng) PNP 過(guò)程。

            最后,那么來(lái)說(shuō),在 USB 混合設(shè)備中,一定是 X 個(gè)接口對(duì)應(yīng)于 X 個(gè)功能設(shè)備(有一個(gè)物理設(shè)備對(duì)象)嗎?一般情況下是這樣的,但也有例外,這就是接口組:在符合一定條件的情況下,多個(gè)接口中的某些接口可以被組合為一個(gè)接口組,一個(gè)接口組代表一個(gè)功能,父設(shè)備驅(qū)動(dòng)只為之創(chuàng)建一個(gè)物理設(shè)備對(duì)象。

            接口組的詳情,大家看后面的章節(jié)內(nèi)容。

             

            過(guò)濾驅(qū)動(dòng)
             

            過(guò)濾驅(qū)動(dòng)無(wú)處不在。在很多時(shí)候,它被稱作 Hook ,是一種 Hack 手段;很多時(shí)候它又是必不可少的,這種技術(shù)甚至被操作系統(tǒng)自己使用。沒(méi)有哪一個(gè)殺防毒軟件不使用過(guò)濾驅(qū)動(dòng),我們?cè)谑褂镁W(wǎng)上銀行的時(shí)候,會(huì)用到一些安全控件,基本上都借助了過(guò)濾驅(qū)動(dòng)的技術(shù)。

            過(guò)濾驅(qū)動(dòng)可以位于任何一層驅(qū)動(dòng)的上面,或下面。過(guò)濾的對(duì)象也包括已經(jīng)存在于系統(tǒng)中的其他的過(guò)濾驅(qū)動(dòng)。當(dāng)它位于某層驅(qū)動(dòng)( D 驅(qū)動(dòng))上面的時(shí)候,所有目標(biāo)發(fā)往 D 驅(qū)動(dòng)的請(qǐng)求,都首先被它截取;當(dāng)它位于某層驅(qū)動(dòng)下面的時(shí)候,所有和 D 驅(qū)動(dòng)相關(guān)的從更底層驅(qū)動(dòng)反饋回來(lái)的的“完成消息”都預(yù)先被過(guò)濾驅(qū)動(dòng)截取。這正是它威力強(qiáng)大的原因所在。對(duì)于被過(guò)濾的驅(qū)動(dòng)來(lái)說(shuō),過(guò)濾驅(qū)動(dòng)簡(jiǎn)直就是它的先知了。

            但使用過(guò)濾驅(qū)動(dòng),要很慎重。很容易把系統(tǒng)搞得很不穩(wěn)定。讀者在寫過(guò)濾驅(qū)動(dòng)的時(shí)候,要明白這樣一件事:你想過(guò)濾誰(shuí),得先了解誰(shuí);好像你追求一個(gè)人,要先認(rèn)識(shí)這個(gè)人。否則死機(jī)藍(lán)屏都會(huì)與你不期而遇。

             

            USB 驅(qū)動(dòng)棧、設(shè)備棧
             

            請(qǐng)大家不要把這里的“棧”理解成“程序堆棧”的那個(gè)棧,朋友們要回到這個(gè)字最簡(jiǎn)單的本意來(lái)理解它,而不是想象成一個(gè)數(shù)據(jù)結(jié)構(gòu)。就看成草垛柴堆一樣。

            驅(qū)動(dòng)棧、設(shè)備棧本質(zhì)上是并行概念。驅(qū)動(dòng)之間的聯(lián)系是通過(guò)設(shè)備對(duì)象進(jìn)行的,所以驅(qū)動(dòng)之間是間接聯(lián)系的,驅(qū)動(dòng)棧多少也就只是概念上的,他用來(lái)表示一個(gè)設(shè)備能夠在系統(tǒng)中識(shí)別、運(yùn)行,從上到下中共需要哪些驅(qū)動(dòng)程序支持。

            設(shè)備棧則是由據(jù)可查的。系統(tǒng)中每個(gè) DevNode 就表現(xiàn)一個(gè)設(shè)備棧。可以這樣理解,多個(gè)設(shè)備棧,串聯(lián)成了驅(qū)動(dòng)棧。

            使用 WinDBG 的 !devnode 命令,可以列舉系統(tǒng)中的設(shè)備樹。下圖截取了 CY001 相關(guān)片段。

             


            圖 3 CY001 DevNode 片段 

            上圖中紅色框標(biāo)出的三個(gè) DevNode ,正好對(duì)應(yīng)于三個(gè)內(nèi)核驅(qū)動(dòng),建立了 CY001 的驅(qū)動(dòng)棧。從上到下分別是控制器驅(qū)動(dòng) (usbEHCI) ,集線器驅(qū)動(dòng) (usbHUB) ,和功能驅(qū)動(dòng) (CY001) 。下圖以 CY001 為例,更加清晰詳細(xì)地描繪了驅(qū)動(dòng)棧、設(shè)備棧的面貌。


            圖 4 CY001 的驅(qū)動(dòng)棧和設(shè)備棧 

            上圖是單接口 CY001 設(shè)備的驅(qū)動(dòng)棧、設(shè)備棧全圖。也適用于所有其他的單接口 USB 設(shè)備。如果是多接口混合設(shè)備,就要多一個(gè)通用父驅(qū)動(dòng),稍微復(fù)雜一點(diǎn)。

            最上面是可能存在的過(guò)濾驅(qū)動(dòng),因?yàn)橹皇?#8220;可能存在”,所以都用虛框表示。其實(shí)過(guò)濾驅(qū)動(dòng)可以存在于設(shè)備棧的任何一個(gè)位置,而不僅僅是最上層。筆者不可能盡皆畫全,以上層過(guò)濾為例,能說(shuō)明問(wèn)題也就可以了。

            過(guò)濾驅(qū)動(dòng)生成的過(guò)濾設(shè)備對(duì)象,掛載到 CY001 驅(qū)動(dòng)生成的功能設(shè)備對(duì)象上;這樣所有發(fā)送給 CY001 功能設(shè)備對(duì)象的請(qǐng)求,過(guò)濾設(shè)備對(duì)象總是先得到。根集線器生成了 CY001 驅(qū)動(dòng)的物理設(shè)備對(duì)象。三個(gè)設(shè)備連在一起,就是 CY001 驅(qū)動(dòng)程序的設(shè)備棧。

            但還沒(méi)有完,根集線器驅(qū)動(dòng)并不是最底層驅(qū)動(dòng),他必須得到控制器驅(qū)動(dòng)的支持,于是加上可能存在的過(guò)濾驅(qū)動(dòng),由此形成中間的那條設(shè)備棧。

            仍舊未結(jié)束,控制器驅(qū)動(dòng)也不是直接和系統(tǒng)交互的,而是通過(guò)更底層的系統(tǒng)總線如 PCI 、 ACPI 進(jìn)行的。這樣,右側(cè)的第三條設(shè)備棧也形成了。

            由此也可以看出設(shè)備棧、驅(qū)動(dòng)棧之間的關(guān)系了。驅(qū)動(dòng)棧是形而上的,設(shè)備棧是形而下的。


            本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/changpei/archive/2010/05/06/5562542.aspx

            posted @ 2010-11-30 10:36 wrh 閱讀(1772) | 評(píng)論 (0)編輯 收藏

            在MFC類庫(kù)提供了CWnd::OnCtlColor函數(shù),在工作框架的子窗口被重畫時(shí)將調(diào)用該成員函數(shù).因此可以重載WM_CTLCOLOR消息的響應(yīng)函數(shù).此函數(shù)的原型:
              
            afx_msg HBRUSH OnCtlColor(CDC *pDC,CWnd *pWnd,UINT nCtlColor);
                       參數(shù)nCtlColor用于指定控件的類型,可以是:
                       .CTLCOLOR_BTN                按鈕控件
                       .CTLCOLOR_DLG                對(duì)話框
                       .CTLCOLOR_EDIT               編輯框
                       .CTLCOLOR_LISTBOX            列表控件
                       .CTLCOLOR_MSGBOX             消息控件
                       .CTLCOLOR_SCROLLBAR 滾動(dòng)條控件
                       .CTLCOLOR_STATIC             靜態(tài)控件
            [程序?qū)崿F(xiàn)]
                       假設(shè)你已有了名為My的對(duì)話框工程.你有了一個(gè)STATIC的控件,ID為IDC_STATIC1.
              
            HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
                       {
                    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
              
                    // TODO: Change any attributes of the DC here
                       if (nCtlColor==CTLCOLOR_STATIC)

                          {
                                pDC->SetTextColor(RGB(255,0,0));
              
            //字體顏色
                                pDC->SetBkColor(RGB(0, 0, 255));   //字體背景色  

                            }
                   
            // TODO: Return a different brush if the default is not desired
                    return hbr;
                       }


            如果要指定某個(gè)特定控件可以這樣寫:ID為IDC_STATIC1

            if (pWnd->GetDlgCtrlID()==IDC_STATIC1)
            {
                   pDC->SetTextColor(
            RGB(255,0,0));  //設(shè)置字體顏色
                   pDC->SetBkMode(TRANSPARENT); //設(shè)置字體背景為透明
            // TODO: Return a different brush if the default is not desired
              return (HBRUSH)::GetStockObject(BLACK_BRUSH);  // 設(shè)置背景色
            }
            else
            return hbr;

            【注】

            BLACK_BRUSH:黑色

            WHITE_BRUSH:白色

            GRAY_BRUSH:灰色

            NULL_BRUSH:透明

            HOLLOW_BRUSH :透明











            1.為對(duì)話框類添加WM_CTLCOLOR的響應(yīng)函數(shù)afx_msg HBRUSH OnCtlColor(CDC*pDC,CWnd*pWnd,UINT nCtlColor){...}

            2.定義一個(gè)m_brush(CBrush類型)的成員變量和一個(gè)m_font(CFont類型)成員變量,在構(gòu)造函數(shù)中初始化,例如:m_brush.CreateSolidBrush(RGB(0,0,255));m_font.CreatePointFont(200,"華文行楷");

            3.改變背景顏色和文本顏色和字體:在OnCtlColor()添加代碼:

            if(pWnd->GetDlgCtrlID()==IDC_LINE_STYLE/*控件ID*/)

            {

            pDC->SetTextColor(RGB(255,0,0));

            pDC->SetBkMode(TRANSPARENT);//設(shè)置文本背景色為透明

            pDC->SelectObject(&m_font);//設(shè)置字體

            return m_brush;//設(shè)置控件背景顏色

            }

            //對(duì)于按鈕來(lái)說(shuō)上面的方法無(wú)效


            posted @ 2010-11-29 11:24 wrh 閱讀(2493) | 評(píng)論 (0)編輯 收藏

            VC通用控件自適應(yīng)屏幕類

            此為我程序中的一個(gè)類,本用于WinCE,但在桌面系統(tǒng)上也同樣適用!

            使用方法(在WM_INITDIALOG或WM_CREATE消息中加入):

            CWindowAnchor::BeginControlBound(hwnd)

             

            手動(dòng)調(diào)整控件位置:

            CWindowAnchor::AddControl(hwnd,IDC_STATIC1,&WindowAnchorInfo(WAT_LEFT|WAT_TOP,2,8,4,10));
            CWindowAnchor::AddControl(hwnd,IDC_STATIC1,
            &WindowAnchorInfo(WAT_LEFT|WAT_TOP|WAT_RIGHT,2,20,4,10));
            CWindowAnchor::AddControl(hwnd,IDC_STATIC1,
            &WindowAnchorInfo(WAT_LEFT|WAT_TOP,2,8,40,10));


            自動(dòng)調(diào)整控件位置(跟據(jù)設(shè)計(jì)時(shí)資源文件中控件的大小及位置):

            CWindowAnchor::AddControl(hwnd,IDC_STATIC1,&WindowAnchorInfo(WAT_LEFT|WAT_TOP));
            CWindowAnchor::AddControl(hwnd,IDC_STATIC1,
            &WindowAnchorInfo(WAT_LEFT|WAT_TOP|WAT_RIGHT));

             

            響應(yīng)WM_SIZE消息:

            case WM_SIZE:
                
            return HANDLE_WM_SIZE(hwndDlg,wParam,lParam,CWindowAnchor::OnSize);

             

            響應(yīng)WM_DESTROY消息:

            CWindowAnchor::EndControlBound(hwnd);

             

             

            代碼:

            #pragma once
            #include 
            <map>

            #if defined (_MSC_VER)
                
            #pragma warning(disable: 4786)
            #endif

            /*用于WindowAnchorInfo結(jié)構(gòu)的停靠類型*/
            typedef 
            enum WindowAnchorType
            {
                WAT_TOP
            =0x0001,
                WAT_LEFT
            =0x0002,
                WAT_RIGHT
            =0x0004,
                WAT_BOTTOM
            =0x0008
            };

            /*控件定位描述信息*/
            typedef 
            struct WindowAnchorInfo{
                DWORD dwAnchor; 
            //WAT_*
                RECT rcOriginalRect; //控件的原始邊距,如果為空則自動(dòng)獲?。▋H適用于WM_INIT中)
                
                WindowAnchorInfo(DWORD pAnchor
            =WAT_TOP|WAT_LEFT,LONG pLeft=0,LONG pTop=0,LONG pRight=0,LONG pBottom=0)
                {
                    dwAnchor
            =pAnchor;
                    rcOriginalRect.left
            =pLeft;
                    rcOriginalRect.top
            =pTop;
                    rcOriginalRect.right
            =pRight;
                    rcOriginalRect.bottom
            =pBottom;
                };
            };

            typedef std::map
            <HWND,WindowAnchorInfo> ControlHashtable;

            typedef 
            struct{
                INT nWidth; 
            //對(duì)話框?qū)挾?/span>
                INT nHeight; //對(duì)話框高度
                INT nMinHeight; //對(duì)話框最小高度
                ControlHashtable mapControls; //對(duì)話框所有子控件
            }WindowAnchorDialog;

            /*
             * 對(duì)話框子控件定位
             * 2009.03.29 By Frank
            */
            static class CWindowAnchor
            {
            private:
                
            static BOOL _ReSize(HWND hwndDlg, const WindowAnchorDialog *wad, HWND hwndCtrl, const WindowAnchorInfo *wai);

            public:
                
            /*
                 * 開始調(diào)整(此調(diào)用中會(huì)獲取當(dāng)前對(duì)話框的大小,如果在設(shè)計(jì)后要調(diào)整對(duì)話框大小,請(qǐng)先調(diào)用此方法)
                 * hwndDlg:對(duì)話框句柄
                
            */
                
            static BOOL BeginControlBound(HWND hwndDlg);

                
            /*
                 * 結(jié)束調(diào)整
                 * hwndDlg:對(duì)話框句柄
                
            */
                
            static BOOL EndControlBound(HWND hwndDlg);

                
            /*
                 * 添加一個(gè)控件到調(diào)整列表
                 * hWndInsertAfter:HWND_BOTTOM |HWND_NOTOPMOST | HWND_TOP | HWND_TOPMOST |-2不改變 | Is Hwnd
                
            */
                
            static BOOL AddControl(HWND hwndDlg, INT nCtrlID, WindowAnchorInfo *wai, HWND hWndInsertAfter=(HWND)-2);

                
            /*
                 * 調(diào)整一個(gè)指定控件的大小
                
            */
                
            static BOOL ReSize(HWND hwndDlg, HWND hwndCtrl);

                
            /*
                 * 響應(yīng)WM_SIZE消息
                
            */
                
            static BOOL OnSize(HWND hwndDlg, UINT state, int cx, int cy);

                
            /*相應(yīng)WM_VSCROLL消息*/
                
            static BOOL OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos);
            };

             

            下載地址:單擊下載

            posted @ 2010-11-29 11:14 wrh 閱讀(812) | 評(píng)論 (0)編輯 收藏

            1.首先在初始化函數(shù)中,F(xiàn)ormView在OnInitialUpdate(),Dialog在OnInitDialog()中初始化控件的大小。

            view plaincopy to clipboardprint?
            01.//開始初始化控件大小  
            02. m_IsInitialed = false;  
            03. 
            04. CRect m_ClientRect;  
            05. this->GetClientRect(&m_ClientRect);  
            06. CSize m_Forsize;  
            07. m_Forsize = GetTotalSize();//在資源編輯器中定好大小后,程序運(yùn)行時(shí)大小(不管最大化和最小化,該大小均為同一個(gè)值),客戶區(qū)大于或等于顯示的大小  
            08. double m_x = (double)m_ClientRect.Width() / m_Forsize.cx;//寬度方向發(fā)大倍數(shù)  
            09. double m_y = (double)m_ClientRect.Height() / m_Forsize.cy;//高度方向發(fā)大倍數(shù)  
            10. 
            11. //調(diào)整控件的大小  
            12. CWnd *pWnd = NULL;   
            13. pWnd = GetWindow(GW_CHILD);  
            14. while(pWnd)//判斷是否為空,因?yàn)閷?duì)話框創(chuàng)建時(shí)會(huì)調(diào)用此函數(shù),而當(dāng)時(shí)控件還未創(chuàng)建  
            15. {  
            16.  CRect rect;   //獲取控件變化前大小  
            17.  pWnd->GetWindowRect(&rect);  
            18.  ScreenToClient(&rect);//將控件大小轉(zhuǎn)換為在對(duì)話框中的區(qū)域坐標(biāo)  
            19.  m_ControlRect.insert(pair<int, CRect>(pWnd->GetDlgCtrlID(), rect));//保存控件的初始大小,以便在OnSize函數(shù)中繼續(xù)使用  
            20.  int width = rect.Width();  
            21.  int height = rect.Height();  
            22. 
            23.  WCHAR szBuf[256];  
            24.  GetClassName(pWnd->m_hWnd,szBuf,256);           
            25.  if( _tcsicmp(szBuf,_T("Edit")) == 0)     
            26.  {   
            27.   //Edit只是位置變化,大小沒(méi)有變  
            28.   rect.top = m_y * rect.top;  
            29.   rect.left = m_x * rect.left;  
            30.   rect.bottom = rect.top + height;  
            31.   rect.right = rect.left + width;  
            32.  }  
            33.  else 
            34.  {  
            35.   //其它控件位置和大小均變化  
            36.   rect.top = m_y * rect.top;  
            37.   rect.left = m_x * rect.left;  
            38.   rect.bottom = m_y * rect.bottom;  
            39.   rect.right = m_x * rect.right;  
            40.  }  
            41. 
            42.  pWnd->MoveWindow(&rect);//設(shè)置控件大小  
            43.  pWnd = pWnd->GetWindow(GW_HWNDNEXT);  
            44. }  
            45.   
            46. //控件初始化結(jié)束  
            47. m_IsInitialed = true; 
            //開始初始化控件大小
             m_IsInitialed = false;

             CRect m_ClientRect;
             this->GetClientRect(&m_ClientRect);
             CSize m_Forsize;
             m_Forsize = GetTotalSize();//在資源編輯器中定好大小后,程序運(yùn)行時(shí)大小(不管最大化和最小化,該大小均為同一個(gè)值),客戶區(qū)大于或等于顯示的大小
             double m_x = (double)m_ClientRect.Width() / m_Forsize.cx;//寬度方向發(fā)大倍數(shù)
             double m_y = (double)m_ClientRect.Height() / m_Forsize.cy;//高度方向發(fā)大倍數(shù)

             //調(diào)整控件的大小
             CWnd *pWnd = NULL;
             pWnd = GetWindow(GW_CHILD);
             while(pWnd)//判斷是否為空,因?yàn)閷?duì)話框創(chuàng)建時(shí)會(huì)調(diào)用此函數(shù),而當(dāng)時(shí)控件還未創(chuàng)建
             {
              CRect rect;   //獲取控件變化前大小
              pWnd->GetWindowRect(&rect);
              ScreenToClient(&rect);//將控件大小轉(zhuǎn)換為在對(duì)話框中的區(qū)域坐標(biāo)
              m_ControlRect.insert(pair<int, CRect>(pWnd->GetDlgCtrlID(), rect));//保存控件的初始大小,以便在OnSize函數(shù)中繼續(xù)使用
              int width = rect.Width();
              int height = rect.Height();

              WCHAR szBuf[256];
              GetClassName(pWnd->m_hWnd,szBuf,256);        
              if( _tcsicmp(szBuf,_T("Edit")) == 0)  
              {
               //Edit只是位置變化,大小沒(méi)有變
               rect.top = m_y * rect.top;
               rect.left = m_x * rect.left;
               rect.bottom = rect.top + height;
               rect.right = rect.left + width;
              }
              else
              {
               //其它控件位置和大小均變化
               rect.top = m_y * rect.top;
               rect.left = m_x * rect.left;
               rect.bottom = m_y * rect.bottom;
               rect.right = m_x * rect.right;
              }

              pWnd->MoveWindow(&rect);//設(shè)置控件大小
              pWnd = pWnd->GetWindow(GW_HWNDNEXT);
             }
             
             //控件初始化結(jié)束
             m_IsInitialed = true;
             

            2.如果界面在運(yùn)行時(shí)大小可以改變,則在OnSize函數(shù)中加入如下代碼

            view plaincopy to clipboardprint?
            01.// TODO: 在此處添加消息處理程序代碼  
            02.    CFormView::ShowScrollBar(SB_BOTH, false);//設(shè)置沒(méi)有滾動(dòng)條,視情況而定。  
            03.         //在界面不是最小化并且已經(jīng)初始化完畢  
            04.    if (!IsIconic() && m_IsInitialed)  
            05.    {  
            06.        CSize m_Forsize;  
            07.        m_Forsize = GetTotalSize();  
            08.        double m_x = (double)cx / m_Forsize.cx;  
            09.        double m_y = (double)cy / m_Forsize.cy;  
            10. 
            11.                //讀取控件的初始大小  
            12.        map<int, CRect>::iterator pos = m_ControlRect.begin();  
            13.        for (; pos != m_ControlRect.end(); ++pos)  
            14.        {  
            15.            CRect rect = pos->second;  
            16.            int width = rect.Width();  
            17.            int height = rect.Height();  
            18. 
            19.            WCHAR szBuf[256];  
            20.            GetClassName(GetDlgItem(pos->first)->m_hWnd,szBuf,256);                     
            21.            if( _tcsicmp(szBuf,_T("Edit")) == 0)     
            22.            {   
            23.                rect.top = m_y * rect.top;  
            24.                rect.left = m_x * rect.left;  
            25.                rect.bottom = rect.top + height;  
            26.                rect.right = rect.left + width;  
            27.            }  
            28.            else 
            29.            {  
            30.                rect.top = m_y * rect.top;  
            31.                rect.left = m_x * rect.left;  
            32.                rect.bottom = m_y * rect.bottom;  
            33.                rect.right = m_x * rect.right;  
            34.            }  
            35.            GetDlgItem(pos->first)->MoveWindow(rect);  
            36.        }  
            37.    } 
            // TODO: 在此處添加消息處理程序代碼
             CFormView::ShowScrollBar(SB_BOTH, false);//設(shè)置沒(méi)有滾動(dòng)條,視情況而定。
                     //在界面不是最小化并且已經(jīng)初始化完畢
             if (!IsIconic() && m_IsInitialed)
             {
              CSize m_Forsize;
              m_Forsize = GetTotalSize();
              double m_x = (double)cx / m_Forsize.cx;
              double m_y = (double)cy / m_Forsize.cy;

                            //讀取控件的初始大小
              map<int, CRect>::iterator pos = m_ControlRect.begin();
              for (; pos != m_ControlRect.end(); ++pos)
              {
               CRect rect = pos->second;
               int width = rect.Width();
               int height = rect.Height();

               WCHAR szBuf[256];
               GetClassName(GetDlgItem(pos->first)->m_hWnd,szBuf,256);         
               if( _tcsicmp(szBuf,_T("Edit")) == 0)  
               {
                rect.top = m_y * rect.top;
                rect.left = m_x * rect.left;
                rect.bottom = rect.top + height;
                rect.right = rect.left + width;
               }
               else
               {
                rect.top = m_y * rect.top;
                rect.left = m_x * rect.left;
                rect.bottom = m_y * rect.bottom;
                rect.right = m_x * rect.right;
               }
               GetDlgItem(pos->first)->MoveWindow(rect);
              }
             }

            或在OnShowWindow()函數(shù)中加入也可以(特別是在對(duì)話框作為tabpage時(shí))

             

            本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/ybw20041910/archive/2010/06/19/5679730.aspx

            posted @ 2010-11-29 11:06 wrh 閱讀(1659) | 評(píng)論 (0)編輯 收藏

            1.

            重載OnCtlColor    (CDC*    pDC,    CWnd*    pWnd,    UINT    nCtlColor),即WM_CTLCOLOR消息。  
               ----    ①在CExampleDlgDlg的頭文件中,添加一CBrush的成員變量:    
               class    CExampleDlgDlg    :    public    CDialog  
               {...  
               protected:  
               CBrush    m_brush;    
               ...  
               };  
               ----    ②在OnInitDialog()函數(shù)中添加如下代碼:    
               BOOL    CExampleDlgDlg::OnInitDialog()    
               {  
               ...  
               //    TODO:    Add    extra    initialization    here  
               m_brush.CreateSolidBrush(RGB(0,    255,    0));    //    生成一綠色刷子    
               ...  
               }    
               ----    ③利用ClassWizard重載OnCtlColor(…),即WM_CTLCOLOR消息:    
               HBRUSH    CExampleDlgDlg::OnCtlColor  
               (CDC*    pDC,    CWnd*    pWnd,    UINT    nCtlColor)    
               {  
               /*  
               **    這里不必編寫任何代碼!  
               **下行代碼要注釋掉  
               **    HBRUSH    hbr    =    CDialog::OnCtlColor(pDC,    pWnd,    nCtlColor);  
               */  
               return    m_brush;        //返加綠色刷子  
               }

            2.

               修改對(duì)話框的OnPaint,在else中添加如下代碼  
                       CPaintDC    dc(this);  
                       CRect    rect;    
                       GetClientRect(rect);    
                       dc.FillSolidRect(rect,    RGB(0,0,0));    
                       CDialog::OnPaint();

            3.

            在對(duì)話框的應(yīng)用類(App)的.cpp的Initinstance()中加入代碼:  
                               //加在int    nResponse=dlg.DoModal();  
                               前一個(gè)RGB設(shè)置背景色,第二個(gè)設(shè)置字體顏色  
               SetDialogBkColor(RGB(0,0,255),RGB(0,255,0));

            4.

            1.在對(duì)話框類中添加成員變量:  
               public:  
                       CBrush          m_brushBlue;  
               
               2.在對(duì)話框類的OnInitDialog()中添加代碼:  
               m_brushBlue.CreateSolidBrush(RGB(0,0,255));  
               
               3.用ClassWizard在對(duì)話框類中添加成員函數(shù)OnCtlCollor(),并在其中添加代碼:  
               if(nCtlColor==CTLCOLOR_DLG)  
               return    m_brushBlue;


            本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/mfreesky/archive/2007/08/27/1760222.aspx

            posted @ 2010-11-29 10:55 wrh 閱讀(1526) | 評(píng)論 (0)編輯 收藏
            =============獲取設(shè)備描述符
            bRequestType:80
            bRequest    :06
            wValue      :0100
            wIndex      :0000
            wLength     :0040 期望長(zhǎng)度64字節(jié)

            usb bus Reset總線復(fù)位我的usb設(shè)備
            =============發(fā)出為我的usb設(shè)備設(shè)置地址指令,我的usb地址被設(shè)置為0x04
            bRequestType:00
            bRequest    :05
            wValue      :0004
            wIndex      :0000
            wLength     :0000

            =============獲取配置描述符
            bRequestType:80
            bRequest    :06
            wValue      :0200
            wIndex      :0000
            wLength     :0009 期望長(zhǎng)度9字節(jié)

            =============嘗試讀取配置描述符0xff長(zhǎng)度
            bRequestType:80
            bRequest    :06
            wValue      :0200
            wIndex      :0000
            wLength     :00ff

            =============嘗試讀取配置描述符0x12長(zhǎng)度,不會(huì)超時(shí)
            bRequestType:80
            bRequest    :06
            wValue      :0200
            wIndex      :0000
            wLength     :0012

            =============嘗試讀取配置描述符0x09長(zhǎng)度,正好
            bRequestType:80
            bRequest    :06
            wValue      :0200
            wIndex      :0000
            wLength     :0009

            =============讀取配置描述符總長(zhǎng)度0x22
            bRequestType:80
            bRequest    :06
            wValue      :0200
            wIndex      :0000
            wLength     :0022

            =============設(shè)置配置,將配置生效,使能cpu上的endpoint端點(diǎn)
            bRequestType:00
            bRequest    :09
            wValue      :0001 將配置數(shù)值設(shè)置為1
            wIndex      :0000
            wLength     :0000

            =============
            bRequestType:00
            bRequest    :09
            wValue      :0001
            wIndex      :0000
            wLength     :0000

            =============
            bRequestType:81 讀取接口
            bRequest    :06 讀取接口描述符
            wValue      :2200 讀取報(bào)告描述符
            wIndex      :0000
            wLength     :0072

            =============
            bRequestType:81
            bRequest    :06
            wValue      :2200
            wIndex      :0000
            wLength     :0072

            ============= 讀取配置描述符
            bRequestType:80
            bRequest    :06
            wValue      :0200
            wIndex      :0000
            wLength     :0022

            posted @ 2010-11-26 13:29 wrh 閱讀(272) | 評(píng)論 (0)編輯 收藏
                 摘要: 一、USB命令   在USB規(guī)范里,對(duì)命令一詞提供的單詞為“Request”,但這里為了更好的理解主機(jī)與設(shè)備之間的主從關(guān)系,將它定義成“命令”。   所有的USB設(shè)備都要求對(duì)主機(jī)發(fā)給自己的控制命令作出響應(yīng),USB規(guī)范定義了11個(gè)標(biāo)準(zhǔn)命令,它們分別是:C...  閱讀全文
            posted @ 2010-11-24 14:39 wrh 閱讀(1021) | 評(píng)論 (0)編輯 收藏

            在《USB系列之三》中,我們實(shí)現(xiàn)了一系列的SCSI命令,在這個(gè)系列中,我們要實(shí)現(xiàn)向U盤上寫扇區(qū)的命令,所以,本文相對(duì)比較容易,更多地是給出一個(gè)實(shí)現(xiàn)的源程序。

                在《USB系列之三》中,我們實(shí)現(xiàn)的SCSI命令有:INQUIRY、READ CAPACITY(10)、TEST UNIT READY、REQUEST SENSE、READ(10);都是一些讀出的命令,所以不會(huì)破壞U盤的內(nèi)容,在文檔SBC-2的第29頁(yè)有一個(gè)SCSI命令的表,在這個(gè)表中列出了所有的命令,其TYPE為“M”的都是SCSI設(shè)備必須實(shí)現(xiàn)的命令,這些命令有:

            Num

            Command Name

            Operation Code

             Type

             Reference

             1

             FORMAT UNIT

             04h

             M

             SBC-2

             2

             INQUIRY

             12h

             M

             SPC-3

             3

             READ(6)

             08h

             M

             SBC-2

             4

             READ(10)

             28h

             M

             SBC-2

             5

             READ(16)

             88h

             M

             SBC-2

             6

             READ CAPACITY(10)

             25h

             M

             SBC-2

             7

             READ CAPACITY(16)

             9Eh/10h

             M

             SBC-2

             8

             REQUEST SENSE

             03h

             M

             SPC-3

             9

             SEND DIAGNOSTIC

             1Dh

             M

             SPC-3

             10

             TEST UNIT READY

             00h

             M

             SPC-3

             11

             WRITE(10)

             2Ah

             O

             SBC-2

                這里面最后的一個(gè)命令并不是SBC-2中要求強(qiáng)制實(shí)現(xiàn)的,而是可選的,但如果我們不去實(shí)現(xiàn),U盤的操作將失色很多;我們不打算去實(shí)現(xiàn)序號(hào)為1、3、5、7和9的命令,READ(6)、READ(16)和READ(10)十分相似,只是LBA的長(zhǎng)度不同而已,如果需要實(shí)現(xiàn),參考READ(10)就可以了,F(xiàn)ORMAT和SEND DIAGNOSTIC兩個(gè)命令對(duì)使用芯片的U盤來(lái)說(shuō)沒(méi)有什么意義,當(dāng)然對(duì)硬盤是有意義的,所以在本文中,我們只需要實(shí)現(xiàn)一個(gè)很重要的WRTE(10),向U盤上寫數(shù)據(jù),我們需要準(zhǔn)備一張沒(méi)有有用數(shù)據(jù)的U盤,因?yàn)槲覀円淖兤渲械膬?nèi)容。

                WRITE(10)源代碼下載地址:

                http://blog.hengch.com/source/usb-write.zip

                程序中,我們向《USB系列三》中的程序一樣,先reset,然后得到最大的LUN,這個(gè)步驟不是必須的,然后我們向device發(fā)出WRITE(10)命令,注意,這是一個(gè)OUT事務(wù),所以,CBW_FLAGS=0X00而不是像以前一樣是0X80,發(fā)出WRITE(10)命令后,我們還要向device發(fā)送要寫入的數(shù)據(jù),每次64個(gè)字節(jié),一個(gè)扇區(qū)512字節(jié)需要啟動(dòng)8個(gè)OUT事務(wù),這個(gè)工作又函數(shù)putData完成,每次發(fā)送的64個(gè)字節(jié)我們分別寫入了0--63,程序中,我們把這些數(shù)據(jù)寫入到了LBA=100的扇區(qū)中,寫入后,我們?cè)谑褂迷凇禪SB系列之三》中介紹過(guò)的READ(10)命令把相同的扇區(qū)讀出來(lái),我們會(huì)看到我們所希望的結(jié)果,由于在讀之前,我們已經(jīng)把buffer全部清為0了,所以我們有把握相信,我們讀到的數(shù)據(jù)是真實(shí)的。

                到這里,我們已經(jīng)把控制U盤的主要命令都介紹完了,利用DOSUSB,我們已經(jīng)有可能為U盤編寫一個(gè)簡(jiǎn)單的驅(qū)動(dòng)程序,但可能我們還不知道DOS下的驅(qū)動(dòng)程序該如何寫,從下一篇文章開始,我們將暫時(shí)放下USB系列文章,介紹一下DOS下驅(qū)動(dòng)程序的寫法。

            posted @ 2010-11-24 14:10 wrh 閱讀(1216) | 評(píng)論 (0)編輯 收藏

            U盤是我們最常使用的一種USB設(shè)備,本文繼續(xù)使用DOSUSB做驅(qū)動(dòng),試圖以讀取扇區(qū)的方式讀取你的U盤。
                本文可能涉及的協(xié)議可能會(huì)比較多。
            一、了解你的U盤
                首先我們用上一篇文章介紹的程序usbview.exe去看一下你的U盤,我在本文中用于測(cè)試的U盤情況如下:
                Device Descriptor: (設(shè)備描述符)
                USB Address:             1
                Length:                  18
                Descriptor Type:         1
                USB Specification nr.:   0x0110
                Calss Code:              Class code specified by interface
                Subclass Code:           0x00
                Protocol Code:           0x00
                MAX Packet Size:         0x08
                Vendor ID:               0x058f
                Product ID:              0x9321
                Device Code:             0x0100
                Manufacture Index:       1
                Product Index:           2
                Serial Number Index:     0
                Number of Configuration: 1

                String Descriptor: (字符串描述符)
                Manufacturer: Alcor Micro
                Product: Mass Storage Device

                Configuration Descriptor: (配置描述符)
                Length:               9
                Descriptor Type:      2
                Total Length:         32
                Number of Interfaces: 1
                Configuration Value:  1
                Configuration Index:  0
                Attributes:           Bus Powered
                Max Power:            50mA

                Interface Descriptor: (接口描述符)
                Length:               9
                Descriptor Type:      4
                Interface Number:     0
                Alternate Setting:    0
                Number of Endpoints:  2
                Interface Class:      Mass Storage Device
                Interface Sub Class:  6
                Interface Protocol:   80
                Interface Index:      0

                Endpoint Descriptor: (端點(diǎn)描述符)
                Length:               7
                Descriptor Type:      5
                Endpoint Address:     
            1 OUT endpoint
                Attributes:           Bulk
                Max Packet Size:      64
                Interval:             0

                Endpoint Descriptor: (端點(diǎn)描述符)
                Length:               7
                Descriptor Type:      5
                Endpoint Address:     
            2 IN endpoint
                Attributes:           Bulk
                Max Packet Size:      64
                Interval:             0

                各種描述符的含義在以前的文章中介紹過(guò)了,或者去翻閱USB的specification,這里就不多說(shuō)了,我們從接口描述符開始就一些關(guān)鍵點(diǎn)進(jìn)行一下說(shuō)明。
                首先看接口描述符,Interface Class = 8,表明是Mass Storage Device;Sub Class = 6,表明執(zhí)行SCSI命令;Interface Protocol = 0x80,表明支持Bulk傳輸;另外,Number of Endpoints = 2,表明有兩個(gè)端點(diǎn)。
                兩個(gè)端點(diǎn)描述符要注意的是,Endpoint Address = 1的是OUT端點(diǎn),Endpoint Address = 2的是IN端點(diǎn),有些可能會(huì)不一樣;有些U盤可能還會(huì)有第三個(gè)端點(diǎn),比如支持中斷傳輸?shù)腢盤還會(huì)有一個(gè)Interrupt端點(diǎn),不過(guò)這都沒(méi)有關(guān)系。
                我大概看了我手頭有的5個(gè)U盤,都支持批量傳輸,且支持SCSI命令,所以,這可能是一個(gè)比較典型的例子,我們就以它為例。

            二、CBW(Command Block Wrapper)和CSW(Command Status Wrapper)

                在《USB系列之一》中,我們安裝了一個(gè)DOSUSB,在《USB系列之二》中,我們利用USBDOS讀取了所有的描述表,掌握這些內(nèi)容需要了解USB協(xié)議1.1(USB Specification Revision 1.1)即可,當(dāng)然還要了解USBDOS,不過(guò)這個(gè)比較簡(jiǎn)單。

                在系列一和系列二中,我們已經(jīng)對(duì)DOSUSB的一個(gè)數(shù)據(jù)結(jié)構(gòu)URB有所了解,本文中還要大量用到,我們還接觸了一個(gè)結(jié)構(gòu)叫device_request,這個(gè)結(jié)構(gòu)是在USB協(xié)議中定義的,用于向設(shè)備發(fā)送命令(Request),本文也會(huì)用到。

                與前面不同的是,前面的兩個(gè)系列可以針對(duì)任何USB設(shè)備,比如U盤、攝像頭、打印機(jī)等,而本文將只針對(duì)我們經(jīng)常使用的USB設(shè)備----U盤,如果你打算嘗試本文所介紹的內(nèi)容,請(qǐng)準(zhǔn)備好一個(gè)U盤,什么樣子的都行,或者是一個(gè)USB讀卡器,不過(guò)要記得插一張卡進(jìn)去,實(shí)際上本文所載范例就是使用一個(gè)USB的CF卡讀卡器完成的,不用擔(dān)心損害你的U盤中的數(shù)據(jù),本文不會(huì)對(duì)U盤進(jìn)行任何寫操作,僅僅做一些讀操作。

                這個(gè)系列中我們需要針對(duì)U盤讀更多的規(guī)范,如下:

                 不用為規(guī)范發(fā)愁,實(shí)際上,前兩個(gè)規(guī)范都很短,其中第一個(gè)對(duì)實(shí)際編程沒(méi)有什么作用,但最好看一下;第二個(gè)規(guī)范連目錄一共22頁(yè),其中13頁(yè)以前的內(nèi)容可以跳過(guò)(很多和USB Specification中相同),第三個(gè)規(guī)范主要看第6章,第四個(gè)規(guī)范主要看第5章,后兩個(gè)規(guī)范在編程時(shí)需要經(jīng)常翻閱,以便了解你正在實(shí)現(xiàn)的SCSI命令的具體格式和參數(shù)。

                本節(jié)我們主要介紹兩個(gè)新的數(shù)據(jù)結(jié)構(gòu),這兩個(gè)結(jié)構(gòu)都是在第二個(gè)規(guī)范中定義的。

                第一個(gè)數(shù)據(jù)結(jié)構(gòu)叫CBW(Command Block Wrapper)


                這個(gè)結(jié)構(gòu)將承載具體的與設(shè)備有關(guān)的命令發(fā)送到設(shè)備上去,這個(gè)結(jié)構(gòu)分成兩部分,第一部分從byte[0]--byte[14]共15個(gè)字節(jié),第而部分從byte[15]--byte[30]共16個(gè)字節(jié),整個(gè)數(shù)據(jù)結(jié)構(gòu)為31個(gè)字節(jié)。規(guī)范中并沒(méi)有定義第二部分的內(nèi)容,這是因?yàn)榈诙糠殖休d的具體的命令,既與命令集(SCSI命令集)有關(guān),也與具體的命令有關(guān),我們使用SCSI命令集,所以后16個(gè)字節(jié)的內(nèi)容在前面提到的后面兩個(gè)規(guī)范中有定義。

                比如我們要向設(shè)備發(fā)出一個(gè)SCSI命令I(lǐng)NQUIRY(我們姑且先不要管命令的含義),那么這個(gè)命令的結(jié)構(gòu)在SPC-3的第142頁(yè)有定義,如下:


                對(duì)于SCSI INQUIRY這條命令而言,CBW的第二部分的定義就是上面的這六個(gè)字節(jié),不同的命令,定義也會(huì)不同。

                好,我們回到CSW的結(jié)構(gòu)上來(lái),根據(jù)規(guī)范,dCBWSignature的值必須是0X43425355,其實(shí)就是USBC這幾個(gè)字母倒過(guò)來(lái),這是因?yàn)镃BW的字符順序是little endian(這個(gè)東東在以前有關(guān)網(wǎng)絡(luò)編程的文章中介紹過(guò)),而我們PC機(jī)中的字符順序是big endian,所以要顛倒一下,總之寫dCBWSignature = 0X43425355就OK了;dCBWTag僅僅是一個(gè)標(biāo)志,你可以填任何值,這里要先說(shuō)一下CSW(Command Status Wrapper),我們每發(fā)出一個(gè)命令,設(shè)備都會(huì)返回一個(gè)CSW(這個(gè)東東下面很快就要介紹了),以說(shuō)明命令的執(zhí)行狀態(tài),這個(gè)結(jié)構(gòu)中也有Signature和Tag這兩個(gè)字段,其中Tag字段和發(fā)出命令時(shí)CBW中的Tag字段相同,這樣就可以區(qū)分這個(gè)CSW是和那個(gè)CBW對(duì)應(yīng)的了,至于Signature,下面再說(shuō)。

                下一個(gè)字段是dCBWDataTransferLength,表示的是當(dāng)這個(gè)命令發(fā)出后,我們希望設(shè)備返回?cái)?shù)據(jù)的字符數(shù)或者我們要向設(shè)備傳輸?shù)淖址麛?shù),本文僅涉及從設(shè)備返回?cái)?shù)據(jù),不涉及向設(shè)備傳輸數(shù)據(jù);舉例來(lái)說(shuō):我們發(fā)送INQIURY命令到設(shè)備,按照SPC-3第144頁(yè)的說(shuō)明,該命令返回的數(shù)據(jù)至少為36個(gè)字節(jié),所以,此時(shí)這個(gè)字節(jié)應(yīng)該填36;再如:我們讀取U盤的一個(gè)扇區(qū),如果扇區(qū)的長(zhǎng)度是512個(gè)字節(jié),那么這個(gè)字段就要填512。

                再下來(lái)是bmCBWFlags字段,這個(gè)字段只有bit 7有意義,為0表示要向設(shè)備傳輸數(shù)據(jù),為1表示要從設(shè)備獲得數(shù)據(jù)。

                bCBWLUN字段總是填0,因?yàn)榻^大多數(shù)的U盤都不支持多LUN(Logical Unit Number),只有一個(gè)邏輯單元自然好嗎就是0了。

                bCBWCBLength字段是只CBW第二部分的長(zhǎng)度,像前面舉例的INQUIRY命令,長(zhǎng)度為6個(gè)字節(jié),則這個(gè)字段就應(yīng)該填6,再如:READ(10)命令的長(zhǎng)度是10個(gè)字節(jié)(SBC-2第42頁(yè)有定義),這個(gè)字段當(dāng)然要填10了。

                第二個(gè)要說(shuō)的數(shù)據(jù)結(jié)構(gòu)是CSW,當(dāng)host向device發(fā)送一個(gè)CBW后,接著就可以從device收到數(shù)據(jù)(或者發(fā)數(shù)據(jù)到device),當(dāng)接受完所需的的數(shù)據(jù)后,就可以從device獲得一個(gè)CSW(Command Status Wrapper),CSW的結(jié)構(gòu)如下:

                 前面說(shuō)過(guò),在CBW中的dCBWSignature的值恒為:0x43425355,得到的CSW中的dCSWSignature的值為:0x53425355,dCSWTag與dCBWTag中的一致。

                在得到的CSW中,恒定有13個(gè)字節(jié),bCSWStatus的定義如下:

             

            三、發(fā)送命令和接收數(shù)據(jù)

                我們知道USB協(xié)議中定義了三種傳輸方式,控制傳輸、批量傳輸、中斷傳輸和實(shí)時(shí)傳輸,在《USB系列二》中我們一直都在使用控制傳輸,我們應(yīng)該比較熟悉了,本文中將涉及批量傳輸。

                我們?cè)谑褂每刂苽鬏敃r(shí),我們?cè)O(shè)置好URB啟動(dòng)傳輸事務(wù),相應(yīng)的結(jié)果將返回到制定得buffer中,批量傳輸沒(méi)有那么簡(jiǎn)單,批量傳輸分為輸出事務(wù)和輸入事務(wù),我們應(yīng)該注意到,前面在看U盤的描述表時(shí),在端點(diǎn)這一級(jí)有兩個(gè)端點(diǎn),一個(gè)叫OUT端點(diǎn),一個(gè)叫IN端點(diǎn),當(dāng)我們啟動(dòng)一個(gè)輸出事務(wù)時(shí),一定要發(fā)送給OUT端點(diǎn),當(dāng)我們啟動(dòng)一個(gè)輸入事務(wù)時(shí),一定要發(fā)送到輸入端點(diǎn)。下面我們簡(jiǎn)單描述一下如何啟動(dòng)批量傳輸事務(wù)。

                在使用控制傳輸時(shí),我們應(yīng)該閱讀過(guò)DOSUSB的說(shuō)明,并且對(duì)URB結(jié)構(gòu)比較熟悉,URB中有一個(gè)字段叫transation_type,當(dāng)這個(gè)值為0x2d時(shí)為控制傳輸;當(dāng)為0x69時(shí)為批量傳輸?shù)腎N事務(wù);當(dāng)為0xe1時(shí)為批量傳輸?shù)腛UT事務(wù);當(dāng)我們啟動(dòng)一個(gè)傳輸時(shí),一定要正確地設(shè)置這個(gè)值。

                我們以一個(gè)具體的例子來(lái)說(shuō)明如何啟動(dòng)一個(gè)傳輸,我們以SCSI INQUIRY命令為了,關(guān)于這個(gè)命令的定義在SPC-3的第142頁(yè)--157頁(yè)有說(shuō)明,篇幅很長(zhǎng),但絕大多數(shù)篇幅用來(lái)解釋返回?cái)?shù)據(jù)的含義,我們可以暫時(shí)不去理會(huì)。首先我們要填寫CBW結(jié)構(gòu),CBW結(jié)構(gòu)的第一部分的填寫前面已經(jīng)說(shuō)的很明白了,第二部分的定義在SPC-3的第142頁(yè),共有6個(gè)字節(jié),我們要按照定義填寫好,實(shí)際上只要填兩個(gè)字段,一個(gè)是OPERATION CODE = 0X12,第二個(gè)就是ALLOCATION CODE = 36,表示需要返回36個(gè)字節(jié)的內(nèi)容;CBW填好后,我們開始填寫URB,首先把CBW的偏移和段地址放到URB的buffer_off和buffer_seg中,把transation_type=0xe1,表示一個(gè)輸出事務(wù),注意把end_point字段一定要放OUT endpoint的地址,從前面的描述符表中看,應(yīng)該是1(2是IN endpoint的地址,你的機(jī)器可能不同),其它字段的填法在《USB系列二》中已經(jīng)介紹過(guò)了,填完以后調(diào)用DOSUSB,這樣,一個(gè)承載著INQUIRY命令的輸出事務(wù)就發(fā)送到由URB中dev_add和end_point兩個(gè)字段指定的端點(diǎn)上去了。

                接下來(lái)我們要接收device返回的執(zhí)行INQUIRY命令的結(jié)果,這要啟動(dòng)一個(gè)輸入事務(wù),相對(duì)容易一些,只要填寫URB就可以了,把transation_type=0x69,把end_point填上OUT endpoint的地址,本例中為2,buffer_off和buffer_seg指向緩沖區(qū)buffer,把buffer_length和actual_length均填為64,因?yàn)榍懊娑它c(diǎn)描述符表中寫明包的最大長(zhǎng)度為64,其它字段按常規(guī)填寫,調(diào)用DOSUSB,在buffer中就可以得到返回的內(nèi)容,按照SPC-3中對(duì)返回內(nèi)容的解釋即可了解設(shè)備的一些情況。

                接收晚數(shù)據(jù)后,不要忘了接收CSW,方法也是啟動(dòng)一個(gè)輸入事務(wù),與接收數(shù)據(jù)完全相同,然后根據(jù)CSW的結(jié)構(gòu)解釋其含義。至此一個(gè)命令執(zhí)行完畢。

            四、范例

                在本文的范例中,我們實(shí)現(xiàn)了如下內(nèi)容:

            • 實(shí)現(xiàn)了Bulk-Only Mass Storage Reset
            • 實(shí)現(xiàn)了Get Max LUN
            • 實(shí)現(xiàn)了SCSI INQUIRY Command
            • 實(shí)現(xiàn)了SCSI READ CAPACITY (10) Command
            • 實(shí)現(xiàn)了SCSI REQUEST SENSE Command
            • 實(shí)現(xiàn)了SCSI TEST UNIT READY Command
            • 實(shí)現(xiàn)了SCSI READ (10) Command

                最后的一個(gè)命令,我將從你的U盤上讀出一個(gè)扇區(qū)。

                最前面的兩個(gè)命令,請(qǐng)翻閱《Universal Serial Bus Mass Storage Class - Bulk-Only Transport》第7頁(yè);INQUIRY、REQUEST SENSE、TEST UNIT READY三個(gè)命令請(qǐng)翻閱SPC-3的第142、221和232頁(yè);READ CAPACITY(10)和READ(10)命令,請(qǐng)翻閱SBC-2的第42和44頁(yè)。

                源代碼請(qǐng)?jiān)谙旅婢W(wǎng)址下載:

                http://blog.hengch.com/source/reader.rar

                各種概念在前面已經(jīng)介紹過(guò)了,程序無(wú)非就是實(shí)現(xiàn)這些概念,幾乎所有的代碼都是圍繞著填寫數(shù)據(jù)結(jié)構(gòu)和顯示返回結(jié)果的,所以代碼本身并不難,更重要的是理解數(shù)據(jù)結(jié)構(gòu)中個(gè)字段的含義,這可能不得不閱讀一些規(guī)范,我想我不可能比規(guī)范說(shuō)的更嚴(yán)謹(jǐn)更完整。要注意的是,你使用的U盤不可能和我的完全一致,一般情況下有可能有變化的是:設(shè)備地址devAddr、輸出端點(diǎn)地址outEndpoint和輸入端點(diǎn)地址inEndpoint,所以在編譯程序之前一定要使用《USB系列之二》中的方法仔細(xì)查看一下你的U盤的各種描述符表,如果這些值和我的U盤不同,請(qǐng)?jiān)谥鞒绦蜷_始的地方,更改這幾個(gè)變量;另外,在主程序6th step中,scsiRead10(0),傳遞給scsiRead10的參數(shù)為0,含義是從LBA(Logical Block Address)為0的地方讀取一個(gè)扇區(qū),如果你向讀取其它扇區(qū),可以更改這個(gè)值,其最大值我們?cè)趯?shí)現(xiàn) READ CAPACITY時(shí)已經(jīng)讀出了,可以參考;此外,注意CBW的字符順序是little endian,所以我們?cè)谔顚慙BA和讀取最大LBA時(shí)都做了相應(yīng)的轉(zhuǎn)換。

                好了,應(yīng)該沒(méi)有什么了!

                Enjoy it.

            posted @ 2010-11-24 14:09 wrh 閱讀(1391) | 評(píng)論 (1)編輯 收藏
             USB現(xiàn)在已經(jīng)成為PC機(jī)必不可少的接口之一,幾乎所有的設(shè)備都可以接在USB設(shè)備上,USB鍵盤、鼠標(biāo)、打印機(jī)、攝像頭,還有常用的U盤等等,從本篇文章開始,將集中篇幅介紹一下在DOS中使用USB設(shè)備的方法,具體會(huì)有幾篇暫不好定,寫到哪里算哪里吧,三、四篇總是少不了的。
                本文介紹如何使用我以前文章中介紹過(guò)的知識(shí)在你的機(jī)器中找到USB設(shè)備,并判定設(shè)備類型。
                一個(gè)USB系統(tǒng)一般由一個(gè)USB主機(jī)(HOST)、一個(gè)或多個(gè)USB集線器(HUB,但不是局域網(wǎng)里的集線器)和一個(gè)或多個(gè)USB設(shè)備節(jié)點(diǎn)(NODE)組成,一個(gè)系統(tǒng)中只有一個(gè)HOST,我們PC機(jī)里的USB實(shí)際上就是HOST和HUB兩部分,你的PC機(jī)可能會(huì)有4個(gè)USB口,其實(shí)是一個(gè)HOST,一個(gè)HUB,HUB為你提供了4個(gè)端口,我們插在USB口上的器件,一般是USB設(shè)備,比如U盤,USB打印機(jī)等,當(dāng)然我們也可以插一個(gè)集線器上去,使你的一個(gè)USB口擴(kuò)展成多個(gè)。
                實(shí)際上我們說(shuō)在DOS下使用USB,就是對(duì)USB系統(tǒng)中的HOST進(jìn)行編程管理,根據(jù)USB的規(guī)范,HOST將對(duì)連接在上面的HUB和USB設(shè)備進(jìn)行管理,不用我們操心。HOST器件目前有三個(gè)規(guī)范,OHCI(Open Host Controller Interface)、UHCI(Universal Host Controller Interface)支持USB1.1,EHCI(Enhanced Host Controller Interface)支持USB2.0,以后的文章中,我們將側(cè)重介紹OHCI和EHCI。
                學(xué)習(xí)USB編程,讀規(guī)范是少不了的,以下是一些應(yīng)該閱讀的規(guī)范下載:
                OHCI規(guī)范:
            http://blog.hengch.com/specification/usb_ohci_r10a.pdf
                EHCI規(guī)范:http://blog.hengch.com/specification/usb_ehci_r10.pdf
                USB規(guī)范1.1:http://blog.hengch.com/specification/usb_spec11.pdf
                USB規(guī)范2.0:http://blog.hengch.com/specification/usb_spec20.pdf
                本文介紹的內(nèi)容不需要學(xué)習(xí)規(guī)范。

                下面進(jìn)入正題,列出你的USB設(shè)備,USB的HOST是掛接在PCI總線上的,所以通過(guò)PCI設(shè)備的遍歷就可以找到你的機(jī)器上的所有USB設(shè)備,在以前介紹PCI的配置空間時(shí),曾經(jīng)介紹過(guò)在配置空間中有一個(gè)占三個(gè)字節(jié)的分類代碼字段(如果不知道,請(qǐng)參閱我以前的博文
            《遍歷PCI設(shè)備》),在偏移為0x0B的字節(jié)叫基本分類代碼,在偏移為0x0A的字節(jié)叫子分類代碼,在偏移為0x09的字節(jié)叫編程接口代碼,對(duì)于USB設(shè)備類說(shuō),基本分類代碼為0x0C,子分類代碼為0x03,對(duì)于符合不同規(guī)范的HOST器件而言,編程接口代碼是不同的,UHCI的編程接口代碼是0x00,OHCI的編程接口代碼是0x10,EHCI的編程接口代碼是0x20,我想了解這些就足夠了。
                下面列出USB設(shè)備的源程序。
            #include <stdio.h>
            #include <stdlib.h>
            #include <dpmi.h>

            typedef unsigned long      UDWORD;
            typedef short int          WORD;
            typedef unsigned short int UWORD;
            typedef unsigned char      UBYTE;

            typedef union {
              struct {
                UDWORD edi;
                UDWORD esi;
                UDWORD ebp;
                UDWORD res;
                UDWORD ebx;
                UDWORD edx;
                UDWORD ecx;
                UDWORD eax;
              } d;
              struct {
                UWORD di, di_hi;
                UWORD si, si_hi;
                UWORD bp, bp_hi;
                UWORD res, res_hi;
                UWORD bx, bx_hi;
                UWORD dx, dx_hi;
                UWORD cx, cx_hi;
                UWORD ax, ax_hi;
                UWORD flags;
                UWORD es;
                UWORD ds;
                UWORD fs;
                UWORD gs;
                UWORD ip;
                UWORD cs;
                UWORD sp;
                UWORD ss;
              } x;
              struct {
                UBYTE edi[4];
                UBYTE esi[4];
                UBYTE ebp[4];
                UBYTE res[4];
                UBYTE bl, bh, ebx_b2, ebx_b3;
                UBYTE dl, dh, edx_b2, edx_b3;
                UBYTE cl, ch, ecx_b2, ecx_b3;
                UBYTE al, ah, eax_b2, eax_b3;
              } h;
            } X86_REGS;
            /*************************************************************
             * Excute soft interrupt in real mode
             *************************************************************/
            int x86_int(int int_num, X86_REGS *x86_reg) {
              __dpmi_regs d_regs;
              int return_value;

              d_regs.d.edi = x86_reg->d.edi;
              d_regs.d.esi = x86_reg->d.esi;
              d_regs.d.ebp = x86_reg->d.ebp;
              d_regs.d.res = x86_reg->d.res;
              d_regs.d.ebx = x86_reg->d.ebx;
              d_regs.d.ecx = x86_reg->d.ecx;
              d_regs.d.edx = x86_reg->d.edx;
              d_regs.d.eax = x86_reg->d.eax;
              d_regs.x.flags = x86_reg->x.flags;
              d_regs.x.es = x86_reg->x.es;
              d_regs.x.ds = x86_reg->x.ds;
              d_regs.x.fs = x86_reg->x.fs;
              d_regs.x.gs = x86_reg->x.gs;
              d_regs.x.ip = x86_reg->x.ip;
              d_regs.x.cs = x86_reg->x.cs;
              d_regs.x.sp = x86_reg->x.sp;
              d_regs.x.ss = x86_reg->x.ss;

              return_value = __dpmi_int(int_num, &d_regs);

              x86_reg->d.edi = d_regs.d.edi;
              x86_reg->d.esi = d_regs.d.esi;
              x86_reg->d.ebp = d_regs.d.ebp;
              x86_reg->d.res = d_regs.d.res;
              x86_reg->d.ebx = d_regs.d.ebx;
              x86_reg->d.ecx = d_regs.d.ecx;
              x86_reg->d.edx = d_regs.d.edx;
              x86_reg->d.eax = d_regs.d.eax;
              x86_reg->x.flags = d_regs.x.flags;
              x86_reg->x.es = d_regs.x.es;
              x86_reg->x.ds = d_regs.x.ds;
              x86_reg->x.fs = d_regs.x.fs;
              x86_reg->x.gs = d_regs.x.gs;
              x86_reg->x.ip = d_regs.x.ip;
              x86_reg->x.cs = d_regs.x.cs;
              x86_reg->x.sp = d_regs.x.sp;
              x86_reg->x.ss = d_regs.x.ss;

              return return_value;
            }
            /**********************************
             * Read Configuration WORD if PCI
             **********************************/
            UWORD ReadConfigWORD(WORD pciAddr, int reg) {
              X86_REGS inregs;

              inregs.x.ax = 0xB109;    // Read Configuration word
              inregs.x.bx = pciAddr;
              inregs.x.di = reg;       // Register number
              x86_int(0x1A, &inregs);

              return inregs.d.ecx;     // the value
            }
            // main program
            int main(void) {
              UWORD pciAddr;
              UWORD subClass;
              int ehciCount = 0, ohciCount = 0, uhciCount = 0;

              for (pciAddr = 0; pciAddr < 0xffff; pciAddr++) {
                if (ReadConfigWORD(pciAddr, 0) != 0xFFFF) {
                  // Read Class Code
                  if (ReadConfigWORD(pciAddr, 0x000a ) == 0x0c03) {  // Usb Host Controller
                    // Read SubClass Code
                    subClass = ReadConfigWORD(pciAddr, 0x0008);
                    if ((subClass & 0xff00) == 0x2000) {  // uhci
                      ehciCount++;
                    } else if ((subClass & 0xff00) == 0x1000) {  // ohci
                      ohciCount++;
                    } else if ((subClass & 0xff00) == 0x00) {    // uhci
                      uhciCount++;
                    }
                  }
                }
              }
              printf("There are %d ohci device(s).\n", ohciCount);
              printf("There are %d ehci device(s).\n", ehciCount);
              printf("There are %d uhci device(s).\n", uhciCount);
            }

                程序非常簡(jiǎn)單,所有概念在以前的博文中均有過(guò)介紹,其中的子程序大多是以前程序范例中使用過(guò)的,所以在這里就不做更多的解釋了,程序中,我們僅僅列出了設(shè)備的數(shù)量,但很顯然,用這種方法,我們可以從配置空間里讀出基地址等信息,這些在以后的文章中會(huì)用到。
            posted @ 2010-11-24 14:07 wrh 閱讀(762) | 評(píng)論 (0)編輯 收藏
            僅列出標(biāo)題
            共25頁(yè): First 2 3 4 5 6 7 8 9 10 Last 

            導(dǎo)航

            <2011年3月>
            272812345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            統(tǒng)計(jì)

            常用鏈接

            留言簿(19)

            隨筆檔案

            文章檔案

            收藏夾

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久综合香蕉国产蜜臀AV| 精品久久久久久国产三级| 精品久久久久久国产| 亚洲精品乱码久久久久久| 久久精品亚洲精品国产色婷| 久久精品麻豆日日躁夜夜躁| 精品久久人人妻人人做精品| 亚洲欧洲久久av| AV无码久久久久不卡蜜桃| 亚洲国产成人精品久久久国产成人一区二区三区综 | 色偷偷888欧美精品久久久| 97久久精品人人做人人爽| 成人综合久久精品色婷婷| 久久香蕉国产线看观看99| 少妇熟女久久综合网色欲| 国产精品久久久久久影院| 久久人人爽人人爽人人片AV不 | 欧美综合天天夜夜久久| 久久亚洲sm情趣捆绑调教| 久久免费高清视频| 亚洲国产精品成人久久| 伊人久久精品影院| 91性高湖久久久久| 亚洲国产二区三区久久| 99精品久久精品| 久久er99热精品一区二区| 久久精品aⅴ无码中文字字幕不卡 久久精品成人欧美大片 | 国产精品久久久久久久久软件| 久久精品国产一区| 精品免费久久久久久久| 久久青青草原亚洲av无码app| 中文字幕无码久久精品青草| 久久久精品国产亚洲成人满18免费网站| 丰满少妇高潮惨叫久久久| 亚洲综合精品香蕉久久网| 97久久国产综合精品女不卡 | 久久丫忘忧草产品| 亚洲人成电影网站久久| 中文精品99久久国产| 伊人久久大香线蕉综合5g| 精品国产日韩久久亚洲|