Driver development in Windows CE

Source: Internet
Author: User
Tags builtin

I think even though the readers have read Microsoft's training materials on driver development and the driver part in CE help documentation, they are still confused. To truly understand the driver program, you must combine some driver source code. Here I will give a brief introduction to the basic knowledge of driver development based on the initialization process in the serial port driver (com16550.

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 of the serial driver. 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 subdirectory 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:

[HKEY_LOCAL_MACHINE \ drivers \ builtin \ serial_1]

Key Meaning
"Sysintr" = DWORD: 13 The interrupt ID of Serial Port 1 is decimal 13
"Iobase" = DWORD: 02f8 The first address of the IO space in Serial Port 1 is hexadecimal 2f8.
"Iolen" = DWORD: 8 The IO space length of Serial Port 1 is 8 bytes
"Devicearrayindex" = DWORD: 0 The index of Serial Port 1 is the origin of 1
"Order" = DWORD: 0 Serial Port 1 driver Loading Sequence
"Devicetype" = DWORD: 0 Device Type of Serial Port 1
"Devconfig" = HEX: 10, 00 .... Configuration of Serial Port 1 when communicating with modem devices, such as baud rate and parity check
"Friendlyname" = "COM1 :" Serial Port 1 name displayed in the dialup Program
"TSP" = "unimodem. dll" The TSP (TAPI Service Provider) DLL to be loaded when the serial port 1 is used to communicate with the modem device
"Prefix" = "com" Prefix of the stream interface of Serial Port 1
"DLL" = "com16550.dll" Driver DLL of Serial Port 1

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: runtime, the file system is responsible for communicating with the serial port driver. The serial port driver is loaded by device.exe at the cestartup.

The following describes the initialization process of the serial driver from the MDD layer function com_init. It is a string pointer, and the content of the string is HLM \ drivers \ Active \ xx,xxis a decimal 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. The path here 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 returns the hwobj struct. This struct mainly contains pointers of the PDD layer and ser16550 defined functions.

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 is the logical interrupt number. isrdll indicates the name of the DLL where the currently installable ISR of the driver is located, isrhandler
The name of the function that can install ISR.

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 interrupt is sent, 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 converted physical address. 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, so that the driver accesses pvirtualaddress 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 final parameter 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. In case of interrupted sharing, ISR is responsible for accessing 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, and then calls the interruptdone function to notify the kernel that the current serial port can interrupt processing, and then calls the createthread function to create the IST thread. (OVER. Next, it's related to the serial port hardware. I'm also tired of reading code without comments !!)

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.