如何制作支持VMWare的Windows CE BSP

不知道有没有人试过在VMWare中跑过Windows CE。可能有人会问:在VMWare中跑Windows CE有什么意义?Windows CE不是有基于Vritual PC的emulator吗?要做干吗不做一个基于Microsoft自己的Virtual PC的?

简单的答案是,VMWare支持一些Virtual PC(包括Windows CE emulator)不支持的硬件。对我来说,最吸引我的是VMWare支持USB设备。另外根据我的经验VMWare的性能比Virtual PC强。

本文介绍如何针对VMWare支持的硬件,做一个相应的Windows CE BSP。根据VMWare虚拟机的spec,我打算支持的硬件列表如下(SCSI设备、软驱这些用的比较少就算了):

Graphics

•VGA and SVGA support

IDE Drives

•IDE virtual disks up to 
950  GB

•Serial 
( COM )  Ports

•Up to four serial 
( COM )  ports

USB ports

•Two-port USB 
1.1  UHCI controller

Keyboard

104 -key Windows  95 / 98  enhanced

Mouse and Drawing Tablets

•PS
/ 2  mouse

Ethernet Card

•AMD PCnet-PCI II compatible

Sound

•Emulates Creative Labs Sound Blaster AudioPCI 
( MIDI input ,  game controllers and joysticks are  not  supported ,  except  for  USB devices )

 

其实从根本上来说,给VMWare用的Windows CE跟普通PC或者Virtual PC的并无多大区别。因此基本做法和一些概念都可以看MSDN里对CEPC的相关介绍。这里只重点介绍一些针对VMWare的不同地方。

给Windows CE用的VMWare虚拟机可以用DOS的。BSP可以直接copy一份Platform Builder带的CEPC BSP做为起始的BSP,然后在此基础上修改。从IDE硬盘、声卡、显卡、键盘鼠标的支持比较简单,用Platform Builder中现成的driver就行了。

如何制作支持VMWare的Windows CE BSP_第1张图片

比较麻烦的是网卡和USB host controller。

VMWare虚拟的网卡兼容AMD PCnet-PCI II,具体型号是AMD AM79C970A,这个在Windows CE下可实在不好找,自己写一个就太麻烦了。不过在google大法的强力帮助下,我花了很长时间终于找到一个编译好的driver-还是for Windows CE 2.0的(现在想不起来在哪儿找到的了,想要的直接找我吧)。相关注册表设置见附录。

比较头疼的是USB host driver。VMWare中支持的USB host controller是USB 1.1 UHCI controller。Windows CE下有现成的driver,但是直接拿来用的话你会发现不能工作。一番研究后发现问题出在LEGACY SUPPORT REGISTER (LEGSUP)的设置上-VMWare在初始化UHCI controller的时候enable了SMI generation,而且没有把USB中断route到PIRQD上。简单说来就是VMWare对UHCI controller的初始化和Windows CE自带driver要求的不一样(其实是和UHCI spec定义的不一致,UHCI spec定义LEGSUP寄存器默认值是2000h(见5.2.1节),VMWare中是3Bh)。为了能让UHCI controller工作,需要修改UHCI driver的初始化例程,重新编译一个版本。具体修改见附录。

这样,一个支持声卡、网卡、USB设备的VMWare BSP就基本搞定了。

附上两张运行时的截图:

如何制作支持VMWare的Windows CE BSP_第2张图片

 如何制作支持VMWare的Windows CE BSP_第3张图片

附1,网卡的注册表设置,放在platform.reg里:

[HKEY_LOCAL_MACHINE Comm PCNTN4M]
    
" DisplayName " = " PCNTN4M Compatible Ethernet Driver "
    
" Group " = " NDIS "
    
" ImagePath " = " pcntn4m.dll "

[HKEY_LOCAL_MACHINE
Comm PCNTN4M Linkage]
    
" Route " = multi_sz: " PCNTN4M1 "

[HKEY_LOCAL_MACHINE
Comm PCNTN4M1]
    
" DisplayName " = " PCNTN4M Compatible Ethernet Driver "
    
" Group " = " NDIS "
    
" ImagePath " = " pcntn4m.dll "


[HKEY_LOCAL_MACHINE
Comm PCNTN4M1 Parms]
;      " BusNumber " = dword: 0
;      " BusType " = dword: 05
;      " Interrupt " = dword: 05
;      " IOAddress " = dword: 0300


[HKEY_LOCAL_MACHINE
Comm Tcpip Linkage] 
      
