Comparison of Device Driver Models in Linux and Windows

This article navigation
  • 1. Device Driver Architecture08%

    • 1.1. Windows Driver Architecture09%

    • 1.2. Linux Driver Architecture16%

  • 2. Device Driver API22%

    • Driver I/O Models on Windows46%

    • Driver I/O Models on Linux51%

    • Registering Devices on Windows27%

    • Registering Devices on Linux33%

    • 2.1. Initialization23%

    • 2.2. Naming and Declaring Devices27%

    • 2.3. Data Exchange42%

  • 3. Device Driver Development Environment56%

    • Windows Driver Kit56%

    • Linux Source Code60%

    • 3.1. Device Driver Framework56%

    • 3.2. Build System for Device Drivers65%

    • 3.3. Documentation Support69%

    • 3.4. Debugging Support72%

  • 4. Device Driver Distribution79%

    • 4.2. Updating Device Drivers84%

    • 4.3. Security Considerations88%

  • 5. Conclusion92%

Comparison of Device Driver Models in Linux and Windows

Abbreviations:

  • API Application Programming InterfaceApplication Program Interface
  • ABI Application Binary InterfaceApplication Binary Interface

Device drivers are part of the operating system that facilitate the use of hardware devices through specific programming interfaces, allowing software to control and operate these devices. Since each driver corresponds to different operating systems, you need different Linux, Windows, or Unix device drivers to use your device on different computers. This is why it is crucial to check the experience of a driver developer or a research service provider in developing drivers for various operating system platforms when hiring one.

Comparison of Device Driver Models in Linux and Windows

The first step in driver development is to understand how each operating system handles its drivers differently, the underlying driver models, the architectures it uses, and the available development tools. For instance, the Linux driver model is fundamentally different from Windows. While Windows encourages separate driver and operating system development and combines them through a set of ABI calls, Linux device driver development does not rely on any stable ABI or API, so its driver code is not included in the kernel. Each model has its advantages and disadvantages, but if you want to provide comprehensive support for your device, it is important to have a thorough understanding of them.

In this article, we will compare Windows and Linux device drivers, exploring different architectures, APIs, build development, and distribution, hoping to provide you with a deeper understanding of how to start writing device drivers for each operating system.

1. Device Driver Architecture

The architecture of Windows device drivers is different from that used in Linux, each having its own strengths and weaknesses. The differences are primarily influenced by the fact that Windows is a closed-source operating system while Linux is open-source. Comparing the architectures of Linux and Windows device drivers will help us understand the core differences behind Windows and Linux drivers.

1.1. Windows Driver Architecture

While the Linux kernel distribution comes with Linux drivers, the Windows kernel does not include device drivers. In contrast, modern Windows device drivers are written using the Windows Driver Model (WDM), which is a model that fully supports plug-and-play and power management, allowing drivers to be loaded and unloaded as needed.

The handling of requests from applications is done by a part of the Windows kernel known as the I/O Manager. The role of the I/O Manager is to translate these requests into I/O Request PacketsIO Request Packets (IRP), which can be used to identify requests and transfer data at the driver level.

The Windows Driver Model (WDM) provides three types of drivers, forming three layers:

  • FilterFilter

    Drivers provide optional additional processing on the IRP.

  • FunctionFunction

    Drivers are the main drivers that implement interfaces and communicate with each device.

  • BusBus

    Drivers service different adapters and various bus controllers to implement host mode control of devices.

An IRP travels through these layers as it reaches the underlying hardware via the I/O Manager. Each layer can independently handle an IRP and send it back to the I/O Manager. At the hardware level, there is a Hardware Abstraction Layer (HAL) that provides a common interface to physical devices.

1.2. Linux Driver Architecture

In contrast to Windows device drivers, the fundamental difference in Linux device driver architecture is that Linux does not have a standard driver model or a cleanly separated layer. Each device driver is implemented as a module that can automatically load and unload from the kernel. Linux provides some means for plug-and-play devices and power management devices so that the drivers can use them to manage these devices correctly, but this is not mandatory.

Modules output the functions they provide and communicate by calling these functions and passing in arbitrarily defined data structures. Requests from user applications in the file system or network layer are transformed into the required data structures. Modules can be stacked in layers, where one module processes the request, and then another processes it; some modules provide a common calling interface for a class of devices, such as USB devices.

Linux device drivers support three types of devices:

  • Character devices that implement a byte stream interface.

  • Block devices used for storing file systems and handling multi-byte data block I/O.

  • Network interfaces for transmitting packets over a network.

