青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 74, 文章 - 0, 評(píng)論 - 26, 引用 - 0
數(shù)據(jù)加載中……

2009年2月16日

Write HBITMAP Object in to BMP File 轉(zhuǎn)

void WriteBMPFile(HBITMAP bitmap, LPTSTR filename, HDC hDC)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;

// create the bitmapinfo header information

if (!GetObject( (bitmap, sizeof(BITMAP), (LPSTR)&bmp)){
AfxMessageBox("Could not retrieve bitmap info");
return;
}

// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;

// Allocate memory for the BITMAPINFO structure.
if (cClrBits != 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));

// Initialize the fields in the BITMAPINFO structure.

pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);

// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;

// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8 * pbmi->bmiHeader.biHeight * cClrBits;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;

// now open file and save the data
pbih = (PBITMAPINFOHEADER) pbmi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

if (!lpBits) {
AfxMessageBox("writeBMP::Could not allocate memory");
return;
}

// Retrieve the color table (RGBQUAD array) and the bits
if (!GetDIBits(hDC, HBITMAP(bitmap), 0, (WORD) pbih->biHeight, lpBits, pbmi,
DIB_RGB_COLORS)) {
AfxMessageBox("writeBMP::GetDIB error");
return;
}

// Create the .BMP file.
hf = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, (DWORD) 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE){
AfxMessageBox("Could not create file for writing");
return;
}
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;

// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);

// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, NULL)) {
AfxMessageBox("Could not write in to file");
return;
}

// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL))){
AfxMessageBox("Could not write in to file");
return;
}


// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)){
AfxMessageBox("Could not write in to file");
return;
}

// Close the .BMP file.
if (!CloseHandle(hf)){
AfxMessageBox("Could not close file");
return;
}

// Free memory.
GlobalFree((HGLOBAL)lpBits);
}

posted @ 2009-02-16 11:32 井泉 閱讀(1229) | 評(píng)論 (2)編輯 收藏

2009年1月9日

用VS2008 Feature Pack 修改您現(xiàn)有的Visual C++的程序界面 轉(zhuǎn)

上次給大家介紹了Visual C++ 2008 的Feature Pack的界面庫(kù)新特性。今天給大家介紹一下,怎樣用Feature Pack把您現(xiàn)有的Visual C++ 程序界面修改得漂亮些。

      所需的修改環(huán)境:
      Visual C++ 2008  (Team Suite版Express版都可以,但必須是英文版,否則Feature Pack不支持)
      正確的安裝了Visual C++ 2008 Feature Pack beta
      您要修改的Visual C++ 的程序源代碼工程

      具備了以上三點(diǎn)就可以開(kāi)始進(jìn)行修改操作了。但是有一點(diǎn)提請(qǐng)注意,那就是您程序中是否用到了MS C++ 9.0 編譯器不再支持的語(yǔ)法特性?如果有,那很不幸,我個(gè)人不推薦您升級(jí)您的程序界面,畢竟程序運(yùn)行的穩(wěn)定性才是最重要的。為了漂亮的界面修改已經(jīng)測(cè)試過(guò)并穩(wěn)定 運(yùn)行的代碼,可不是一個(gè)明智的選擇。

      下面我就用一個(gè)Visual C++的入門(mén)Demo Scribble 來(lái)修改。這個(gè)Scribble您可以在MSDN網(wǎng)站上下載到,但請(qǐng)您注意,我給出的這個(gè)下載工程是VS2005 for x64的。下載后,您需要進(jìn)行以下改動(dòng):
       1) 將Scribble工程屬性中,C/C++編譯器的Treat Warnings as error 關(guān)閉,否則您的工程將會(huì)因?yàn)橐粋€(gè)Warning沒(méi)有解決,導(dǎo)致整個(gè)程序編譯失敗。如圖:
       

      2) 編譯時(shí),將Target 改為Win32;

      羅嗦了這么多,開(kāi)始修改吧!

      第一步: 請(qǐng)確定CScribbleApp::InitialInstance() 方法中已經(jīng)調(diào)用了AfxOleInit();
      第二步: 在stdafx.h文件中加入 #include"afxcontrolbars.h" ,這頭文件包含了Feature Pack新增的界面類(lèi)聲明;
      第三步:修改CScribbleApp類(lèi)繼承的父類(lèi),由CWinApp改為CWinAppEx;這個(gè)CWinAppEx類(lèi)比CWinApp添 加了很多的功能,說(shuō)個(gè)簡(jiǎn)單的,CWinAppEx提供了一個(gè)SetRegistryBase方法,這個(gè)方法可以用來(lái)設(shè)定當(dāng)前App所使用的注冊(cè)表的根。
      第四步:修改主框架類(lèi),將CMainFrame的父類(lèi)由CMDIFrameWnd改為CMDIFrameWndEx;這個(gè)修改設(shè)計(jì)到類(lèi)聲明、 IMPLEMENT_DYNAMIC宏、MESSAGE_MAP宏、OnCreate函數(shù)等調(diào)用到靜態(tài)方法的地方、以及其它等等。最好是直接用 Replace all文本替換掉;
      第五步:將CMDIChildWnd類(lèi)替換為CMDIChildWndEx,主框架換了,子窗體也要換;
      第六步:替換CTooBar為CMFCTooBar,替換CStatusBar為CMFCStatusBar;就是修改一下m_wndStatusBar和m_wndToolBar兩個(gè)變量的聲明處;
      第七步:替換CMainFrame::OnCreate()函數(shù)中m_wndToolBar 和m_wndStatusBar停靠的相關(guān)代碼;將Set/Get BarStyle改為Set/Get PaneStyle 也是文本替換一下,很簡(jiǎn)單。將DockControlBar(&m_wndStatusBar);改為 DockPane(&m_wndStatusBar);
      完成以上七步,基本改造就算完成了。但是如果您現(xiàn)在編譯您的程序,您會(huì)發(fā)現(xiàn)Scribble界面基本上沒(méi)有任何改變。下面的才是更重要的,我們要添加RibbonBar了:

      第一步:在CMainFrame類(lèi)中聲明一個(gè)CMFCRibbonBar類(lèi)型的變量m_wndRibbonBar。這個(gè)變量就代表Office2007界面里面那個(gè)替代了菜單的東東;
      第二步:在CMainFrame類(lèi)中聲明一個(gè)CMFCRibbonApplicationButton的變量m_MainButton。這個(gè)變量代表了Office2007界面左上角那個(gè)Home按鈕;
      第三步:在CMainFrame::OnCreate函數(shù)中添加代碼。首先是創(chuàng)建RibbonBar對(duì)象,老規(guī)矩:
      if(!m_wndRibbonBar.Create(this))
      {
          return -1;
      }
      第四步:設(shè)定m_MainButton對(duì)象:
      m_MainButton.SetImage(……);   //設(shè)定圖標(biāo)
      m_MainButton.SetToolTipText(……); //設(shè)定提示文本
      m_MainButton.SetText(……);   //設(shè)定按鈕文本
      第五步:在CMainFrame::OnCreate函數(shù)中添加代碼,通過(guò)RibbonBar對(duì)象添加一個(gè)Category:
      CMFCRibbonMainPanel *pMainPanel = m_wndRibbonBar.AddMainCategory(_T("File"));
      這實(shí)際上就類(lèi)似于創(chuàng)建了一個(gè)名為File的主菜單項(xiàng);
      第六步:給這個(gè)Panel添加按鈕(其實(shí)就是子菜單項(xiàng)):
      pMainPanel->Add(new CMFCRibbonButton(ID_FILE_OPEN,_T("打開(kāi)")));
      ……
      第七步:
      在CMainFrame::OnCreate()函數(shù)的最后部分,添加代碼設(shè)定當(dāng)前界面的風(fēng)格:
      CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerOffice2007));
      CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerOffice2007::Office2007Luna_Blue);
      代碼的第一行用來(lái)設(shè)定可視化管理器為Office2007類(lèi)型,可供選擇的還有OfficXP、Office2003、VS2005 三種,換句話說(shuō),我們可以將我們的程序界面修改為Office2007、Office2003、OfficeXP、VS2005四種風(fēng)格;
      代碼的第二行用來(lái)設(shè)定Office2007界面的色調(diào);
      完成以上步驟后,就可以編譯運(yùn)行啦,看看界面是不是改變了呢?
      修改前:
     

      修改后:
      

      我沒(méi)有找到比較好看的圖標(biāo),也沒(méi)有把菜單都實(shí)現(xiàn)出來(lái),僅僅是作為一個(gè)演示。相信在美工的幫助下,我們的MFC程序界面一定會(huì)漂亮起來(lái)的。

      說(shuō)到最后,我要提醒大家一下,發(fā)布程序前,已經(jīng)要靜態(tài)鏈接MFC的庫(kù)。在現(xiàn)在VC8.0 的RTM尚不普及的情況下,就別指望您的用戶(hù)安裝部署了支持Feature Packe的FTM庫(kù)了。粗粗看了一下,一個(gè)用向?qū)傻闹С諪eature Pack的Application(是的,安裝了Feature Pack在用AppWizard生新程序的時(shí)候,就可以指定Feature Pack支持了),什么代碼都不加,靜態(tài)編譯一般在6M-8M之間(還算可以接受的說(shuō))。
      
     

posted @ 2009-01-09 13:51 井泉 閱讀(2555) | 評(píng)論 (3)編輯 收藏

2008年12月31日

.net制作的wap網(wǎng)站在手機(jī)中的測(cè)試 轉(zhuǎn)

訪問(wèn)asp站點(diǎn)的時(shí)候會(huì)根據(jù)訪問(wèn)的設(shè)備,輸出不同的內(nèi)容,如果用IE訪問(wèn)就輸出的是html,手機(jī)訪問(wèn),輸出就是WML。是什么讓他這么智能化呢?關(guān)鍵之處就在配置文件的<browserCaps>節(jié)!

在webconfig中加上這個(gè),他可以強(qiáng)制輸出wml,還有其他的移動(dòng)設(shè)置屬性都在這。

<browserCaps>
            
<result type="System.Web.Mobile.MobileCapabilities, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
            