" Bind " = multi_sz: " ppp " , " PCNTN4M1 "

    
;  Registry values  for  the pcntn4m1 driver
[HKEY_LOCAL_MACHINE
Comm PCNTN4M1 Parms TcpIp]
    
;  This enable the DHCP .  In Win CE  2.1  Preview version statically allocated IP address 
       
;  does  not  work .  So we  use  the DHCP server to allocate the IP address .  
    
" EnableDHCP " = dword: 1
    
;  This should be MULTI_SZ
    
" DefaultGateway " = ""
    
;  This should be SZ ...   If  null it means  use  LAN ,   else  WAN and Interface .
    
" LLInterface " = ""
    
;   Use  zero  for  broadcast address?  ( or  255.255 . 255.255 )
    
" UseZeroBroadcast " = dword: 0
    
;  Thus should be MULTI_SZ ,  the IP address list
    
" IpAddress " = " 0.0.0.0 "
    
;  This should be MULTI_SZ ,  the subnet masks  for  the above IP addresses
    
" Subnetmask " = " 0.0.0.0 "

;
;  Template the PCI bus driver uses to match a AM79C970 PC- net  card
;
[HKEY_LOCAL_MACHINE
Drivers BuiltIn PCI Template PCNTN4M]
   
" Dll " = " NDIS.dll "
   
" Class " = dword: 02
   
" SubClass " = dword: 00
   
" ProgIF " = dword: 0
   
" VendorID " = multi_sz: " 1022 "
   
" DeviceID " = multi_sz: " 2000 "
;    " Entry " = " NdisPCIBusDeviceInit "
   
" Prefix " = " NDS "
   
" Transceiver " = dword: 3
   
" IsrDll " = " giisr.dll "
   
" IsrHandler " = " ISRHandler "
   
" PortIsIO " = dword: 1
   
" PortOffset " = dword :C          ;  TBD
   
" PortSize " = dword: 2
   
" PortMask " = dword:5F00     ;  TBD

[HKEY_LOCAL_MACHINE
Comm ConnectionSharing]
   
" PrivateInterface " = " PCI/PCNTN4M "

附2,UHCI driver需要修改的代码(红色部分,在$(DRIVERS)/USB/HCD/UHC/system.csystem.c中):

// Inline functions
__inline static WORD
PCIConfig_ReadWord(
                   ULONG BusNumber,
                   ULONG Device,
                   ULONG Function,
                   ULONG Offset
                   )
{
    WORD RetVal = 0;
    PCI_SLOT_NUMBER SlotNumber;

    SlotNumber.u.AsULONG = 0;
    SlotNumber.u.bits.DeviceNumber = Device;
    SlotNumber.u.bits.FunctionNumber = Function;
    HalGetBusDataByOffset(PCIConfiguration, BusNumber, SlotNumber.u.AsULONG, &RetVal, Offset, sizeof(RetVal));

    return RetVal;
}


__inline static void
PCIConfig_Write(
                ULONG BusNumber,
                ULONG Device,
                ULONG Function,
                ULONG Offset,
                ULONG Value,
                ULONG Size
                )
{
    PCI_SLOT_NUMBER SlotNumber;

    SlotNumber.u.AsULONG = 0;
    SlotNumber.u.bits.DeviceNumber = Device;
    SlotNumber.u.bits.FunctionNumber = Function;

    HalSetBusDataByOffset(PCIConfiguration, BusNumber, SlotNumber.u.AsULONG, &Value, Offset, Size);
}


/* InitializeUHCI
*
* Configure and initialize UHCI card
*
* Return Value:
* Return TRUE if card could be located and configured, otherwise FALSE
*/

static BOOL
InitializeUHCI(
               SUhcdPdd 
* pPddObject, // IN - Pointer to PDD structure
               LPCWSTR szDriverRegKey) // IN - Pointer to active registry key string
{
    PUCHAR ioPortBase 
= NULL;
    DWORD dwAddrLen;
    DWORD dwIOSpace;
    BOOL InstallIsr 
= FALSE;
    BOOL fResult 
= FALSE;
    LPVOID pobMem 
= NULL;
    LPVOID pobUhcd 
= NULL;
    DWORD PhysAddr;
    DWORD dwHPPhysicalMemSize;
    HKEY hKey;
    WORD LegSup;

    DDKWINDOWINFO dwi;
    DDKISRINFO dii;
    DDKPCIINFO dpi;
    
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,szDriverRegKey,0,0,&hKey)!= ERROR_SUCCESS) {
        DEBUGMSG(ZONE_ERROR,(TEXT(
"InitializeUHCI:GetRegistryConfig RegOpenKeyEx(%s) failed "),
            szDriverRegKey));
        
return FALSE;
    }

    dwi.cbSize