Linux also has a Hardware Abstraction Layer (HAL), which essentially acts as the device driver interface for physical hardware.

2. Device Driver API

Both Linux and Windows driver APIs are event-driven types: the driver code executes only when certain events occur—when the user’s application wants to retrieve something from the device, or when the device has requests to inform the operating system.

2.1. Initialization

On Windows, drivers are represented as DriverObject structures, which are initialized during the execution of the DriverEntry function. These entry points also register callback functions to respond to device addition and removal, driver unloading, and handling new incoming IRPs. When a device connects, Windows creates a device object that handles all application requests behind the device driver.

In contrast to Windows, the lifecycle of Linux device drivers is managed by the kernel module’s module_init and module_exit functions, which are used for loading and unloading the module, respectively. They are responsible for registering the module to handle device requests through the kernel interface. This module needs to create a device file (or a network interface), specify a numeric identification number for the device it wishes to manage, and register some callback functions used when users interact with the device file.

2.2. Naming and Declaring Devices

Registering Devices on Windows

Windows device drivers are notified of newly connected devices by the callback function AddDevice. It then creates a Device Objectdevice object to identify the specific driver instance for that device. Depending on the type of driver, the device object can be a Physical Device ObjectPhysical Device Object (PDO), Function Device ObjectFunction Device Object (FDO), or Filter Device ObjectFilter Device Object (FIDO). Device objects can be stacked, with the PDO at the bottom.

The device object exists for as long as the device is connected to the computer. The DeviceExtension structure can be used to associate global data with a device object.

Device objects can have names in the form of \\.\DeviceName, which the system uses to identify and locate them. Applications can use the CreateFile API function to open a file with the above name, obtaining a handle that can be used to interact with the device.

However, typically only PDOs have their own names. Unnamed devices can be accessed through device-level interfaces. Device drivers register one or more interfaces, identified by a 128-bit globally unique identifier (GUID). User applications can use known GUIDs to obtain a handle for a device.

Registering Devices on Linux

On the Linux platform, user applications access devices through file system entries, typically located in the /dev directory. During module initialization, it creates all necessary entries by calling the kernel function register_chrdev. Applications can initiate a open system call to obtain a file descriptor to interact with the device. This call is subsequently sent to a callback function, and this call (along with future calls using the returned file descriptor, such as read, write, or close) will be assigned to callback functions stored in data structures like file_operations or block_device_operations that the module installs.

The device driver module is responsible for allocating and maintaining any necessary data structures for operation. The file structure passed into the file system callback functions has a private_data field that can be used to store a pointer to the specific driver data. Block device and network interface APIs also provide similar fields.

While applications use file system nodes to locate devices, Linux internally uses the concepts of major numbersmajor numbers and minor numbersminor numbers to identify devices and their drivers. The major number is used to identify the device driver, while the minor number is used by the driver to identify the device it manages. To manage one or more fixed major numbers, the driver must first register itself or let the system allocate unused device numbers to it.

Currently, Linux uses a 32-bit value for major-minor pairsmajor-minor pairs, with 12 bits allocated for the major number, allowing for up to 4096 different devices. Major-minor pairs are distinct for character devices and block devices, so a character device and a block device can use the same device pair without conflict. Network interfaces are identified by symbolic names like eth0, which are distinct from the major-minor pairs used for character and block devices.

2.3. Data Exchange

Both Linux and Windows support three methods for transferring data between user-level applications and kernel-level drivers:

  • Buffered Input-OutputBuffered Input-Output

    It uses buffers managed by the kernel. For write operations, the kernel copies data from the user-space buffer to a kernel-allocated buffer and then transfers it to the device driver. Read operations are similar, where the kernel copies data from the kernel buffer to the application-provided buffer.

  • Direct Input-OutputDirect Input-Output

    It does not use copy functionality. Instead, the kernel pins a user-allocated buffer in physical memory so that it remains there and is not swapped out during data transfer.

  • Memory mappingMemory mapping

    It can also be managed by the kernel, allowing the kernel and user-space applications to access the same memory pages through different addresses.

Driver I/O Models on Windows

Supporting buffered I/O is a built-in feature of WDM. Buffers can be accessed by the device driver through the AssociatedIrp.SystemBuffer field in the IRP structure. When communication with user space is needed, the driver simply reads and writes from this buffer.

Direct I/O on Windows is mediated by memory descriptor listsmemory descriptor lists (MDL). This semi-transparent structure is accessed through the MdlAddress field in the IRP. They are used to locate the physical addresses of buffers allocated by user applications and pinned during I/O requests.