<use var="HTTP_USER_AGENT"/>

            browser=Unknown
            version=0.0
            majorversion=0
            minorversion=0
            frames=false
            tables=false
            cookies=false
            backgroundsounds=false
            vbscript=false
            javascript=false
            javaapplets=false
            activexcontrols=false
            win16=false
            win32=false
            beta=false
            ak=false
            sk=false
            aol=false
            crawler=false
            cdf=false
            gold=false
            authenticodeupdate=false
            tagwriter=System.Web.UI.Html32TextWriter
            ecmascriptversion=0.0
            msdomversion=0.0
            w3cdomversion=0.0
            platform=Unknown
            css1=false
            css2=false
            xml=false

            mobileDeviceManufacturer = "Unknown"
            mobileDeviceModel = "Unknown"

            gatewayVersion = "None"
            gatewayMajorVersion = "0"
            gatewayMinorVersion = "0"

           preferredRenderingType = "wml11"
           preferredRenderingMime = "text/vnd.wap.wml"
           preferredImageMime = "image/vnd.wap.wbmp"

            defaultScreenCharactersWidth = "12"
            defaultScreenCharactersHeight = "6"
            defaultScreenPixelsWidth = "96"
            defaultScreenPixelsHeight = "72"
            defaultCharacterWidth = "8"
            defaultCharacterHeight = "12"
            screenBitDepth = "1"
            isColor = "false"
            inputType = "telephoneKeypad"

            numberOfSoftkeys = "0"
            maximumSoftkeyLabelLength = "5"

            canInitiateVoiceCall = "false"

            canSendMail = "true"
            hasBackButton = "true"
            rendersWmlDoAcceptsInline = "true"
            rendersWmlSelectsAsMenuCards = "true"
            rendersBreaksAfterWmlAnchor = "false"
            rendersBreaksAfterWmlInput = "false"
            rendersBreakBeforeWmlSelectAndInput = "true"
            requiresAttributeColonSubstitution = "true"
            requiresPhoneNumbersAsPlainText = "false"
            requiresUrlEncodedPostfieldValues = "false"
            requiredMetaTagNameValue = ""
            rendersBreaksAfterHtmlLists = "true"
            requiresUniqueHtmlCheckboxNames = "true"
            requiresUniqueHtmlInputNames = "true"
            requiresUniqueFilePathSuffix = "true"
            supportsCss = "false"
            hidesRightAlignedMultiselectScrollbars = "false"
            canRenderAfterInputOrSelectElement = "true"
            canRenderInputAndSelectElementsTogether = "true"
            canRenderOneventAndPrevElementsTogether = "true"
            canCombineFormsInDeck = "true"
            canRenderMixedSelects = "true"
            canRenderPostBackCards = "true"
            canRenderSetvarZeroWithMultiSelectionList = "true"
            supportsImageSubmit = "true"
            supportsSelectMultiple = "true"
            requiresHtmlAdaptiveErrorReporting = "false"
            requiresContentTypeMetaTag = "false"
            requiresDBCSCharacter = "false"
            requiresOutputOptimization = "false"
            supportsAccesskeyAttribute = "false"
            supportsInputIStyle = "false"
            supportsInputMode = "false"
            supportsIModeSymbols = "false"
            supportsJPhoneSymbols = "false"
            supportsJPhoneMultiMediaAttributes = "false"
            maximumRenderedPageSize = "2000"
            requiresSpecialViewStateEncoding = "false"
            requiresNoBreakInFormatting = "false"
            requiresLeadingPageBreak = "false"
            supportsQueryStringInFormAction = "true"
            supportsCacheControlMetaTag = "true"
            supportsUncheck = "true"
            canRenderEmptySelects = "true"
            supportsRedirectWithCookie = "true"
            supportsEmptyStringInCookieValue = "true"
            cachesAllResponsesWithExpires = "false"
            requiresNoSoftkeyLabels = "false"
            defaultSubmitButtonLimit = "1"
            
            supportsBold = "false"
            supportsItalic = "false"
            supportsFontSize = "false"
            supportsFontName = "false"
            supportsFontColor = "true"
            supportsBodyColor = "true"
            supportsDivAlign = "true"
            supportsDivNoWrap = "false"
            supportsCharacterEntityEncoding = "true"

            isMobileDevice="false"
</browserCaps> 

另外通過(guò)設(shè)置
         Page.Response.Expires = -1;
   Response.CacheControl 
= "Public";
可以取消移動(dòng)設(shè)備緩存,并通過(guò)RedirectToMobilePage函數(shù)進(jìn)行重定向。

posted @ 2008-12-31 10:46 井泉 閱讀(536) | 評(píng)論 (1)編輯 收藏

2008年12月24日

WINCE Driver and BSP Develop Blog 轉(zhuǎn)

開(kāi)發(fā)優(yōu)秀的驅(qū)動(dòng)程序

作為驅(qū)動(dòng)開(kāi)發(fā)工程師,我們需要在每一行代碼上下功夫,因?yàn)轵?qū)動(dòng)程序的效率直接影響著系統(tǒng)的性能.而新手往往不會(huì)注意到這些細(xì)節(jié)。以為功能實(shí)現(xiàn)以后就萬(wàn)事大吉了,其實(shí)不然,好的驅(qū)動(dòng)程序不只是能實(shí)現(xiàn)預(yù)期的功能。它同樣需要高的效率與規(guī)范的風(fēng)格。用戶(hù)花錢(qián)買(mǎi)我們系統(tǒng)是給他/她做事的,而不是給我們做測(cè)試的,所以我們要盡可能提高效率。同時(shí)好的代碼風(fēng)格能大大降低我們自己的維護(hù)成本。

高效率看似容易,但要注意到每個(gè)細(xì)節(jié)還是挺難的,我們可以從以下幾點(diǎn)去注意這個(gè)問(wèn)題:

1, 不要使用無(wú)關(guān)的代碼,這點(diǎn)容易理解,尤其是調(diào)試代碼,RELEASE時(shí)一定要去除這些代碼。

2, 去掉多余的函數(shù)調(diào)用,盡可能的保存一些數(shù)據(jù)。即使是最快的函數(shù),調(diào)用它時(shí)也會(huì)引發(fā)壓棧與出棧,所以要盡量少做函數(shù)調(diào)用。當(dāng)然如果一個(gè)函數(shù)返回的數(shù)據(jù)比較大,保存那些數(shù)據(jù)將占用比較多的內(nèi)在空間,保存返回值就得不償失了。比如,看到有的人每次在使用一個(gè)地址時(shí)就調(diào)用MmmapIoSpace將這個(gè)地址映射到程序地址空間,用完以后又立即Unmap這個(gè)地址,下次使用時(shí)又做MAP,這就是一種及不好的方法,每次需要多調(diào)用兩個(gè)系統(tǒng)函數(shù)。

3.如果可行,不要在循環(huán)中使用條件判斷,尤其在一個(gè)次數(shù)很多的循環(huán)中更應(yīng)該如此。

比如:

For( i=0; i<1000;>

If( m==1) ..

Else if (m==2 )….

Else …..

}

這種代碼,我們可以把If 寫(xiě)在for 之外,即,每一種不同的條件寫(xiě)一個(gè)循環(huán)體。

If( m==1) for ...

Else if (m==2 ) for ….

Else for …..

標(biāo)簽:

2007-03-10

開(kāi)發(fā)DMA驅(qū)動(dòng)

使用DMA的好處就是它不需要CPU的干預(yù)而直接服務(wù)外設(shè),這樣CPU就可以去處理別的事務(wù),從而提高系統(tǒng)的效率,對(duì)于慢速設(shè)備,如UART,其作用只是降低CPU的使用率,但對(duì)于高速設(shè)備,如硬盤(pán),它不只是降低CPU的使用率,而且能大大提高硬件設(shè)備的吞吐量。因?yàn)閷?duì)于這種設(shè)備,CPU直接供應(yīng)數(shù)據(jù)的速度太低。

CPU只能一個(gè)總線周期最多存取一次總線,而且對(duì)于ARM,它不能把內(nèi)存中A地址的值直接搬到B地址。它只能先把A地址的值搬到一個(gè)寄存器,然后再?gòu)倪@個(gè)寄存器搬到B地址。也就是說(shuō),對(duì)于ARM,要花費(fèi)兩個(gè)總線周期才能將A地址的值送到B地址。而DMA就不同了,一般系統(tǒng)中的DMA都有突發(fā)(Burst)傳輸?shù)哪芰Γ谶@種模式下,DMA能一次傳輸幾個(gè)甚至幾十個(gè)字節(jié)的數(shù)據(jù),所以使用DMA能使設(shè)備的吞吐能力大為增強(qiáng)。

使用DMA時(shí)我們必須要注意如下事實(shí):

1. DMA使用物理地址,程序是使用虛擬地址的,所以配置DMA時(shí)必須將虛擬地址轉(zhuǎn)化成物理地址。

2. 因?yàn)槌绦蚴褂锰摂M地址,而且一般使用CACHED地址,所以虛擬地址中的內(nèi)容與其物理地址上的內(nèi)容不一定一致辭,所以在啟動(dòng)DMA傳輸之前一定要將該地址的CACHE刷新,即寫(xiě)入內(nèi)存。

3. OS并不能保證每次分配到的內(nèi)在空間在物理上是連續(xù)的。尤其是在系統(tǒng)使用過(guò)一段時(shí)間而又分配了一塊比較大的內(nèi)存時(shí)。

所以每次都需要判斷地址是不是連續(xù)的,如果不連續(xù)就需要把這段內(nèi)存分成幾段讓DMA完成傳輸。

標(biāo)簽:

2007-03-03

WINCE下USBFN驅(qū)動(dòng)程序的一些概念

USBFN,即USB客戶(hù)端驅(qū)動(dòng),用來(lái)將一個(gè)WINCE設(shè)備模擬成一定的USB設(shè)備,讓主機(jī)端(如PC)訪問(wèn)。目前WINCE提供的USB客戶(hù)端有存儲(chǔ)設(shè)備,串口設(shè)備,及RNDIS網(wǎng)絡(luò)接口設(shè)備。

存儲(chǔ)設(shè)備用來(lái)將WINCE設(shè)備上的存儲(chǔ)空間,例如FLASH,當(dāng)作一塊存儲(chǔ)介質(zhì)給主機(jī)訪問(wèn),即將WINCE設(shè)備模擬成一個(gè)U盤(pán)。

串口設(shè)備將設(shè)備與主機(jī)的USB連線模擬成串口,WINCE和主機(jī)端都認(rèn)為它們之前連接上了一根串口線,它們之間可以做串口通信,典型的應(yīng)用是用來(lái)實(shí)現(xiàn)WINCEPC機(jī)的同步連接。

RNDIS設(shè)備使兩端認(rèn)為它們之間建立了網(wǎng)絡(luò)連接,通過(guò)注冊(cè)表設(shè)置可以讓主機(jī)通過(guò)WINCE設(shè)備上網(wǎng)或者使WINCE設(shè)備通過(guò)主機(jī)上網(wǎng)。

WINCE已經(jīng)提供了以上三種設(shè)備的驅(qū)動(dòng)程序,在同一時(shí)刻只能使用一個(gè)設(shè)備。而我們需要做的只是提供USBFN總線控制器的驅(qū)動(dòng)程序。USBFN系統(tǒng)各個(gè)模塊的關(guān)系如下:

USBFN總路線控制器作為一個(gè)總線驅(qū)動(dòng)程序,被設(shè)備管理器加載,根據(jù)注冊(cè)表設(shè)置加載相應(yīng)的客戶(hù)驅(qū)動(dòng)程序,即存儲(chǔ)設(shè)備,串口設(shè)備或者RNDIS設(shè)備。客戶(hù)驅(qū)動(dòng)程序即啟動(dòng)USBFN,引發(fā)主機(jī)配置設(shè)備,配置完成以后即可開(kāi)始工作。

USBFN總路線控制器驅(qū)動(dòng)的MDD部分WINCE本身已經(jīng)提供,PDD只需初始化硬件設(shè)備,提供傳輸即可。MDD在初始化時(shí)調(diào)用UfnPdd_Init函數(shù)得到PDD層的函數(shù)表,之后會(huì)根據(jù)需要調(diào)用各個(gè)函數(shù)。PDD還需要提供IST,用以處理各個(gè)中斷。需要注意的是USBFN有一個(gè)與其它設(shè)備不同之處,它的注冊(cè)表需要這樣一個(gè)設(shè)置:

"BusIoctl"=dword:2a0048,用以讓系統(tǒng)加載完設(shè)備之后調(diào)用值為0x2a0048IOCTL代碼去完成初始化,其定義為IOCTL_BUS_POSTINIT

標(biāo)簽:

2007-02-28

SOURCES文件詳解

SOURCES文件是WINCE底層開(kāi)發(fā)中最重要的文件之一,主要的配置項(xiàng)如下:

TARGETNAME,定義模塊名稱(chēng).
TARGETTYPE,
模塊的種類(lèi),可以是DYNLINK, LIBRARY,EXE.
如果TARGETTYPEDLL,則可以定義DLLENTRY,Dll入口定義成別的不是DLLMain的函數(shù),如果DLL的入口是DllMain,則不需要?jiǎng)e的定義。
如果TARGETTYPEEXE,則可以定義EXEENTRY,用于指定EXE的入口函數(shù).

如果TARGETTYPELIBRARY,則不需要定義入口函數(shù)。


INCLUDES
,如果一個(gè)模塊需要使用非標(biāo)準(zhǔn)路徑下的頭文件時(shí),需要定義INCLUDES,用于包含更多的頭文件路徑,用法如下:

INCLUDES=$(INCLUDES);\new directory\...,
注意定義新的INCLUDES時(shí),需要包含INCLUDES原來(lái)的值,否則就需要包含所有可能的目錄。

TARGETLIBS,SOURCELIBS
用于定義該模塊需要鏈接哪些庫(kù)文件.


