Transplantation of RTOS Device Drivers to Embedded Linux

Publisher:SecretWhisperLatest update time:2012-04-22 Source: 21IC Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Linux has taken the embedded system market by storm. Analysts point out that about 1/3 to 1/2 of new 32/64-bit embedded system designs use Linux. Embedded Linux has shown its advantages in many application areas, such as SOHO home networks and imaging/multi-function peripherals. It has also achieved significant development in (NAS/SAN) storage, home digital entertainment (HDTV/PVR/DVR/STB), and handheld devices/wireless devices, especially digital mobile phones.

New embedded Linux applications do not emerge from the minds of developers out of thin air. Most projects consist of thousands or even millions of lines of code. Hundreds of embedded projects have successfully ported existing codes from other platforms to Linux, such as WindRiverVxWorks and pSOS, VRTX, Nucleus and other RTOS. These porting works have important value and practical significance.

So far, most of the literature on porting existing RTOS applications to embedded Linux focuses on RTOS interfaces (APIs), tasks, scheduling modes, and how to map them to the corresponding user space. Equally important is how to port RTOS hardware interface code to more standardized Linux device drivers in I/O call-intensive embedded programs.

This article will outline several common memory-mapped I/O methods that are often found in existing embedded applications. They range from special uses of interrupt service routines and user threads to hardware access to semi-standard driver models found in some ROTS. This will provide some inspiration for porting RTOS code to standardized Linux device launchers, and introduce some porting methods. In particular, this article will focus on memory mapping in RTOS and Linux, porting based on I/O dispatch queues, and redefining RTO I/O into drivers and daemons under Linux.

RTOSI/O Concept

"Unstandardized" is the best word to describe most RTOS system I/O. Most RTOS were designed for older CPUs without MMUs, so they ignored the memory management part, even when MMUs came out: no distinction between physical and logical addresses. Most RTOS also run entirely in privileged mode, although this ostensibly enhances performance. All RTOS application and system code have access to the entire address space, memory-mapped devices, and other I/O operations. This makes it difficult, if not impossible, to distinguish RTOS application code from driver code.

The non-standard structure leads to the peculiarities of I/O implementation. In many cases, there is a lack of recognition of the device driver model. In view of this non-hierarchical nature, it is very instructive to review some important concepts and idioms used in RTOS-based software.

Inline memory access

In the mid-1980s, when RTOS products were commercialized, most embedded software had a large loop with strict execution time requirements, using I/O queries and interrupt service routines. Developers adopted RTOS and execution programs in their projects mainly to enhance parallelism and multi-tasking synchronization, and to bypass other program structures that hindered the achievement of this goal. Thus, even though RTOS provided a formal method for I/O calls, embedded programmers continued to use direct I/O operations:

#defineDATA_REGISTER0xF00000F5

chargetchar(void){

return(*((char*)DATA_REGISTER));/*readfromport*/

}

void putchar(charc){

*((char*)DATA_REGISTER)=c;/*writetoport*/

}

Most trained developers will separate direct I/O code like this from hardware code, but I still see I/O calls like this all the time.

When starting to use direct memory mapped I/O, embedded developers new to Linux always want to move this code to user space and replace the #define statements that define register addresses with mmap() calls. This approach is OK for some prototypes, but it cannot support interrupt processing, limits real-time response, is particularly unsafe, and is not suitable for the release of commercial products.

RTOS interrupt service routine

