Analysis of driver development code in Windows CE

Source: Internet
Author: User
Tags builtin
To really understand the driver, you must combine some driver source code. Here I will give a brief introduction to the initialization process in the serial port driver (COM16550 ). Driver development.
The serial port driver in Windows CE can process all devices with similar I/O behaviors, including devices based on 16450 UART AND 16550 UART (general asynchronous transceiver chip) and some devices using DMA, common interfaces include 9-pin serial ports, infrared I/O ports, and Modem ports. In the % _ WINCEROOT % \ Public \ Common \ OAK \ Drivers \ Serial directory, the COM_MDD2 sub-directory contains the new Serial driver MDD layer function code. The COM16550 subdirectory contains the PDD Layer Code driven by the serial port. The SER16550 sub-directory contains a series of functions dedicated to controlling the 16550 compatible UART, so the main task of the PDD layer is to call the functions in SER16550. There is also an ISR16550 sub-directory that contains the serial port driver dedicated to installable ISR (interrupt service routines), while many hardware device drivers use CE default installable ISR giisr. dll. An example of registry settings for a serial port device is as follows:
Key meaning
"SysIntr" = dword: 13 the interrupt ID of Serial Port 1 is decimal 13
"IoBase" = dword: 02F8 Serial Port 1 the first address of the IO space is hexadecimal 2F8
"IoLen" = dword: 8 Serial Port 1's IO space length is 8 bytes
"DeviceArrayIndex" = dword: 0 serial port 1 index, which is the origin of 1
"Order" = dword: 0 serial port 1 driver Loading Sequence
"DeviceType" = dword: 0 serial port 1 device type
"DevConfig" = hex:... the configuration of Serial Port 1 when communicating with the Modem device, such as the baud rate and parity check.
"FriendlyName" = "COM1:" Name of Serial Port 1 displayed in the dialup Program
"Tsp" = "Unimodem. dll" Serial Port 1 is the TSP (TAPI Service provider) DLL to be loaded when it is used to communicate with the Modem device
"Prefix" = "COM" Prefix of the stream interface of Serial Port 1
"Dll" = "com16550.Dll" Serial Port 1 driver DLL
SysIntr is pre-defined by ce in the file Nkintr. h and is used to uniquely identify the Interrupt Device. OEMs can define their own SysIntr in the file Oalintr. h. Common predefined SysIntr includes SYSINTR_NOP (interrupt is only handled by ISR, IST is not processed), SYSINTR_RESCHED (rescheduling thread), and SYSINTR_DEVICES (Base Value of the device interrupt ID predefined by CE ), SYSINTR_PROFILE, SYSINTR_TIMING, and SYSINTR_FIRMWARE are all defined based on SYSINTR_DEVICES. IoBase is the first address of the IO address space of Serial Port 1, and IoLen is the size of IO space. The I/O address space only exists on the x86 platform. If the hardware registers on other platforms must be mapped to the physical address space, the sub-keys are named MemBase and MemLen. On the x86 platform, more hardware registers are mapped to physical address space due to restrictions of I/O space. DeviceArrayIndex is a device index used to differentiate devices of the same type. Prefix is the Prefix of the stream driver. When the application calls the CreateFile function to pass the COM1: parameter, the file system is responsible for communicating with the serial driver, the serial port driver is loaded by device.exe at CE startup.

