Operating system abstraction layer implementation method applied to drive development
By building an operating system abstraction layer between the kernel and the driver, encapsulating the kernel capabilities and providing a stable interface, the problem of old version drivers being incompatible on the new version of the kernel is solved, and the compatibility and stability of the drivers are achieved, reducing the need for recompilation and testing.
Patent Information
- Application Number
- CN202510245483.X
- Authority / Receiving Office
- CN · China
- Patent Type
- Applications(China)
- Current Assignee / Owner
- Filing Date
- 2025-03-04
- Publication Date
- 2025-04-01
- Estimated Expiration
- 2045-03-04
AI Technical Summary
The driver binary files compiled by the old version cannot run directly on the new version of the kernel, resulting in the driver recompilation and testing after each kernel upgrade to ensure compatibility, which increases the development workload of device manufacturers.
By building an operating system abstraction layer between the kernel and the driver, encapsulating kernel capabilities and providing stable interfaces, drivers can be written through these interfaces instead of directly calling the system's KAPI functions. The KABI of the abstraction layer remains stable, ensuring that the old version driver module can run on the new version system.
It realizes driver compatibility and stability, avoids the need for recompilation and testing after kernel upgrades, and reduces the development workload of device manufacturers.
Smart Images

Figure CN119739390A_ABST
Abstract
Description
Technical Field
[0001] The present invention relates to the field of kernel technology, and specifically provides an operating system abstraction layer implementation method applied to driver development. Background Art
[0002] A Linux kernel driver is a piece of code that can be loaded into the operating system kernel, and is usually used to manage hardware devices and provide an interface between the operating system and the hardware. These drivers provide the operating system with the ability to interact with hardware devices, allowing the operating system to identify, manage, and control hardware devices. In Linux systems, kernel drivers exist in the form of modules and can be dynamically loaded into or unloaded from the kernel. This modular design allows the Linux system to flexibly support a variety of hardware devices without having to recompile the entire kernel every time hardware is added or replaced.
[0003] KABI (Kernel Application Binary Interface) refers to the kernel application binary interface, which is a stable binary interface provided by the kernel to kernel module programs. Ensuring the stability of KABI is very important to ensure module compatibility when the kernel version is upgraded. KABI is designed to enable kernel modules to interact smoothly with the Linux kernel, especially when the kernel is upgraded. With the rapid iteration and upgrade of kernel versions, each version of KABI (Kernel ABI, i.e. kernel application binary interface) is not compatible. Since the driver binary file relies on the stable interface provided by KABI to interact with the kernel, this leads to a significant problem. The driver binary file compiled with the old version cannot be directly run on the new version of the kernel, but must be recompiled on the new kernel. Therefore, after each kernel upgrade, additional time and resources are required to recompile and test the driver to ensure its compatibility with the new kernel. This situation undoubtedly increases the development workload of device manufacturers. Summary of the invention
[0004] In order to overcome the above defects, the present invention is proposed to provide a technical solution to the incompatibility of driver binary files on different kernel versions.
[0005] The present invention provides an operating system abstraction layer implementation method for driver development, comprising the following steps: According to different basic capabilities, define corresponding abstract layer structures and abstract layer interface functions, or define corresponding abstract layer structures, abstract layer interface functions and abstract layer callback functions, encapsulate the basic capabilities of the operating system, provide stable interfaces and export them to the driver for use; Provide a common data structure, implement its operation methods, and export it to the driver for use; Implement an abstract layer device driver subsystem to provide a stable framework for writing drivers; Implement a platform peripheral driver set and provide it to other driver modules in the form of services or exported interfaces; Implement a generic user space driver access library and communicate with the driver through library functions.
[0006] Furthermore, the step of defining the abstract layer structure includes: Based on the basic capabilities, define an abstract layer structure named "OsalXXX", where Osal is a specified prefix and XXX is the representation of the current basic capabilities; In the abstract layer structure, define a pointer member variable realXXX of empty type, which is used to associate with a specific kernel structure instance in subsequent operations, where real is a specified prefix; Implement an abstract layer structure initialization function to associate the kernel structure with the abstract layer structure, and save the kernel structure variable as a pointer member variable of the abstract layer structure; Implement an abstract layer structure operation function. The first parameter of the function is the pointer variable of the abstract layer structure, and the following parameters correspond to the parameters of the specific kernel function. Implement an abstract layer structure destruction function to deinitialize the associated kernel structure and release memory.
[0007] Further, the implementation of an abstract layer structure initialization function for associating the kernel structure with the abstract layer structure and saving the kernel structure variable as a pointer member variable of the abstract layer structure also includes: When the basic capability involves multiple structure types or requires auxiliary variables, the related structure types and auxiliary variables are integrated into an intermediate structure, and then the instance of the intermediate structure type is saved in the pointer member variable of the abstract layer structure.
[0008] Further, the step of defining the abstract layer interface function includes: Use a unified naming convention to define abstract layer interface functions. The interface function name starts with Osal, and the function parameters are basic data types or abstract layer structure types. The function body implementation includes the unique logic of the abstract layer and calling the kernel function to implement specific operations; Export kernel symbols using EXPORT_SYMBOL.
[0009] Further, the step of defining the abstract layer callback function includes: Define an abstract layer callback function pointer type, which is used to drive the callback function registered with the abstract layer; Implement a transfer callback function, which is triggered when the system calls, and calls the corresponding abstract layer callback function inside the function; Implement the abstract layer callback registration function to register the transfer callback function into the system and bind the transfer callback function with the abstract layer callback function; Implement the abstract layer callback deregistration function to call the kernel's deregistration function and cancel the association between the transfer callback function and the abstract layer callback function.
[0010] Furthermore, the general data structure includes a linked list, a queue, a red-black tree and a general data buffer structure for data communication between the user space and the kernel space, and realizes the function of writing and reading basic data types to the buffer by providing a set of operation functions.
[0011] Furthermore, the abstract layer device driver subsystem provides registration service, deregistration service, event sending and message dispatching callback interface for driver development, and the abstract layer device driver subsystem provides binding service, unbinding service, message sending, event listener registration and event listener deregistration interface for application development. The driver is registered with the abstract layer device driver framework in the form of a service. The application establishes a connection with the driver development through the service name and sends a message to the driver service as a client. The driver processes the message and returns the result to the application.
[0012] Furthermore, the platform peripheral driver set includes GPIO, I2C, SPI, UART, ADC, DAC, RTC and DMA peripheral drivers. By registering a service for each driver, a set of operation function sets are defined according to different device types. The function set internally accesses the corresponding IO function of the kernel. Other device drivers obtain the corresponding operation function through the service interface and implement the IO operation of the underlying data by sending messages to the service.
[0013] Furthermore, the general user space driver access library encapsulates service binding, data sending and event monitoring functions into one library file, and the user space program uses the library function to send data to the server and register the listener.
[0014] Working principle and beneficial effects of the present invention: In the technical solution of the present invention, an operating system abstraction layer is constructed between the kernel and the driver, and the kernel capabilities required by the driver are encapsulated into a stable interface of KABI (Application Binary Interface). The driver is written by calling the interfaces provided by these abstract layers, rather than directly calling the system's KAPI function. When the kernel is upgraded, since the KABI of the abstract layer remains stable, the driver module compiled on the old version of the kernel can run directly on the new version of the system, thereby ensuring the compatibility and stability of the driver. The driver compatibility problem that may be caused by the kernel upgrade is effectively solved. BRIEF DESCRIPTION OF THE DRAWINGS
[0015] The disclosure of the present invention will become more easily understood with reference to the accompanying drawings. It is easy for those skilled in the art to understand that these drawings are only for illustrative purposes and are not intended to limit the scope of protection of the present invention. In addition, similar numbers in the figures are used to represent similar components, among which: Figure 1 It is a schematic diagram of the structure of the interaction of the abstract layer in the operating system in the present invention; Figure 2 It is a schematic diagram of the process of realizing the abstract layer structure at the abstract layer in the present invention; Figure 3 It is a schematic diagram of a flow chart of realizing an abstract layer interface function at an abstract layer in the present invention; Figure 4 It is a flow chart of realizing the abstract layer callback function in the abstract layer in the present invention. DETAILED DESCRIPTION
[0016] Some embodiments of the present invention are described below with reference to the accompanying drawings. It should be understood by those skilled in the art that these embodiments are only used to explain the technical principles of the present invention and are not intended to limit the protection scope of the present invention.
[0017] In the present invention, the main reason for the instability of KABI is that the exported kernel structure is too complex, and the use rights of most members of these structures are in the kernel, not in the driver. As the kernel functions are iteratively upgraded, these structure members will change (add and delete some member variables), and these changes will often only affect the kernel, but not the driver, because the driver will not use the member variables added or deleted by the changed structure, but at this time KABI has changed, causing the driver to have to recompile. The method to avoid KABI changes is to export a more streamlined structure as much as possible, and the member variables that only the kernel needs to use do not need to be exported to the driver for use. Therefore, the main design idea of the system abstraction layer is not to use the kernel structure outside the function (including as a function parameter), and to use function implementation when the structure members need to be operated.
[0018] The operating system abstraction layer implementation method for driver development in this embodiment is intended to enable the driver binary file to work normally on different kernel versions, and mainly includes the following steps S10 to S50. Figure 1 Schematic diagram of the structure of the abstract layer in the operating system in the present invention. After executing steps S10-S50, the abstract layer is implemented as follows Figure 1 shown.
[0019] Step S10: According to different basic capabilities, the operating system abstraction layer defines corresponding abstraction layer structures and abstraction layer interface functions, or defines corresponding abstraction layer structures, abstraction layer interface functions and abstraction layer callback functions, encapsulates the basic capabilities of the operating system, provides a stable interface, and exports it to the driver (i.e. Figure 1 The "driver" in the following text also corresponds to Figure 1 The encapsulated abstract layer structure, abstract layer interface function and abstract layer callback function correspond to Figure 1 “System Basic Capabilities”.
[0020] In one implementation, the basic capabilities of the operating system include thread usage, process synchronization, memory usage, interrupt usage, IO mapping, file access, and timer usage.
[0021] Among them, thread use includes thread creation, thread binding (thread binding to a specified CPU core), thread start and thread destruction. Process synchronization includes mutex locks, spin locks, semaphores and work queues. Memory use includes memory allocation and memory recycling. Interrupt use includes interrupt mapping, interrupt application, interrupt deregistration, interrupt enable and interrupt disable. IO mapping includes mapping IO addresses to virtual address space, unmapping, reading IO addresses and writing IO addresses. File access includes opening files, reading file contents, writing data to files, moving read and write positions and closing files. Timer use includes creating timers, starting timers and destroying timers.
[0022] In this embodiment, not all basic capabilities need to define three objects. The abstract layer structure and abstract layer interface function are required, and the abstract layer callback function is optional according to the basic capability needs. In other words, each basic capability corresponds to a function of the kernel. Around this function, the kernel will define a series of structures and functions to support the basic capabilities of the operating system, and the abstract layer will hide the implementation details of the kernel structure. Taking thread use as an example, you only need to create a structure, create, bind, start and destroy the corresponding abstract layer interface function and abstract layer callback function.
[0023] In one embodiment, if Figure 2As shown, for each basic capability, follow the steps below to build the abstract layer structure: 1.1 Based on the basic capabilities, define an abstract layer structure named "OsalXXX", where Osal is the specified prefix and XXX is the representation of the current basic capabilities. For example, for timer use, the abstract layer structure is named OsalTimer.
[0024] 1.2 In the abstract layer structure, define a pointer member variable realXXX of empty type, which is used to associate with a specific kernel structure instance in subsequent operations, where real is a specified prefix. The driver does not care what type this pointer member variable points to, because the instantiation and use of the actual type are completed through specific functions. Take the abstract layer structure type named OsalTimer as an example. It only contains a pointer member variable named realTimer, whose type is void. .
[0025] Among them, a pointer represents the address of a variable in memory, and a pointer member variable represents a member of a structure type, which is a pointer type. For example: int a, int b; a is an integer variable, which means it stores an integer. b is an integer pointer variable, which means it stores a memory address. Define a structure A as struct A{int a;int b;}, it contains two member variables, a is an integer member variable, b is an integer pointer member variable. struct A aa, struct A bb; aa is structure A type, bb is a pointer to structure A type.
[0026] 1.3 Implement an abstract layer structure initialization function OsalXXXCreate, which is used to associate the kernel structure and the abstract layer structure, and save the kernel structure variable as a pointer member variable of the abstract layer structure.
[0027] Taking the timer as an example, the corresponding initialization function is named OsalTimerCreate. The first parameter of the initialization function is the pointer type of the abstract layer structure OsalTimer. The realTimer pointer member variable is taken out through the first parameter in the initialization function. The goal of this initialization function is to associate the realTimer pointer member variable with the timer kernel structure.
[0028] In one implementation, when the kernel capability corresponding to the basic capability involves multiple structure types or requires auxiliary variables, the related structure types and auxiliary variables need to be integrated into an intermediate structure, and then the instance of the intermediate structure type is saved in the pointer member variable of the abstract layer structure (corresponding to Figure 2 "Instantiating a void pointer into a concrete kernel structure" in the .
[0029] The encapsulation of the timer belongs to the case mentioned above that requires auxiliary variables. It is necessary to define an intermediate structure to integrate the core structure type and auxiliary variables related to the timer. This intermediate structure type is named osal_timer, and it contains the following members: (1) A timer_list kernel structure variable. In the kernel, timers are represented by the timer_list structure, which is affected by the system KABI; (2) An integer variable used to store the timeout period; (3) A pointer to a timer timeout handling function.
[0030] In the OsalTimerCreate function, a pointer variable of the osal_timer intermediate structure type is first created, and memory is allocated for it through the dynamic memory allocation function. The members of the osal_timer intermediate structure (the above three) are initialized through the parameters of OsalTimerCreate, and then the pointer variable is forced to be converted into a null pointer type, and finally saved to the realTimer pointer member variable of the first parameter of the OsalTimerCreate function (OsalTimer structure type variable). After abstraction, OsalTimer defines realTimer as a null pointer member variable, so it does not expose the kernel structure. OsalTimerCreate uses the pointer of the OsalTimer abstract layer structure as a parameter to hide the call of the kernel function inside the function, and also does not expose the kernel function, so it will not be affected by the system KABI.
[0031] 1.4 Implement an abstract layer structure operation function, which is used to implement the specific functions of the kernel structure. The first parameter of the function is the pointer member variable of the abstract layer structure defined above, and the following parameters correspond to the parameters of the specific kernel function. These parameters must be basic types or structure types defined in the abstract layer. First, take out the empty type pointer member variable from the first parameter, and then force it to be converted into the corresponding kernel structure object (corresponding to Figure 2"In the operation function, the kernel structure is retrieved through the null pointer of the abstract layer structure"), and then according to the function to be implemented, the kernel structure object is used as a parameter to call the specific kernel function for implementation (corresponding to Figure 2 "Calling kernel API functions as parameters to implement specific functions").
[0032] Taking the timer as an example, the operation function for starting the timer can be defined as OsalTimerStart. The member variable realTimer is taken out through the pointer of the first parameter OsalTimer, and it is forced to be converted into the osal_timer intermediate structure pointer type (the kernel structure is stored inside the osal_timer intermediate structure, and the timer requires auxiliary variables, so realTimer does not directly point to the corresponding kernel structure timer_list, but the custom intermediate structure osal_timer), and then the pointer member variable of the timer_list structure type is taken out, and the add_timer kernel function is called, using this timer_list structure type pointer variable as a parameter to start the kernel timer.
[0033] 1.5 Implement an abstract layer structure destruction function OsalXXXDelete. In the destruction function, it is necessary to deinitialize the associated kernel structure and release the memory requested during initialization.
[0034] For the abstract layer timer destruction function, its first parameter is the pointer member variable of the abstract layer structure defined above, and the following parameters correspond to the parameters of the specific kernel function. These parameters must be basic types or structure types defined in the abstract layer. Taking the timer as an example, the timer destruction function is named OsalTimerDelete. It takes out the realTimer pointer member variable through the first parameter OsalTimer pointer, takes out the member of the timer_list kernel structure type through realTimer, and then calls the kernel function to deinitialize timer_list.
[0035] In one embodiment, if Figure 3 As shown, the steps to define the abstract layer interface function in the operating system abstract layer are as follows: 2.1 Use a unified naming convention to define abstract layer interface functions. Function names are prefixed with Osal, and function parameters are basic data types (basic data types in C language, such as integer, character, and floating point) or abstract layer structure types. This is to prevent KABI from affecting abstract layer interface functions. For example, the interface function for dynamically applying for memory is void. OsalMemAlloc(size_t size); In this embodiment, the abstract layer interface function and the abstract layer structure jointly implement the basic functions of a kernel, which is similar to object-oriented programming. The abstract layer structure is equivalent to the attribute, and the abstract layer interface function is equivalent to the method. Since the C language does not have the concept of class, the first parameter of the abstract layer interface function must be the pointer type of the corresponding abstract layer structure. The abstract layer can be understood as a Java virtual machine. The structure and interface function exposed by the abstract layer are equivalent to bytecode, and the kernel structure and function are equivalent to machine code. The kernel structure and function may change during the version iteration process, resulting in changes in KABI, but the structure and function of the abstract layer will not change with the kernel version. It only needs to be "translated" into a new kernel structure and function when the version is upgraded.
[0036] 2.2 Function body implementation. Specifically, it includes two parts. One part is to implement the unique logic of the abstract layer. The unique logic refers to the logic that is unrelated to calling kernel functions, that is, the logic code that is unrelated to kernel functions. For example, when allocating memory, different kernel functions can be called to allocate according to the size of the memory to be allocated. Some memory statistics functions can also be added. The allocated and released memory can be recorded in a global data structure as a reference data for subsequent software debugging. The other part is to call the kernel function to implement specific operations (corresponding to Figure 3 In the "Call kernel function to implement interface functions"), for example, when applying for memory, kmalloc or vmalloc is called to allocate memory. In this way, additional logic of the operating system abstraction layer can be added when allocating and releasing memory, and kmalloc or vmalloc can be called according to the memory size. More importantly, the driver will not call kmalloc and vmalloc functions directly. During the kernel version upgrade process, if the parameters or function names of the corresponding kernel functions change, only the implementation part of the abstraction layer function needs to be modified, and the external interface remains unchanged.
[0037] 2.3 Use EXPORT_SYMBOL to export kernel symbols so that the driver can call abstract layer functions when compiled as a module.
[0038] This is an operation of the kernel. If a function needs to be used by an external module, it needs to use the EXPORT_SYMBOL macro to export the symbol. The abstract layer code will be compiled together with the kernel code, and the driver will be compiled as a module. If the driver needs to call the abstract layer function, it needs to export the abstract layer function symbol.
[0039] In one embodiment, if Figure 4 The figure shows the steps of defining the abstract layer callback function in the operating system abstract layer.
[0040] It should be noted in advance that not all kernel functions require callback functions. The difference between the abstract layer callback function and the abstract layer interface function is that the abstract layer interface function is actively called by the user program. The kernel does not know the abstract layer callback function type and can only recognize the callback function type defined in the kernel. The kernel callback function type cannot be directly exposed to the driver. Therefore, it is necessary to implement a transit callback function. The transit callback function is a type that the kernel can recognize, and then call the corresponding abstract layer callback function inside the function.
[0041] like Figure 4 As shown, the steps of defining the abstract layer callback function in the operating system abstract layer include: 3.1 Define an abstract layer callback function pointer type. This pointer type is used by the driver to register the callback function with the abstract layer. The parameters and return values of this function pointer type do not contain the kernel structure type. For example, the kernel timer timeout can be defined as: typedef void ( OsalTimerFunc)(uintptr_t arg); 3.2 Implement a transfer callback function, which is a type that the kernel can recognize. It is used to trigger when the system is called and call the corresponding abstract layer callback function inside the function. The parameters of the transfer callback function and the abstract layer callback function are different. The transfer callback function contains parameters of the kernel structure data type. These parameters need to be processed or converted into basic types inside the transfer callback function. Get the abstract layer callback function pointer associated with the transfer callback function and initiate a function call (see 3.3 for the association method). These parameters will be passed to the abstract layer callback function. For example, the kernel timer timeout callback uses the timer_setup (refer to kernel-6.6) function to set the callback function. The callback function type is: void ( func)(struct timer_list ).
[0042] Obviously, it is risky if the driver directly uses the func type above to define the callback function, because its parameter is of the timer_list type, which is a kernel structure type. Therefore, the callback function of the func type only serves as a transfer function, responsible for triggering the system call, and then calling the abstract layer callback function defined above inside the transfer callback function. Note that the parameters of the transfer callback function and the abstract layer callback function are different. The abstract layer callback function instance is implemented by a specific driver, so its parameters cannot contain kernel structure types.
[0043] 3.3 Implement the abstract layer callback registration function, register the transfer callback function to the system, and bind the transfer callback function to the abstract layer callback function, so that when the transfer callback function is called, the corresponding abstract layer callback function pointer can be found. The binding method can be achieved by embedding the transfer callback function and the abstract layer callback function into a larger structure, and the structure instance is associated with the members of the corresponding abstract layer structure.
[0044] For the timer, the timer_setup function is called to set the transfer callback function. The first parameter of the transfer callback function is the timer_list type. The callback function type defined by the abstract layer is OsalTimerFunc. The timer_list and OsalTimerFunc can be embedded in a larger structure. The instance of this structure is associated with the realTimer member of OsalTimer. In this way, the pointer to the large structure can be found in the transfer callback function according to the formal parameter timer_list, and then the function pointer of the OsalTimerFunc type can be taken out.
[0045] 3.4 Implement the abstract layer callback deregistration function, call the kernel's deregistration function when necessary, and cancel the association between the transit callback function and the abstract layer callback function.
[0046] Step S20: Provide a general data structure, implement its operation method, and export it to the driver for use. This part corresponds to Figure 1 The functions in the "Common Data Structures" section.
[0047] In this embodiment, the driver is a piece of code working in the kernel space, and needs to assume the following functions: communicate with the user space, communicate with the hardware, and communicate with the kernel. To communicate with the user space, it is necessary to use the abstract layer device driver subsystem, and to communicate with the hardware and the kernel, it is necessary to use the basic capabilities of the operating system, and the basic capabilities of the operating system need to be encapsulated using the above-mentioned abstract layer structure, abstract layer interface function, and abstract layer callback function encapsulation method. The general data structure is a data structure provided by the kernel to speed up the development of programs. This part has nothing to do with the kernel function, but is a more basic "toolkit", such as linked lists, queues, red-black trees, etc. The reason why it is a general data structure is that it can be applied to more than just the kernel.
[0048] In driver development, it is inevitable to use some kernel data structures. These data structures are widely used and powerful, but they may be affected by KABI (Kernel ABI, i.e. kernel application binary interface). Therefore, in order to achieve better compatibility and stability, it is necessary to implement a set of data structures used internally in the abstract layer to replace the kernel data structures. Among them, the linked list is the most important part. In addition, for data communication between user space and kernel space, it is necessary to define a common data buffer structure and provide a set of operation functions. This set of functions should be able to implement the functions of writing basic data types to the buffer and reading basic data types from the buffer. These basic data types include integers of different lengths, characters, strings, arrays, and floating-point types. Through such a design, data interaction between user space and kernel space can be easily realized. Step S30: Implement an abstract layer device driver subsystem to provide a stable framework for writing driver programs.
[0049] The driver is the link between the user program and the hardware. Taking character devices as an example, traditional driver development requires providing a file_operations structure instance to the kernel. The user space uses the virtual file system to initiate IO operations through system calls, and a function in the corresponding file_operations will be called, thus establishing a channel for information transmission between the user space and the kernel space. This process is closely related to KABI. The driver registration function and the operation function set are affected by KABI. For example, most operation functions use the file structure as the first parameter. The file structure will be affected by the kernel configuration, causing KABI to change, which will cause the driver binary file to be unable to run on kernel images with different configurations. Therefore, it is necessary to design an abstract layer device driver subsystem to shield the implementation details of driver registration related functions and file operation functions.
[0050] The abstract layer device driver subsystem is a new driver development framework, which is implemented based on the character device driver framework at the bottom layer. The driver registers with the abstract layer device driver framework as a service. The application establishes a connection with the driver service through the name of the service. As a client, the application can send messages to the driver service. After the driver receives the message and performs corresponding processing, it needs to reply the processing result to the client. At the same time, the abstract layer device driver framework provides an event subscription mechanism. The client can subscribe to the events of interest. When the driver detects the occurrence of the corresponding event, it will notify all clients that have subscribed to this event. When developing drivers using this framework, there is no need to use the registration and deregistration functions of character devices, nor to implement the set of operation functions in file_operations, thus avoiding the impact of KABI. The abstract layer device driver subsystem provides the following interfaces for driver development: registering services, deregistering services, sending events, and message dispatching callbacks (corresponding to Figure 1 The abstract layer device driver subsystem provides the following interfaces for application development: bind service (corresponding to the open system call), unbind service (corresponding to the close system call), send message (corresponding to the ioctl KOS_WRITE_READ command), register event listener (corresponding to the poll and ioctl KOS_LISTEN_EVENT_START command), and unregister event listener (corresponding to the ioctl KOS_LISTEN_EVENT_STOP command).
[0051] The following briefly describes the implementation details of the abstract layer device driver subsystem.
[0052] Register service interface: receive two parameters, one is the service name, the other is the service message processing function set. Use the service name as the device name, register a character device, implement the open\release\ioctl\poll operation function. And create an IDeviceObject structure to record the information related to this service, including: service name, message processing function set, associated character device, client list. Finally, return the IDeviceObject structure pointer.
[0053] Unregister service interface: Receives a parameter, which is a pointer to the IDeviceObject structure. Takes out the associated character device through the IDeviceObject structure, calls the character device unregister function in the kernel to unregister the character device, and releases the memory occupied by the IDeviceObject structure and its members.
[0054] Message dispatch callback interface: This interface is a set of callback functions, which are called by the abstract layer device driver subsystem. When registering a service, the second parameter is the service message processing function set. This function set is saved in a structure named IDeviceIoService, whose members include: Open function, Dispatch function, Release function. These functions are the interface for message dispatch, which are implemented by the specific driver.
[0055] When the character device corresponding to the service is opened, the open function in file_operations will be called first. This open function is implemented in the abstract layer device driver subsystem. Its implementation logic is as follows: First, create an IDeviceIoClient structure to represent a connected client. The member variables of this structure mainly include: the IDeviceObject structure pointer of the corresponding service, the client's pending event list, and private data. Then save the IDeviceIoClient structure pointer to the client list in the IDeviceObject structure, then take out the IDeviceIoService structure from the IDeviceObject structure, and call the IDeviceIoService structure Open function (Open is a function type, which is initialized to a specific function in the driver. Open can be used to call the Open function in the driver.) The corresponding driver's Open function will be called, and the IDeviceIoClient structure pointer will be passed to the driver as a parameter. The driver can call the operating system basic capabilities and platform peripheral drivers encapsulated in the abstract layer in the Open function to implement some logic for hardware initialization. Finally, save the IDeviceIoClient structure pointer to the private variable of the file structure associated with the character device.
[0056] When the character device corresponding to the service is closed, the release function in file_operations will be called first. This release function is implemented in the abstract layer device driver subsystem. Its implementation logic is as follows: first, take out the private variable from the file structure associated with the character device and convert it into the IDeviceIoClient structure pointer type. Then, get the IDeviceObject structure pointer through the members of the IDeviceIoClient structure. Then, take out the IDeviceIoService structure from the IDeviceObject structure and call its member function Release. The Release function in the corresponding driver will be called, and the IDeviceIoClient structure pointer will be passed to the driver as a parameter. The driver can call the basic operating system capabilities and platform peripheral drivers encapsulated by the abstract layer in the Release function to implement hardware sleep, power-off and other logic.
[0057] The application sends a message to the driver by sending a command to the character device associated with the service through ioctl. The corresponding message sending command is KOS_WRITE_READ. The abstract layer device driver subsystem will process this command and send the data carried by the command to the service Dispatch function in the form of a message. The specific logic is as follows: first, take out the private variable from the file structure associated with the character device, convert it into the IDeviceIoClient structure pointer type, then obtain the IDeviceObject structure pointer through the member of IDeviceIoClient, then take out IDeviceIoService from IDeviceObject, call its Dispatch function, the corresponding driver Dispatch function will be called, and the IDeviceIoClient structure pointer will be passed to the driver as a parameter, and the user space parameters carried by ioctl will be copied to the kernel space and passed to the driver as the Dispatch parameter. The driver can call the operating system basic capabilities and platform peripheral drivers encapsulated by the abstract layer in the Dispatch function to implement the specific operation logic of the hardware. After the processing is completed, the processing result will be written to a buffer and then copied to the user space memory area pointed to by the ioctl parameter.
[0058] Send event interface: There are three parameters. The first parameter is the IDeviceObject structure pointer, the second parameter is the event ID, and the third parameter is the content of the event. When the driver needs to actively send information to the application, such as the timer expires, the interrupt comes, etc., the application can be notified through the event mechanism. For the client that needs to receive the event, it is necessary to first register the event listener with the service, send the KOS_LISTEN_EVENT_START command through ioctl, and the corresponding IDeviceIoClient structure will record that this client is in the listening state. Then the application uses the poll system call to monitor whether the character device corresponding to the service has data to read. When the driver detects that an event has occurred, it will call the event sending interface to send the event. The so-called event is a piece of data stored in the memory. The first parameter of the event sending interface is the IDeviceObject structure pointer type, and the parameter is the return value of the registered service. Through IDeviceObject, you can get the list of connected clients, and then add the event ID (defined by the user to distinguish different events) and the event content to the event list of the client that has entered the listening state. Finally, wake up the user process in the poll state. At this time, the application can send the KOS_READ_DEV_EVENT command through ioctl to take out the event from the event list of the client IDeviceIoClient structure and copy it to the user space. When an application opens the character device file node corresponding to a service through open, the Open function in the service will be called. When an application closes the character device file node corresponding to a service through close, the Release function in the service will be called. The Open function is called to indicate that a new client is connecting, and the Release function is called to indicate that a client is disconnecting. In addition to initializing and deinitializing the hardware, the Open and Realse functions can also process the client's private data. The client's private data can be initialized in Open and deinitialized in Release. For example: a driver needs to manage several memory blocks. When client A connects, memory block 1 is allocated to A for use. When client B connects, memory block 2 is allocated to B for use. The address of the memory block can be used as the client's private data and saved in the private member of IDeviceIoClient. When the client communicates with the service later, it can obtain the IDeviceIoClient structure pointer corresponding to the client from the first parameter of the Dispatch function to obtain the client's private data and finally know which memory block needs to be processed.
[0059] In addition, an error can be returned in the driver's Open to reject the client's connection. For example, many hardware devices are exclusive, such as printers, which only allow one user at a time. So when the second client initiates a connection, its connection needs to be rejected.
[0060] The driver service business in the kernel space is carried by the message processing function. When the driver is initialized, a service entity is registered with the abstract layer device driver framework, and an instance of the message processing function is carried. When the user space sends a message to the service, the service in the kernel space will receive the message and perform corresponding business processing according to the content of the message. After the processing is completed, it will send the result to the client in the user space in the form of a reply message. If there are interrupts or scheduled tasks in the driver, and a message needs to be sent to the user space in the interrupt service function or task processing function, the user space can be actively notified through the event reporting channel. In addition, if different drivers in the kernel space need to access each other, in addition to direct function calls, they can also find the corresponding services through the service management system and send messages to the services to achieve mutual access between driver functions. This method provides a more flexible and decoupled communication mechanism between drivers. Step S40: Implement the platform peripheral driver set and provide it to other driver modules in the form of services or export interfaces. Figure 1 The functions of the "Platform Driver" section in .
[0061] The platform driver is equivalent to a development library and may be used by many other device drivers. For example, driver A is a sensor that uses I2C communication, and driver B is a sound card that also needs to use I2C communication. If both driver A and driver B implement the I2C communication function internally, a lot of duplicate code will be added, so it is necessary to implement this I2C driver in advance for all drivers to use. On the other hand, the platform driver can be regarded as part of the abstract layer. The driver can reference kernel structures and functions internally without worrying about being affected by KABI. It only needs to ensure that a unified interface is exposed to the outside. If the platform driver is implemented outside the abstract layer, all kernel structures and function calls need to be replaced with the structures and functions of the abstract layer. This is a huge workload, which is equivalent to re-implementing the kernel function, because it is wise to encapsulate from a higher dimension.
[0062] These platform peripheral drivers cover various types such as GPIO, I2C, SPI, UART, ADC, DAC, RTC, DMA, etc. They are on-chip peripheral drivers of SOC, usually used to provide services for other modules of the kernel and implement IO operations with peripheral modules. In order to improve the reusability of the code, this part of the driver is pre-written. In this way, other modules can easily use these drivers by simply calling the corresponding interfaces. The encapsulation of the platform driver is based on the original kernel driver framework. Specifically, a service is registered for each driver. In the service, we define a set of operation function sets according to different device types. These function sets will access the corresponding IO functions of the kernel. When writing other device drivers, you can get the corresponding operation functions through the service interface. Then, by sending commands to the service, you can implement the IO operations of the underlying data. This design makes the use of the driver more flexible and convenient.
[0063] Taking the most widely used GPIO driver as an example, the implementation principle of the platform driver is explained. The GPIO platform peripheral driver needs to implement the following interface functions: GpioRead, GpioWrite, GpioGetDir, GpioSetDir, GpioSetIrq, GpioUnsetIrq, GpioEnableIrq, GpioDisableIrq.
[0064] GpioRead: reads the GPIO level status, receives a parameter, the GPIO number, and returns the current GPIO pin level status, 1 represents high level, 0 represents low level. The function calls the gpio_get_value_cansleep kernel function internally, reads the GPIO related registers, and obtains the GPIO input level status.
[0065] GpioWrite: Sets the GPIO level state, receives two parameters, the first parameter is the GPIO number, and the second parameter is the level state. When the second parameter is 1, it indicates a high level output, and when it is 0, it indicates a low level output. The function calls the gpio_set_value_cansleep kernel function to set the GPIO related registers to achieve output level control.
[0066] GpioGetDir: Get the GPIO IO direction, receive a parameter, the GPIO number, return the IO direction of the current GPIO pin, return 1 for output mode, return 0 for input mode. The function calls the gpiod_get_direction kernel function internally, reads the GPIO related registers, and obtains the IO direction.
[0067] GpioSetDir: Set the IO direction of GPIO, receive two parameters, the first parameter is the GPIO number, the second parameter is the IO direction. When the second parameter is set to 1, it means that it is set to output mode, call the kernel function gpio_direction_output to set the GPIO to output mode; when the second parameter is set to 0, it means that it is set to input mode, call the kernel function gpio_direction_input to set the GPIO to input mode.
[0068] GpioSetIrq: Register GPIO interrupt, receive four parameters, the first parameter is the GPIO number, the second parameter is the interrupt mode, the third parameter is the pointer to the interrupt processing function, and the fourth parameter will be passed to the interrupt processing function for processing. First, call the kernel function gpio_to_irq to obtain the corresponding interrupt number through the GPIO number, and then call the kernel function request_irq to apply for the interrupt according to the interrupt number and interrupt mode. The interrupt service function set when applying is just a transfer function named LinuxGpioIrq. The last parameter of the request_irq function is used to pass the parameters to the interrupt service program. Therefore, the third and fourth parameters of the GpioSetIrq function can be packaged and passed to LinuxGpioIrq for processing through this parameter. Enter the LinuxGpioIrq function, first obtain the interrupt processing function set by GpioSetIrq and the parameters that need to be processed by the interrupt service program, and then call the interrupt processing function and pass it the parameters just obtained to process the interrupt transaction.
[0069] GpioUnsetIrq: cancels the GPIO interrupt, receives a parameter, the GPIO number. First, call the kernel function gpio_to_irq to obtain the corresponding interrupt number through the GPIO number, and then call the kernel function free_irq to cancel the interrupt according to the interrupt number.
[0070] GpioEnableIrq: Enable GPIO interrupt, receive a parameter, GPIO number. First call the kernel function gpio_to_irq to get the corresponding interrupt number through the GPIO number, and then call the kernel function enable_irq according to the interrupt number to enable the corresponding interrupt.
[0071] GpioDisableIrq: Disable GPIO interrupts, receive a parameter, the GPIO number. First call the kernel function gpio_to_irq to get the corresponding interrupt number through the GPIO number, and then call the kernel function disable_irq_nosync according to the interrupt number to disable the corresponding interrupt. Step S50: Use of the general user space driver access library. The driver access library is part of the application.
[0072] In the user space program, use the library function to bind the service, call the library function to send data and register the event listener. For example, call the BindService function (the parameter is the service name, the character device corresponding to the service is opened internally through the open system call, and the file descriptor is encapsulated into a service object and returned.) to bind to the required driver service, use the SendData function (the parameters are the service object and the message buffer. The ioctl system call is called internally, the KOS_WRITE_READ command is used, the message buffer is used as a parameter, the message is sent to the service, and the service's Dispath function will be called.) to send data, call the RegisterEventListener function (the parameters are the service object and the event callback function, the ioctl system call is called internally, the KOS_LISTEN_EVENT_START command is used to notify the service to mark that the client needs to receive event notifications, and then start a thread, call the poll system call in the thread, and listen to the read status of the character device corresponding to the service. When the service actively reports an event, poll will wake up from waiting and sleeping, read the event content from the service through the KOS_READ_DEV_EVENT ioctl command, and then call the registered event callback function to handle the event.) Register an event listener. In this way, when a user space program acts as a client, it can easily send data to the server and register a listener to listen to events reported by the server. This encapsulation measure greatly simplifies the steps of writing applications.
[0073] Based on the above steps S10-S50, an operating system abstraction layer is constructed between the kernel and the driver, and the kernel capabilities required by the driver are encapsulated into a stable interface of KABI (Application Binary Interface). The driver is written by calling the interfaces provided by these abstract layers instead of directly calling the system's KAPI functions. When the kernel is upgraded, since the KABI of the abstract layer remains stable, the driver module compiled on the old version of the kernel can run directly on the new version of the system, thereby ensuring the compatibility and stability of the driver. This effectively solves the driver compatibility issues that may be caused by kernel upgrades.
[0074] It should be pointed out that although the various steps in the above embodiments are described in a specific order, those skilled in the art can understand that in order to achieve the effects of the present invention, different steps do not have to be performed in such an order, and they can be performed simultaneously (in parallel) or in other orders. These changes are within the scope of protection of the present invention.
[0075] So far, the technical solutions of the present invention have been described in conjunction with the preferred embodiments shown in the accompanying drawings. However, it is easy for those skilled in the art to understand that the protection scope of the present invention is obviously not limited to these specific embodiments. Without departing from the principle of the present invention, those skilled in the art can make equivalent changes or substitutions to the relevant technical features, and the technical solutions after these changes or substitutions will fall within the protection scope of the present invention.
Claims
1. A method for implementing an operating system abstraction layer for driver development, characterized in that: The following steps are involved: According to different basic capabilities, define corresponding abstract layer structures and abstract layer interface functions, or define corresponding abstract layer structures, abstract layer interface functions and abstract layer callback functions, encapsulate the basic capabilities of the operating system, provide stable interfaces and export them to the driver for use; Provide a common data structure, implement its operation methods, and export it to the driver for use; Implement an abstract layer device driver subsystem to provide a stable framework for writing drivers; Implement a platform peripheral driver set and provide it to other driver modules in the form of services or exported interfaces; Implement a generic user space driver access library and communicate with the driver through library functions.
2. The method for implementing an operating system abstraction layer according to claim 1, characterized in that: The steps of defining the abstract layer structure include: Based on the basic capabilities, define an abstract layer structure named "OsalXXX", where Osal is a specified prefix and XXX represents the current basic capabilities; In the abstract layer structure, define a pointer member variable realXXX of empty type, which is used to associate with a specific kernel structure instance in subsequent operations, where real is a specified prefix; Implement an abstract layer structure initialization function to associate the kernel structure with the abstract layer structure, and save the kernel structure variable as a pointer member variable of the abstract layer structure; Implement an abstract layer structure operation function. The first parameter of the function is the pointer variable of the abstract layer structure, and the following parameters correspond to the parameters of the specific kernel function. Implement an abstract layer structure destruction function to deinitialize the associated kernel structure and release memory.
3. The method for implementing an operating system abstraction layer according to claim 2, characterized in that: The implementation of an abstract layer structure initialization function for associating the kernel structure with the abstract layer structure and saving the kernel structure variable as a pointer member variable of the abstract layer structure also includes: When the basic capability involves multiple structure types or requires auxiliary variables, the related structure types and auxiliary variables are integrated into an intermediate structure, and then the instance of the intermediate structure type is saved in the pointer member variable of the abstract layer structure.
4. The method for implementing an operating system abstraction layer according to claim 1, characterized in that: The steps of defining the abstract layer interface function include: Use a unified naming convention to define abstract layer interface functions. The interface function name starts with Osal, and the function parameters are basic data types or abstract layer structure types. The function body implementation includes the unique logic of the abstract layer and calling the kernel function to implement specific operations; Export kernel symbols using EXPORT_SYMBOL.
5. The method for implementing an operating system abstraction layer according to claim 1, characterized in that: The steps of defining the abstract layer callback function include: Define an abstract layer callback function pointer type, which is used to drive the callback function registered with the abstract layer; Implement a transfer callback function, which is triggered when the system calls, and calls the corresponding abstract layer callback function inside the function; Implement the abstract layer callback registration function to register the transfer callback function into the system and bind the transfer callback function with the abstract layer callback function; Implement the abstract layer callback deregistration function to call the kernel's deregistration function and cancel the association between the transfer callback function and the abstract layer callback function.
6. The method for implementing an operating system abstraction layer according to claim 1, characterized in that: The general data structure includes a linked list, a queue, a red-black tree and a general data buffer structure used for data communication between the user space and the kernel space, and realizes the function of writing and reading basic data types to the buffer by providing a group of operation functions.
7. The method for implementing an operating system abstraction layer according to claim 1, characterized in that: The abstract layer device driver subsystem provides registration service, deregistration service, event sending and message dispatching callback interface for driver development, and the abstract layer device driver subsystem provides binding service, unbinding service, message sending, event listener registration and event listener deregistration interface for application development; The driver is registered with the abstract layer device driver framework in the form of a service. The application establishes a connection with the driver development through the service name and sends a message to the driver service as a client. The driver processes the message and returns the result to the application.
8. The method for implementing an operating system abstraction layer according to claim 1, characterized in that: The platform peripheral driver set includes GPIO, I2C, SPI, UART, ADC, DAC, RTC and DMA peripheral drivers. By registering a service for each driver, a set of operation function sets are defined according to different device types. The function set internally accesses the corresponding IO function of the kernel. Other device drivers obtain the corresponding operation function through the service interface and implement the IO operation of the underlying data by sending messages to the service.
9. The method for implementing an operating system abstraction layer according to claim 1, characterized in that: The general user space driver access library encapsulates the service binding, data sending and event monitoring functions into one library file, and the user space program uses the library function to send data to the server and register the listener.
Citation Information
Patent Citations
Abstracting operating environment from operating system
CN101627377A
Satellite-borne driving framework design method based on real-time operating system
CN111651144A
Hardware equipment driving system based on microkernel and driving method thereof
CN113268275A
Software architecture and hardware architecture for forming numerical control system
CN113534744A
Handle management or communication method, electronic device, storage medium and program product
CN114462388A
Cited By
Multimedia data transmission system implementation method and device, equipment and storage medium
CN120017882A
Analog machine modular distributed interface and multi-bus fusion system and data processing method
CN120295953A
A simulator modular distributed interface and multi-bus fusion system and data processing method
CN120295953B
Operating system abstraction layer implementation method applied to driver development
WO2026184207A1