TARGETLIBS
,如果一個(gè)庫(kù)以DLL的形式提供給調(diào)用者,就需要用TARGETLIBS,它只鏈接一個(gè)函數(shù)地址,系統(tǒng)執(zhí)行時(shí)會(huì)將被鏈接的庫(kù)加載。比如coredll.lib就是這樣的庫(kù)文件。即動(dòng)態(tài)鏈接。

SOURCELIBS,將庫(kù)中的函數(shù)實(shí)體鏈接進(jìn)來(lái)。即靜態(tài)鏈接,用到的函數(shù)會(huì)在我們的文件中形成一份拷貝。


注意,內(nèi)核這個(gè)執(zhí)行文件是沒(méi)有TARGETLIBS的,GIISR.DLL也不能有TARGETLIBS


WINCECOD,
如果將其定義為1,則編譯器會(huì)為每一個(gè)文件生成.cod文件,它是一個(gè)匯編文件,調(diào)試時(shí)查看匯編代碼也是一種很好的辦法。

SOURCES,
定義該模塊需要哪些源文件.

標(biāo)簽:

2007-02-17

多個(gè)設(shè)備共享同一個(gè)硬件中斷

硬件中斷線總是有限的,我們可能需要在已有的系統(tǒng)上做一些擴(kuò)展,比如將串口擴(kuò)展成好幾個(gè),有些硬件本身就設(shè)計(jì)成多個(gè)設(shè)備共享一條中斷線,比如我的系統(tǒng)中兩個(gè)串口就共享同一個(gè)CPU中斷,任何一個(gè)串口發(fā)生中斷以后都會(huì)觸發(fā)CPU的同一條中斷線,需要判斷別的寄存器來(lái)確定是哪個(gè)串口發(fā)生了什么中斷。

我們可以在OAL中分析各個(gè)中斷源,然后返回不同的SYSINTR值,但這種做法擴(kuò)展性不好。例如,OAL中設(shè)值某個(gè)中斷源最多會(huì)產(chǎn)生三個(gè)SYSINTR,但以后擴(kuò)展成了四個(gè)設(shè)備,有一個(gè)設(shè)備就無(wú)法正常工作了。

WINCE引入了可裝載中斷處理例程的概念。即在需要與別的設(shè)備共享中斷的驅(qū)動(dòng)程序中加載一個(gè)ISR,一般使用WINCE提供的GIISR即成滿足需求。將其安裝到內(nèi)核。OAL中發(fā)生中斷時(shí)調(diào)用NKCallIntChain來(lái)得到SYSINTR,這個(gè)函數(shù)會(huì)引起系統(tǒng)逐個(gè)調(diào)用在該IRQ上加載的所有可裝載的ISR,當(dāng)某個(gè)ISR認(rèn)為這個(gè)中斷是由它引發(fā)的時(shí)就返回其SYSINTR,否則就返回SYSINTR_CHAIN系統(tǒng)就會(huì)接著調(diào)用其它的ISR,甚至所有的ISR都被調(diào)用或者有一個(gè)ISR返回了正確的SYSINTR

驅(qū)動(dòng)程序中的調(diào)用辦法如下(CE幫助文檔):

if (InstallIsr) {

// Install ISR handler

g_IsrHandle = LoadIntChainHandler(IsrDll, IsrHandler, (BYTE)Irq);

if (!g_IsrHandle) {

DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: Couldn't install ISR handler\r\n"));

} else {

GIISR_INFO Info;

PVOID PhysAddr;

DWORD inIoSpace = 1; // io space

PHYSICAL_ADDRESS PortAddress = {ulIoBase, 0};

if (!TransBusAddrToStatic(PCIBus, 0, PortAddress, ulIoLen, &inIoSpace, &PhysAddr)) {

DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: Failed TransBusAddrToStatic\r\n"));

return FALSE;

}

DEBUGMSG(ZONE_PDD, (L"WAVEDEV: Installed ISR handler, Dll = '%s', Handler = '%s', Irq = %d, PhysAddr = 0x%x\r\n", IsrDll, IsrHandler, Irq, PhysAddr));

// Set up ISR handler

Info.SysIntr = ulSysIntr;

Info.CheckPort = TRUE;

Info.PortIsIO = TRUE;

Info.UseMaskReg = FALSE;

Info.PortAddr = (DWORD)PhysAddr + ES1371_dSTATUS_OFF;

Info.PortSize = sizeof(DWORD);

Info.Mask = ES1371_INTSTAT_PENDING;

if (!KernelLibIoControl(g_IsrHandle, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL)) {

DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: KernelLibIoControl call failed.\r\n"));

}

}

}

這里需要注意一下,因?yàn)?/span>ISR在內(nèi)核態(tài)運(yùn)行,Info.PortAddr必須是系統(tǒng)最原始的虛擬地址,即沒(méi)有用VirtualCopy映射過(guò)的,從OEMAddressTable中計(jì)算出來(lái)的虛擬地址。在這個(gè)例子中用TransBusAddrToStatic函數(shù)可以直接把物理地址轉(zhuǎn)換成這種地址。而MmMapIoSpace得到是在當(dāng)前程序空間中的地址,不能使用。而且GIIR要被加載到內(nèi)核空間,所以在加入到OS包中時(shí)需要加上K標(biāo)志,否則LoadIntChainHandler函數(shù)會(huì)失敗。

posted @ 2008-12-24 15:12 井泉 閱讀(759) | 評(píng)論 (0)編輯 收藏

2008年12月19日

如何使用CeLog調(diào)試Windows mobile設(shè)備驅(qū)動(dòng) 轉(zhuǎn)

一.     介紹
本文主要介紹在不進(jìn)入KITL模式, 使用CeLog工具來(lái)調(diào)試Windows mobile設(shè)備的方法,該方法可以抓到使用DEBUGMSG打出的log信息,注意:本方法只在RETAIL版本上實(shí)驗(yàn)通過(guò)。
二.     工具
Platform Builder
 Readlog.exe
CeLogStopFlush.exe
Readlog.exe可以在路徑:_WINCEROOT\SDK\BIN\I386下面找到,CeLogStopFlush.exe
的源代碼可以在_WINCEROOT\Public\Common\SDK\Samples\CeLog\Flush\Stopflush找到,使用build –c可以生成CeLogStopFlush.exe。
三.     步驟
1.建立一個(gè)Start Log的快捷方式,具體新建一個(gè)文件,復(fù)制下面的語(yǔ)句:
83#\Windows\celogflush.exe -buf 0x100000 -time 60000 -n \celog.clg -z 0x00800000 -ui 1
將其重命名為Start Log.lnk
2.檢查手機(jī)的\Windows目錄是否有以下兩個(gè)文件CeLog.dll and CeLogFlush.exe,如果沒(méi)有的話,將它們拷貝到\Windows目錄(release目錄可以找到)
3.拷貝Start Log.lnk和CeLogStopFlush.exe到開(kāi)始菜單中,修改注冊(cè)表,增加或修改下面的鍵值。(注意ZoneCE是16進(jìn)制的)
[HKEY_LOCAL_MACHINE\System\CeLog]
    "Transport"="LocalFile"
    "FileName"="celog.clg"
    "ZoneCE"=dword:800000
4.點(diǎn)擊Start Log開(kāi)始抓log
5.點(diǎn)擊CeLogStopFlush.exe停止抓log
6.將根目錄下生成的celog.clg文件拷貝到PC機(jī)與Readlog同一級(jí)目錄
7.打開(kāi)DOS命令行,轉(zhuǎn)到Readlog.exe目錄中,輸入下面的命令:
Readlog.exe celog.clg celog.log
8.使用文本工具查看celog.log中的log信息,下面是一個(gè)log的實(shí)例。

posted @ 2008-12-19 14:07 井泉 閱讀(856) | 評(píng)論 (0)編輯 收藏

2008年12月16日

Linux應(yīng)用程序安裝與管理-轉(zhuǎn)

Linux應(yīng)用程序安裝與管理        
        
目標(biāo):
        了解linux應(yīng)用程序的組成部分
        掌握使用RPM工具管理軟件包的方法
        掌握應(yīng)用程序源代碼包的編譯安裝方法
        掌握?qǐng)D形界面下應(yīng)用程序管理工具的使用
1、Linux應(yīng)用程序基礎(chǔ)
2、RPM包管理
        包管理系統(tǒng)初步:
        RPM:RPM Package Manager        
        
http://www.rpm.org
        RPM包管理系統(tǒng):
                $ rpm
        RPPM包的文件名稱(chēng):
           bash-3.0-19.2.i386.rpm
                bash:軟件名稱(chēng)。
                3.0-19.2:軟件的版本號(hào)。
                i386:軟件所運(yùn)行的最低硬件平臺(tái)。
                rpm:文件的擴(kuò)展名,用來(lái)標(biāo)識(shí)當(dāng)前文件是rpm格式的軟件包。
        RPM包管理功能:
            rpm命令配合不同的參數(shù)可以實(shí)現(xiàn)以下的rpm包的管理功能:
                查詢(xún)已安裝在linux系統(tǒng)中的RPM軟件包的信息。
                查詢(xún)RPM軟件包安裝文件的信息。
                安裝RPM軟件包到當(dāng)前l(fā)inux系統(tǒng)。
                從當(dāng)前l(fā)inux系統(tǒng)中卸載已安裝的RPM軟件包。
                從當(dāng)前l(fā)inux系統(tǒng)中升級(jí)已安裝的RPM軟件包。
使用rpm命令查詢(xún)軟件包:
        1、查詢(xún)系統(tǒng)中安裝的所有RPM包
                $ rpm  -qa                                      查詢(xún)當(dāng)前l(fā)inux系統(tǒng)中已經(jīng)安裝的軟件包。
                例:$ rpm -qa | grep -i x11 | head -3  察看系統(tǒng)中包含x11字符串的前3行軟件包。
        2、查詢(xún)軟件包是否安裝
                $ rpm –q rpm包名稱(chēng)                        察看系統(tǒng)中指定軟件包是否安。
                例: $ rpm -q bash                            察看系統(tǒng)中bash軟件包是否安裝。
           "rpm -q"命令中指定的軟件包名稱(chēng)需要準(zhǔn)確的拼寫(xiě),該命令不會(huì)在軟件包的名稱(chēng)中進(jìn)行局部匹配的查詢(xún)。
        3、查詢(xún)已安裝軟件包詳細(xì)信息
                $ rpm –qi RPM包名稱(chēng)                       查詢(xún)linux系統(tǒng)中指定名稱(chēng)軟件包的詳細(xì)信息。
                例:$ rpm -qi bash                          察看bash軟件包的詳細(xì)信息。
           "rpm -qi"命令的執(zhí)行結(jié)果中包含較詳細(xì)的信息,包括:軟件名稱(chēng),版本信息,包大小,描述,等。
        4、查詢(xún)已安裝軟件包中的文件列表
                $ rpm –ql RPM包名稱(chēng)                       查詢(xún)已安裝軟件包在當(dāng)前系統(tǒng)中安裝了哪些文件。
                例:$ rpm -ql bash | head -3            查看bash軟件在系統(tǒng)中已安裝文件的前3行文件列表。
                    $ rpm -ql bash | grep bin             用過(guò)濾方式察看bash中包含bin字符串的文件列表。
        5、查詢(xún)系統(tǒng)中文件所屬的軟件包
                $ rpm –qf 文件名稱(chēng)    查詢(xún)linux系統(tǒng)中指定文件所屬的軟件包。
                例:$ rpm -qf /bin/bash                   察看bash文件所屬的軟件包。
                    bash-3.0-19.2      顯示結(jié)果。
        6、查詢(xún)RPM安裝包文件中的信息
                $ rpm –qpi RPM包文件名                  察看RPM包未安裝前的詳細(xì)信息。
                $ rpm –qpl RPM包文件名                  察看RPM包未安裝前的文件列表。
           "rpm -qpi和rpm -qpl 這兩條命令可作為在安裝軟件包之前對(duì)其的了解。
        7、rpm命令查詢(xún)實(shí)例
                $ which mount                               獲得mount命令的可執(zhí)行文件路徑。
                $ rpm –qf  /bin/mount                     查詢(xún)/bin/mount所屬的軟件包。
                $ rpm –qi util-linux                           查詢(xún)/bin/mount所屬軟件包的詳細(xì)信息。
                $ rpm –qf util-linux | grep mount       查詢(xún)/bin/mount所屬軟件包中包括mount相關(guān)所有文件。