The following describes the initialization process of the serial driver from the MDD layer function COM_Init. Com_initis called by the Device Manager device.exe after the serial port device is detected. It is a string pointer and the content of the string is HLM \ Drivers \ Active \ xx. xxis an input number. device.exe tracks every driver in the system, record the loaded driver under the Active key ). COM_Init first allocates an HW_INDEP_INFO struct, which is independent of the header information of the serial port hardware. (MDD, PDD, and SER16550 both contain their own unique struct, for specific struct definitions, see serial driver source code). After allocation, initialize each member in the struct, initialize the struct, and call OpenDeviceKey (LPCTSTR) Identifier) open the Registry path contained in HLM \ Drivers \ Active \ xx \ Key, where the path is generally HLM \ Drivers \ BuiltIn \ Serial, that is, the location of the serial port driver information in the registry. COM_Init then queries the DeviceArrayIndex and Priority256 values under HLM \ Drivers \ BuiltIn \ Serial. Priority256 specifies the driver priority. If no value exists, the default priority is used. Next, call GetSerialObject (DeviceArrayIndex). This function is defined by the PDD layer and returned to the HWOBJ struct. This struct mainly contains the pointer of the PDD layer and the function defined by SER16550. That is to say, by calling this function, MDD can call the underlying implemented function. Most of the subsequent work is to call the underlying function for initialization. The underlying function SerInit of the first call mainly sets hardware configurations set by the user, such as line control and baud rate. It calls the Ser_GetRegistryData function to obtain the hardware information stored in the registry, ser_GetRegistryData internally calls the kernel and ddkreg_getjavaswinfo functions provided by the system to obtain IRQ, SysIntr, IsrDll, IsrHandler, IoBase, and IoLen stored in HLM \ Drivers \ BuiltIn \ Serial. IRQ indicates the logical interrupt number. IsrDll indicates the name of the DLL where the currently installable ISR program is located, and IsrHandler indicates the name of the function where the ISR can be installed. By the way, you can install ISR. In my previous article on OAL, I learned that OEM associates IRQ and SysIntr with the OEMInit function. When the hardware device is interrupted, ISR will disable same-level and low-level interruptions, and then return the associated SysIntr Based on IRQ. The kernel will wake up the corresponding IST Based on SysIntr returned by ISR (SysIntr is associated with the Event created by IST ), call InterruptDone to cancel the interruption prohibition after the IST process is interrupted. The disadvantage of the association in OEMInit is that once the CE kernel is compiled, this association cannot be added, and some hardware devices may be plugged in or out at any time or share interruption, to associate such a hardware device, you can install ISR to handle the interruption caused by a specified hardware device, therefore, if the hardware device needs to install ISR, you must add IsrDll and IsrHandler to the Registry. Most hardware devices use CE's default installable ISR giisr. dll in the following format:
"IsrDll" = "giisr. dll"
"IsrHandler" = "ISRHandler"
If a hardware driver needs to be able to install ISR, and developers do not want to write one themselves, they can use giisr. dll. In addition to adding the above in the registry, you also need to call the relevant function registration in the driver to install ISR. The pseudocode is as follows:
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 );
The LoadIntChainHandler function registers the installable ISR. Parameter 1 is the DLL name, parameter 2 is the ISR function name, and parameter 3 is IRQ. The TransBusAddrToStatic function is described later. If you want to use giisr. dll as the installable ISR, you must first fill in the GIISR_INFO structure. CheckPort = TRUE indicates that giisr needs to check the specified register to determine whether the current interrupt is the device. PortIsIO indicates the address space where the Register address belongs, FALSE indicates the inner space, and TRUE indicates the IO space. UseMaskReg = TRUE indicates that the device has a mask register, which is used to specify whether the current device is the interrupt source, that is, the outgoing interrupt, and MaskAddr indicates the address of the mask register. For Info. if the Mask value is assigned, PortAddr indicates a special register address. If the value of this register is true with the value of Mask & the result of the operation, it indicates that the current device is the interrupt source, otherwise, the system returns SYSINTR_CHAIN (indicating that the current ISR is not interrupted and the kernel will call the next ISR In the ISR chain). If UseMaskReg is set to TRUE, if the value of the MaskReg register and the value of the Register specified by PortAddr are true, the current device is the source of interruption.
The SerInit function then calls the Ser_InternalMapRegisterAddresses function to convert the IO address and map the address. Ser_InternalMapRegisterAddresses internally calls the HalTranslateBusAddress (Isa, 0, ioPhysicalBase, & inIoSpace, & iophysical) the function converts bus-related addresses to system addresses. Parameter 1 is the bus type, parameter 2 is the bus number, and parameter 3 is the address to be converted (PHYSICAL_ADDRESS type, actually LARGE_INTEGER). Parameter 4 specifies whether the Register address belongs to the IO address space or physical address space. Parameter 5 returns the physical address after conversion. Observe the source code of HalTranslateBusAddress and find that if it is on the x86 platform, this function does nothing except assign parameter 3 to parameter 5, instead of setting the inIoSpace value to 0 on the x86 platform, it must be a physical address. Before calling HalTranslateBusAddress, determine the address space of the Register address obtained from the registry. For example:
ULONG inIoSpace = 1; // 1 indicates the IO Space
PHYSICAL_ADDRESS ioPhysicalBase = {iobase, 0}; // equivalent to ioPhysicalBase. LowPart = iobase
After address translation, You need to map the converted address to the virtual address space (less than 0x80000000) that can be accessed by the driver (generally, the IST runs in user mode like the application) and static virtual address space that ISR can access (0x80000000 or above ). For example:
//// If the address belongs to the physical address space
IoPortBase = (PUCHAR) MmMapIoSpace (ioPhysicalBase, Size, FALSE );
TransBusAddrToStatic (Isa, 0, ioPhysicalBase, Size, & inIoSpace, ppStaticAddress );
The MmMapIoSpace function maps physical addresses to virtual address spaces that can be accessed by the driver. MmMapIoSpace is called internally through source code analysis:
PVirtualAddress = VirtualAlloc (0, SourceSize, MEM_RESERVE, PAGE_NOACCESS );
VirtualCopy (pVirtualAddress, (PVOID) (SourcePhys> 8), SourceSize, PAGE_PHYSICAL |
PAGE_READWRITE | (CacheEnable? 0: PAGE_NOCACHE ));
VirtualAlloc allocates a virtual address space of the same size as MemLen. Because parameter 1 is 0, the kernel is automatically allocated. Generally, MemLen is smaller than 2 MB, so it is allocated in the application address space. VirtualCopy is responsible for ing the physical address of the hardware device register with the virtual address allocated by VirtualAlloc. In this way, access to PvirtualAddress in the driver program is actually accessing the first register. Because the physical address of the hardware device register must be above 512 MB (CE supports the maximum RAM value), in addition to the last parameter to add PAGE_PHYSICAL, the physical address of the second parameter must also be shifted to eight places (or divided by 256 ). Of course, PAGE_NOCACHE must be used to map hardware registers. The TransBusAddrToStatic function maps physical addresses to static virtual address spaces that can be accessed by ISR. When Yes, ISR accesses a register of the hardware device to determine the interrupt source, therefore, it is necessary to map the physical address of the register to the static virtual address space (ISR can only access the static virtual address space ). Static Virtual Address Space refers to the virtual address space defined in OEMAddressTable (of course, 0x80000000 or above ). On the x86 platform, this table only defines the correspondence between the physical and virtual addresses of RAM, while the register addresses of hardware devices are not defined in this table, therefore, to create a static virtual address space for ISR access, you must call the CreateStaticMapping function to allocate the space between 0xC4000000 and 0xE0000000. The TransBusAddrToStatic function internally calls the CreateStaticMapping function. Note: The Register address of the hardware device can also be defined in OEMAddressTable.
//// If the address belongs to the IO Space
IoPortBase = (PUCHAR) ioPhysicalBase. LowPart;
* PpStaticAddress = ioPortBase
This situation only belongs to the x86 platform and can be accessed directly from the I/O space, even in the user mode.
The SerInit function then initializes the SER_INFO struct member and then calls the SL_Init function. This function is defined in ser16550 to initialize the SER16550_INFO struct and stores the address of eight registers in the serial port. After the SerInit function is executed, the COM_Init function creates the receiving buffer and calls the StartDispatchThread function to initialize the interrupt and create the IST. The StartDispatchThread function calls the InterruptInitialize function internally to associate SysIntr and Event. Then, the InterruptDone function is called to tell the kernel that the current serial port can interrupt processing and call the CreateThread function to create an IST thread.