In Linux, interrupt services belong to the kernel layer; in an RTOS, the interrupt service routine code has no special provisions and is often no different from the application code (except for the return sequence). Many RTOS provide system calls or macros to allow the code to detect its own switching state (such as WindRiverVxWorks's intContext()). Interrupt service routines also usually use standard library functions, which also have problems such as reentrancy and portability.

Most RTOS support registering interrupt service routine code, interrupt judgment and interrupt service call. Some simple embedded programs only support inserting the starting address of the interrupt service routine in the hardware vector table.

If you try to perform read and write operations directly in user program space, you will have to put the Linux interrupt service routine into kernel program space.

RTOSI/O Subsystem

Most RTOS will provide a custom standard C runtime library (such as pREPC for pSOS), or modify the compiler vendor's C library (libc) or modify glibc. At a minimum, most RTOS support a subset of standard C I/O (open/close/read/write/ioctl). In most cases, these calls and calls derived from them are converted into simple wrappers for basic I/O. Interestingly, because most RTOS do not support file systems, these platforms do not provide file storage for flash and other storage media, often using completely different code implementations or other application programming interfaces (APIs) (such as pHILE for pSOS).

Wind River VxWorks does better than other RTOS in this regard. It provides a feature-rich I/O subset and effectively and extensively integrates network interfaces and network media.

Delayed processing

Many RTOS also support a mechanism called "bottom half" to put I/O processing into an interruptible or preemptible context switch. Other RTOS provide similar mechanisms such as interrupt nesting to achieve the same effect.

I/O architecture of typical RTOS applications

The following describes a typical I/O diagram (input only) and the path it takes to pass data to the main application. The processing is as follows:

A hardware interrupt triggers the execution of an interrupt service routine.

The interrupt service routine does basic processing, completes local input operations, or lets the RTOS schedule delayed processing. In some cases, the delayed processing is handled by a user process in Linux, which is a normal RTOS task in this case.

When data is acquired (interrupt service routine or delayed switch), the prepared data is put into the queue (RTOS interrupt service routine can access application queues through application program interface (API) and other inter-process communication (IPC), see the API table below).

One or more application tasks read messages from the queue and retrieve data

Comparison of Typical I/O between Traditional RTOS and Linux

Output is often done by a similar mechanism - instead of a write() or similar system call, one or more RTOS tasks put data in a queue. The data in the queue is taken out by one of several processes: an I/O program or an interrupt service routine that responds to a "ready to send" interrupt, a system timer, or other application task that blocks on the data queue and then performs the I/O operation (either polling or via DMA).

Mapping RTOSI/O into Linux

The queue-based producer/consumer I/O model described above is just one of the special methods used in many traditional designs. Let's continue with this direct example to discuss several implementation methods under embedded Linux:

Large-scale porting to user space

For developers who are just getting started with Linux device driver design or have no experience, most of these queue-based programs may be ported to user space intact. In this driver mapping, the memory mapping can operate the physical I/O interface in user space through the pointer provided by the mmap() function.

#include

#defineREG_SIZE0x4/*deviceregistersize*/

#defineREG_OFFSET0xFA400000

/*physicaladdressofdevice*/

void*mem_ptr;/*de-referenceformemory-mappedaccess*/

intfd;

fd=open("/dev/mem", O_RDWR);/*openphysicalmemory(mustberoot)*/

mem_ptr=mmap((void*)0x0, REG_AREA_SIZE, PROT_READ+PROT_WRITE,

MAP_SHARED, fd, REG_OFFSET);

/*actualcalltommap()*/

A user thread under a process runs operations similar to an RTOS interrupt service routine or delayed task, and then uses the SVR4 inter-process communication function msgsnd() to put the message into the queue, waiting to be retrieved by another local thread or another process using the function msgrcv().

This fast and unskillful approach is a good prototype, but it also brings great challenges to code model building. First of all, it is important to scan interrupts in user space. Projects like DOSEMU provide signal-based I/O interrupt methods, but the interrupt processing process in user space is very slow (usually millisecond interrupt latency compared to tens of microsecond interrupt latency of kernel interrupt service routines). Furthermore, even with a preemptive Linux kernel and real-time scheduling strategy, user space switching scheduling cannot guarantee 100% timely execution of I/O threads.

Reference address:Transplantation of RTOS Device Drivers to Embedded Linux

Previous article:Ultra-low power wireless parking lot detection system based on MMC3282+EFM32
Next article:The embedded field of Linux operating system faces new challenges

Latest Industrial Control Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号