使用rpm命令安裝軟件包
        1、rpm軟件包地基本安裝
                $ rpm –i  rpm安裝包文件名                安裝該軟件包中的文件到當(dāng)前系統(tǒng),安裝過(guò)程不提示任何信息。
        2、在安裝軟件包的同時(shí)顯示詳細(xì)信息
                $ rpm –ivh rpm安裝包文件                 安裝該軟件包中的文件到當(dāng)前系統(tǒng),安裝過(guò)程會(huì)以百分比的形式
                                                                    顯示安裝的進(jìn)度和一些其他信息。
        3、RPM軟件包安裝的依賴(lài)關(guān)系
                強(qiáng)制安裝:$ rpm  --force  –i  rpm包文件名
            注:要先滿足軟件包的依賴(lài)關(guān)系后再進(jìn)行軟件包的安裝,使用強(qiáng)制安裝命令安裝不能保證軟件安裝到系統(tǒng)后一定能
                  正常運(yùn)行,因此建議慎重使用。
使用rpm命令卸載軟件包:
        1、RPM軟件包的卸載
                $ rpm  -e  軟件包名稱(chēng)                       軟件包的卸載,在卸載時(shí)不顯示任何信息。
            注:RPM軟件包的卸載同樣存在依賴(lài)關(guān)系,只有在沒(méi)有依賴(lài)關(guān)系存在時(shí)才能對(duì)其進(jìn)行卸載。
        2、rpm軟件包卸載的依賴(lài)關(guān)系
                  在使用RPM命令進(jìn)行卸載時(shí),RPM命令會(huì)分析要卸載的軟件包的依賴(lài)關(guān)系,當(dāng)存在依賴(lài)關(guān)系時(shí)會(huì)自動(dòng)停止,并顯由   
                  哪個(gè)軟件造成的卸載失敗。根據(jù)RPM提示的錯(cuò)誤信息,確定先卸載的軟件包,再卸載被依賴(lài)的軟件包。
使用rpm命令升級(jí)軟件包:
        $ rpm  - U  rpm安裝包文件名
            注:"rpm -u"命令中使用的升級(jí)軟件包文件最好使用RED HAT公司針對(duì)當(dāng)前的linux版本官方推出的升級(jí)文件,建議不要  
                  使用第三方提供的升級(jí)包。
   
應(yīng)用程序編譯
        開(kāi)放源代碼應(yīng)用程序的編譯安裝
        (下面以多線程下載軟件"prozilla"的源代碼編譯安裝為例來(lái)說(shuō)明源代碼編譯安裝的整個(gè)過(guò)程)
        編譯應(yīng)用程序前的準(zhǔn)備工作:
        1、確認(rèn)系統(tǒng)中已經(jīng)安裝了編譯環(huán)境
                $ rpm  -qa  | grep gcc                       確定當(dāng)前系統(tǒng)中安裝了gcc編譯器環(huán)境。
        2、下載prozilla程序的源代碼安裝包文件
                 略
        3、釋放已下載的源代碼軟件包文件
                $ tar jxf prozilla-2.0.4.tar.bz2              釋放以下載的源代碼軟件包文件到當(dāng)前目錄。解壓后的文件
                                                                     名:prozilla-2.0.4
           擴(kuò)展:tar的xzvf參數(shù)用于釋放以tar.gz格式的壓縮包。
        4、進(jìn)入源代碼目錄
                $ cd prozilla-2.0.4                             進(jìn)入目錄。
                $ pwd                                            顯示當(dāng)前目錄路徑。
                /home/teacher/download/prozilla-2.0.4      顯示結(jié)果。
           編譯軟件安裝的路徑:
                $ ./configure --prefix=/home/teacher/proz  
                在prozilla程序的配置中,使用"--prdfix"選項(xiàng)可以指定應(yīng)用程序編譯后的安裝路徑,如果不使用"--prefix"
                選項(xiàng)指定安裝路徑,configure程序?qū)⑴渲胮rozilla的默認(rèn)安裝路徑為"/usr/local/bin"目錄。
         5、程序編譯過(guò)程
                $ make                                           使用make命令進(jìn)行程序的二進(jìn)制編譯。
        6、程序安裝過(guò)程
                $ make install
                "make install"命令將按照configuer命令的"--prefix"選項(xiàng)中設(shè)定的安裝路徑將已編譯完成的應(yīng)用程序安裝
                 到目標(biāo)目錄。
        7、驗(yàn)證編譯安裝的程序
                $ ls /home/teacher/proz                    察看proz文件夾中的文件。
                bin  include lib man share
        
        
        編譯前的配置
                $ ./configure  - - help                    
        編譯與安裝:
        1、程序編譯過(guò)程
                $ make
        2、程序安裝過(guò)程
                $ make install
        3、驗(yàn)證編譯安裝的程序
使用圖形界面系統(tǒng)工具完成RPM保的安裝
         略

posted @ 2008-12-16 11:34 井泉 閱讀(271) | 評(píng)論 (0)編輯 收藏

2008年12月3日

Windows CE usb驅(qū)動(dòng)程序

上述講了堆理論,可能讀者腦袋都已經(jīng)大了,為此,我們舉個(gè)簡(jiǎn)單的例子來(lái)詳細(xì)說(shuō)明一下驅(qū)動(dòng)程序的開(kāi)發(fā)過(guò)程。

例如我們有個(gè)USB Mouse設(shè)備,設(shè)備信息描述如下:
Device Descriptor:
bcdUSB: 0x0100
bDeviceClass: 0x00
bDeviceSubClass: 0x00
bDeviceProtocol: 0x00
bMaxPacketSize0: 0x08 (8)
idVendor: 0x05E3 (Genesys Logic Inc.)
idProduct: 0x0001
bcdDevice: 0x0101
iManufacturer: 0x00
iProduct: 0x01
iSerialNumber: 0x00
bNumConfigurations: 0x01

ConnectionStatus: DeviceConnected
Current Config value: 0x01
Device Bus Speed: Low
Device Address: 0x02
Open Pipes: 1

Endpoint Descriptor:
bEndpointAddress: 0x81
Transfer Type: Interrupt
wMaxPacketSize: 0x0003 (3)
bInterval: 0x0A

可以看出上述設(shè)備有一個(gè)中斷PIPE,包的最大值為3。可能有人問(wèn)上述的值怎么得到的,win2k 的DDK中有個(gè)usbview的例程,編譯一下,將你的USB設(shè)備插到PC機(jī)的USB口中,運(yùn)行usbview.exe即可看得相應(yīng)的設(shè)備信息。

有了這些基本信息,就可以編寫(xiě)USB設(shè)備了,首先聲明一下,下面的代碼取自微軟的USB鼠標(biāo)樣本程序,版權(quán)歸微軟所有,此處僅僅借用來(lái)描述一下USB鼠標(biāo)驅(qū)動(dòng)的開(kāi)發(fā)過(guò)程,讀者如需要引用此代碼,需要得到微軟的同意。

首先,必須輸出USBD要求調(diào)用的三個(gè)函數(shù),首先到設(shè)備插入到USB端口時(shí),USBD會(huì)調(diào)用USBDeviceAttach()函數(shù),相應(yīng)的代碼如下:
extern "C" BOOL
USBDeviceAttach(
USB_HANDLE hDevice, // USB設(shè)備句柄
LPCUSB_FUNCS lpUsbFuncs, // USBDI的函數(shù)集合
LPCUSB_INTERFACE lpInterface, // 設(shè)備接口描述信息
LPCWSTR szUniqueDriverId, // 設(shè)備ID描述字符串。
LPBOOL fAcceptControl, // 返回TRUE,標(biāo)識(shí)我們可以控制此設(shè)備, 反之表示不能控制
DWORD dwUnused)
{
*fAcceptControl = FALSE;
// 我們的鼠標(biāo)設(shè)備有特定的描述信息,要檢測(cè)是否是我們的設(shè)備。
if (lpInterface == NULL)
return FALSE;
// 打印相關(guān)的USB設(shè)備接口描述信息。
DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: DeviceAttach, IF %u, #EP:%u, Class:%u, Sub:%u,Prot:%u\r\n"), lpInterface->Descriptor.bInterfaceNumber,lpInterface->Descriptor.bNumEndpoints, lpInterface->Descriptor.bInterfaceClass,lpInterface->Descriptor.bInterfaceSubClass,lpInterface->Descriptor.bInterfaceProtocol));
// 初試數(shù)據(jù)USB鼠標(biāo)類(lèi),產(chǎn)生一個(gè)接受USB鼠標(biāo)數(shù)據(jù)的線程
CMouse * pMouse = new CMouse(hDevice, lpUsbFuncs, lpInterface);
if (pMouse == NULL)
return FALSE;

if (!pMouse->Initialize())
{
delete pMouse;
return FALSE;
}

// 注冊(cè)一個(gè)監(jiān)控USB設(shè)備事件的回調(diào)函數(shù),用于監(jiān)控USB設(shè)備是否已經(jīng)拔掉。
(*lpUsbFuncs->lpRegisterNotificationRoutine)(hDevice,
USBDeviceNotifications, pMouse);

*fAcceptControl = TRUE;
return TRUE;
}

第二個(gè)函數(shù)是 USBInstallDriver()函數(shù),
一些基本定義如下:
const WCHAR gcszRegisterClientDriverId[] = L"RegisterClientDriverID";
const WCHAR gcszRegisterClientSettings[] = L"RegisterClientSettings";
const WCHAR gcszUnRegisterClientDriverId[] = L"UnRegisterClientDriverID";
const WCHAR gcszUnRegisterClientSettings[] = L"UnRegisterClientSettings";
const WCHAR gcszMouseDriverId[] = L"Generic_Sample_Mouse_Driver";

函數(shù)接口如下:
extern "C" BOOL
USBInstallDriver(
LPCWSTR szDriverLibFile) // @parm [IN] - Contains client driver DLL name
{
BOOL fRet = FALSE;
HINSTANCE hInst = LoadLibrary(L"USBD.DLL");

// 注冊(cè)USB設(shè)備信息
if(hInst)
{
LPREGISTER_CLIENT_DRIVER_ID pRegisterId = (LPREGISTER_CLIENT_DRIVER_ID)
GetProcAddress(hInst, gcszRegisterClientDriverId);

LPREGISTER_CLIENT_SETTINGS pRegisterSettings =
(LPREGISTER_CLIENT_SETTINGS) GetProcAddress(hInst,
gcszRegisterClientSettings);

if(pRegisterId && pRegisterSettings)
{
USB_DRIVER_SETTINGS DriverSettings;

DriverSettings.dwCount = sizeof(DriverSettings);

// 設(shè)置我們的特定的信息。
DriverSettings.dwVendorId = USB_NO_INFO;
DriverSettings.dwProductId = USB_NO_INFO;
DriverSettings.dwReleaseNumber = USB_NO_INFO;

DriverSettings.dwDeviceClass = USB_NO_INFO;
DriverSettings.dwDeviceSubClass = USB_NO_INFO;
DriverSettings.dwDeviceProtocol = USB_NO_INFO;

DriverSettings.dwInterfaceClass = 0x03; // HID
DriverSettings.dwInterfaceSubClass = 0x01; // boot device
DriverSettings.dwInterfaceProtocol = 0x02; // mouse

fRet = (*pRegisterId)(gcszMouseDriverId);

if(fRet)
{
fRet = (*pRegisterSettings)(szDriverLibFile,
gcszMouseDriverId, NULL, &DriverSettings);

if(!fRet)
{
//BUGBUG unregister the Client Driver’s ID
}
}
}
else
{
RETAILMSG(1,(TEXT("!USBMouse: Error getting USBD function pointers\r\n")));
}
FreeLibrary(hInst);
}
return fRet;
}
上述代碼主要用于產(chǎn)生USB設(shè)備驅(qū)動(dòng)程序需要的注冊(cè)表信息,需要注意的是:USB設(shè)備驅(qū)動(dòng)程序不使用標(biāo)準(zhǔn)的注冊(cè)表函數(shù),而是使用RegisterClientDriverID()和RegisterClientSettings來(lái)注冊(cè)相應(yīng)的設(shè)備信息。