Registry operation functions:

Windows CE 6.0 has four basic registry key values: HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, and HKEY_USERS. The other keys are the subkeys of the four registries.

Take a built-in serial port driver as an example. the descriptions in reg are as follows: the Prefix and Dll items are essential. The Prefix indicates the Prefix of the device file name, the device name of the registry sub-key is "COM1:", which can be called by CreateFile. Dll is the name of the dynamic link library. Index indicates the device number. If the value of Flags is 1, the system is not loaded at startup. If the value of Flags is 0, the driver is loaded at system startup.

[HKEY_LOCAL_MACHINE \ Drivers \ BuiltIn \ Serial]
"Prefix" = "COM"
"Dll" = "$ (_ TGTPLAT_PFX) _ serial. dll"

"Flags" = dword: 0
"Index" = dword: 1

In the directory % WinCE Dir % Public % Common % OAK % INC %, the cregedit. h file defines a class CRegistryEdit to encapsulate registry operations. Many hardware drivers, such as serial ports, also inherit the CRegistryEdit class.

Another method is to use the APIS provided by windows CE to perform registry key operations.

Method 1:Use the Registry class CRegistryEdit provided by the system

The class is defined in the regedit. h file.

In the build function, the HANDLE of the current registry subkey is obtained. There are three constructors. The first one is constructed by calling hKey = OpenDeviceKey (TEXT ("HKEY_LOCAL_MACHINE \ Drivers \ BuiltIn \ Serial") in the full path, second, if the parent registry subkey is known, call RegOpenKeyEx (HKEY_LOCAL_MACHINE, TEXT ("Drivers \ BuiltIn \ Serial"), & hKey ). the third method is to use RegCreateKeyEx ().

In the destructor, call RegCloseKey (hKey) to disable the reference to the registry subkey.

You can use the GetRegValue method to read the registry key, and the RegSetValueEx method to write the registry key. The implementation of this method is also through the windows ce api. For details, refer to the next Section.

The RegGetList and RegSetList methods provide read and write operations on the values of REG_MULTI_SZ and DWORD types.

Other methods include getjavaswinfo, GetIsrInfo, and GetPciInfo to obtain more information about the registry.

Method 2: Use windows CE API

To obtain a registry key value, you must first call RegOpenKeyEx. For example, RegOpenKey (HKEY_LOCAL_MACHINE, TEXT ("Drivers \ BuiltIn \ Serial"), 0, 0, & hKey ), the hKey is the HANDLE of the registry subkey we obtained. The next operation on the registry subkey is implemented through hKey.

With the hKey, you can read and write the content of the registry subkey. If you want to read the Prefix of the subkey, call the RegQueryValueEx (hKey, TEXT ("Prefix") function, NULL, & lpType, & lpData, & lpcbData), lpType, lpData is the Prefix returned type and value. In this example, lpType = REG_SZ, lpData = TEXT ("COM "). when the lpbdata is called, the modification limits the size of lpData in bytes. The returned SIZE is the size of the lpData return value.

For read operations, write operations are the opposite process. RegSetValueEx (hKey, TEXT ("Prefix"), NULL, REG_SZ, PBYTE (TEXT ("TST ")), wcslen (TEXT ("TST") * 2 ). the item to be written is the Prefix item, the type is REG_SZ, the value is TEXT ("TST"), and the last parameter is the write SIZE in bytes. pay attention to the second parameter. If this item exists in the registry, rewrite its value. If no, a new registry key is created.

In some cases, we need to delete a registry key. You only need to call RegDeleteKey (hKey, TEXT ("Index") to delete the Index registry key.

To disable a registry subkey, you only need to call RegCloseKey (hKey.

Summary:

There are many more in-depth use of the registry, such as RegEnumValue. For more information about registry items, see the MSDN manual.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.