I focus on how to write drivers, I don't have to introduce for some applications, because I have little written for those ongoing things. If you talk about it, there is a shroud, huh! As a new technique recommended by Win98 and Win2K, the USB driver is different from the driver of the Win95 VXD that is directly intended to hardware, it should be WDM type.
The USB WDM interface block diagram is as follows (this figure can be said to be the overall box of USB software)
For the HID device, you can use the structure on the upper left side of the graph, other classes use the upper right structure, in fact, the structure on the right can be subjected to two layers, and the layer is the Class Driver, one layer is MiniPort Driver. The UHCD and OpenHCI of the last line of the countdown are the primary driver standards associated with both Intel and Compaq, respectively, and you can choose according to the required choices.
For USB drivers, you have to learn about WDM drivers, or earlier NT drivers, in fact, WDM drivers can be seen as an UPDATE of the NT driver, just add some new features.
"Write driver is a very long and cumbersome work, before this, you'd better be familiar with hardware, familiar with C / C , and use DDK, you will use some debuggers, such as Softice and Windbg. If everything Ready, you can start writing a driver, and the process of work is sometimes depends on your luck. " (This is a friend who is airsite to me. I wrote it out and share it)
Here is a summary of an article I got from a friend:
The hierarchical driver of the NT driver refers to a program code for managing a peripheral device. NT uses a more flexible hierarchical drive method, allowing there to be several driver hierarchies between the mixed applications and the hardware. The hierarchical mechanism allows NT to be more widely defined, including file systems, logical volume managers, and various network components, various physical device drivers, and more. 1. Equipment drivers These are drivers that manage actual data transfer and control specific types of physical devices, including any error processes that start and complete I / O operations, processing interrupts, and perform specific devices. 2, the intermediate driver NT allows any number of intermediate drivers to be hierarchically on the physical device driver. These intermediate layers provide a function of extending the I / O system without having to modify the underlying driver. This is also a flexible side of the Microsoft advocated their system! In fact, I think so, I sacrifice some efficient things. 3, File System Driver (FSD) FSD is a class of special drivers, typically responsible for maintaining the disk structure required for various file systems. Note We can't use DDK to develop FSD, but you must use Microsoft's File System Developer Toolkit. Generally, less write intermediate filtration drivers, filter driver It intercepts and modifies the high-level request to send to the class driver. This allows the functions of the existing class driver to be used without having to write all programs from the beginning. NT core mode objects in our actual development process are devices, because the port driver has hidded hardware control operation, so I don't tell some parties related to hardware. If the future development object is different, it is necessary to make a detailed understanding of the interrupt, DMA, etc., which can be used for the interrupt, DMA, etc., which can be used for DDK. NT uses object technology to manage all data, and some of the objects involved in the general driver are described below. However, before introducing these objects, it is necessary to introduce the structure of the driver.
The driver structure NT driver is different from a general DOS / Windows C language program, it doesn't have main () or WinMain () function portals. Similarly, it reveals a function named DriveREntry () to the operating system. When the driver is started, the operating system will call this entry. In addition to making some of the necessary devices initialization, DriveRentry is initialized to initialize some DISPATCH routines. We know that the NT application and device driver deal is primarily made by CreateFile, ReadFile, Writefile, and DeviceIocontrol, etc. Win32 API. These APIs actually correspond to some of the DISPATCH routines of the driver. In addition to Driverentry, the driver is mainly composed of these DISPATCH cases. For example, when calling Win32 API CREATEFILE, the operating system is finally converted to the call to the DISPATCH routine corresponding to the driver IRP_MJ_CREATE function code. If the driver does not provide this routine, the CreateFile call will fail. Some common function code and Win32 API object relationships are as follows. Open function code IRP_MJ_CREATE apparatus CreateFile IRP_MJ_CLEANUP off the device, cancel the pending I / O requests CloseHandle IRP_MJ_CLOSE off device CloseHandle IRP_MJ_READ ReadFile IRP_MJ_WRITE data obtained from the control device to the user mode or kernel mode available to the client device transmits data WriteFile IRP_MJ_DEVICE_CONTROL length SetFileLength IRP_MJ_FLUSH_BUFFERS operation DeviceIoControl IRP_MJ_INTERNAL_DEVICE_CONTROL only kernel-mode operation of the client program can control the length of the file obtained IRP_MJ_QUERY_INFORMATION GetFileLength IRP_MJ_SET_INFORMATION setting file write output buffer or input buffer discards FlushFileBuffers FlushConsoleInputBuffer PurgeComm IRP_MJ_SHUTDOWN InitialSystemShutdown closed above system driver support functions Correspondence The general driver looks like the following.
DriverEntry (...) // driver entry {... DeviceObject-> MajorFunction [IRP_MJ_CREATE] = XXDriverCreateClose; // XX corresponds to your name of your driver DeviceObject-> MajorFunction [IRP_MJ_CLOSE] = XXDriverCreateClose; DeviceObject-> MajorFunction [IRP_MJ_READ] = XXDriverReadWrite; DeviceObject-> MajorFunction [IRP_MJ_WRITE] = XXDriverReadWrite;. ...} XXDriverCreateClose (...) // IRP_MJ_CLOSE routine and corresponds IRP_MJ_CREATE {// .........} XXDriverDeviceControl (...) // routines corresponding to IRP_MJ_DEVICE_CONTROL {// .........} XXDriverReadWrite (...) // Corresponds to IRP_MJ_READ and IRP_MJ_WRITE {// ....................................................... Interact with user mode client, then IRP_MJ_CREATE and IRP_MJ_CLOSE are supported. If the device does not support device reading, IRP_MJ_READ and IRP_MJ_WRITE are not supported. The driver object is in the operating system launch driver, which has been created before calling the driver entry Driverentry, and is passed to the driver as the parameter of the DriveREntry function. If the driver starts failed, the operating system will delete the object. The data structure of this object is as follows. Note The following table is not fully listing all data items of the Device_Object structure in NTDDK.H, where only the data items that may be used in general drivers. DRIVER object data item Description PDEvice_Object DeviceObject PDEVICE Objects Created by this driver ULONG FLAGS PDRIVER_INITIALIZE DRIONIT driver initialization routine (generally less) PDRIVER_STARTIO DriverStartio StartIO routine portfolio, generally this routine is used by low-layer device drivers Many, high-level drivers use less use this routine. PDRIVER_UNLOAD DRIVERUNLOAD Uninstall the driver routine, and if you want to stop the device in the control panel, you should provide this routine. PDRIVER_DISPATCH MAJORFUNCTION [IRP_MJ_MAXIMUM_FUNCTION 1] DISPATCH routines for drivers
It is mentioned above that the driver is managing all devices that manage the same type, so the DeviceObject pointing to the device is not a single device object, but an object chain table, which can be seen when describing the Device object below. The Device object and the Device Extension driver created a Device object after calling the IOCREATEVICE function successfully. Here is a more important data for the Device object.
Device object data item Description PVOID DeviceExtension points to the pointer of the Device Extension structure PDRIVER_OBJECT DRIVEROBJECT points to the pointer of the Driver object of this device, and IOCREATEDEVICE will automatically fill in this data. Ulong Flags Specifies the buffer policy PDEvice_Object nextDevice of this device points to the next device object belonging to this driver, relying on this data to maintain the minimum number of I / O stack units required by the device object chain table Cchar StackSize to the IRP of this device, general pair The layered driver, this data should be aligned than the memory required by the large 1ulong alignmentRequirement buffer of the lower device, the hierarchical driver, this value should be the characteristics of the device with the alignment of the lower equipment. And status information, all virtual, logical, and physical devices on the system have a Device object. For example, for a hard disk driver, there is a DEVICE object named partition0 for a physical hard disk, corresponding to the entire physical disk, and has a device object for each partition of the hard disk, and their names are PartitionX (X from 1 Start, each partition corresponds to a number). Device Extension is a very important data structure that connects to the Device object. Its data structure is determined by the driver designer. When you call iocreateDevice, you should specify its size. Device Extension is actually not A piece of memory assigned to each device object in the memory pool. Since the driver must be completely reusable, it is not a good way to use any global variables and static variables. Generally speaking, any information related to the device should be placed in the Device Extension. The buffering strategy of the device must also mention that the FLAG's buffering strategy is primarily determined when the device read and write (function code IRP_MJ_READ and IRP_MJ_WRITE), and the buffer policy when IRP_MJ_DEVICE_CONTROL is determined by the IOCTL control code itself. Both can't be mixed. Below I will specifically use the section to discuss the I / O buffer strategy. I / O Request Pack (IRP) has an IRP in the above structure, which is explained here. In NT, almost all I / O are driven, which can be said that other parts of the driver and operating system are interacting through the I / O request package. Let's take a look at an execution process of an I / O request. (1) The I / O Manager of the operating system allocates an IRP from the non-page memory, responding to an I / O request. Based on the I / O function specified by the customer, the I / O Manager passes the IRP to the Dispatch routine of the appropriate driver. (2) The Dispatch routine checks if the request is valid. If it is valid, the driver performs a series of operations according to the contents of the request. Otherwise, set the error status information to return directly. (3) When the operation is completed, the data (if any) and status information are stored in the IRP and returned to the I / O Manager. (4) The I / O Manager returns the last state and data (if any) to the user after proper processing returned IRP. The main data items of an IRP are shown below. IRP includes an IRP header and an area of IRP Stack. Since the WDM mode is driven, IRP can be said to be a very important stuff. There is also the damn URB (God Damn URB!) [People are really more addictive, some people or some things you can't like or do, but sometimes you have to do it again, just like one The big pile of fans is a bunch of dog-like Chinese football. The class is really wanting to lose, and the face of the Chinese is lost.
IORP primary data item Description IO_STATUS_BLOCK IOSTATUS Status PVOID AssociatedIrp.systemBuffer If the device performs buffer I / O, the pointer to the system space buffer. Otherwise, if the device executes direct I / O, the user's space address of the memory description table pointing to the user space buffer, the user space address of the PVOID UserBufferi / O buffer indicates that the IRP has been canceled about associatedirp.systembuffer, MDLADDRESS and Userbuffer will be The following I / O buffer strategy is discussed in more detail. NT also has more other objects, such as interrupt objects, controller objects, timer objects, etc., but is not used in our development drivers, so do not introduce here. I / O buffer strategies, drivers and client applications often need data exchange, but we know that drivers and client applications may not be in the same address space, so the operating system must solve data exchange between the two. This is designed to the I / O buffer strategy of the device. The I / O buffer policy of the read and write request says before the FLAG that sets the Device object can select the I / O buffer policy that controls the read and write request. The following is a description of these buffer strategies. 1. Buffer I / O (Do_Buffered_IO) At the beginning of the read and write request, the I / O Manager checks the accessibility of the user buffer, and then assigns a non-paged pool as large as the caller's buffer, and puts it The address is placed in an IRP's AssociatedIrp.systemBuffer domain. The driver uses this domain to transmit the actual data. For IRP_MJ_READ read requests, I / O Manager also sets the IRP's UserBuffer domain to the user's space address of the caller buffer. When the request is completed, the I / O Manager uses this address to copy the data from the driver's system space to the buffer buffer. For IRP_MJ_WRITE write requests, UserBuffer is set to NULL and copy the data of the user buffer to the system buffer. 2. Direct I / O (Do_Direct_IO) I / O Manager first checks the accessibility of the user buffer and locks it in physical memory. It then creates a memory descriptor table (MDL) for the buffer and stores the address of the MDL in the IRP's MDLADDRESS domain. Associatedirp.systemBuffer and UserBuffer are set to NULL. The driver can call the function MMgetsystemAddressFormDL to get the system space address of the user buffer to perform data operations. This function maps the caller's buffer to the address space of the non-column. After the driver completes the I / O request, the system automatically releases the mapping of the buffer from the system space. 3. Both of these methods are less used, as this requires the driver to handle the buffer problem. The I / O Manager puts only the user space addresses of the caller buffer in the UserBuffer domain of the IRP. We don't recommend this way.
Buffer policy IOCTL requests from the IOCTL buffer relate to an input buffer from the caller and the output buffer returned to the caller. To understand the IOCTL request, let's first take a look at the prototype of the Win32 API Deviceiocontrol function. BOOL DeviceIoControl (HANDLE hDevice, // device handle DWORD dwIoControlCode, // IOCTL request operation code LPVOID lpInBuffer, // the address input buffer DWORD nInBufferSize, // input buffer size LPVOID lpOutBuffer, // output buffer addresses DWORD nOutBufferSize, / / Output Buffer Size LPDWORD LPBYTESRETURNED, / / Store Returns LPPED LPOVERLAPPED // The Overlapped Structural Pointer for Synchronous Operations); IOCTL requests have four buffer policies, which will be described below. 1. Input Output Buffer I / O (Method_Buffered) I / O Manager first assigns a non-paged pool, which is sufficient to store the input or output buffer of the transferr (no matter which larger). The address of the non-page buffer is placed in the IRP's AssociatedirP.SystemBuffer domain, and then copies the IOCTL input data into this non-page buffer and sets the IRP's UserBuffer domain to the user space address of the caller output buffer. When the driver completes the IOCTL request, the I / O Manager copies the data in this non-page buffer to the caller's output buffer. Note that the same non-changer is used for input and output buffers simultaneously, so the drivers should read all the data entered before writing something to the buffer. 2. Direct input buffer output I / O (Method_IN_DIRECT) I / O Manager first checks the accessibility of the caller input buffer and locks it in physical memory. Then create an MDL for the input buffer and store the pointer to the specified MDL to the IRP's MDLADDRESS domain. At the same time, I / O Manager is also assigned an output buffer in a non-pilot and stores the address of this buffer in an IRP's AssociatedirP.SystemBuffer domain, and sets the IRP's UserBuffer domain to a caller output buffer. User space address. When the driver completes the IOCTL request, the I / O Manager copies the data in the non-page buffer to the caller's output buffer. 3. Buffer input Direct Output I / O (Method_out_direct) I / O Manager first checks the accessibility of the caller output buffer and locks it in physical memory. Then create an MDL for the output buffer and store the pointer to the specified MDL to the IRP's MDLADDRESS domain. At the same time, I / O Manager also assigns an input buffer in a non-pitch, and stores the address of this buffer in the IRP's associatedirp.systemBuffer domain, and simultaneously copy the data in the buffer in the buffer to In the system buffer, the IRP's UserBuffer domain is set to NULL. 4, the above three methods are not (Method_neither) I / O Manager put the address of the caller's input buffer to the parameters.devi Ceiocontrol.TypeInputBuffer domain of the IRP current I / O stack unit, put the address of the output buffer Store in the UserBuffer domain of IRP. Both addresses are user spatial addresses. As can be seen from the above description, the I / O Manager will allocate memory in the non-cuff, if the caller's buffer is relatively large, the allocated non-page pool will also be compared Big.
Non-copy pilot is a relatively valuable resource system, so if the caller's buffer is relatively large, we generally use direct I / O methods (such as disk read and write requests, etc.), which not only saves system resources, on the other hand The efficiency is improved due to the provision of data copies between the I / O manager in the system buffer and the caller buffer, which is especially obvious for drivers that exist in large amounts of data transfer. It can be noted that the read and write requests of almost all routines in the DDK are direct I / O, and for the IOCTL request is a lot of buffer I / O. Start Driver Design The following text is selected from the Microsoft's DDK Help, which makes our understanding of the design driver should pay attention to what problems, which are universal development guidelines. Which I / O requests should be supported Before starting to write any code, you should first determine which IRP routines should be processed by our driver. If you are designing a device driver, you should support the same IRP_MJ_XXX and IOCTL request code as the NT driver of other same types of devices. If you are designing an intermediate NT driver, you should first confirm that the device managed by your lower driver, because a high-level driver must have a low-level driver most IRP_MJ_XXX routine entry. When the high-level driver is connected to the I / O request, set the stack unit of the next low-level driver in the IRP in determining the current stack unit parameters, and then call the IocallDriver to pass the request to the lower driver. . Once you decide, which IRP_MJ_XXX should be processed, you can start determining how many DISPATCH routines should be made. Of course, it is also possible to consider combining the routines that have some RP_MJ_xxx processing into the same routine. For example, in ChangerDisk and VDISK, the routines for IRP_MJ_CREATE and IRP_MJ_CLOSE are the same function. The routines processed for IRP_MJ_READ and IRP_MJ_WRITE are also the same function. How many DEVICE objects should I? A driver must create a naming device object for each physical and logic device that can be managed for each of the I / O requests. Some low-level drivers may also create some unseasive number of Device objects. For example, a hard drive must create a Device object for each physical hard disk while also creating a Device object for each logical partition on each physical disk. A high-level drive driver must create a Device object for the virtual devices he represent, such a higher-level driver to connect their Device objects to the Device object of this driver. In addition, a high-level driver typically creates a series of virtual or logical Device objects for Device objects created by its low-level driver. Although you can design your driver in phases, a driver in the development phase does not have to create all the Device objects that it will be processed. However, from the beginning, you will make sure that all the Device objects you want to create will help any synchronization issues to be solved by the designers. In addition, it is determined that the Device object to be created also helps you define the contents and data structures of the Device Extension of the Device object. The development of starting driver development drivers is a process from rough to strip. NT DDK's SRC / directory has a huge model code, almost all types of device drivers, high-level drivers, and filter drivers. Before starting to develop your drivers, you should look for whether there is a similar type of routine that you want to develop below this model library. For example, the driver we developed, although DDK describes the USB description, we can still discover a lot of drivers related to USB devices at the src / storage / class directory. Let's take a look at the basic steps to develop drivers. The Skyline Driver Framework 1, write a DriveREntry routine, call IOCREATEDEVICE inside to create a Device object.
2. Write a basic framework for processing the DISPATCH routine requesting IRP_MJ_CREATE (see the most basic work to be completed by DDK Kernel-Mode Drivers 4.4.3. Of course, after the DISPATCREATE routine is written, you want to be in the DriveRETRY routine. Initializing the IRP_MJ_CREATE initialization routine). If the driver creates more than one Device object, you must write a routine for IRP_MJ_CLOSE requests. This routine is usually shared with DispatchCreate, see DDK Kernel-Mode Drivers 4.4.3. 3, compile connect your driver. Test your driver with the following method. First install the driver first as described above. Second, we have to establish a symbolic connection between the NT logical device name and the target Device object name. We already know that the Device object name is invisible to Win32 user mode, can't access it directly through the API, Win 32 API Only the NT logical device name can be accessed. We can establish a symbol connection between the two names by modifying the registry. Running regedt32.exe Create a symbolic connection under / hkey_local_machine / system / currentControlSet / Control / Session Manager / DOS Devices (this symbol connection can also be created in the driver to call function IOCREATESYMBOLICLICLINK). Restart the system. Write a simple test program to call the Win32API CreateFile function to open this device with the NT logical device name you named. If it is successful, then you will successfully write a simplest driver. Support for more device I / O requests, for example, your driver may need to respond to IRP_MJ_READ requests (you can use the Win32 API ReadFile function after completion). If your driver needs to be able to uninstall, you must also respond to IRP_MJ_CLOSE. Write the processing routine for you to handle IRP_MJ_XXX, and initialize these advances in Driverentry. A low-level driver may require at least one StartIO, ISR, and DPCFORSR routines, which may require a SYNCRITSECTION routine. If the device uses DMA, you may need an AdapterControl routine. For these routines, please refer to the DDK corresponding document. For high-level drivers, one or more IOCOMPLETION routines may require one or more IOCOMPLETITION routines, at least check the I / O status block and then call IOCOMPLETEREQUEST. If necessary, make some modifications to the Device Extension data structure and content.