• <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 軟件結構 》

             

            軟件結構比硬件來的復雜很多。因為它包含了許多從表面上看不到的層次。比如總線驅動、功能驅動、過濾驅動等。套用社會學的話,這體現了功能應用中的分工和統籌。下面我們逐層來看它們。

              總線驅動
            總線驅動位于驅動棧的最低層,處理復雜的任務,必須資源分配,子設備管理。作為下層驅動,負責處理上層驅動發下來的請求。 USB 設備中的總線驅動主要有兩類:控制器驅動、 HUB 驅動;另外還有一個端口驅動。

            1 ) 控制器驅動: Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 。

            首先解釋一下 HCI ,它是主機控制接口( Host Control Interface )的縮寫。前后一共有三種 HCI 協議出現: USB 1.1 時代,有 OHCI (開發 HCI )協議和 UHCI (通用 HCI )協議; USB2.0 時代,有 EHCI (擴展 HCI )協議。三個協議分別對應了上面的三個驅動程序。因為 USB 是向后兼容的,所以 UsbEHC.sys 中也包含了 UsbOhci 和 UsbUhci 的功能。請看下圖。

             


            圖 1 總線設備 

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

            這說明同一臺主機特別是筆記本電腦中, 1.1 和 2.0 的控制器并存,像筆記本電腦中的鍵盤通過內置 USB 接口與系統連接,其數據吞吐量小,只需要 1.1 的控制器就能滿足;而暴露在外供用戶使用的接口則需要 2.0 。

            2) Hub 驅動: UsbHub.sys 。 Hub 驅動是所有 USB 設備的父驅動。下圖描繪了這一景況:


            圖2


            上圖中看到, Hub 驅動的子設備要不是獨立設備,如左側圖;要么是一個含有多個子設備的父設備,如右側圖。 Hub 驅動只為直系子設備創建唯一的物理設備對象。上圖中, Hub 驅動為設備 1 和設備 2 創建物理設備對象,但并不為設備 2 的子設備創建物理設備對象。

            3 ) Port 驅動: UsbPort.sys 。這是個框架驅動,比較復雜,很少人會用到。而上面的 Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 其實都是他的微端口驅動。對于這么偏門的框架,不提也罷。

              系統類驅動
            所以出現類驅動,體現了 USB 總線在應用上的繁榮景象。只有用得多了,才有被歸類的可能。就像人類社會中有幾百個國家,幾千個民族,正是體現了人類這個團體的多樣性與繁榮。只有在“多”的基礎上,分類才是有必要的;少數人的小群體,再怎么獨立特行,也都不足以被分類,甚至定義為“ XX 民族”。

            USB 設備包含很多的通用的功能類,比如: USB 集線器設備, USB HID 設備, USB 音頻設備, USB MIDI 設備, USB 存儲設備。為了讓開發工作變得更加簡單, Windows 操作系統為他們提供了系統驅動程序。

            大部分時候,系統類驅動就是功能驅動。下表是系統提供的 USB 類驅動。

             

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

             

            表 1   系統提供的 USB 類驅動

             

            功能驅動
             

            不是所有的 USB 設備都有類驅動,但功能驅動卻是它們唯一的身份證。沒有功能驅動,設備就不足以在系統中存在。它的作用是為設備創造一個獨一無二的內核設備對象( DEVICE_OBJCET ),并因此而在需要的時候,系統能夠通過此內核設備對象找到它。

            如果要讓用戶層也能夠知道并使用 USB 設備,功能驅動更加不可少。它為設備在用戶程序可見的名字空間中,為它起一個別名,這個別名可以是一個符號鏈接,也可以是一個由 GUID 定義的設備 Interface 。通過對這個別名進行操作,也就是對設備本身進行操作。

            OK ,上面的話說得太滿了,有例外的!唯一的例外是以 RAW 模式驅動的設備。這種設備直接由總線驅動來驅動其工作,不需要功能驅動。這種例子真的不多見,也許只有很很底層的控制器設備、 Hub 設備之類,才會這樣做。對于 RAW 模式驅動的設備,當收到 IRP_MN_QUERY_CAPABILITIES 查詢請求的時候,在返回的 DEVICE_CAPABILITIES 結構體中,必須將 RawDeviceOK 位設置為 TRUE 。

            建議讀者在需要的時候使用 WinOBJ.exe 工具查看系統空間中的設備與別名。

             

            父驅動與混合設備
             

            通過一定的設置, USB 設備中的每個接口可以擁有不同的 Class 和 Protocol 定義,從而實現:一個設備,多個功能。這種設備被稱為混合設備。混合設備的前提是擁有多個接口,對單接口設備談 “ 混合 ” 是沒有意義的。

            滿足了如下兩個條件的多接口 USB 設備,被系統認為是混合設備:

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

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

             

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

            當然,能夠用上 IAD 的設備,一定是有多接口存在了,否則就是多此一舉了。 IAD 描述符的識別和實現是通用父驅動完成的,所以有 IAD 支持的設備,都被認作混合設備。而識別設備是否有 IAD 支持,是通過設備描述符中的如下值判斷的:

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

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

            我們下面來說說當一個多接口混合設備插入電腦后,系統是如何識別它,并為它加載驅動的。這里大家要注意到一點,就是系統是如何把一個設備,通過多接口,識別為多個物理設備的。

            一開始,系統 PNP 管理器安裝常規,讀取并分析 USB 設備描述符,然后為它分配如下設備 ID :

             

            USB\VID_vvvv&PID_pppp

            USB\VID_vvvv&PID_pppp&REV_rrrr

            (vvvv, pppp, rrrr: 4 位 16 進制數,分別代表了廠商 ID ,產品 ID , USB 版本。對應于設備描述符中的這些值: idVendor/ idProduct/ bcdDevice)

             

            和兼容 ID :

             

            USB\COMPOSITE

             

            PNP 管理器首先按照常規,根據上述的設備 ID 為設備尋找合適的驅動程序:根據設備 ID 到注冊表的設備安裝信息庫(對應于 Enum 和 Class 兩個鍵)中進行搜索,如果找到了安裝記錄,就根據記錄中的信息加載驅動程序。

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

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

             

            USB\VID_vvvv&PID_pppp&MI_mm

            USB\VID_vvvv&PID_pppp&REV_rrrr&MI_mm

            (mm: 兩個 16 位數字表示的接口號 )

             

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

             

            USB\CLASS_cc

            USB\CLASS_cc&SUBCLASS_ss

            USB\CLASS_cc&SUBCLASS_ss&PROT_pp

            (cc/ ss / pp: 兩位 16 進制數。分別對應于接口描述符中的: bInterfaceClass/ bInterfaceSubClass/ bInterfaceProtocol)

             

            通用父設備驅動為每個接口創建了物理設備對象后,將接口的設備 ID 、兼容 ID 信息提交給 PNP 管理器, PNP 管理器就有責任為這些“虛假的”物理設備安裝驅動。仍然重復上面的過程:根據每個接口的設備 ID 和兼容 ID 在注冊表的設備安裝信息庫中搜索,試圖找到相關的安裝記錄,如果找到了,就為接口加載相應的驅動程序。如果找不到,系統就會彈出“發現新設備”的對話框,啟動驅動安裝向導。此時用戶需為這些接口手動安裝驅動。

            所以,對于多接口的混合設備( composite device ),其設備驅動的安裝分為兩個過程,先嘗試安裝混合驅動,如果找不到,就默認安裝通用父設備驅動;接下來通用父設備驅動為每個接口分配設備 ID ,并要求系統為每個接口啟動 PNP 過程。

            最后,那么來說,在 USB 混合設備中,一定是 X 個接口對應于 X 個功能設備(有一個物理設備對象)嗎?一般情況下是這樣的,但也有例外,這就是接口組:在符合一定條件的情況下,多個接口中的某些接口可以被組合為一個接口組,一個接口組代表一個功能,父設備驅動只為之創建一個物理設備對象。

            接口組的詳情,大家看后面的章節內容。

             

            過濾驅動
             

            過濾驅動無處不在。在很多時候,它被稱作 Hook ,是一種 Hack 手段;很多時候它又是必不可少的,這種技術甚至被操作系統自己使用。沒有哪一個殺防毒軟件不使用過濾驅動,我們在使用網上銀行的時候,會用到一些安全控件,基本上都借助了過濾驅動的技術。

            過濾驅動可以位于任何一層驅動的上面,或下面。過濾的對象也包括已經存在于系統中的其他的過濾驅動。當它位于某層驅動( D 驅動)上面的時候,所有目標發往 D 驅動的請求,都首先被它截取;當它位于某層驅動下面的時候,所有和 D 驅動相關的從更底層驅動反饋回來的的“完成消息”都預先被過濾驅動截取。這正是它威力強大的原因所在。對于被過濾的驅動來說,過濾驅動簡直就是它的先知了。

            但使用過濾驅動,要很慎重。很容易把系統搞得很不穩定。讀者在寫過濾驅動的時候,要明白這樣一件事:你想過濾誰,得先了解誰;好像你追求一個人,要先認識這個人。否則死機藍屏都會與你不期而遇。

             

            USB 驅動棧、設備棧
             

            請大家不要把這里的“棧”理解成“程序堆棧”的那個棧,朋友們要回到這個字最簡單的本意來理解它,而不是想象成一個數據結構。就看成草垛柴堆一樣。

            驅動棧、設備棧本質上是并行概念。驅動之間的聯系是通過設備對象進行的,所以驅動之間是間接聯系的,驅動棧多少也就只是概念上的,他用來表示一個設備能夠在系統中識別、運行,從上到下中共需要哪些驅動程序支持。

            設備棧則是由據可查的。系統中每個 DevNode 就表現一個設備棧??梢赃@樣理解,多個設備棧,串聯成了驅動棧。

            使用 WinDBG 的 !devnode 命令,可以列舉系統中的設備樹。下圖截取了 CY001 相關片段。

             


            圖 3 CY001 DevNode 片段 

            上圖中紅色框標出的三個 DevNode ,正好對應于三個內核驅動,建立了 CY001 的驅動棧。從上到下分別是控制器驅動 (usbEHCI) ,集線器驅動 (usbHUB) ,和功能驅動 (CY001) 。下圖以 CY001 為例,更加清晰詳細地描繪了驅動棧、設備棧的面貌。


            圖 4 CY001 的驅動棧和設備棧 

            上圖是單接口 CY001 設備的驅動棧、設備棧全圖。也適用于所有其他的單接口 USB 設備。如果是多接口混合設備,就要多一個通用父驅動,稍微復雜一點。

            最上面是可能存在的過濾驅動,因為只是“可能存在”,所以都用虛框表示。其實過濾驅動可以存在于設備棧的任何一個位置,而不僅僅是最上層。筆者不可能盡皆畫全,以上層過濾為例,能說明問題也就可以了。

            過濾驅動生成的過濾設備對象,掛載到 CY001 驅動生成的功能設備對象上;這樣所有發送給 CY001 功能設備對象的請求,過濾設備對象總是先得到。根集線器生成了 CY001 驅動的物理設備對象。三個設備連在一起,就是 CY001 驅動程序的設備棧。

            但還沒有完,根集線器驅動并不是最底層驅動,他必須得到控制器驅動的支持,于是加上可能存在的過濾驅動,由此形成中間的那條設備棧。

            仍舊未結束,控制器驅動也不是直接和系統交互的,而是通過更底層的系統總線如 PCI 、 ACPI 進行的。這樣,右側的第三條設備棧也形成了。

            由此也可以看出設備棧、驅動棧之間的關系了。驅動棧是形而上的,設備棧是形而下的。


            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/changpei/archive/2010/05/06/5562542.aspx

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

            在MFC類庫提供了CWnd::OnCtlColor函數,在工作框架的子窗口被重畫時將調用該成員函數.因此可以重載WM_CTLCOLOR消息的響應函數.此函數的原型:
              
            afx_msg HBRUSH OnCtlColor(CDC *pDC,CWnd *pWnd,UINT nCtlColor);
                       參數nCtlColor用于指定控件的類型,可以是:
                       .CTLCOLOR_BTN                按鈕控件
                       .CTLCOLOR_DLG                對話框
                       .CTLCOLOR_EDIT               編輯框
                       .CTLCOLOR_LISTBOX            列表控件
                       .CTLCOLOR_MSGBOX             消息控件
                       .CTLCOLOR_SCROLLBAR 滾動條控件
                       .CTLCOLOR_STATIC             靜態控件
            [程序實現]
                       假設你已有了名為My的對話框工程.你有了一個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;
                       }


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

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

            【注】

            BLACK_BRUSH:黑色

            WHITE_BRUSH:白色

            GRAY_BRUSH:灰色

            NULL_BRUSH:透明

            HOLLOW_BRUSH :透明











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

            2.定義一個m_brush(CBrush類型)的成員變量和一個m_font(CFont類型)成員變量,在構造函數中初始化,例如: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);//設置文本背景色為透明

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

            return m_brush;//設置控件背景顏色

            }

            //對于按鈕來說上面的方法無效


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

            VC通用控件自適應屏幕類

            此為我程序中的一個類,本用于WinCE,但在桌面系統上也同樣適用!

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

            CWindowAnchor::BeginControlBound(hwnd)

             

            手動調整控件位置:

            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));


            自動調整控件位置(跟據設計時資源文件中控件的大小及位置):

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

             

            響應WM_SIZE消息:

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

             

            響應WM_DESTROY消息:

            CWindowAnchor::EndControlBound(hwnd);

             

             

            代碼:

            #pragma once
            #include 
            <map>

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

            /*用于WindowAnchorInfo結構的??款愋?/span>*/
            typedef 
            enum WindowAnchorType
            {
                WAT_TOP
            =0x0001,
                WAT_LEFT
            =0x0002,
                WAT_RIGHT
            =0x0004,
                WAT_BOTTOM
            =0x0008
            };

            /*控件定位描述信息*/
            typedef 
            struct WindowAnchorInfo{
                DWORD dwAnchor; 
            //WAT_*
                RECT rcOriginalRect; //控件的原始邊距,如果為空則自動獲?。▋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; 
            //對話框寬度
                INT nHeight; //對話框高度
                INT nMinHeight; //對話框最小高度
                ControlHashtable mapControls; //對話框所有子控件
            }WindowAnchorDialog;

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

            public:
                
            /*
                 * 開始調整(此調用中會獲取當前對話框的大小,如果在設計后要調整對話框大小,請先調用此方法)
                 * hwndDlg:對話框句柄
                
            */
                
            static BOOL BeginControlBound(HWND hwndDlg);

                
            /*
                 * 結束調整
                 * hwndDlg:對話框句柄
                
            */
                
            static BOOL EndControlBound(HWND hwndDlg);

                
            /*
                 * 添加一個控件到調整列表
                 * 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);

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

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

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

             

            下載地址:單擊下載

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

            1.首先在初始化函數中,FormView在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();//在資源編輯器中定好大小后,程序運行時大?。ú还茏畲蠡妥钚』摯笮【鶠橥粋€值),客戶區大于或等于顯示的大小  
            08. double m_x = (double)m_ClientRect.Width() / m_Forsize.cx;//寬度方向發大倍數  
            09. double m_y = (double)m_ClientRect.Height() / m_Forsize.cy;//高度方向發大倍數  
            10. 
            11. //調整控件的大小  
            12. CWnd *pWnd = NULL;   
            13. pWnd = GetWindow(GW_CHILD);  
            14. while(pWnd)//判斷是否為空,因為對話框創建時會調用此函數,而當時控件還未創建  
            15. {  
            16.  CRect rect;   //獲取控件變化前大小  
            17.  pWnd->GetWindowRect(&rect);  
            18.  ScreenToClient(&rect);//將控件大小轉換為在對話框中的區域坐標  
            19.  m_ControlRect.insert(pair<int, CRect>(pWnd->GetDlgCtrlID(), rect));//保存控件的初始大小,以便在OnSize函數中繼續使用  
            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只是位置變化,大小沒有變  
            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);//設置控件大小  
            43.  pWnd = pWnd->GetWindow(GW_HWNDNEXT);  
            44. }  
            45.   
            46. //控件初始化結束  
            47. m_IsInitialed = true; 
            //開始初始化控件大小
             m_IsInitialed = false;

             CRect m_ClientRect;
             this->GetClientRect(&m_ClientRect);
             CSize m_Forsize;
             m_Forsize = GetTotalSize();//在資源編輯器中定好大小后,程序運行時大?。ú还茏畲蠡妥钚』?,該大小均為同一個值),客戶區大于或等于顯示的大小
             double m_x = (double)m_ClientRect.Width() / m_Forsize.cx;//寬度方向發大倍數
             double m_y = (double)m_ClientRect.Height() / m_Forsize.cy;//高度方向發大倍數

             //調整控件的大小
             CWnd *pWnd = NULL;
             pWnd = GetWindow(GW_CHILD);
             while(pWnd)//判斷是否為空,因為對話框創建時會調用此函數,而當時控件還未創建
             {
              CRect rect;   //獲取控件變化前大小
              pWnd->GetWindowRect(&rect);
              ScreenToClient(&rect);//將控件大小轉換為在對話框中的區域坐標
              m_ControlRect.insert(pair<int, CRect>(pWnd->GetDlgCtrlID(), rect));//保存控件的初始大小,以便在OnSize函數中繼續使用
              int width = rect.Width();
              int height = rect.Height();

              WCHAR szBuf[256];
              GetClassName(pWnd->m_hWnd,szBuf,256);        
              if( _tcsicmp(szBuf,_T("Edit")) == 0)  
              {
               //Edit只是位置變化,大小沒有變
               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);//設置控件大小
              pWnd = pWnd->GetWindow(GW_HWNDNEXT);
             }
             
             //控件初始化結束
             m_IsInitialed = true;
             

            2.如果界面在運行時大小可以改變,則在OnSize函數中加入如下代碼

            view plaincopy to clipboardprint?
            01.// TODO: 在此處添加消息處理程序代碼  
            02.    CFormView::ShowScrollBar(SB_BOTH, false);//設置沒有滾動條,視情況而定。  
            03.         //在界面不是最小化并且已經初始化完畢  
            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);//設置沒有滾動條,視情況而定。
                     //在界面不是最小化并且已經初始化完畢
             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()函數中加入也可以(特別是在對話框作為tabpage時)

             

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/ybw20041910/archive/2010/06/19/5679730.aspx

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

            1.

            重載OnCtlColor    (CDC*    pDC,    CWnd*    pWnd,    UINT    nCtlColor),即WM_CTLCOLOR消息。  
               ----    ①在CExampleDlgDlg的頭文件中,添加一CBrush的成員變量:    
               class    CExampleDlgDlg    :    public    CDialog  
               {...  
               protected:  
               CBrush    m_brush;    
               ...  
               };  
               ----    ②在OnInitDialog()函數中添加如下代碼:    
               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.

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

            3.

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

            4.

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


            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/mfreesky/archive/2007/08/27/1760222.aspx

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

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

            =============獲取配置描述符
            bRequestType:80
            bRequest    :06
            wValue      :0200
            wIndex      :0000
            wLength     :0009 期望長度9字節

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

            =============嘗試讀取配置描述符0x12長度,不會超時
            bRequestType:80
            bRequest    :06
            wValue      :0200
            wIndex      :0000
            wLength     :0012

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

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

            =============設置配置,將配置生效,使能cpu上的endpoint端點
            bRequestType:00
            bRequest    :09
            wValue      :0001 將配置數值設置為1
            wIndex      :0000
            wLength     :0000

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

            =============
            bRequestType:81 讀取接口
            bRequest    :06 讀取接口描述符
            wValue      :2200 讀取報告描述符
            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 閱讀(274) | 評論 (0)編輯 收藏
                 摘要: 一、USB命令   在USB規范里,對命令一詞提供的單詞為“Request”,但這里為了更好的理解主機與設備之間的主從關系,將它定義成“命令”。   所有的USB設備都要求對主機發給自己的控制命令作出響應,USB規范定義了11個標準命令,它們分別是:C...  閱讀全文
            posted @ 2010-11-24 14:39 wrh 閱讀(1031) | 評論 (0)編輯 收藏

            在《USB系列之三》中,我們實現了一系列的SCSI命令,在這個系列中,我們要實現向U盤上寫扇區的命令,所以,本文相對比較容易,更多地是給出一個實現的源程序。

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

            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

                這里面最后的一個命令并不是SBC-2中要求強制實現的,而是可選的,但如果我們不去實現,U盤的操作將失色很多;我們不打算去實現序號為1、3、5、7和9的命令,READ(6)、READ(16)和READ(10)十分相似,只是LBA的長度不同而已,如果需要實現,參考READ(10)就可以了,FORMAT和SEND DIAGNOSTIC兩個命令對使用芯片的U盤來說沒有什么意義,當然對硬盤是有意義的,所以在本文中,我們只需要實現一個很重要的WRTE(10),向U盤上寫數據,我們需要準備一張沒有有用數據的U盤,因為我們要改變其中的內容。

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

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

                程序中,我們向《USB系列三》中的程序一樣,先reset,然后得到最大的LUN,這個步驟不是必須的,然后我們向device發出WRITE(10)命令,注意,這是一個OUT事務,所以,CBW_FLAGS=0X00而不是像以前一樣是0X80,發出WRITE(10)命令后,我們還要向device發送要寫入的數據,每次64個字節,一個扇區512字節需要啟動8個OUT事務,這個工作又函數putData完成,每次發送的64個字節我們分別寫入了0--63,程序中,我們把這些數據寫入到了LBA=100的扇區中,寫入后,我們在使用在《USB系列之三》中介紹過的READ(10)命令把相同的扇區讀出來,我們會看到我們所希望的結果,由于在讀之前,我們已經把buffer全部清為0了,所以我們有把握相信,我們讀到的數據是真實的。

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

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

            U盤是我們最常使用的一種USB設備,本文繼續使用DOSUSB做驅動,試圖以讀取扇區的方式讀取你的U盤。
                本文可能涉及的協議可能會比較多。
            一、了解你的U盤
                首先我們用上一篇文章介紹的程序usbview.exe去看一下你的U盤,我在本文中用于測試的U盤情況如下:
                Device Descriptor: (設備描述符)
                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: (端點描述符)
                Length:               7
                Descriptor Type:      5
                Endpoint Address:     
            1 OUT endpoint
                Attributes:           Bulk
                Max Packet Size:      64
                Interval:             0

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

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

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

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

                在系列一和系列二中,我們已經對DOSUSB的一個數據結構URB有所了解,本文中還要大量用到,我們還接觸了一個結構叫device_request,這個結構是在USB協議中定義的,用于向設備發送命令(Request),本文也會用到。

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

                這個系列中我們需要針對U盤讀更多的規范,如下:

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

                本節我們主要介紹兩個新的數據結構,這兩個結構都是在第二個規范中定義的。

                第一個數據結構叫CBW(Command Block Wrapper)


                這個結構將承載具體的與設備有關的命令發送到設備上去,這個結構分成兩部分,第一部分從byte[0]--byte[14]共15個字節,第而部分從byte[15]--byte[30]共16個字節,整個數據結構為31個字節。規范中并沒有定義第二部分的內容,這是因為第二部分承載的具體的命令,既與命令集(SCSI命令集)有關,也與具體的命令有關,我們使用SCSI命令集,所以后16個字節的內容在前面提到的后面兩個規范中有定義。

                比如我們要向設備發出一個SCSI命令INQUIRY(我們姑且先不要管命令的含義),那么這個命令的結構在SPC-3的第142頁有定義,如下:


                對于SCSI INQUIRY這條命令而言,CBW的第二部分的定義就是上面的這六個字節,不同的命令,定義也會不同。

                好,我們回到CSW的結構上來,根據規范,dCBWSignature的值必須是0X43425355,其實就是USBC這幾個字母倒過來,這是因為CBW的字符順序是little endian(這個東東在以前有關網絡編程的文章中介紹過),而我們PC機中的字符順序是big endian,所以要顛倒一下,總之寫dCBWSignature = 0X43425355就OK了;dCBWTag僅僅是一個標志,你可以填任何值,這里要先說一下CSW(Command Status Wrapper),我們每發出一個命令,設備都會返回一個CSW(這個東東下面很快就要介紹了),以說明命令的執行狀態,這個結構中也有Signature和Tag這兩個字段,其中Tag字段和發出命令時CBW中的Tag字段相同,這樣就可以區分這個CSW是和那個CBW對應的了,至于Signature,下面再說。

                下一個字段是dCBWDataTransferLength,表示的是當這個命令發出后,我們希望設備返回數據的字符數或者我們要向設備傳輸的字符數,本文僅涉及從設備返回數據,不涉及向設備傳輸數據;舉例來說:我們發送INQIURY命令到設備,按照SPC-3第144頁的說明,該命令返回的數據至少為36個字節,所以,此時這個字節應該填36;再如:我們讀取U盤的一個扇區,如果扇區的長度是512個字節,那么這個字段就要填512。

                再下來是bmCBWFlags字段,這個字段只有bit 7有意義,為0表示要向設備傳輸數據,為1表示要從設備獲得數據。

                bCBWLUN字段總是填0,因為絕大多數的U盤都不支持多LUN(Logical Unit Number),只有一個邏輯單元自然好嗎就是0了。

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

                第二個要說的數據結構是CSW,當host向device發送一個CBW后,接著就可以從device收到數據(或者發數據到device),當接受完所需的的數據后,就可以從device獲得一個CSW(Command Status Wrapper),CSW的結構如下:

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

                在得到的CSW中,恒定有13個字節,bCSWStatus的定義如下:

             

            三、發送命令和接收數據

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

                我們在使用控制傳輸時,我們設置好URB啟動傳輸事務,相應的結果將返回到制定得buffer中,批量傳輸沒有那么簡單,批量傳輸分為輸出事務和輸入事務,我們應該注意到,前面在看U盤的描述表時,在端點這一級有兩個端點,一個叫OUT端點,一個叫IN端點,當我們啟動一個輸出事務時,一定要發送給OUT端點,當我們啟動一個輸入事務時,一定要發送到輸入端點。下面我們簡單描述一下如何啟動批量傳輸事務。

                在使用控制傳輸時,我們應該閱讀過DOSUSB的說明,并且對URB結構比較熟悉,URB中有一個字段叫transation_type,當這個值為0x2d時為控制傳輸;當為0x69時為批量傳輸的IN事務;當為0xe1時為批量傳輸的OUT事務;當我們啟動一個傳輸時,一定要正確地設置這個值。

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

                接下來我們要接收device返回的執行INQUIRY命令的結果,這要啟動一個輸入事務,相對容易一些,只要填寫URB就可以了,把transation_type=0x69,把end_point填上OUT endpoint的地址,本例中為2,buffer_off和buffer_seg指向緩沖區buffer,把buffer_length和actual_length均填為64,因為前面端點描述符表中寫明包的最大長度為64,其它字段按常規填寫,調用DOSUSB,在buffer中就可以得到返回的內容,按照SPC-3中對返回內容的解釋即可了解設備的一些情況。

                接收晚數據后,不要忘了接收CSW,方法也是啟動一個輸入事務,與接收數據完全相同,然后根據CSW的結構解釋其含義。至此一個命令執行完畢。

            四、范例

                在本文的范例中,我們實現了如下內容:

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

                最后的一個命令,我將從你的U盤上讀出一個扇區。

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

                源代碼請在下面網址下載:

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

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

                好了,應該沒有什么了!

                Enjoy it.

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

                下面進入正題,列出你的USB設備,USB的HOST是掛接在PCI總線上的,所以通過PCI設備的遍歷就可以找到你的機器上的所有USB設備,在以前介紹PCI的配置空間時,曾經介紹過在配置空間中有一個占三個字節的分類代碼字段(如果不知道,請參閱我以前的博文
            《遍歷PCI設備》),在偏移為0x0B的字節叫基本分類代碼,在偏移為0x0A的字節叫子分類代碼,在偏移為0x09的字節叫編程接口代碼,對于USB設備類說,基本分類代碼為0x0C,子分類代碼為0x03,對于符合不同規范的HOST器件而言,編程接口代碼是不同的,UHCI的編程接口代碼是0x00,OHCI的編程接口代碼是0x10,EHCI的編程接口代碼是0x20,我想了解這些就足夠了。
                下面列出USB設備的源程序。
            #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);
            }

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

            導航

            <2025年6月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            統計

            常用鏈接

            留言簿(19)

            隨筆檔案

            文章檔案

            收藏夾

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            久久99热只有频精品8| 色综合久久中文字幕无码| 999久久久免费精品国产| 97超级碰碰碰久久久久| 国内精品伊人久久久久| 久久精品成人| 亚洲AV伊人久久青青草原| 亚洲精品白浆高清久久久久久| 久久精品国产亚洲精品2020| 久久伊人精品青青草原高清| 国产激情久久久久影院老熟女| 国内精品久久久久影院亚洲| 国产精品无码久久久久久| 看全色黄大色大片免费久久久| 三上悠亚久久精品| 久久综合九色综合欧美就去吻| 99国产精品久久| 中文无码久久精品| 久久夜色精品国产亚洲av| 精品国产一区二区三区久久久狼| 日日狠狠久久偷偷色综合96蜜桃| 俺来也俺去啦久久综合网| 日韩精品无码久久一区二区三| 九九精品99久久久香蕉| 久久精品一区二区三区AV| 久久se这里只有精品| 亚洲国产精品久久| 国产亚洲综合久久系列| 亚洲成色WWW久久网站| 超级碰碰碰碰97久久久久| 久久无码一区二区三区少妇| 久久精品人人做人人爽电影| 国产婷婷成人久久Av免费高清 | 精品久久久久久久久免费影院| 国产精品久久久久久久| 97久久国产露脸精品国产| 欧美粉嫩小泬久久久久久久 | 久久成人国产精品| 久久亚洲美女精品国产精品| 中文字幕乱码久久午夜| 亚洲精品乱码久久久久久蜜桃图片 |