[轉載]清除無主套件及套件安裝暫存檔
http://renlife.wordpress.com/2007/02/19/%E6%B8%85%E9%99%A4%E7%84%A1%E4%B8%BB%E5%A5%97%E4%BB%B6%E5%8F%8A%E5%A5%97%E4%BB%B6%E5%AE%89%E8%A3%9D%E6%9A%AB%E5%AD%98%E6%AA%94/
由於在使用 apt-get install 時,會把相依性套件都安裝起來,但是在 apt-get remove 時,並不會把相依套件刪除,所以會留下很多沒有使用的函式庫。
可以使用下列指令刪除:
$ sudo apt-get autoremove
而在下載完套件後,套件安裝檔在安裝完後也不會自行移除。
可以使用下列指令移除:
移除「所有」快取套件暫存安装档:
$ sudo apt-get clean
移除「舊版」快取套件暫存安装档:
$ sudo apt-get autoclean
How to build linux kernel 2.6.26 on Ubuntu 8.04
1. sudo passwd root [Enter] 建立 root 最大權限使用者的密碼
2. su [Enter] 切換成 root
3. Get the late source code of linux from http://www.kernel.org &
Put this file to /home/gary/
4. cd /home/gary/ [Enter]
5.
[轉載]How to build PLAYWND at WINCE5.0 step by step
WINCE5.0中PLAYWND的編譯步驟
PLAYWND源文件的目錄是
$(_PUBLICROOT)\DIRECTX\SDK\SAMPLES \DSHOW\PLAYERS\PLAYWND,
默認情況下WINCE是不會將playwnd加入到內核中去的。
下面介紹的是如何成功編譯playwnd項目及將playwnd.exe加入到wince內核中。
1. 打開PLAYWND目錄下的source文件,
將TARGETTYPE=LIBRARY改為TARGETTYPE=PROGRAM,
通過這個修改就能把該project編譯成playwnd.exe文件,
而不是只編譯聲成playwnd.lib的庫文件。
2. 此時你可以在FileView中將鼠標移動到playwnd的目錄上,
右鍵點擊並選擇Build Current Project,
在Build的過程中可能會出現一些Link的錯誤
(我的是有出現錯誤,當然如果您沒有出現該錯誤就不用繼續看錯誤處理方法了呵),
這是因為該project沒有加入一些庫文件導致,解決方法見第三點。
3. 如2中的方法,右鍵點playwnd,
選擇Setting->Link,在Additional Libraries一欄中增加如下幾個lib文件
(變量的路徑可在PB上Build OS->Open Release Directory的命令行上輸入env來查看,或者直接進wince.bat查看):
$(_PUBLICROOT)\DIRECTX\SDK\LIB\X86\RETAIL\strmiids.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\coredll.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\commctrl.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\ole32.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\oleaut32.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\uuid.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\commdlg.lib
加完Build過之後這些庫路徑也會被填寫到source文件中,
但除非你對source文件非常熟悉,否則不要直接修改該文件,
筆者就是因為直接修改source文件走了不少彎路,
具體不再多說^_^。
4. 加完第3步驟的lib文件,重新Build, 成功之後會在$(_FLATRELEASEDIR)目錄下產生一個PlayWnd.exe文件,這是就是playwnd的可執行程序。下面介紹一下怎麼將該程序加入到WINCE內核中。
5. 在PB左邊的ParameterView中打開Project.bib文件,加入一行:
PlayWnd.exe $(_FLATRELEASEDIR)\PlayWnd.exe NK S
並在Project.dat中增加下面一行:
Directory("\Windows\MYAPP"):-File("PlayWnd.exe","\Windows\PlayWnd.exe")
修改完該配置文件之後
在PB中選擇Build OS->Copy Files To Relsase Directory使配置文件生效,
但此時$(_FLATRELEASEDIR)目錄下的PlayWnd.exe可能已經被刪除,
必須重新Build 一個,然後在PB中選擇Build OS->Make Runtime Image.
這時產生的NK.BIN進入CE之後在\WINDOWS\MYAPP目錄下就有PlayWnd.exe程序。
6. 如果想用DirectShow寫個播放器,PLAYWND可是個好例子哦。。。
PLAYWND源文件的目錄是
$(_PUBLICROOT)\DIRECTX\SDK\SAMPLES \DSHOW\PLAYERS\PLAYWND,
默認情況下WINCE是不會將playwnd加入到內核中去的。
下面介紹的是如何成功編譯playwnd項目及將playwnd.exe加入到wince內核中。
1. 打開PLAYWND目錄下的source文件,
將TARGETTYPE=LIBRARY改為TARGETTYPE=PROGRAM,
通過這個修改就能把該project編譯成playwnd.exe文件,
而不是只編譯聲成playwnd.lib的庫文件。
2. 此時你可以在FileView中將鼠標移動到playwnd的目錄上,
右鍵點擊並選擇Build Current Project,
在Build的過程中可能會出現一些Link的錯誤
(我的是有出現錯誤,當然如果您沒有出現該錯誤就不用繼續看錯誤處理方法了呵),
這是因為該project沒有加入一些庫文件導致,解決方法見第三點。
3. 如2中的方法,右鍵點playwnd,
選擇Setting->Link,在Additional Libraries一欄中增加如下幾個lib文件
(變量的路徑可在PB上Build OS->Open Release Directory的命令行上輸入env來查看,或者直接進wince.bat查看):
$(_PUBLICROOT)\DIRECTX\SDK\LIB\X86\RETAIL\strmiids.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\coredll.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\commctrl.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\ole32.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\oleaut32.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\uuid.lib
$(_PROJECTROOT)\cesysgen\sdk\lib\x86\retail\commdlg.lib
加完Build過之後這些庫路徑也會被填寫到source文件中,
但除非你對source文件非常熟悉,否則不要直接修改該文件,
筆者就是因為直接修改source文件走了不少彎路,
具體不再多說^_^。
4. 加完第3步驟的lib文件,重新Build, 成功之後會在$(_FLATRELEASEDIR)目錄下產生一個PlayWnd.exe文件,這是就是playwnd的可執行程序。下面介紹一下怎麼將該程序加入到WINCE內核中。
5. 在PB左邊的ParameterView中打開Project.bib文件,加入一行:
PlayWnd.exe $(_FLATRELEASEDIR)\PlayWnd.exe NK S
並在Project.dat中增加下面一行:
Directory("\Windows\MYAPP"):-File("PlayWnd.exe","\Windows\PlayWnd.exe")
修改完該配置文件之後
在PB中選擇Build OS->Copy Files To Relsase Directory使配置文件生效,
但此時$(_FLATRELEASEDIR)目錄下的PlayWnd.exe可能已經被刪除,
必須重新Build 一個,然後在PB中選擇Build OS->Make Runtime Image.
這時產生的NK.BIN進入CE之後在\WINDOWS\MYAPP目錄下就有PlayWnd.exe程序。
6. 如果想用DirectShow寫個播放器,PLAYWND可是個好例子哦。。。
Windows CE下驅動開發
要想真正瞭解驅動程序必須結合一些驅動程序源碼,
在此我以串口驅動程序(COM16550)中初始化過程為線索簡單講一講驅動開發的基礎知識。
Windows CE下的串口驅動程序能夠處理所有I/O行為類似串口的設備,
包括基於16450、16550 UART(通用異步收發芯片)
的設備和一些採用DMA的設備,常見的有9針串口、紅外I/O口、Modem等。
在%_WINCEROOT%\Public\Common\OAK\Drivers\Serial目錄下,
COM_MDD2子目錄包含新的串口驅動MDD層函數代碼。
COM16550子目錄包含串口驅動PDD層代碼。
SER16550子目錄包含的一系列函數專用於控制與16550兼容的UART,
這樣PDD層的主要工作就是調用SER16550中的函數。
還有一個ISR16550子目錄包含的是串口驅動程序專用的可安裝ISR(中斷服務例程),
而很多硬件設備驅動程序採用CE默認的可安裝ISR giisr.dll。
一般串口設備相應的註冊表設置例子及意義如下: 鍵 意義
"SysIntr"=dword:13 串口1的中斷ID為十進制13
"IoBase"=dword:02F8 串口1的IO空間首地址為十六進制2F8
"IoLen"=dword:8 串口1的IO空間長度為8個字節
"DeviceArrayIndex"=dword:0 串口1的索引,是1的由來
"Order"=dword:0 串口1驅動的加載順序
"DeviceType"=dword:0 串口1的設備類型
"DevConfig"=hex: 10,00 .... 串口1在與Modem設備通訊時的配置,如波特率、奇偶校檢等 "FriendlyName"="COM1:" 串口1在撥號程序中顯示的名字
"Tsp"="Unimodem.dll" 串口1 被用於與Modem設備通訊的時候要加載的TSP
(TAPI Service provider)DLL
"Prefix"="COM" 串口1的流接口的前綴
"Dll"="com16550.Dll" 串口1的驅動程序DLL
SysIntr由CE在文件Nkintr.h中預定義,用於唯一標識中斷設備。
OEM可以在文件Oalintr.h中定義自己的SysIntr。
常見的預定義
SysIntr有SYSINTR_NOP(中斷只由ISR處理,IST不再處理),
SYSINTR_RESCHED(重新調度線程),
SYSINTR_DEVICES(由CE預定義的設備中斷ID的基值),
SYSINTR_PROFILE、SYSINTR_TIMING、SYSINTR_FIRMWARE等都是基於SYSINTR_DEVICES定義的。
IoBase是串口1的IO地址空間的首地址,IoLen是IO空間的大小。
IO地址空間只存在於x86平台,如果在其它平台硬件寄存器必須映射到物理地址空間,
那子鍵的名稱為MemBase和MemLen。
在x86平台更多硬件的寄存器由於IO空間的局限也映射到物理地址空間。
DeviceArrayIndex是設備的索引,用於區分同類型的設備。
Prefix是流驅動程序的前綴,當應用程序調用CreateFile函數傳遞COM1:參數時,
文件系統負責與串口驅動程序通信,串口驅動程序是在CE啟動時由device.exe加載的。
下面從MDD層函數COM_Init開始探索串口驅動的初始化過程。
COM_Init是在串口設備被檢測後由設備管理器device.exe調用的,
主要的作用是初始化設備,它的唯一參數Identifier是由device.exe傳遞的,
其類型是一個字符串指針,字符串的內容是HLM\Drivers\Active\xx,
xx是一個十進制數(device.exe會跟蹤系統中每個驅動程序,
把加載的驅動程序記錄在Active鍵下)。
COM_Init先分配一個HW_INDEP_INFO結構體,
這個結構體是獨立於串口硬件的頭信息(MDD、PDD、SER16550都包含自己獨特的結構體,
具體的結構體定義請參見串口驅動源碼),分配之後再初始化結構體中每個成員,
初始化結構體後調用
OpenDeviceKey((LPCTSTR)Identifier)打開
HLM\Drivers\Active\xx\Key包含的註冊表路徑,
在這裡路徑一般為HLM\Drivers\BuiltIn\Serial,
即串口的驅動程序信息在註冊表中所處的位置。
COM_Init接著在HLM\Drivers\BuiltIn\Serial
下查詢DeviceArrayIndex、Priority256的值,
Priority256指定了驅動程序的優先級,
如果沒有就用默認的優先級。
接下來調用GetSerialObject(DeviceArrayIndex),
這個函數由PDD層定義,返回HWOBJ結構體,
這個結構體主要包含PDD層和SER16550定義的函數的指針。
也就是說MDD通過調用這個函數才能調用底層實現的函數。
接下來的大多數工作都是調用底層函數實現初始化。
第一個調用的底層函數SerInit主要設置由用戶設置的硬件配置,
例如線路控制、波特率。它調用Ser_GetRegistryData函數得到保存在註冊表中的硬件信息,Ser_GetRegistryData在內部調用系統提供的DDKReg_GetIsrInfoDDK和DDKReg_GetWindowInfo函數得到在HLM\Drivers\BuiltIn\Serial下
保存的IRQ、SysIntr、IsrDll、IsrHandler、IoBase、IoLen。
IRQ是邏輯中斷號,IsrDll表示當前驅動程序的可安裝ISR所在的DLL名稱,
IsrHandler 表示可安裝ISR的函數名稱。
在這裡順便提一下可安裝ISR,
讀者在我以前發表的關於OAL的文章中可以瞭解到OEM在OEMInit函數中關聯IRQ和SysIntr,當硬件設備發生中斷時,ISR會禁止同級和低級中斷,然後根據IRQ返回關聯的SysIntr,
內核根據ISR返回的SysIntr喚醒相應的IST(SysIntr與IST創建的Event關聯),
IST處理中斷之後調用InterruptDone解除中斷禁止。
在OEMInit中關聯的缺點是一旦編譯了CE內核後就無法添加這種關聯了,
而一些硬件設備會隨時插拔或者共享中斷,要關聯這樣的硬件設備解決方法就是可安裝ISR,
可安裝ISR專用於處理指定的硬件設備發出的中斷,
所以如果硬件設備需要可安裝ISR必須在註冊表中添加IsrDll、IsrHandler。
多數硬件設備採用CE默認的可安裝ISR giisr.dll,
格式如下: "IsrDll"="giisr.dll" "IsrHandler"="ISRHandler"
如果一個硬件驅動程序需要可安裝ISR而開發者又不想自己寫一個,
那麼可以利用giisr.dll來實現。除了在註冊表中添加如上所示外,
還要在驅動程序中調用相關函數註冊可安裝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函數負責註冊可安裝ISR,參數1為DLL名稱,
參數2為ISR函數名稱,
參數3為IRQ。
TransBusAddrToStatic函數在後面講。
如果要利用giisr.dll作為可安裝ISR,必須先填充GIISR_INFO結構體,
CheckPort=TRUE表示giisr要檢測指定的寄存器來確定當前發出中斷的是否是這個設備。PortIsIO表示寄存器地址屬於哪個地址空間,
FALSE表示是內定空間,TRUE表示IO空間。
UseMaskReg=TRUE表示設備有一個掩碼寄存器,
專用於指定當前設備是否是中斷源,也就是發出中斷,
而MaskAddr表示掩碼寄存器的地址。如果對Info.Mask賦值,
那麼PortAddr表示一個特殊的寄存器地址,
這個寄存器的值與Mask的值&運算的結果如果為真,
則證明當前設備是中斷源,否則返回SYSINTR_CHAIN
(表示當前ISR沒有處理中斷,內核將調用ISR鏈中下一個ISR),
如果UseMaskReg=TRUE,
那麼MaskReg寄存器的值與PortAddr指定的寄存器的值&運算的結果如果為真,
則證明當前設備是中斷源。
函數SerInit接著調用函數Ser_InternalMapRegisterAddresses轉換IO地址並且映射地址,Ser_InternalMapRegisterAddresses在
內部調用系統提供的HalTranslateBusAddress(Isa, 0, ioPhysicalBase, &inIoSpace, &ioPhysicalBase)函數將
與總線相關的地址轉換為系統地址,
參數1為總線類型,
參數2為總線號,
參數3為要轉換的地址
(PHYSICAL_ADDRESS類型,實際是LARGE_INTEGER型),
參數4指定寄存器地址屬於IO地址空間還是物理地址空間,
參數5返回轉換後的物理地址。
觀察HalTranslateBusAddress的源碼得知如果是在x86平台,
這個函數除了把參數3賦給了參數5其餘什麼都沒有做,
而非x86平台將inIoSpace的值置為0,表示一定是物理地址。
在調用HalTranslateBusAddress前要確定從註冊表中
得到的寄存器地址到底是屬於哪個地址空間的,
例如:
ULONG inIoSpace = 1; ///1表示是IO空間
PHYSICAL_ADDRESS ioPhysicalBase = {iobase, 0}; ///相當於ioPhysicalBase.LowPart = iobase
在地址轉換後就要將轉換後的地址映射到驅動程序
(一般IST和應用程序一樣運行在用戶模式)能夠訪問的虛擬地址空間
(0x80000000以下)和ISR能夠訪問的靜態虛擬地址空間中(0x80000000以上)。
例如:
////如果地址屬於物理地址空間
ioPortBase = (PUCHAR)MmMapIoSpace(ioPhysicalBase, Size, FALSE);
TransBusAddrToStatic(Isa, 0, ioPhysicalBase, Size, &inIoSpace, ppStaticAddress);
MmMapIoSpace函數負責將物理地址映射到驅動程序能夠訪問的虛擬地址空間中,
通過源碼分析MmMapIoSpace在內部分別調用:
pVirtualAddress =VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS); VirtualCopy(pVirtualAddress,
(PVOID)(SourcePhys >> 8),
SourceSize, PAGE_PHYSICAL
PAGE_READWRITE
(CacheEnable ? 0 : PAGE_NOCACHE));
VirtualAlloc分配一塊和MemLen一樣大小的虛擬地址空間,因為參數1為0,所以內核自動分配。
一般MemLen小於2MB,所以會在應用程序的地址空間中分配。
VirtualCopy負責將硬件設備寄存器的物理地址與VirtualAlloc分配的虛擬地址做一個映射關係,
這樣驅動程序訪問PvirtualAddress實際上就是訪問第一個寄存器。
因為硬件設備寄存器的物理地址一定是在512MB(CE支持RAM的最大值)以上,
所以除了最後的參數要加PAGE_PHYSICAL外,
第二個參數物理地址也要右移8位(或者除以256)。
映射硬件寄存器當然PAGE_NOCACHE是必須加的。
TransBusAddrToStatic函數
負責將物理地址映射到ISR能夠訪問的靜態虛擬地址空間中,當出現中斷共享時,
ISR要負責訪問硬件設備的某一個寄存器來判斷中斷源,所以將寄存器的物理地址映射到靜態虛擬地址空間中是必要的(ISR只能訪問靜態的虛擬地址空間)。
所謂靜態虛擬地址空間是指在OEMAddressTable中定義的虛擬地址空間(當然是0x80000000以上)。
在x86平台一般這個表只定義RAM的物理地址與虛擬地址對應關係,
而硬件設備的寄存器地址並不在該表中定義,
所以如果要創建一塊靜態的虛擬地址空間供ISR訪問,
必須在此之前調用
CreateStaticMapping函數
在0xC4000000到0xE0000000虛擬地址空間中分配。
TransBusAddrToStatic函數
在內部就是調用了CreateStaticMapping函數。
註:硬件設備的寄存器地址也可以在OEMAddressTable中定義。
////如果地址屬於IO空間
ioPortBase = (PUCHAR)ioPhysicalBase.LowPart;
*ppStaticAddress=ioPortBase
這種情況只屬於x86平台,是IO空間就可以直接訪問,即使是用戶模式。
SerInit函數接著初始化SER_INFO結構體成員,之後調用SL_Init函數,
這個函數在ser16550中定義,負責初始化SER16550_INFO結構體,
在這個結構體中保存串口8個寄存器的地址。
SerInit函數執行完畢後COM_Init函數創建接收緩衝區,
然後調用StartDispatchThread函數初始化中斷並且創建IST。
StartDispatchThread函數在內部調用InterruptInitialize函數關聯SysIntr和Event,
然後調用InterruptDone函數告訴內核當前串口可以中斷處理,
接著調用CreateThread函數創建IST線程。
(over吧,再往下說就和串口硬件有關了,看多了沒註釋的代碼我也煩!!)
在此我以串口驅動程序(COM16550)中初始化過程為線索簡單講一講驅動開發的基礎知識。
Windows CE下的串口驅動程序能夠處理所有I/O行為類似串口的設備,
包括基於16450、16550 UART(通用異步收發芯片)
的設備和一些採用DMA的設備,常見的有9針串口、紅外I/O口、Modem等。
在%_WINCEROOT%\Public\Common\OAK\Drivers\Serial目錄下,
COM_MDD2子目錄包含新的串口驅動MDD層函數代碼。
COM16550子目錄包含串口驅動PDD層代碼。
SER16550子目錄包含的一系列函數專用於控制與16550兼容的UART,
這樣PDD層的主要工作就是調用SER16550中的函數。
還有一個ISR16550子目錄包含的是串口驅動程序專用的可安裝ISR(中斷服務例程),
而很多硬件設備驅動程序採用CE默認的可安裝ISR giisr.dll。
一般串口設備相應的註冊表設置例子及意義如下: 鍵 意義
"SysIntr"=dword:13 串口1的中斷ID為十進制13
"IoBase"=dword:02F8 串口1的IO空間首地址為十六進制2F8
"IoLen"=dword:8 串口1的IO空間長度為8個字節
"DeviceArrayIndex"=dword:0 串口1的索引,是1的由來
"Order"=dword:0 串口1驅動的加載順序
"DeviceType"=dword:0 串口1的設備類型
"DevConfig"=hex: 10,00 .... 串口1在與Modem設備通訊時的配置,如波特率、奇偶校檢等 "FriendlyName"="COM1:" 串口1在撥號程序中顯示的名字
"Tsp"="Unimodem.dll" 串口1 被用於與Modem設備通訊的時候要加載的TSP
(TAPI Service provider)DLL
"Prefix"="COM" 串口1的流接口的前綴
"Dll"="com16550.Dll" 串口1的驅動程序DLL
SysIntr由CE在文件Nkintr.h中預定義,用於唯一標識中斷設備。
OEM可以在文件Oalintr.h中定義自己的SysIntr。
常見的預定義
SysIntr有SYSINTR_NOP(中斷只由ISR處理,IST不再處理),
SYSINTR_RESCHED(重新調度線程),
SYSINTR_DEVICES(由CE預定義的設備中斷ID的基值),
SYSINTR_PROFILE、SYSINTR_TIMING、SYSINTR_FIRMWARE等都是基於SYSINTR_DEVICES定義的。
IoBase是串口1的IO地址空間的首地址,IoLen是IO空間的大小。
IO地址空間只存在於x86平台,如果在其它平台硬件寄存器必須映射到物理地址空間,
那子鍵的名稱為MemBase和MemLen。
在x86平台更多硬件的寄存器由於IO空間的局限也映射到物理地址空間。
DeviceArrayIndex是設備的索引,用於區分同類型的設備。
Prefix是流驅動程序的前綴,當應用程序調用CreateFile函數傳遞COM1:參數時,
文件系統負責與串口驅動程序通信,串口驅動程序是在CE啟動時由device.exe加載的。
下面從MDD層函數COM_Init開始探索串口驅動的初始化過程。
COM_Init是在串口設備被檢測後由設備管理器device.exe調用的,
主要的作用是初始化設備,它的唯一參數Identifier是由device.exe傳遞的,
其類型是一個字符串指針,字符串的內容是HLM\Drivers\Active\xx,
xx是一個十進制數(device.exe會跟蹤系統中每個驅動程序,
把加載的驅動程序記錄在Active鍵下)。
COM_Init先分配一個HW_INDEP_INFO結構體,
這個結構體是獨立於串口硬件的頭信息(MDD、PDD、SER16550都包含自己獨特的結構體,
具體的結構體定義請參見串口驅動源碼),分配之後再初始化結構體中每個成員,
初始化結構體後調用
OpenDeviceKey((LPCTSTR)Identifier)打開
HLM\Drivers\Active\xx\Key包含的註冊表路徑,
在這裡路徑一般為HLM\Drivers\BuiltIn\Serial,
即串口的驅動程序信息在註冊表中所處的位置。
COM_Init接著在HLM\Drivers\BuiltIn\Serial
下查詢DeviceArrayIndex、Priority256的值,
Priority256指定了驅動程序的優先級,
如果沒有就用默認的優先級。
接下來調用GetSerialObject(DeviceArrayIndex),
這個函數由PDD層定義,返回HWOBJ結構體,
這個結構體主要包含PDD層和SER16550定義的函數的指針。
也就是說MDD通過調用這個函數才能調用底層實現的函數。
接下來的大多數工作都是調用底層函數實現初始化。
第一個調用的底層函數SerInit主要設置由用戶設置的硬件配置,
例如線路控制、波特率。它調用Ser_GetRegistryData函數得到保存在註冊表中的硬件信息,Ser_GetRegistryData在內部調用系統提供的DDKReg_GetIsrInfoDDK和DDKReg_GetWindowInfo函數得到在HLM\Drivers\BuiltIn\Serial下
保存的IRQ、SysIntr、IsrDll、IsrHandler、IoBase、IoLen。
IRQ是邏輯中斷號,IsrDll表示當前驅動程序的可安裝ISR所在的DLL名稱,
IsrHandler 表示可安裝ISR的函數名稱。
在這裡順便提一下可安裝ISR,
讀者在我以前發表的關於OAL的文章中可以瞭解到OEM在OEMInit函數中關聯IRQ和SysIntr,當硬件設備發生中斷時,ISR會禁止同級和低級中斷,然後根據IRQ返回關聯的SysIntr,
內核根據ISR返回的SysIntr喚醒相應的IST(SysIntr與IST創建的Event關聯),
IST處理中斷之後調用InterruptDone解除中斷禁止。
在OEMInit中關聯的缺點是一旦編譯了CE內核後就無法添加這種關聯了,
而一些硬件設備會隨時插拔或者共享中斷,要關聯這樣的硬件設備解決方法就是可安裝ISR,
可安裝ISR專用於處理指定的硬件設備發出的中斷,
所以如果硬件設備需要可安裝ISR必須在註冊表中添加IsrDll、IsrHandler。
多數硬件設備採用CE默認的可安裝ISR giisr.dll,
格式如下: "IsrDll"="giisr.dll" "IsrHandler"="ISRHandler"
如果一個硬件驅動程序需要可安裝ISR而開發者又不想自己寫一個,
那麼可以利用giisr.dll來實現。除了在註冊表中添加如上所示外,
還要在驅動程序中調用相關函數註冊可安裝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函數負責註冊可安裝ISR,參數1為DLL名稱,
參數2為ISR函數名稱,
參數3為IRQ。
TransBusAddrToStatic函數在後面講。
如果要利用giisr.dll作為可安裝ISR,必須先填充GIISR_INFO結構體,
CheckPort=TRUE表示giisr要檢測指定的寄存器來確定當前發出中斷的是否是這個設備。PortIsIO表示寄存器地址屬於哪個地址空間,
FALSE表示是內定空間,TRUE表示IO空間。
UseMaskReg=TRUE表示設備有一個掩碼寄存器,
專用於指定當前設備是否是中斷源,也就是發出中斷,
而MaskAddr表示掩碼寄存器的地址。如果對Info.Mask賦值,
那麼PortAddr表示一個特殊的寄存器地址,
這個寄存器的值與Mask的值&運算的結果如果為真,
則證明當前設備是中斷源,否則返回SYSINTR_CHAIN
(表示當前ISR沒有處理中斷,內核將調用ISR鏈中下一個ISR),
如果UseMaskReg=TRUE,
那麼MaskReg寄存器的值與PortAddr指定的寄存器的值&運算的結果如果為真,
則證明當前設備是中斷源。
函數SerInit接著調用函數Ser_InternalMapRegisterAddresses轉換IO地址並且映射地址,Ser_InternalMapRegisterAddresses在
內部調用系統提供的HalTranslateBusAddress(Isa, 0, ioPhysicalBase, &inIoSpace, &ioPhysicalBase)函數將
與總線相關的地址轉換為系統地址,
參數1為總線類型,
參數2為總線號,
參數3為要轉換的地址
(PHYSICAL_ADDRESS類型,實際是LARGE_INTEGER型),
參數4指定寄存器地址屬於IO地址空間還是物理地址空間,
參數5返回轉換後的物理地址。
觀察HalTranslateBusAddress的源碼得知如果是在x86平台,
這個函數除了把參數3賦給了參數5其餘什麼都沒有做,
而非x86平台將inIoSpace的值置為0,表示一定是物理地址。
在調用HalTranslateBusAddress前要確定從註冊表中
得到的寄存器地址到底是屬於哪個地址空間的,
例如:
ULONG inIoSpace = 1; ///1表示是IO空間
PHYSICAL_ADDRESS ioPhysicalBase = {iobase, 0}; ///相當於ioPhysicalBase.LowPart = iobase
在地址轉換後就要將轉換後的地址映射到驅動程序
(一般IST和應用程序一樣運行在用戶模式)能夠訪問的虛擬地址空間
(0x80000000以下)和ISR能夠訪問的靜態虛擬地址空間中(0x80000000以上)。
例如:
////如果地址屬於物理地址空間
ioPortBase = (PUCHAR)MmMapIoSpace(ioPhysicalBase, Size, FALSE);
TransBusAddrToStatic(Isa, 0, ioPhysicalBase, Size, &inIoSpace, ppStaticAddress);
MmMapIoSpace函數負責將物理地址映射到驅動程序能夠訪問的虛擬地址空間中,
通過源碼分析MmMapIoSpace在內部分別調用:
pVirtualAddress =VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS); VirtualCopy(pVirtualAddress,
(PVOID)(SourcePhys >> 8),
SourceSize, PAGE_PHYSICAL
PAGE_READWRITE
(CacheEnable ? 0 : PAGE_NOCACHE));
VirtualAlloc分配一塊和MemLen一樣大小的虛擬地址空間,因為參數1為0,所以內核自動分配。
一般MemLen小於2MB,所以會在應用程序的地址空間中分配。
VirtualCopy負責將硬件設備寄存器的物理地址與VirtualAlloc分配的虛擬地址做一個映射關係,
這樣驅動程序訪問PvirtualAddress實際上就是訪問第一個寄存器。
因為硬件設備寄存器的物理地址一定是在512MB(CE支持RAM的最大值)以上,
所以除了最後的參數要加PAGE_PHYSICAL外,
第二個參數物理地址也要右移8位(或者除以256)。
映射硬件寄存器當然PAGE_NOCACHE是必須加的。
TransBusAddrToStatic函數
負責將物理地址映射到ISR能夠訪問的靜態虛擬地址空間中,當出現中斷共享時,
ISR要負責訪問硬件設備的某一個寄存器來判斷中斷源,所以將寄存器的物理地址映射到靜態虛擬地址空間中是必要的(ISR只能訪問靜態的虛擬地址空間)。
所謂靜態虛擬地址空間是指在OEMAddressTable中定義的虛擬地址空間(當然是0x80000000以上)。
在x86平台一般這個表只定義RAM的物理地址與虛擬地址對應關係,
而硬件設備的寄存器地址並不在該表中定義,
所以如果要創建一塊靜態的虛擬地址空間供ISR訪問,
必須在此之前調用
CreateStaticMapping函數
在0xC4000000到0xE0000000虛擬地址空間中分配。
TransBusAddrToStatic函數
在內部就是調用了CreateStaticMapping函數。
註:硬件設備的寄存器地址也可以在OEMAddressTable中定義。
////如果地址屬於IO空間
ioPortBase = (PUCHAR)ioPhysicalBase.LowPart;
*ppStaticAddress=ioPortBase
這種情況只屬於x86平台,是IO空間就可以直接訪問,即使是用戶模式。
SerInit函數接著初始化SER_INFO結構體成員,之後調用SL_Init函數,
這個函數在ser16550中定義,負責初始化SER16550_INFO結構體,
在這個結構體中保存串口8個寄存器的地址。
SerInit函數執行完畢後COM_Init函數創建接收緩衝區,
然後調用StartDispatchThread函數初始化中斷並且創建IST。
StartDispatchThread函數在內部調用InterruptInitialize函數關聯SysIntr和Event,
然後調用InterruptDone函數告訴內核當前串口可以中斷處理,
接著調用CreateThread函數創建IST線程。
(over吧,再往下說就和串口硬件有關了,看多了沒註釋的代碼我也煩!!)
Windows CE 簡介
Windows CE為微軟研發的嵌入式作業系統,可以應用在各種嵌入式系統,或是硬體規格層次較低的電腦系統(例如很少的記憶體,較慢的中央處理器等)。微軟並未定義CE縮寫由來,一般解釋則有Customer Embedded、Compact Edition、Consumer Electronics等等。
電視機上盒、生產線上的控制設備、公共場所的資訊站等等,
有些裝置甚至沒有任何人機介面。
Windows CE 並非從桌上型電腦的Windows(NT,98,XP...)修改縮小而來,
而是使用一套完全重新設計的核心,所以它可以在功能非常有限的硬體上執行。
雖然核心不同,但是它卻提供了高度的Win32 API軟體開發介面的相容性,
功能有記憶體管理、檔案操作、多執行緒、網路功能等。
因此,開發桌上型電腦軟體的人可以很容易編寫甚或直接移植軟體到Windows CE上。
一個與其他微軟作業系統的差異是 Windows CE 提供原始碼,
一個與其他微軟作業系統的差異是 Windows CE 提供原始碼,
首先已經提供了原始碼給部分廠商,讓廠商能夠依照他們自己的硬體架構修改原始碼,
例如:
在 Windows CE 的開發 IDE 軟體 Platform Builder 中就提供了
許多開放原碼的常用軟體元件,
但是一些與硬體架構的軟體元件仍然以二進制檔案形式來提供。
版本
Windows CE 1.0是最早期的版本,非常不穩定,隨便版本不斷的釋出,使得WinCE日趨穩定。
Windows CE 1.0 (Pegasus)
Windows CE 2.0, 2.11, 2.12 (Mercury)
Windows CE 3.0 (Cedar)
Windows CE .NET (4.0, 4.1, 4.2) (Talisker)
Windows CE 5.0 (Macallan)
Windows Embedded CE 6.0 (Yamazaki)
Windows CE 1.0是最早期的版本,非常不穩定,隨便版本不斷的釋出,使得WinCE日趨穩定。
Windows CE 1.0 (Pegasus)
Windows CE 2.0, 2.11, 2.12 (Mercury)
Windows CE 3.0 (Cedar)
Windows CE .NET (4.0, 4.1, 4.2) (Talisker)
Windows CE 5.0 (Macallan)
Windows Embedded CE 6.0 (Yamazaki)
最新功能
目前最新的Windows CE為Windows CE 6.0,這個版本在核心部分有很大的進步:
所有系統元件都由EXE改為DLL,並移到 kernel space.
全新設計的虛擬記憶體架構
全新的裝置驅動程式架構,同時支援 User Mode 與 Kernel Mode 兩種驅動程式。
突破只能執行 32 個工作元(process)的限制,可以執行 32768 個工作元。
每一工作元的的虛擬記憶體限制由32 M 增加到全系統總虛擬記憶體。
Platform Builder IDE 整合到 Microsoft Visual Studio 2005。
新的安全架構,確保只有被信任的軟體可以在系統中執行。
UDF 2.5 檔案系統。
支援 802.11i (WPA2)及 802.11e (QoS) 等無線規格, 及多重 radio support.
支援 x86, ARM, SH4, MIPS 等各種處理器。
提供新的 Cellcore components 使系統在行動電話網路中更容易建立資料連結及啟動通話。
在開發環境上,微軟也提供相容於.NET Framework的開發元件:.NET Compact Framework,讓正在學習.NET或已擁有.NET程式開發技術的開發人員能迅速而順利的在搭載Windows CE .NET系統的裝置上開發應用程式。
用於掌上電腦Pocket PC以及智慧手機Smart Phone上的Windows CE系統稱為Windows Mobile,目前的最新版本為Windows Mobile 6.1。
目前最新的Windows CE為Windows CE 6.0,這個版本在核心部分有很大的進步:
所有系統元件都由EXE改為DLL,並移到 kernel space.
全新設計的虛擬記憶體架構
全新的裝置驅動程式架構,同時支援 User Mode 與 Kernel Mode 兩種驅動程式。
突破只能執行 32 個工作元(process)的限制,可以執行 32768 個工作元。
每一工作元的的虛擬記憶體限制由32 M 增加到全系統總虛擬記憶體。
Platform Builder IDE 整合到 Microsoft Visual Studio 2005。
新的安全架構,確保只有被信任的軟體可以在系統中執行。
UDF 2.5 檔案系統。
支援 802.11i (WPA2)及 802.11e (QoS) 等無線規格, 及多重 radio support.
支援 x86, ARM, SH4, MIPS 等各種處理器。
提供新的 Cellcore components 使系統在行動電話網路中更容易建立資料連結及啟動通話。
在開發環境上,微軟也提供相容於.NET Framework的開發元件:.NET Compact Framework,讓正在學習.NET或已擁有.NET程式開發技術的開發人員能迅速而順利的在搭載Windows CE .NET系統的裝置上開發應用程式。
用於掌上電腦Pocket PC以及智慧手機Smart Phone上的Windows CE系統稱為Windows Mobile,目前的最新版本為Windows Mobile 6.1。
限制
Windows CE 只支援 UNICODE,故char必須改為TCHAR, WCHAR。
Windows CE不支援重疊I/O。
WinCE的許多APIs功能都受限,如:CreateThread 函式在許多參數在Windows CE下都不支援,第1、2、5的參數值必須設為NULL或0。 HThread = CreateThread(NULL, 0, Thread, nParameter, 0, &dwThreadID);
Windows CE 只支援 UNICODE,故char必須改為TCHAR, WCHAR。
Windows CE不支援重疊I/O。
WinCE的許多APIs功能都受限,如:CreateThread 函式在許多參數在Windows CE下都不支援,第1、2、5的參數值必須設為NULL或0。 HThread = CreateThread(NULL, 0, Thread, nParameter, 0, &dwThreadID);
Windows CE.net 系統註冊表
限制:
鍵或鍵值項的名字最多為255個字元 , 資料最大4KB , 鍵嵌套層次最多16層
Windows CE.net 註冊表根鍵 :
HKEY_LOCAL_MACHINE -> 硬體及驅動程式配置資料
HKEY_CURRENT_USER -> 使用者配置資料
HKEY_CLASSES_ROOT -> OLE和檔案類型匹配配置資料
HKEY_USERS -> 適用於所有使用者的儲存資料
KernelIoControl [轉載]
參考 msdn http://msdn2.microsoft.com/en-us/library/ms886729.aspx
argument 依序是
CommandConde
InBuffer Pointer
InBuffer Size
OutBuffer Pointer
OutBuffer Size
Buffer Pointer to Save the returned value (DWORD)就和 Linux 的 IOControl 一樣。
但是這個 IOControl 是對 Kernel ,在source code上來說,也就是 OAL。
CE 的 Driver 都是run 在 User Space (6.0以前),
所以driver 的access 方法不一樣。
driver 的iocontrol 和 linux 的比較類似,
就是 open device,然後用iocontrol function。
但是 kernel 不是 driver,所以就不用open 了,
直接呼叫 KernelIoControl( )就可以。
自己要加一些 Kernel IoControl command的話,要
先define IOControl code ,
用 CTL_CODE 宣告,
有很多argument和限制,
參考 http://msdn2.microsoft.com/en-us/library/ms904001.aspx
在oal 中寫好處理的function,
通常會是 OALIoCtlXXX( ),
這個funciton 的argument也是限制的,
要符合iocontrol 的四個argument : in,insize,out,outsize。
在oak_ioctl_tab.h 中建立IOcontrol code和 iocontrol function 的關聯。
在 CTL_CODE的說明頁,
有說明可以指定 cotrol type.(oal 的就是FILE_DEVICE_HAL)。
這樣看來,也可以用 FILE_DEVICE_BATTERY 來指定由battery 來handle ?
argument 依序是
CommandConde
InBuffer Pointer
InBuffer Size
OutBuffer Pointer
OutBuffer Size
Buffer Pointer to Save the returned value (DWORD)就和 Linux 的 IOControl 一樣。
但是這個 IOControl 是對 Kernel ,在source code上來說,也就是 OAL。
CE 的 Driver 都是run 在 User Space (6.0以前),
所以driver 的access 方法不一樣。
driver 的iocontrol 和 linux 的比較類似,
就是 open device,然後用iocontrol function。
但是 kernel 不是 driver,所以就不用open 了,
直接呼叫 KernelIoControl( )就可以。
自己要加一些 Kernel IoControl command的話,要
先define IOControl code ,
用 CTL_CODE 宣告,
有很多argument和限制,
參考 http://msdn2.microsoft.com/en-us/library/ms904001.aspx
在oal 中寫好處理的function,
通常會是 OALIoCtlXXX( ),
這個funciton 的argument也是限制的,
要符合iocontrol 的四個argument : in,insize,out,outsize。
在oak_ioctl_tab.h 中建立IOcontrol code和 iocontrol function 的關聯。
在 CTL_CODE的說明頁,
有說明可以指定 cotrol type.(oal 的就是FILE_DEVICE_HAL)。
這樣看來,也可以用 FILE_DEVICE_BATTERY 來指定由battery 來handle ?
訂閱:
文章 (Atom)