=sizeof(dwi);
    dii.cbSize
=sizeof(dii);
    dpi.cbSize
=sizeof(dpi);
    if ( DDKReg_GetWindowInfo(hKey, &dwi ) !=ERROR_SUCCESS ||
        DDKReg_GetIsrInfo (hKey, &dii ) != ERROR_SUCCESS ||
        DDKReg_GetPciInfo (hKey, &dpi ) != ERROR_SUCCESS) 
{
            DEBUGMSG(ZONE_ERROR,(TEXT("InitializeUHCI:DDKReg_GetWindowInfo or DDKReg_GetWindowInfo or DDKReg_GetPciInfo failed "
)));
            
goto InitializeUHCI_Error;
        }

    
if (dwi.dwNumMemWindows!=0{
        PhysAddr 
= dwi.memWindows[0].dwBase;
        dwAddrLen
= dwi.memWindows[0].dwLen;
        dwIOSpace 
= 0;
    }

    
else if (dwi.dwNumIoWindows!=0{
        PhysAddr
= dwi.ioWindows[0].dwBase;
        dwAddrLen 
= dwi.ioWindows[0].dwLen;
        dwIOSpace 
= 1;
    }

    
else
        
goto InitializeUHCI_Error;
    DEBUGMSG(ZONE_INIT,(TEXT(
"UHCD: Read config from registry: Base Address: 0x%X, Length: 0x%X, I/O Port: %s, SysIntr: 0x%X, Interface Type: %u, Bus Number: %u "),
        PhysAddr, dwAddrLen, dwIOSpace 
? L"YES" : L"NO", dii.dwSysintr, dwi.dwInterfaceType, dwi.dwBusNumber));

    ioPortBase 
= (PUCHAR)PhysAddr;
    
if (!(fResult = ConfigureUHCICard(pPddObject, &ioPortBase, dwAddrLen, dwIOSpace, dwi.dwInterfaceType, dwi.dwBusNumber))) {
        
goto InitializeUHCI_Error;
    }

    
if (dii.szIsrDll[0!= 0 && dii.szIsrHandler[0]!=0 && dii.dwIrq<0xff && dii.dwIrq>0 ) {
        
// Install ISR handler
        pPddObject->IsrHandle = LoadIntChainHandler(dii.szIsrDll,dii.szIsrHandler, (BYTE)dii.dwIrq);

        
if (!pPddObject->IsrHandle) {
            DEBUGMSG(ZONE_ERROR, (L
"UHCD: Couldn't install ISR handler "));
        }
 else {
            GIISR_INFO Info;
            PHYSICAL_ADDRESS PortAddress 
= {PhysAddr, 0};
            DEBUGMSG(ZONE_INIT, (L
"UHCD: Installed ISR handler, Dll = '%s', Handler = '%s', Irq = %d ",
                dii.szIsrDll, dii.szIsrHandler, dii.dwIrq));
            
if (!BusTransBusAddrToStatic(pPddObject->hParentBusHandle,dwi.dwInterfaceType, dwi.dwBusNumber, PortAddress, dwAddrLen, &dwIOSpace, &(PVOID)PhysAddr)) {
                DEBUGMSG(ZONE_ERROR, (L
"UHCD: Failed TransBusAddrToStatic "));
                
return FALSE;
            }

            
// Set up ISR handler
            Info.SysIntr = dii.dwSysintr;
            Info.CheckPort 
= TRUE;
            Info.PortIsIO 
= (dwIOSpace) ? TRUE : FALSE;
            Info.UseMaskReg 
= TRUE;;
            Info.PortAddr 
= PhysAddr + 0x2;
            Info.PortSize 
= sizeof(WORD);
            Info.MaskAddr 
= PhysAddr + 0x4;
            
if (!KernelLibIoControl(pPddObject->IsrHandle, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL)) {
                DEBUGMSG(ZONE_ERROR, (L
"UHCD: KernelLibIoControl call failed. "));
            }

        }

    }

    
// The PDD can supply a buffer of contiguous physical memory here, or can let the
    
// MDD try to allocate the memory from system RAM. We will use the HalAllocateCommonBuffer()
    
// API to allocate the memory and bus controller physical addresses and pass this information
    
// into the MDD.
    if (GetRegistryPhysicalMemSize(szDriverRegKey,&pPddObject->dwPhysicalMemSize)) {
        
// A quarter for High priority Memory.
        dwHPPhysicalMemSize = pPddObject->dwPhysicalMemSize/4;
        
// Align with page size.
        pPddObject->dwPhysicalMemSize = (pPddObject->dwPhysicalMemSize + PAGE_SIZE -1& ~(PAGE_SIZE -1);
        dwHPPhysicalMemSize 
= ((dwHPPhysicalMemSize + PAGE_SIZE -1& ~(PAGE_SIZE -1));
    }

    
else {
        pPddObject
->dwPhysicalMemSize=0;
        dwHPPhysicalMemSize 
= 0;
    }

    
if (pPddObject->dwPhysicalMemSize<gcTotalAvailablePhysicalMemory) // Setup Minimun requirement.
        pPddObject->dwPhysicalMemSize = gcTotalAvailablePhysicalMemory;
        dwHPPhysicalMemSize 
= gcHighPriorityPhysicalMemory;
    }


    pPddObject
->AdapterObject.ObjectSize = sizeof(DMA_ADAPTER_OBJECT);
    pPddObject
->AdapterObject.InterfaceType = dwi.dwInterfaceType;
    pPddObject
->AdapterObject.BusNumber = dwi.dwBusNumber;
    
if ((pPddObject->pvVirtualAddress = HalAllocateCommonBuffer(&pPddObject->AdapterObject, pPddObject->dwPhysicalMemSize, &pPddObject->LogicalAddress, FALSE)) == NULL) {
        
goto InitializeUHCI_Error;
    }

    
if (!(pobMem = HcdMdd_CreateMemoryObject(pPddObject->dwPhysicalMemSize, dwHPPhysicalMemSize, (PUCHAR) pPddObject->pvVirtualAddress, (PUCHAR) pPddObject->LogicalAddress.LowPart))) {
        
goto InitializeUHCI_Error;
    }


    
if (!(pobUhcd = HcdMdd_CreateHcdObject(pPddObject, pobMem, szDriverRegKey, ioPortBase, dii.dwSysintr))) {
        
goto InitializeUHCI_Error;
    }


    
// set LEGSUP here
    LegSup = PCIConfig_ReadWord(dwi.dwBusNumber, dpi.dwDeviceNumber, dpi.dwFunctionNumber, 0xC0);
    RETAILMSG(1, (TEXT("UHCD: LEGSUP=0x%08x "), LegSup));
    LegSup = 0x2000;
    PCIConfig_Write(dwi.dwBusNumber, dpi.dwDeviceNumber, dpi.dwFunctionNumber, 0xC0, LegSup, sizeof
(LegSup));

    pPddObject
->lpvMemoryObject = pobMem;
    pPddObject
->lpvUhcdMddObject = pobUhcd;
    _tcsncpy(pPddObject
->szDriverRegKey, szDriverRegKey, MAX_PATH);
    pPddObject
->ioPortBase = ioPortBase;
    pPddObject
->dwSysIntr = dii.dwSysintr;
    
// PCI OHCI support suspend and resume
    if ( hKey!=NULL) {
        DWORD dwCapability;
        DWORD dwType;
        DWORD dwLength 
= sizeof(DWORD);
        
if (RegQueryValueEx(hKey, HCD_CAPABILITY_VALNAME, 0&dwType, (PUCHAR)&dwCapability, &dwLength) == ERROR_SUCCESS)
            HcdMdd_SetCapability (pobUhcd,dwCapability);
        RegCloseKey(hKey);
    }


    
return TRUE;

InitializeUHCI_Error:
    
if (pPddObject->IsrHandle) {
        FreeIntChainHandler(pPddObject
->IsrHandle);
        pPddObject
->IsrHandle = NULL;
    }

    
if (pobUhcd)
        HcdMdd_DestroyHcdObject(pobUhcd);
    
if (pobMem)
        HcdMdd_DestroyMemoryObject(pobMem);
    
if(pPddObject->pvVirtualAddress)
        HalFreeCommonBuffer(
&pPddObject->AdapterObject, pPddObject->dwPhysicalMemSize, pPddObject->LogicalAddress, pPddObject->pvVirtualAddress, FALSE);

    pPddObject
->lpvMemoryObject = NULL;
    pPddObject
->lpvUhcdMddObject = NULL;
    pPddObject
->pvVirtualAddress = NULL;
    
if ( hKey!=NULL)
        RegCloseKey(hKey);

    
return FALSE;
}

你可能感兴趣的:(vmware,windows,Microsoft,null,keyboard,generation)