The third option for data transfer on Windows is called METHOD_NEITHER. In this case, the kernel needs to pass the virtual addresses of the user-space input/output buffers to the driver, without needing to verify their validity or ensure they map to a physical memory address accessible by the device driver. The device driver is responsible for handling the details of these data transfers.

Driver I/O Models on Linux

Linux provides several functions such as clear_user, copy_to_user, strncpy_from_user, and others for transferring buffer data between kernel and user memory. These functions ensure the validity of pointers to data buffer and handle all the details of data transfer by safely copying data between memory regions.

However, block device drivers operate on entire data blocks of known size, which can be moved quickly between kernel and user address spaces without needing to copy them. This situation is handled automatically by the Linux kernel for all block device drivers. The block request queue processes data transfers without unnecessary copying, while the Linux syscall interface translates filesystem requests into block requests.

Ultimately, device drivers can allocate some storage pages from the kernel address space (non-swappable) and use the remap_pfn_range function to directly map these pages into the user process’s address space. The application can then obtain the virtual addresses of these buffers and use them to communicate with the device driver.

3. Device Driver Development Environment

3.1. Device Driver Framework

Windows Driver Kit

Windows is a closed-source operating system. Microsoft provides the Windows Driver Kit to facilitate non-Microsoft vendors in developing Windows device drivers. The kit includes everything needed for development, debugging, testing, and packaging Windows device drivers.

Windows Driver ModelWindows Driver Model (WDM) defines a clean interface framework for device drivers. Windows maintains the source code and binary compatibility of these interfaces. Compiled WDM drivers are usually forward-compatible, meaning an older driver can run on a newer system without recompilation, but it cannot access new features provided by the system. However, drivers do not guarantee backward compatibility.

Linux Source Code

In contrast to Windows, Linux is an open-source operating system, so the entire Linux source code serves as the SDK for driver development. There is no formal framework for driver devices, but the Linux kernel includes many subsystems that provide common services such as driver registration. The interfaces of these subsystems are described in kernel header files.

Although Linux defines interfaces, these interfaces are not designed to be stable. Linux does not provide any guarantees about forward and backward compatibility. Device drivers need to be recompiled for different kernel versions. The lack of stability allows the Linux kernel to undergo rapid development, as developers do not have to support old interfaces and can use the best methods available to solve current problems.

When writing in-treein-tree drivers (referring to the current Linux kernel development trunk), this constantly changing environment does not pose any issues as they are synchronized with the kernel itself. However, closed-source drivers must be developed separately and maintained out-of-tree to support different kernel versions. Therefore, Linux encourages device driver developers to maintain their drivers in-tree.

3.2. Build System for Device Drivers

The Windows Driver Kit adds driver development support to Microsoft Visual Studio and includes compilers for building driver code. Developing Windows device drivers is not much different from developing user-space applications in an IDE. Microsoft provides an enterprise Windows Driver Kit that offers a build environment similar to the Linux command line.

Linux uses Makefile as the build system for both in-tree and out-of-tree system device drivers. The Linux build system is very advanced; usually, a device driver requires only a few lines to produce a working binary code. Developers can use any IDE as long as it can handle the Linux source code repository and run make, and they can also easily compile drivers manually from the terminal.

3.3. Documentation Support

Windows has good documentation support for driver development. The Windows Driver Kit includes documentation and example driver code, and a wealth of information about kernel interfaces is available through MSDN, along with numerous references and guides regarding driver development and the Windows underlying system.

Linux documentation is not descriptive, but the entire Linux source code available to driver developers mitigates this issue. The Documentation directory in the source code tree describes some of Linux’s subsystems, but there are several books[1] that cover Linux device driver development and provide overviews of the Linux kernel in more detail.

Linux does not provide specific sample device drivers, but the source code of existing production-level drivers is available and can serve as a reference for developing new device drivers.

3.4. Debugging Support

Both Linux and Windows have logging mechanisms available for tracing and debugging driver code. In Windows, the DbgPrint function is used, whereas in Linux, the function is called printk. However, not every issue can be resolved by just using logging and source code. Sometimes breakpoints are more useful, as they allow for examining the dynamic behavior of the driver code. Interactive debugging is also essential for investigating the causes of crashes.

Windows supports interactive debugging through its kernel-level debugger WinDbg. This requires connecting two machines through a serial port: one machine runs the kernel being debugged while the other runs the debugger and controls the operating system being debugged. The Windows Driver Kit includes debugging symbols for the Windows kernel, making the data structures partially visible in the debugger.

