Каталог статей
драйвер Linux — драйвер символьного устройства
драйвер Linux — драйвер символьного устройства (автоматически генерирует файл узла устройства)
драйвер Linux — конвертировать модуль Компилировать в ядро
Драйвер символьного устройства
#include "linux/uaccess.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> /* struct file_operations */
#include <linux/uaccess.h> /* copy_to_user() & copy_from_user */
#define CHRDEVBASE_MAJOR 200 /* 主设备号 */
#define CHRDEVBASE_NAME "chrdevbase" /* 设备名 */
static char write_buf[100];
static char read_buf[100];
static char *string_test = "kernel data this tyustli test";
static int chrdevbase_open(struct inode *inode, struct file *file)
{
printk("k: chrdevbase open\r\n");
return 0;
}
static ssize_t chrdevbase_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long ret = 0;
printk("k: chrdevbase read\r\n");
memcpy(read_buf, string_test, strlen(string_test));
ret = copy_to_user(buf, read_buf, count);
if (ret == 0) {
printk("k: read data success\r\n");
} else {
printk("k: read data failed ret = %ld\r\n", ret);
}
return ret;
}
static ssize_t chrdevbase_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long ret = 0;
printk("k: chrdevbase write\r\n");
ret = copy_from_user(write_buf, buf, count);
if (ret == 0) {
printk("k: write data success write data is: %s\r\n", write_buf);
} else {
printk("k: write data failed ret = %ld\r\n", ret);
}
return count;
}
static int chrdevbase_release(struct inode *inode, struct file *file)
{
printk("k: chrdevbase release\r\n");
return 0;
}
static struct file_operations chrdevbase_fops = {
.owner = THIS_MODULE,
.open = chrdevbase_open,
.read = chrdevbase_read,
.write = chrdevbase_write,
.release = chrdevbase_release,
};
static int __init chrdevbase_init(void)
{
int ret = 0;
ret = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
if (ret < 0) {
printk("k: char dev register failed\r\n");
}
printk("k: base module init\r\n");
return 0;
}
static void __exit chrdevbase_exit(void)
{
unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
printk("k: base module exit!\r\n");
}
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
MODULE_INFO(intree, "Y"); /* loading out-of-tree module taints kernel */
Приложение для символьного устройства
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char usrdata[] = {
"user data!" };
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
char readbuf[100], writebuf[100];
if (argc != 3) {
printf("u: error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开驱动文件 */
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("u: can't open file %s\r\n", filename);
return -1;
}
/* 从驱动文件读取数据 */
if (atoi(argv[2]) == 1) {
retvalue = read(fd, readbuf, 50);
if (retvalue < 0) {
printf("u: read file %s failed!\r\n", filename);
} else {
/* 读取成功,打印出读取成功的数据 */
printf("u: read data:%s\r\n", readbuf);
}
}
/* 向设备驱动写数据 */
if (atoi(argv[2]) == 2) {
memcpy(writebuf, usrdata, sizeof(usrdata));
retvalue = write(fd, writebuf, 50);
if (retvalue < 0) {
printf("u: write file %s failed!\r\n", filename);
}
}
/* 关闭设备 */
retvalue = close(fd);
if (retvalue < 0) {
printf("u: can't close file %s\r\n", filename);
return -1;
}
return 0;
}
Работа модуля
Установка модуля
Проверьте, существует ли файл модуля
ls /lib/modules/6.5.7+/my_module.ko
Установить
modprobe my_module
Просмотр устройств, находящихся в настоящее время в системе
cat proc/devices
выход
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
29 fb
90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 chrdevbase
204 ttyAMA
226 drm
249 rpmb
250 usbmon
251 ptp
252 pps
253 rtc
254 gpiochip
Создать узел устройства
Создание узла устройства начинается с создания файла в каталоге /dev, чтобы APP
мог получить доступ к этому файлу
mknod /dev/chrdevbase c 200 0
- mknod создает команду узла устройства
- /dev/chrdevbase — файл узла, который нужно создать.
- c означает символьное устройство
- 200 — это основной номер устройства.
- 0 — младший номер устройства.
Этот файл будет существовать после создания /dev/chrdevbase
ls /dev/chrdevbase -l
Журнал результатов
crw-r--r-- 1 0 0 200, 0 Nov 5 03:10 /dev/chrdevbase
Модуль работы приложения
читать
lib/modules/6.5.7+/my_app /dev/chrdevbase 1
прочитать результаты
k: chrdevbase open
k: chrdevbase read
k: read data success
u: read data:kernel data this tyustli test
k: chrdevbase release
Писать
lib/modules/6.5.7+/my_app /dev/chrdevbase 2
написать результат
k: chrdevbase open
k: chrdevbase write
k: write data success write data is: user data!
k: chrdevbase release
Деинсталлировать и удалить модули
# 删除模块
rmmod my_module
# 删除设备节点文件
rm /dev/chrdevbase
shell
Автоматизация скриптов
shell
Скрипт автоматически компилирует модули и файлы APP и запускает qemu.
# 参数解析
# ./my_module_build.sh para1 para2(可选)
# 脚本名称 指定模块路径 是否执行 make clean 命令
# 判断 shell 脚本有几个参数,如果没有指定 module 目录, shell 脚本就报错退出
if [ $# -eq 0 ]; then
echo "Incorrect number of arguments for command
Usage: my_module_build.sh <module_dir> build your own module"
exit
fi
# 切换到指定的目录
cd $1
# 如果是清除工程,就执行 make clean 命令
if [ "$2" == "clean" ]; then
make clean
exit
fi
# 编译指定目录的模块
make
python3 ../../tools/clang-tools/gen_compile_commands.py
# 编译 APP 文件并将 APP 文件拷贝到根文件系统中
arm-none-linux-gnueabihf-gcc \
/home/tyustli/code/qemu_code/linux_driver/$1/my_app.c -o \
/home/tyustli/code/qemu_code/linux_driver/$1/my_app
cp /home/tyustli/code/qemu_code/linux_driver/$1/my_app /home/tyustli/code/open_source/busybox/rootfs/lib/modules/6.5.7+
# 将生成的 .ko 文件拷贝到根文件系统的 roorfs 中
cp ./my_module.ko /home/tyustli/code/open_source/busybox/rootfs/lib/modules/6.5.7+
# 切换到根文件系统目录
cd /home/tyustli/code/open_source/busybox
# 生成虚拟 SD 卡系统镜像
sudo dd if=/dev/zero of=rootfs.ext3 bs=1M count=128
# 格式化镜像
sudo mkfs.ext3 rootfs.ext3
#将文件复制到镜像中
sudo mkdir tmpfs_rootfs
sudo mount -t ext3 rootfs.ext3 tmpfs_rootfs/ -o loop
sudo cp -r rootfs/* tmpfs_rootfs/
sudo umount tmpfs_rootfs
rmdir tmpfs_rootfs
# 切换回指定的目录
cd /home/tyustli/code/qemu_code/linux_driver/$1
# 启动 kernel
sudo qemu-system-arm -M vexpress-a9 -m 512M \
-kernel /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/zImage \
-dtb /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb -nographic \
-append "root=/dev/mmcblk0 rw console=ttyAMA0" \
-sd /home/tyustli/code/open_source/busybox/rootfs.ext3
Адрес склада
- https://gitee.com/tyustli/qemu