另外一個(gè)函數(shù)是USBUninstallDriver()函數(shù),具體代碼如下:
extern "C" BOOL
USBUnInstallDriver()
{
BOOL fRet = FALSE;
HINSTANCE hInst = LoadLibrary(L"USBD.DLL");

if(hInst)
{
LPUN_REGISTER_CLIENT_DRIVER_ID pUnRegisterId =
(LPUN_REGISTER_CLIENT_DRIVER_ID)
GetProcAddress(hInst, gcszUnRegisterClientDriverId);

LPUN_REGISTER_CLIENT_SETTINGS pUnRegisterSettings =
(LPUN_REGISTER_CLIENT_SETTINGS) GetProcAddress(hInst,
gcszUnRegisterClientSettings);

if(pUnRegisterSettings)
{
USB_DRIVER_SETTINGS DriverSettings;

DriverSettings.dwCount = sizeof(DriverSettings);
// 必須填入與注冊(cè)時(shí)相同的信息。
DriverSettings.dwVendorId = USB_NO_INFO;
DriverSettings.dwProductId = USB_NO_INFO;
DriverSettings.dwReleaseNumber = USB_NO_INFO;

DriverSettings.dwDeviceClass = USB_NO_INFO;
DriverSettings.dwDeviceSubClass = USB_NO_INFO;
DriverSettings.dwDeviceProtocol = USB_NO_INFO;

DriverSettings.dwInterfaceClass = 0x03; // HID
DriverSettings.dwInterfaceSubClass = 0x01; // boot device
DriverSettings.dwInterfaceProtocol = 0x02; // mouse

fRet = (*pUnRegisterSettings)(gcszMouseDriverId, NULL,
&DriverSettings);
}

if(pUnRegisterId)
{
BOOL fRetTemp = (*pUnRegisterId)(gcszMouseDriverId);
fRet = fRet ? fRetTemp : fRet;
}
FreeLibrary(hInst);
}
return fRet;
}
此函數(shù)主要用于刪除USBInstallDriver()時(shí)創(chuàng)建的注冊(cè)表信息,同樣的它使用自己的函數(shù)接口UnRegisterClientDriverID()和UnRegisterClientSettings()來(lái)做相應(yīng)的處理。

另外一個(gè)需要處理的注冊(cè)的監(jiān)控通知函數(shù)USBDeviceNotifications():
extern "C" BOOL USBDeviceNotifications(LPVOID lpvNotifyParameter, DWORD dwCode,
LPDWORD * dwInfo1, LPDWORD * dwInfo2, LPDWORD * dwInfo3,
LPDWORD * dwInfo4)
{
CMouse * pMouse = (CMouse *)lpvNotifyParameter;

switch(dwCode)
{
case USB_CLOSE_DEVICE:
//刪除相關(guān)的資源。
delete pMouse;
return TRUE;
}
return FALSE;
}


USB鼠標(biāo)的類(lèi)的定義如下:
class CMouse
{
public:
CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
LPCUSB_INTERFACE lpInterface);
~CMouse();

BOOL Initialize();
private:
// 傳輸完畢調(diào)用的回調(diào)函數(shù)
static DWORD CALLBACK MouseTransferCompleteStub(LPVOID lpvNotifyParameter);
// 中斷處理函數(shù)
static ULONG CALLBACK CMouse::MouseThreadStub(PVOID context);
DWORD MouseTransferComplete();
DWORD MouseThread();

BOOL SubmitInterrupt();
BOOL HandleInterrupt();

BOOL m_fClosing;
BOOL m_fReadyForMouseEvents;

HANDLE m_hEvent;
HANDLE m_hThread;

USB_HANDLE m_hDevice;
USB_PIPE m_hInterruptPipe;
USB_TRANSFER m_hInterruptTransfer;

LPCUSB_FUNCS m_lpUsbFuncs;
LPCUSB_INTERFACE m_pInterface;

BOOL m_fPrevButton1;
BOOL m_fPrevButton2;
BOOL m_fPrevButton3;

// 數(shù)據(jù)接受緩沖區(qū)。
BYTE m_pbDataBuffer[8];
};

具體實(shí)現(xiàn)如下:

// 構(gòu)造函數(shù),初始化時(shí)調(diào)用
CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
LPCUSB_INTERFACE lpInterface)
{
m_fClosing = FALSE;
m_fReadyForMouseEvents = FALSE;
m_hEvent = NULL;
m_hThread = NULL;

m_hDevice = hDevice;
m_hInterruptPipe = NULL;
m_hInterruptTransfer = NULL;

m_lpUsbFuncs = lpUsbFuncs;
m_pInterface = lpInterface;

m_fPrevButton1 = FALSE;
m_fPrevButton2 = FALSE;
m_fPrevButton3 = FALSE;

memset(m_pbDataBuffer, 0, sizeof(m_pbDataBuffer));
}

// 析構(gòu)函數(shù),用于清除申請(qǐng)的資源。
CMouse::~CMouse()
{
// 通知系統(tǒng)去關(guān)閉相關(guān)的函數(shù)接口。
m_fClosing = TRUE;

// Wake up the connection thread again and give it time to die.
if (m_hEvent != NULL)
{
// 通知關(guān)閉數(shù)據(jù)接受線程。
SetEvent(m_hEvent);

if (m_hThread != NULL)
{
DWORD dwWaitReturn;

dwWaitReturn = WaitForSingleObject(m_hThread, 1000);
if (dwWaitReturn != WAIT_OBJECT_0)
{
TerminateThread(m_hThread, DWORD(-1));
}
CloseHandle(m_hThread);
m_hThread = NULL;
}
CloseHandle(m_hEvent);
m_hEvent = NULL;
}

if(m_hInterruptTransfer)
(*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer);

if(m_hInterruptPipe)
(*m_lpUsbFuncs->lpClosePipe)(m_hInterruptPipe);
}


// 初始化USB鼠標(biāo)驅(qū)動(dòng)程序
BOOL CMouse::Initialize()
{
LPCUSB_DEVICE lpDeviceInfo = (*m_lpUsbFuncs->lpGetDeviceInfo)(m_hDevice);

// 檢測(cè)配置:USB鼠標(biāo)應(yīng)該只有一個(gè)中斷管道
if ((m_pInterface->lpEndpoints[0].Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) != USB_ENDPOINT_TYPE_INTERRUPT)
{
RETAILMSG(1,(TEXT("!USBMouse: EP 0 wrong type (%u)!\r\n"),
m_pInterface->lpEndpoints[0].Descriptor.bmAttributes));
return FALSE;
}
DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: EP 0:MaxPacket: %u, Interval: %u\r\n"),
m_pInterface->lpEndpoints[0].Descriptor.wMaxPacketSize,
m_pInterface->lpEndpoints[0].Descriptor.bInterval));

m_hInterruptPipe = (*m_lpUsbFuncs->lpOpenPipe)(m_hDevice,
&m_pInterface->lpEndpoints[0].Descriptor);

if (m_hInterruptPipe == NULL) {
RETAILMSG(1,(TEXT("Mouse: Error opening interrupt pipe\r\n")));
return (FALSE);
}
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL)
{
RETAILMSG(1,(TEXT("USBMouse: Error on CreateEvent for connect event\r\n")));
return(FALSE);
}
// 創(chuàng)建數(shù)據(jù)接受線程
m_hThread = CreateThread(0, 0, MouseThreadStub, this, 0, NULL);
if (m_hThread == NULL)
{
RETAILMSG(1,(TEXT("USBMouse: Error on CreateThread\r\n")));
return(FALSE);
}

return(TRUE);
}

// 從USB鼠標(biāo)設(shè)備中讀出數(shù)據(jù),產(chǎn)生相應(yīng)的鼠標(biāo)事件。
BOOL CMouse::SubmitInterrupt()
{
if(m_hInterruptTransfer)
(*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer);

// 從USB鼠標(biāo)PIPE中讀數(shù)據(jù)
m_hInterruptTransfer = (*m_lpUsbFuncs->lpIssueInterruptTransfer)
(m_hInterruptPipe, MouseTransferCompleteStub, this,
USB_IN_TRANSFER | USB_SHORT_TRANSFER_OK, // 表示讀數(shù)據(jù)
min(m_pInterface->lpEndpoints[0].Descriptor.wMaxPacketSize,
sizeof(m_pbDataBuffer)),
m_pbDataBuffer,
NULL);

if (m_hInterruptTransfer == NULL)
{
DEBUGMSG(ZONE_ERROR,(L "!USBMouse: Error in IssueInterruptTransfer\r\n"));
return FALSE;
}
else
{
DEBUGMSG(ZONE_TRANSFER,(L"USBMouse::SubmitInterrupt,Transfer:0x%X\r\n",
m_hInterruptTransfer));
}
return TRUE;
}

// 處理鼠標(biāo)中斷傳輸?shù)臄?shù)據(jù)
BOOL CMouse::HandleInterrupt()
{
DWORD dwError;
DWORD dwBytes;

DWORD dwFlags = 0;
INT dx = (signed char)m_pbDataBuffer[1];
INT dy = (signed char)m_pbDataBuffer[2];

BOOL fButton1 = m_pbDataBuffer[0] & 0x01 ? TRUE : FALSE;
BOOL fButton2 = m_pbDataBuffer[0] & 0x02 ? TRUE : FALSE;
BOOL fButton3 = m_pbDataBuffer[0] & 0x04 ? TRUE : FALSE;

if (!(*m_lpUsbFuncs->lpGetTransferStatus)(m_hInterruptTransfer, &dwBytes,&dwError))
{
DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Error in GetTransferStatus(0x%X)\r\n"),
m_hInterruptTransfer));
return FALSE;
}
else
{
DEBUGMSG(ZONE_TRANSFER,(TEXT("USBMouse::HandleInterrupt, hTransfer 0x%X complete (%u bytes, Error:%X)\r\n"),
m_hInterruptTransfer,dwBytes,dwError));
}

if (!SubmitInterrupt())
return FALSE;

if(dwError != USB_NO_ERROR)
{
DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Error 0x%X in interrupt transfer\r\n"),dwError));
return TRUE;
}

if(dwBytes < 3)
{
DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Invalid byte cnt %u from interrupt transfer\r\n"),dwBytes));
return TRUE;
}

if(dx || dy)
dwFlags |= MOUSEEVENTF_MOVE;

if(fButton1 != m_fPrevButton1)
{
if(fButton1)
dwFlags |= MOUSEEVENTF_LEFTDOWN;
else
dwFlags |= MOUSEEVENTF_LEFTUP;
}

if(fButton2 != m_fPrevButton2)
{
if(fButton2)
dwFlags |= MOUSEEVENTF_RIGHTDOWN;
else
dwFlags |= MOUSEEVENTF_RIGHTUP;
}

if(fButton3 != m_fPrevButton3)
{
if(fButton3)
dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
else
dwFlags |= MOUSEEVENTF_MIDDLEUP;
}

m_fPrevButton1 = fButton1;
m_fPrevButton2 = fButton2;
m_fPrevButton3 = fButton3;

DEBUGMSG(ZONE_EVENTS,
(TEXT("USBMouse event: dx:%d, dy:%d, dwFlags:0x%X (B1:%u, B2:%u, B3:%u)\r\n"),
dx,dy,dwFlags,fButton1,fButton2,fButton3));