Linux also supports interactive debugging through KDB and KGDB. Debugging support can be built into the kernel and enabled at boot time. Afterward, debugging can be done directly through a physical keyboard on the system or by connecting from another computer via a serial port. KDB provides a simple command-line interface, which is the only way to debug the kernel on the same machine. However, KDB lacks source-level debugging support. KGDB offers a more complex interface through the serial port. It allows standard application debuggers like GDB to debug the Linux kernel just like any other user-space application.

4. Device Driver Distribution

4.1. Installing Device Drivers

Device drivers installed on Windows are described by text files known as INF, typically stored in the C:\Windows\INF directory. These files are provided by the driver vendor and define which devices are serviced by the driver, where to find the driver’s binary files, and the version of the driver, etc.

When a new device is plugged into the computer, Windows identifies already installed drivers and selects an appropriate one to load. When the device is removed, the driver is automatically unloaded.

On Linux, some drivers are built into the kernel and remain permanently loaded. Non-essential drivers are built as kernel modules, typically stored in the /lib/modules/kernel-version directory. This directory also contains various configuration files, such as modules.dep, which describe the dependencies between kernel modules.

While the Linux kernel can load some modules at boot time, module loading is typically supervised by user-space applications. For example, the init process may load some modules during system initialization, and the udev daemon is responsible for tracking newly inserted devices and loading the appropriate modules for them.

4.2. Updating Device Drivers

Windows provides a stable binary interface for device drivers, so in some cases, there is no need to update the driver binary files along with the system. Any necessary updates are handled by the Windows Update service, which is responsible for locating, downloading, and installing the latest versions of drivers suitable for the system.

However, Linux does not provide a stable binary interface, so it is necessary to recompile and update all required device drivers with each kernel update. Clearly, device drivers built into the kernel will automatically update, but out-of-tree modules can pose slight issues. Maintaining up-to-date module binary files is often addressed by DKMS[2], a service that automatically rebuilds all registered kernel modules when a new kernel version is installed.

4.3. Security Considerations

All Windows device drivers must be digitally signed before Windows loads them. During development, self-signed certificates can be used, but driver packages distributed to end-users must be signed using a valid certificate trusted by Microsoft. Vendors can obtain Software Publisher CertificatesSoftware Publisher Certificate from any trusted certificate authority authorized by Microsoft. This certificate is then cross-signed by Microsoft, and the resulting cross-certificate is used to sign the driver package before release.

The Linux kernel can also be configured to verify signatures before loading kernel modules and prevent untrusted kernel modules. The set of public keys trusted by the kernel is fixed at build time and is fully configurable. The checks performed by the kernel can also be configured for strictness, ranging from simply issuing warnings for untrusted modules to refusing to load anything deemed suspicious.

5. Conclusion

As shown above, the infrastructure of Windows and Linux device drivers has some commonalities, such as the method of calling APIs, but many details are quite different. The most prominent differences arise from the fact that Windows is a closed-source operating system developed by a commercial company. This results in a well-documented, stable driver ABI and formal framework on Windows, while on Linux, much of the source code serves as a beneficial supplement. Documentation support is also more developed in the Windows environment because Microsoft has the resources necessary to maintain it.

On the other hand, Linux does not use frameworks to constrain device driver developers, and the source code for both kernel and production-level device drivers can be helpful when needed. The lack of interface stability also plays a role since it means that the latest device drivers always utilize the latest interfaces, and the kernel itself carries a smaller burden of backward compatibility, resulting in cleaner code.

Understanding these differences and the specifics of each system is the key first step to providing effective driver development and support for your devices. We hope this article comparing Windows and Linux device driver development serves as a great starting point for your research into the device driver development process.

via: http://xmodulo.com/linux-vs-windows-device-driver-model.html

Author: Dennis Turpitka[3] Translator: FrankXinqi & YangYang Proofreader: wxy

This article is originally compiled by LCTT[4] and proudly presented by Linux China

Recommended Articles

Swipe to see more

Comparison of Device Driver Models in Linux and WindowsComparison of Device Driver Models in Linux and WindowsComparison of Device Driver Models in Linux and WindowsComparison of Device Driver Models in Linux and WindowsComparison of Device Driver Models in Linux and Windows

Enter article ID or long press the QR code to access directly

[1]: http://xmodulo.com/go/linux_device_driver_books[2]: http://xmodulo.com/build-kernel-module-dkms-linux.html[3]: http://xmodulo.com/author/dennis[4]: https://github.com/LCTT/TranslateProject

Leave a Comment