Porting Real-Time Device Drivers to Linux Embedded Systems (Part 1)

Porting Real-Time Device Drivers to Linux Embedded Systems (Part 1)

Linux has stormed the embedded systems market. According to industrial analysts, approximately one-third to one-half of new 32-bit and 64-bit embedded system designs utilize Linux. Embedded Linux has demonstrated advantages in many application areas, such as SOHO home networks and imaging/multifunction peripherals, and has significant potential for leapfrog development in areas like (NAS/SAN) storage, home digital entertainment (HDTV/PVR/DVR/STB), and handheld/wireless devices, especially digital mobile phones.

New embedded Linux applications will not suddenly erupt from the minds of developers like the wisdom and craftsmanship of the deity – Lord Luo. A large number of projects must adopt thousands, or even millions, of lines of existing code. Hundreds of thousands of embedded projects have successfully ported existing code from other platforms to Linux, such as Wind River VxWorks and pSOS, VRTX, Nucleus, and other RTOS, and these porting efforts remain valuable and relevant.

So far, most literature on porting old RTOS applications to embedded Linux has focused on RTOS interfaces (APIs), tasks, scheduling patterns, and how to map them to the corresponding user space. In the intensive I/O space of embedded programs, it is equally important to port the hardware interface code of RTOS applications to the more standardized model of Linux device drivers.

This article will overview several common memory-mapped I/O methods that frequently appear in old embedded applications. They cover a range from special uses of interrupt service routines and user threads accessing hardware to semi-standardized driver models found in some RTOS. It is enlightening for porting RTOS code to the standardized model of Linux device drivers and introduces some methods. In particular, this article will focus on and compare memory mapping in RTOS code, the porting of Linux I/O scheduling queues, and redefining RTOS I/O for application in local Linux drivers and daemons.

RTOS I/O Concepts

“Unstandardized” is the best term to describe most I/O in RTOS-based systems. Most RTOS are designed for earlier MMU-less CPUs, ignoring memory management, even when MMUs became available, without distinguishing between physical and logical addresses. Most RTOS also run entirely in privileged mode (system mode), which superficially enhances performance. As such, all RTOS applications and system code can access the entire machine address space, memory-mapped devices, and I/O instructions. In reality, it is very difficult to distinguish RTOS application code from driver code, even if they are different.

This unstandardized structure leads to special implementations of I/O. In many cases, there is a complete lack of recognition of a device driver model. Given the egalitarian and non-hierarchical nature of this work, it is very instructive to review some important concepts and practices used in RTOS software.

Online Memory-Mapped Access

When commercial RTOS products became available in the mid-1980s, most embedded software contained a huge main loop, with registered I/O and interrupt service routines for strict timing operations. Developers integrated RTOS and execution programs into their projects primarily to enhance concurrency and assist with multitasking synchronization, while avoiding any other “interfering” constructs. Similarly, even if an RTOS provided I/O calling forms, embedded programmers continued to use direct I/O operations:

#define DATA_REGISTER 0xF00000F5char getchar(void) {return (*((char *) DATA_REGISTER)); /* read from port */}void putchar(char c) {*((char *) DATA_REGISTER) = c; /* write to port */} Most trained developers often separate such direct I/O code from hardware code. However, I have encountered a lot of spaghetti-style I/O handling code.

When direct memory-mapped I/O became widely used, embedded developers starting to work with Linux always faced the challenge of porting all such code to user space, converting the #define statements defining register addresses into mmap() calls. This approach works well for some types of prototypes but does not support interrupt handling, limits real-time responsiveness, is particularly unsafe, and is not suitable for commercial release. If you want to master C language comprehensively, click to read the original! If you want to learn more practical embedded skills, click to read the original!

If you want to improve your level of experience in project development practice, click to read the original!

Porting Real-Time Device Drivers to Linux Embedded Systems (Part 1)

Porting Real-Time Device Drivers to Linux Embedded Systems (Part 1)

Leave a Comment