// 通知系統(tǒng)產(chǎn)生鼠標(biāo)事件
if (m_fReadyForMouseEvents)
mouse_event(dwFlags, dx, dy, 0, 0);
else
m_fReadyForMouseEvents = IsAPIReady(SH_WMGR);

return TRUE;
}


DWORD CALLBACK CMouse::MouseTransferCompleteStub(LPVOID lpvNotifyParameter)
{
CMouse * pMouse = (CMouse *)lpvNotifyParameter;
return(pMouse->MouseTransferComplete());
}

// 數(shù)據(jù)傳輸完畢回調(diào)函數(shù)
DWORD CMouse::MouseTransferComplete()
{
if (m_hEvent)
SetEvent(m_hEvent);
return 0;
}


ULONG CALLBACK CMouse::MouseThreadStub(PVOID context)
{
CMouse * pMouse = (CMouse *)context;
return(pMouse->MouseThread());
}

// USB鼠標(biāo)線程
DWORD CMouse::MouseThread()
{
DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: Worker thread started\r\n")));
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

if (SubmitInterrupt())
{
while (!m_fClosing)
{
WaitForSingleObject(m_hEvent, INFINITE);

if (m_fClosing)
break;

if ((*m_lpUsbFuncs->lpIsTransferComplete)(m_hInterruptTransfer))
{
if (!HandleInterrupt())
break;
}
else
{
RETAILMSG(1,(TEXT("!USBMouse: Event signalled, but transfer not complete\r\n")));
// The only time this should happen is if we get an error on the transfer
ASSERT(m_fClosing || (m_hInterruptTransfer == NULL));
break;
}
}
}
RETAILMSG(1,(TEXT("USBMouse: Worker thread exiting\r\n")));
return(0);
}

看 到了沒(méi)有,其實(shí)USB的驅(qū)動(dòng)程序編寫(xiě)就這么簡(jiǎn)單,類(lèi)似的其他設(shè)備,例如打印機(jī)設(shè)備,就有Bulk OUT PIPE,需要Bulk傳輸,那就需要了解一下IssueBulkTransfer()的應(yīng)用。當(dāng)然如果是開(kāi)發(fā)USB Mass Storage Disk的驅(qū)動(dòng),那就需要了解更多的協(xié)議,例如Bulk-Only Transport協(xié)議等。

微軟的Windows CE.NET的Platform Build中已經(jīng)帶有USB Printer和USB Mass Storage Disk的驅(qū)動(dòng)的源代碼了,好好研究一下,你一定回受益非淺的。



參考資料:
1. 微軟出版社 <<Windows Ce Device Driver Kit>>
2. <<Universal Serial Bus Specification 1.1>> 來(lái)自http:://www.usb.org

posted @ 2008-12-03 10:35 井泉 閱讀(1319) | 評(píng)論 (0)編輯 收藏

2008年12月2日

Windows CE 6.0中斷處理過(guò)程 by ningling

這里我們主要討論的是CE的中斷建立和中斷相應(yīng)的大概流程以及所涉及的代碼位置。這里所講述的,是針對(duì)ARM平臺(tái)的。在CE的中斷處理里面,有一部分工作是CE Kernel完成的,有一部分工作是要由OEM完成的。

Kernel代碼工作

ExVector.s:中斷向量定義,里面定義的是armtrap.s的函數(shù)地址

Armtrap.s:中斷處理定義,最重要是里面的IRQHandler函數(shù),而其中最重要的是CALL OEMInterruptHandler

Mdarm.c:中斷向量加載

Kdriver.cNKCallIntChain函數(shù):把IRQ轉(zhuǎn)換為SysIntr,值得留意的是pIntChainTable[],是IRQ所對(duì)應(yīng)的ISR處理程序的入口,其中最主要的是其成員函數(shù)pfnHandlerpfnHandler的填充,是在HookIntChain里面,這個(gè)函數(shù)是ISR在初始化的時(shí)候調(diào)用的。在這個(gè)函數(shù)里面,如果pIntChainTable為空,則返回SYSINTR_CHAIN,如果pIntChainTable[]不為空,則調(diào)用pfnHandler得到一個(gè)sysintr值,然后返回之。

 

OEM定義工作:Oalintr.cOEMInterruptHandler函數(shù),通過(guò)查詢(xún)硬件的中斷寄存器,得到硬件IRQ號(hào)。對(duì)于EINT04-23的中斷,通過(guò)EINTMASK寄存器,得到相對(duì)應(yīng)的系統(tǒng)IRQ。注意,這里的IRQCE定義的IRQ,是系統(tǒng)硬件IRQ的擴(kuò)展。然后調(diào)用NKCallIntChain看看這個(gè)IRQ是否是一個(gè)ChainInterrupt。如果函數(shù)返回SYSINTR_CHAIN或者返回一個(gè)不合法的sysintr,則通過(guò)OALIntrTranslateIrqIRQ轉(zhuǎn)化為sysintr。如果是一個(gè)合法的sysintr,則返回該值。

 

單一ISRDevice,主要通過(guò)OEMInterruptHandler處理,在OEMInterruptHandler沒(méi)有定義的IRQ,可以在OAL里面或者驅(qū)動(dòng)的加載里面,通過(guò)HookInterrupt函數(shù)進(jìn)行關(guān)聯(lián)。

多個(gè)ISRDevice,通常這是總線設(shè)備的需求,因?yàn)榭偩€設(shè)備上面通常串有幾個(gè)設(shè)備。這些總線上的設(shè)備,需要有一個(gè)ISR判斷究竟是哪個(gè)設(shè)備發(fā)出的中斷。這個(gè)ISR,是一個(gè)DLL的程序,設(shè)備驅(qū)動(dòng)必須在初始化的時(shí)候通過(guò)LoadIntChainHandler(文件名,函數(shù)名,irq)加載這個(gè)DLL程序。LoadIntChainHandler的定義在kdriver.cNKLoadIntChainHandler里面。對(duì)于大多數(shù)的總線設(shè)備,可以利用微軟已經(jīng)寫(xiě)好的giisr.dll來(lái)實(shí)現(xiàn)。giisr的實(shí)現(xiàn)代碼在Public\common\oak\drivers下面。

 

對(duì)于總線設(shè)備,如果利用GIISR的話,原理如下:

總線設(shè)備驅(qū)動(dòng)在初始化的時(shí)候,通過(guò)LoadIntChainHandler加載GIISR,而加載的時(shí)候,LoadIntChainHandler會(huì)調(diào)用GIISRCreateInstance創(chuàng)建一個(gè)實(shí)例,GIISR會(huì)返回一個(gè)index值給LoadIntChainHandler,以標(biāo)示實(shí)例,LoadIntChainHandler則會(huì)返回一個(gè)Handle給驅(qū)動(dòng),驅(qū)動(dòng)則根據(jù)這個(gè)Handle存取GIISR。得到這個(gè)handle之后,初始化還需要包括從reg表里面讀出相關(guān)的初始化參數(shù),對(duì)GIISR進(jìn)行賦值,譬如Port AddressMask AddressSysIntr等。

 

驅(qū)動(dòng)程序在初始化的時(shí)候:

1、創(chuàng)建一個(gè)EventCreateEvent

2、然后用InterruptInitialize函數(shù)把sysintr和這個(gè)Event相關(guān)聯(lián)

3Kick-off一個(gè)ThreadIST

4、這個(gè)Thread最終是WaitForSingleObjectEventID

具體的例子,可以參閱USBFN的例子:sc2410pdd.cpp里面,UfnPdd_Start函數(shù);

Published Sunday, June 10, 2007 7:13 PM by ningling

posted @ 2008-12-02 17:00 井泉 閱讀(566) | 評(píng)論 (0)編輯 收藏

2008年11月28日

Windows CE下驅(qū)動(dòng)開(kāi)發(fā)基礎(chǔ) 付林林

這是我從1月6日開(kāi)始主持天極網(wǎng)論壇嵌入式開(kāi)發(fā)版以來(lái)第一次發(fā)表文章,加上以前瑣碎的文章共 計(jì)30篇。研究的越多就越感覺(jué)自己懂的太少,其實(shí)在驅(qū)動(dòng)開(kāi)發(fā)方面我還是個(gè)菜鳥(niǎo),我是想再次拋磚引玉,讓做驅(qū)動(dòng)有N年經(jīng)驗(yàn)的人奉獻(xiàn)一點(diǎn)出來(lái),讓大家減少一些 研究驅(qū)動(dòng)源碼而又缺少注釋所帶來(lái)的痛苦。
  我想即使讀者看過(guò)微軟的關(guān)于驅(qū)動(dòng)開(kāi)發(fā)的培訓(xùn)教材和CE幫助文檔中的驅(qū)動(dòng)部分,頭腦中仍然一片茫然。要想真正了解驅(qū)動(dòng)程序必須結(jié)合一些驅(qū)動(dòng)程序源碼,在此我以串口驅(qū)動(dòng)程序(COM16550)中初始化過(guò)程為線索簡(jiǎn)單講一講驅(qū)動(dòng)開(kāi)發(fā)的基礎(chǔ)知識(shí)。
  Windows CE下的串口驅(qū)動(dòng)程序能夠處理所有I/O行為類(lèi)似串口的設(shè)備,包括基于16450、16550 UART(通用異步收發(fā)芯片)的設(shè)備和一些采用DMA的設(shè)備,常見(jiàn)的有9針串口、紅外I/O口、Modem等。在%_WINCEROOT%\Public \Common\OAK\Drivers\Serial目錄下,COM_MDD2子目錄包含新的串口驅(qū)動(dòng)MDD層函數(shù)代碼。COM16550子目錄包含串 口驅(qū)動(dòng)PDD層代碼。SER16550子目錄包含的一系列函數(shù)專(zhuān)用于控制與16550兼容的UART,這樣PDD層的主要工作就是調(diào)用SER16550中 的函數(shù)。還有一個(gè)ISR16550子目錄包含的是串口驅(qū)動(dòng)程序?qū)S玫目砂惭bISR(中斷服務(wù)例程),而很多硬件設(shè)備驅(qū)動(dòng)程序采用CE默認(rèn)的可安裝ISR giisr.dll。一般串口設(shè)備相應(yīng)的注冊(cè)表設(shè)置例子及意義如下:
