在本章中,我们试图在kernel/driver/misc
下添加一个自己的驱动程序,并将其build-in
到系统中。
我们在kernel/driver/misc
下创建一个文件夹my_driver
,并添加.c
、Makefile
和Kconfig
一、hello.c
我们这个应用程序是一个最简单的驱动,它可以从用户空间读取字符串、向用户空间发送字符串,除此之外它没有其他的功能。这个驱动的文件名是hello.c
,设备名是my_driver
。
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#define DEVICE_NAME "my_driver"
static ssize_t my_driver_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
const char *hello_world = "Hello world";
size_t len = strlen(hello_world);
if (*ppos >= len)
return 0;
if (count > len - *ppos)
count = len - *ppos;
if (copy_to_user(buf, hello_world + *ppos, count))
return -EFAULT;
*ppos += count;
return count;
}
static ssize_t my_driver_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
char input_str[256];
if (count > sizeof(input_str) - 1)
count = sizeof(input_str) - 1;
if (copy_from_user(input_str, buf, count))
return -EFAULT;
input_str[count] = '\0';
pr_info("Received string: %s\n", input_str);
return count;
}
static const struct file_operations my_driver_fops = {
.owner = THIS_MODULE,
.read = my_driver_read,
.write = my_driver_write,
};
static struct miscdevice my_driver_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &my_driver_fops,
};
static int __init my_driver_init(void)
{
int ret;
ret = misc_register(&my_driver_device);
if (ret)
pr_err("Failed to register misc device\n");
return ret;
}
static void __exit my_driver_exit(void)
{
misc_deregister(&my_driver_device);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple driver example");
二、Makefile & Kconfig
我们在Makefile
中添加如下内容:
obj-$(CONFIG_MYDRIVER) += hello.o
Kconfig
内容如下:
config ChooseMyDriver
tristate "description"
select MYDRIVER
default y
help
Enable support for the my_driver device.
三、其他配置
我们在misc/Kconfig
中添加:
config MYDRIVER
tristate "description"
default y
help
Say Sth.
和:
source "drivers/misc/my_driver/Kconfig"
同时,我们在misc/Makefile
中添加:
obj-$(CONFIG_MYDRIVER) += my_driver/
我们还需要在kernel
的.config
文件(这里用的是rv1126_defconfig
)中添加:
CONFIG_ChooseMyDriver=y
完成之后,我们可以编译系统。我们可以在/dev/
下发现dev/my_driver
设备,证明其被加载成功。
四、验证程序
我们写一个应用程序来验证驱动,文件目录如下:
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
其中,我们在./build
中构建程序;src
用来存放源码。
在./CMakeLists.txt
中,其内容如下:
PROJECT(Test_Driver_Application)
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
SET(COMPILER_PATH "/home/xjt/_Workspace_/RV1126/RV1126_RV1109_LINUX_SDK_V2.2.5.1_20230530/buildroot/output/rockchip_rv1126_rv1109/host/bin/")
SET(CMAKE_C_COMPILER ${COMPILER_PATH}arm-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER ${COMPILER_PATH}arm-linux-gnueabihf-g++)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -O3 -lrt")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s -O3 -lrt")
ADD_SUBDIRECTORY(src bin)
在./src/CMakeLists.txt
内容如下:
FILE(
GLOB_RECURSE SRC_LIST
./*.c
./*.cpp
)
# Exe output path
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR/bin})
ADD_EXECUTABLE(demo ${SRC_LIST})
# Link lib and so
TARGET_LINK_LIBRARIES(
demo
)
main.c
内容如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#define DEVICE_PATH "/dev/my_driver"
int main(void)
{
int fd;
char buffer[256];
ssize_t bytes_read, bytes_written;
fd = open(DEVICE_PATH, O_RDWR);
if (fd == -1) {
perror("Failed to open device");
exit(EXIT_FAILURE);
}
bytes_read = read(fd, buffer, sizeof(buffer));
if (bytes_read == -1) {
perror("Failed to read from device");
close(fd);
exit(EXIT_FAILURE);
}
printf("Read from device: %.*s\n", (int)bytes_read, buffer);
strcpy(buffer, "Hello from user space");
bytes_written = write(fd, buffer, strlen(buffer));
if (bytes_written == -1) {
perror("Failed to write to device");
close(fd);
exit(EXIT_FAILURE);
}
close(fd);
return 0;
}
我们在./build
下执行cmake ..
,之后再直接make
。即可在./build/bin
下找到最终的程序demo
。