意義
"SysIntr"=dword:13 串口1的中斷ID為十進(jìn)制13
"IoBase"=dword:02F8 串口1的IO空間首地址為十六進(jìn)制2F8
"IoLen"=dword:8 串口1的IO空間長(zhǎng)度為8個(gè)字節(jié)
"DeviceArrayIndex"=dword:0 串口1的索引,是1的由來(lái)
"Order"=dword:0 串口1驅(qū)動(dòng)的加載順序
"DeviceType"=dword:0 串口1的設(shè)備類(lèi)型
"DevConfig"=hex: 10,00 .... 串口1在與Modem設(shè)備通訊時(shí)的配置,如波特率、奇偶校檢等
"FriendlyName"="COM1:" 串口1在撥號(hào)程序中顯示的名字
"Tsp"="Unimodem.dll" 串口1 被用于與Modem設(shè)備通訊的時(shí)候要加載的TSP(TAPI Service provider)DLL
"Prefix"="COM" 串口1的流接口的前綴
"Dll"="com16550.Dll" 串口1的驅(qū)動(dòng)程序DLL

  SysIntr 由CE在文件Nkintr.h中預(yù)定義,用于唯一標(biāo)識(shí)中斷設(shè)備。OEM可以在文件Oalintr.h中定義自己的SysIntr。常見(jiàn)的預(yù)定義 SysIntr有SYSINTR_NOP(中斷只由ISR處理,IST不再處理),SYSINTR_RESCHED(重新調(diào)度線 程),SYSINTR_DEVICES(由CE預(yù)定義的設(shè)備中斷ID的基值),SYSINTR_PROFILE、SYSINTR_TIMING、 SYSINTR_FIRMWARE等都是基于SYSINTR_DEVICES定義的。IoBase是串口1的IO地址空間的首地址,IoLen是IO空間 的大小。IO地址空間只存在于x86平臺(tái),如果在其它平臺(tái)硬件寄存器必須映射到物理地址空間,那子鍵的名稱(chēng)為MemBase和MemLen。在x86平臺(tái) 更多硬件的寄存器由于IO空間的局限也映射到物理地址空間。DeviceArrayIndex是設(shè)備的索引,用于區(qū)分同類(lèi)型的設(shè)備。Prefix是流驅(qū)動(dòng) 程序的前綴,當(dāng)應(yīng)用程序調(diào)用CreateFile函數(shù)傳遞COM1:參數(shù)時(shí),文件系統(tǒng)負(fù)責(zé)與串口驅(qū)動(dòng)程序通信,串口驅(qū)動(dòng)程序是在CE啟動(dòng)時(shí)由 device.exe加載的。

  下面從MDD 層函數(shù)COM_Init開(kāi)始探索串口驅(qū)動(dòng)的初始化過(guò)程。COM_Init是在串口設(shè)備被檢測(cè)后由設(shè)備管理器device.exe調(diào)用的,主要的作用是初始 化設(shè)備,它的唯一參數(shù)Identifier是由device.exe傳遞的,其類(lèi)型是一個(gè)字符串指針,字符串的內(nèi)容是HLM\Drivers \Active\xx,xx是一個(gè)十進(jìn)制數(shù)(device.exe會(huì)跟蹤系統(tǒng)中每個(gè)驅(qū)動(dòng)程序,把加載的驅(qū)動(dòng)程序記錄在Active鍵下)。 COM_Init先分配一個(gè)HW_INDEP_INFO結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體是獨(dú)立于串口硬件的頭信息(MDD、PDD、SER16550都包含自己獨(dú)特的 結(jié)構(gòu)體,具體的結(jié)構(gòu)體定義請(qǐng)參見(jiàn)串口驅(qū)動(dòng)源碼),分配之后再初始化結(jié)構(gòu)體中每個(gè)成員,初始化結(jié)構(gòu)體后調(diào)用 OpenDeviceKey((LPCTSTR)Identifier)打開(kāi)HLM\Drivers\Active\xx\Key包含的注冊(cè)表路徑,在這 里路徑一般為HLM\Drivers\BuiltIn\Serial,即串口的驅(qū)動(dòng)程序信息在注冊(cè)表中所處的位置。COM_Init接著在HLM \Drivers\BuiltIn\Serial下查詢(xún)DeviceArrayIndex、Priority256的值,Priority256指定了驅(qū) 動(dòng)程序的優(yōu)先級(jí),如果沒(méi)有就用默認(rèn)的優(yōu)先級(jí)。接下來(lái)調(diào)用GetSerialObject(DeviceArrayIndex),這個(gè)函數(shù)由PDD層定義, 返回HWOBJ結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體主要包含PDD層和SER16550定義的函數(shù)的指針。也就是說(shuō)MDD通過(guò)調(diào)用這個(gè)函數(shù)才能調(diào)用底層實(shí)現(xiàn)的函數(shù)。接下來(lái) 的大多數(shù)工作都是調(diào)用底層函數(shù)實(shí)現(xiàn)初始化。第一個(gè)調(diào)用的底層函數(shù)SerInit主要設(shè)置由用戶(hù)設(shè)置的硬件配置,例如線路控制、波特率。它調(diào)用 Ser_GetRegistryData函數(shù)得到保存在注冊(cè)表中的硬件信息,Ser_GetRegistryData在內(nèi)部調(diào)用系統(tǒng)提供的 DDKReg_GetIsrInfoDDK和DDKReg_GetWindowInfo函數(shù)得到在HLM\Drivers\BuiltIn\Serial 下保存的IRQ、SysIntr、IsrDll、IsrHandler、IoBase、IoLen。IRQ是邏輯中斷號(hào),IsrDll表示當(dāng)前驅(qū)動(dòng)程序的 可安裝ISR所在的DLL名稱(chēng),IsrHandler 表示可安裝ISR的函數(shù)名稱(chēng)。在這里順便提一下可安裝ISR,讀者在我以前發(fā)表的關(guān)于OAL的文章中可以了解到OEM在OEMInit函數(shù)中關(guān)聯(lián)IRQ和 SysIntr,當(dāng)硬件設(shè)備發(fā)生中斷時(shí),ISR會(huì)禁止同級(jí)和低級(jí)中斷,然后根據(jù)IRQ返回關(guān)聯(lián)的SysIntr,內(nèi)核根據(jù)ISR返回的SysIntr喚醒 相應(yīng)的IST(SysIntr與IST創(chuàng)建的Event關(guān)聯(lián)),IST處理中斷之后調(diào)用InterruptDone解除中斷禁止。在OEMInit中關(guān)聯(lián) 的缺點(diǎn)是一旦編譯了CE內(nèi)核后就無(wú)法添加這種關(guān)聯(lián)了,而一些硬件設(shè)備會(huì)隨時(shí)插拔或者共享中斷,要關(guān)聯(lián)這樣的硬件設(shè)備解決方法就是可安裝ISR,可安裝 ISR專(zhuān)用于處理指定的硬件設(shè)備發(fā)出的中斷,所以如果硬件設(shè)備需要可安裝ISR必須在注冊(cè)表中添加IsrDll、IsrHandler。多數(shù)硬件設(shè)備采用 CE默認(rèn)的可安裝ISR giisr.dll,格式如下:

 "IsrDll"="giisr.dll"
"IsrHandler"="ISRHandler"

  如果一個(gè)硬件驅(qū)動(dòng)程序需要可安裝ISR而開(kāi)發(fā)者又不想自己寫(xiě)一個(gè),那么可以利用giisr.dll來(lái)實(shí)現(xiàn)。除了在注冊(cè)表中添加如上所示外,還要在驅(qū)動(dòng)程序中調(diào)用相關(guān)函數(shù)注冊(cè)可安裝ISR。偽代碼如下:

g_IsrHandle = LoadIntChainHandler(IsrDll, IsrHandler, (BYTE)Irq);
GIISR_INFO Info;
PHYSICAL_ADDRESS PortAddress = {PhysAddr, 0};
TransBusAddrToStatic(BusType, dwBusNumber, PortAddress, dwAddrLen, &dwIOSpace, &(PVOID)PhysAddr)
Info.SysIntr = dwSysIntr;
Info.CheckPort = TRUE;
Info.PortIsIO = (dwIOSpace) ? TRUE : FALSE;
Info.UseMaskReg = TRUE;
Info.PortAddr = PhysAddr + 0x0C;
Info.PortSize = sizeof(DWORD);
Info.MaskAddr = PhysAddr + 0x10;
KernelLibIoControl(g_IsrHandle, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL);

  LoadIntChainHandler 函數(shù)負(fù)責(zé)注冊(cè)可安裝ISR,參數(shù)1為DLL名稱(chēng),參數(shù)2為ISR函數(shù)名稱(chēng),參數(shù)3為IRQ。TransBusAddrToStatic函數(shù)在后面講。如果 要利用giisr.dll作為可安裝ISR,必須先填充GIISR_INFO結(jié)構(gòu)體,CheckPort=TRUE表示giisr要檢測(cè)指定的寄存器來(lái)確 定當(dāng)前發(fā)出中斷的是否是這個(gè)設(shè)備。PortIsIO表示寄存器地址屬于哪個(gè)地址空間,F(xiàn)ALSE表示是內(nèi)定空間,TRUE表示IO空間。 UseMaskReg=TRUE表示設(shè)備有一個(gè)掩碼寄存器,專(zhuān)用于指定當(dāng)前設(shè)備是否是中斷源,也就是發(fā)出中斷,而MaskAddr表示掩碼寄存器的地址。 如果對(duì)Info.Mask賦值,那么PortAddr表示一個(gè)特殊的寄存器地址,這個(gè)寄存器的值與Mask的值&運(yùn)算的結(jié)果如果為真,則證明當(dāng)前 設(shè)備是中斷源,否則返回SYSINTR_CHAIN(表示當(dāng)前ISR沒(méi)有處理中斷,內(nèi)核將調(diào)用ISR鏈中下一個(gè)ISR),如果 UseMaskReg=TRUE,那么MaskReg寄存器的值與PortAddr指定的寄存器的值&運(yùn)算的結(jié)果如果為真,則證明當(dāng)前設(shè)備是中斷 源。
  函數(shù)SerInit接著調(diào)用函數(shù) Ser_InternalMapRegisterAddresses轉(zhuǎn)換IO地址并且映射地 址,Ser_InternalMapRegisterAddresses在內(nèi)部調(diào)用系統(tǒng)提供的HalTranslateBusAddress(Isa, 0, ioPhysicalBase, &inIoSpace, &ioPhysicalBase)函數(shù)將與總線相關(guān)的地址轉(zhuǎn)換為系統(tǒng)地址,參數(shù)1為總線類(lèi)型,參數(shù)2為總線號(hào),參數(shù)3為要轉(zhuǎn)換的地址 (PHYSICAL_ADDRESS類(lèi)型,實(shí)際是LARGE_INTEGER型),參數(shù)4指定寄存器地址屬于IO地址空間還是物理地址空間,參數(shù)5返回轉(zhuǎn) 換后的物理地址。觀察HalTranslateBusAddress的源碼得知如果是在x86平臺(tái),這個(gè)函數(shù)除了把參數(shù)3賦給了參數(shù)5其余什么都沒(méi)有做, 而非x86平臺(tái)將inIoSpace的值置為0,表示一定是物理地址。在調(diào)用HalTranslateBusAddress前要確定從注冊(cè)表中得到的寄存 器地址到底是屬于哪個(gè)地址空間的,例如:

ULONG inIoSpace = 1; ///1表示是IO空間
PHYSICAL_ADDRESS ioPhysicalBase = {iobase, 0}; ///相當(dāng)于ioPhysicalBase.LowPart = iobase

  在地址轉(zhuǎn)換后就要將轉(zhuǎn)換后的地址映射到驅(qū)動(dòng)程序(一般IST和應(yīng)用程序一樣運(yùn)行在用戶(hù)模式)能夠訪問(wèn)的虛擬地址空間(0x80000000以下)和ISR能夠訪問(wèn)的靜態(tài)虛擬地址空間中(0x80000000以上)。例如:

////如果地址屬于物理地址空間
ioPortBase = (PUCHAR)MmMapIoSpace(ioPhysicalBase, Size, FALSE);
TransBusAddrToStatic(Isa, 0, ioPhysicalBase, Size, &inIoSpace, ppStaticAddress);

  MmMapIoSpace函數(shù)負(fù)責(zé)將物理地址映射到驅(qū)動(dòng)程序能夠訪問(wèn)的虛擬地址空間中,通過(guò)源碼分析MmMapIoSpace在內(nèi)部分別調(diào)用:

pVirtualAddress =VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);
VirtualCopy(pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize, PAGE_PHYSICAL |
PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));

  VirtualAlloc 分配一塊和MemLen一樣大小的虛擬地址空間,因?yàn)閰?shù)1為0,所以?xún)?nèi)核自動(dòng)分配。一般MemLen小于2MB,所以會(huì)在應(yīng)用程序的地址空間中分配。 VirtualCopy負(fù)責(zé)將硬件設(shè)備寄存器的物理地址與VirtualAlloc分配的虛擬地址做一個(gè)映射關(guān)系,這樣驅(qū)動(dòng)程序訪問(wèn) PvirtualAddress實(shí)際上就是訪問(wèn)第一個(gè)寄存器。因?yàn)橛布O(shè)備寄存器的物理地址一定是在512MB(CE支持RAM的最大值)以上,所以除了 最后的參數(shù)要加PAGE_PHYSICAL外,第二個(gè)參數(shù)物理地址也要右移8位(或者除以256)。映射硬件寄存器當(dāng)然PAGE_NOCACHE是必須加 的。TransBusAddrToStatic函數(shù)負(fù)責(zé)將物理地址映射到ISR能夠訪問(wèn)的靜態(tài)虛擬地址空間中,當(dāng)出現(xiàn)中斷共享時(shí),ISR要負(fù)責(zé)訪問(wèn)硬件設(shè) 備的某一個(gè)寄存器來(lái)判斷中斷源,所以將寄存器的物理地址映射到靜態(tài)虛擬地址空間中是必要的(ISR只能訪問(wèn)靜態(tài)的虛擬地址空間)。所謂靜態(tài)虛擬地址空間是 指在OEMAddressTable中定義的虛擬地址空間(當(dāng)然是0x80000000以上)。在x86平臺(tái)一般這個(gè)表只定義RAM的物理地址與虛擬地址 對(duì)應(yīng)關(guān)系,而硬件設(shè)備的寄存器地址并不在該表中定義,所以如果要?jiǎng)?chuàng)建一塊靜態(tài)的虛擬地址空間供ISR訪問(wèn),必須在此之前調(diào)用 CreateStaticMapping函數(shù)在0xC4000000到0xE0000000虛擬地址空間中分配。 TransBusAddrToStatic函數(shù)在內(nèi)部就是調(diào)用了CreateStaticMapping函數(shù)。注:硬件設(shè)備的寄存器地址也可以在 OEMAddressTable中定義。

////如果地址屬于IO空間
ioPortBase = (PUCHAR)ioPhysicalBase.LowPart;
*ppStaticAddress=ioPortBase

這種情況只屬于x86平臺(tái),是IO空間就可以直接訪問(wèn),即使是用戶(hù)模式。
  SerInit 函數(shù)接著初始化SER_INFO結(jié)構(gòu)體成員,之后調(diào)用SL_Init函數(shù),這個(gè)函數(shù)在ser16550中定義,負(fù)責(zé)初始化SER16550_INFO結(jié)構(gòu) 體,在這個(gè)結(jié)構(gòu)體中保存串口8個(gè)寄存器的地址。SerInit函數(shù)執(zhí)行完畢后COM_Init函數(shù)創(chuàng)建接收緩沖區(qū),然后調(diào)用 StartDispatchThread函數(shù)初始化中斷并且創(chuàng)建IST。StartDispatchThread函數(shù)在內(nèi)部調(diào)用 InterruptInitialize函數(shù)關(guān)聯(lián)SysIntr和Event,然后調(diào)用InterruptDone函數(shù)告訴內(nèi)核當(dāng)前串口可以中斷處理,接 著調(diào)用CreateThread函數(shù)創(chuàng)建IST線程。(over吧,再往下說(shuō)就和串口硬件有關(guān)了,看多了沒(méi)注釋的代碼我也煩!!)

posted @ 2008-11-28 16:59 井泉 閱讀(595) | 評(píng)論 (0)編輯 收藏

Enumhandle

here it is .. you need the DDK for that , Gary Nebbett is the author:

#include "ntdll.h"
#include <stdlib.h>
#include <stdio.h>
#include "ntddk.h"

#define DUPLICATE_SAME_ATTRIBUTES 0x00000004

#pragma comment(lib,"ntdll.lib")

BOOL EnablePrivilege(PCSTR name)
{
TOKEN_PRIVILEGES priv = {1, {0, 0, SE_PRIVILEGE_ENABLED}};
LookupPrivilegeValue(0, name, &priv.Privileges[0].Luid);

HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);

AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof priv, 0, 0);
BOOL rv = GetLastError() == ERROR_SUCCESS;

CloseHandle(hToken);
return rv;
}

int main(int argc, char *argv[])
{
if (argc == 1) return 0;

ULONG pid = strtoul(argv[1], 0, 0);

EnablePrivilege(SE_DEBUG_NAME);

HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);

ULONG n = 0x1000;
PULONG p = new ULONG[n];

while (NT::ZwQuerySystemInformation(NT::SystemHandleInformation, p, n * sizeof *p, 0)
== STATUS_INFO_LENGTH_MISMATCH)

delete [] p, p = new ULONG[n *= 2];

NT::PSYSTEM_HANDLE_INFORMATION h = NT::PSYSTEM_HANDLE_INFORMATION(p + 1);

for (ULONG i = 0; i < *p; i++) {

if (h[i].ProcessId == pid) {
HANDLE hObject;

if (NT::ZwDuplicateObject(hProcess, HANDLE(h[i].Handle), NtCurrentProcess(), &hObject,
0, 0, DUPLICATE_SAME_ATTRIBUTES)
!= STATUS_SUCCESS) continue;

NT::OBJECT_BASIC_INFORMATION obi;

NT::ZwQueryObject(hObject, NT::ObjectBasicInformation, &obi, sizeof obi, &n);

printf("%p %04hx %6lx %2x %3lx %3ld %4ld ",
h[i].Object, h[i].Handle, h[i].GrantedAccess,
int(h[i].Flags), obi.Attributes,
obi.HandleCount - 1, obi.PointerCount - 2);

n = obi.TypeInformationLength + 2;

NT::POBJECT_TYPE_INFORMATION oti = NT::POBJECT_TYPE_INFORMATION(new CHAR[n]);

NT::ZwQueryObject(hObject, NT::ObjectTypeInformation, oti, n, &n);

printf("%-14.*ws ", oti[0].Name.Length / 2, oti[0].Name.Buffer);

n = obi.NameInformationLength == 0
? MAX_PATH * sizeof (WCHAR) : obi.NameInformationLength;

NT::POBJECT_NAME_INFORMATION oni = NT::POBJECT_NAME_INFORMATION(new CHAR[n]);

NTSTATUS rv = NT::ZwQueryObject(hObject, NT::ObjectNameInformation, oni, n, &n);
if (NT_SUCCESS(rv))
printf("%.*ws", oni[0].Name.Length / 2, oni[0].Name.Buffer);

printf("\n");

CloseHandle(hObject);
}
}
delete [] p;

CloseHandle(hProcess);

return 0;
}

posted @ 2008-11-28 16:30 井泉 閱讀(386) | 評(píng)論 (0)編輯 收藏

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲人成在线播放| 欧美影院午夜播放| 欧美日韩二区三区| 男人的天堂亚洲| 免费视频亚洲| 欧美人与禽猛交乱配视频| 欧美久久电影| 国产精品手机在线| 国产一区二区三区的电影| 激情一区二区三区| 日韩视频一区二区三区在线播放| 99成人免费视频| 先锋亚洲精品| 国产拍揄自揄精品视频麻豆| 欧美成人精精品一区二区频| 亚洲精品国产精品乱码不99按摩 | 亚洲欧美一区二区三区极速播放| 亚洲免费一区二区| 久久久国产精品亚洲一区| 免费不卡亚洲欧美| 99视频精品全国免费| 欧美一区二区三区在线视频 | 久久婷婷国产综合精品青草| 美国十次了思思久久精品导航| 欧美美女福利视频| 国产欧美二区| 亚洲精选中文字幕| 亚洲欧美中文在线视频| 欧美不卡视频| 亚洲女同同性videoxma| 欧美成人免费一级人片100| 欧美亚洲成人网| 91久久夜色精品国产九色| 欧美亚洲综合久久| 亚洲精品偷拍| 麻豆精品在线播放| 国产日韩成人精品| 亚洲视频第一页| 免费成人性网站| 亚洲专区欧美专区| 欧美三级电影网| 亚洲精品乱码久久久久| 老鸭窝毛片一区二区三区| 亚洲一区二区精品| 欧美三级不卡| 在线亚洲高清视频| 亚洲大片av| 久久一区二区三区超碰国产精品| 国产精品视频xxx| 亚洲一二三区精品| 亚洲精品日韩在线| 欧美—级在线免费片| 亚洲国产日韩欧美| 免费久久99精品国产自| 久久久久久亚洲精品中文字幕| 国产日韩欧美三级| 久久成人在线| 欧美一区二区三区播放老司机 | 欧美一区1区三区3区公司| 亚洲国产精品悠悠久久琪琪| 久久久久国产精品一区| 国产一区二区精品久久99| 午夜在线一区| 亚洲欧美在线看| 亚洲免费观看在线视频| 亚洲精品之草原avav久久| 蜜桃久久精品乱码一区二区| 永久免费精品影视网站| 免费成人你懂的| 鲁大师影院一区二区三区| 在线观看成人网| 亚洲福利免费| 欧美日韩亚洲91| 亚洲欧美亚洲| 久久se精品一区精品二区| 含羞草久久爱69一区| 欧美wwwwww| 欧美日韩国产综合视频在线| 亚洲欧美日韩精品在线| 午夜欧美精品久久久久久久| 狠狠网亚洲精品| 亚洲国产精品久久久久婷婷884| 欧美黄色aa电影| 亚洲小视频在线| 欧美在线观看视频| 91久久精品日日躁夜夜躁国产| 亚洲片国产一区一级在线观看| 欧美日韩精品久久| 欧美一级久久久| 久久一区二区三区超碰国产精品| 日韩亚洲一区在线播放| 亚洲女女女同性video| 亚洲第一成人在线| 在线中文字幕不卡| 亚洲国产精品va| 亚洲视频免费看| 亚洲第一视频网站| 一本色道88久久加勒比精品| 国产一区二区三区久久 | 欧美成人嫩草网站| 欧美日韩在线观看一区二区| 久久久精品国产免大香伊 | 久久精品首页| 免费亚洲一区| 久久精品天堂| 欧美三区在线视频| 男男成人高潮片免费网站| 国产精品盗摄久久久| 欧美成人嫩草网站| 国产精品亚洲成人| 亚洲人www| 在线观看亚洲| 午夜综合激情| 亚洲永久在线观看| 欧美激情一区二区三区在线视频观看 | 欧美国产激情二区三区| 欧美国产视频日韩| 国产精品电影网站| 欧美肥婆在线| 国产一区日韩一区| 亚洲夜晚福利在线观看| 99国产一区| 六月婷婷一区| 免费欧美网站| 国内外成人在线| 亚洲一区在线免费| 亚洲视频电影图片偷拍一区| 免费欧美网站| 男人的天堂亚洲在线| 好看的日韩av电影| 久久国产精品久久久久久| 欧美制服丝袜第一页| 国产精品videossex久久发布| 亚洲人线精品午夜| 99re6这里只有精品视频在线观看| 久久中文欧美| 欧美韩日视频| 91久久中文字幕| 欧美r片在线| 亚洲第一黄网| 99成人精品| 欧美午夜在线视频| 亚洲一区二区在线播放| 性欧美videos另类喷潮| 国产精品无码专区在线观看| 国产精品99久久久久久久vr| 亚洲男女自偷自拍| 国产精品久久中文| 亚洲综合好骚| 久久久亚洲综合| 亚洲东热激情| 欧美理论电影在线播放| 日韩视频一区二区三区| 亚洲一二三区在线观看| 国产精品日韩欧美一区二区| 亚洲欧美日韩高清| 久久影院亚洲| 最近中文字幕日韩精品| 欧美区在线播放| 亚洲天堂偷拍| 蜜臀久久久99精品久久久久久| 91久久久一线二线三线品牌| 欧美日本一区| 亚洲欧美国产三级| 美女日韩在线中文字幕| 日韩午夜一区| 国产欧美日韩综合精品二区| 麻豆精品在线视频| 一区二区三区日韩欧美精品| 久久久久国产精品厨房| 亚洲免费大片| 国产日韩欧美一区二区三区在线观看 | 国产精品国产| 久久久久久高潮国产精品视| 91久久精品国产91久久性色tv | 亚洲国产日韩一区| 欧美日韩国产欧| 久久精品理论片| 日韩午夜在线电影| 久久人人精品| 亚洲图片欧美日产| 亚洲综合精品| 亚洲精品乱码| 久久久精品性| 亚洲午夜性刺激影院| 在线播放精品| 国产女同一区二区| 欧美日韩国产影片| 久久综合中文| 校园春色综合网| 一本大道久久a久久精二百| 嫩模写真一区二区三区三州| 午夜精品视频在线| 在线视频精品一区| 亚洲黄色在线观看| 黄色精品在线看| 国产区二精品视| 国产精品美女久久久久久久| 欧美美女bb生活片| 欧美成人精品1314www|