// 首先需要明白,驱动分为,普通字符设备驱动 和 platform平台驱动(这种可以理解用于设备树的)
// 字符设备驱动又分旧和新,不同写法。这里用旧的写法,比较好理解。
// 驱动代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEV_NAME "helloDrv"
#define MAJOR_NUM 123 // 主设备号
// 打开设备
static int hello_open(struct inode *inode, struct file *file)
{
printk("=== 驱动被打开了!open 成功 ===\n");
return 0;
}
// 关闭设备
static int hello_release(struct inode *inode, struct file *file)
{
printk("=== 驱动被关闭了!close 成功 ===\n");
return 0;
}
// 读数据
static ssize_t hello_read(struct file *file, char __user *buf, size_t size, loff_t *loff)
{
char kernel_data[] = "Hello from kernel!";
copy_to_user(buf, kernel_data, sizeof(kernel_data));
return sizeof(kernel_data);
}
// 写数据
static ssize_t hello_write(struct file *file, const char __user *buf, size_t size, loff_t *loff)
{
char kernel_buf[100] = {0};
copy_from_user(kernel_buf, buf, size);
printk("=== 应用发来数据:%s ===\n", kernel_buf);
return size;
}
// 操作函数集合 这里就是应用程序的open close read write 等。
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
};
// 加载
static int __init hello_init(void)
{
register_chrdev(MAJOR_NUM, DEV_NAME, &fops);
printk("=== helloDrv 驱动加载成功 ===\n");
return 0;
}
// 卸载
static void __exit hello_exit(void)
{
unregister_chrdev(MAJOR_NUM, DEV_NAME);
printk("=== helloDrv 驱动卸载成功 ===\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
// Makefile 注意,驱动编译,只能用Makefile
// 这里是在Linux内测试,并没有跨平台,如果需要跨平台,自行添加
obj-m += heloDrv.o # 这里必须和你的 驱动.c 文件名一样!
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
gcc app.c -o app
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
rm -f app
//arm版
# 驱动文件
obj-m += heloDrv.o
# 交叉编译器前缀
CROSS_COMPILE := arm-linux-gnueabihf-
# ARM 架构
ARCH := arm
# 你的内核路径
KDIR := /home/qwer/LinuxCreate/kernel
# 当前目录
PWD := $(shell pwd)
# 编译
all:
make -C $(KDIR) \
ARCH=$(ARCH) \
CROSS_COMPILE=$(CROSS_COMPILE) \
M=$(PWD) modules
# 清理
clean:
make -C $(KDIR) M=$(PWD) clean
// 测试应用 app.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd;
char buf[100];
// 打开设备节点
fd = open("/dev/helloDrv", O_RDWR);
if (fd < 0) {
perror("open failed");
return -1;
}
printf("open /dev/helloDrv 成功\n");
// 读驱动
read(fd, buf, sizeof(buf));
printf("从驱动读到:%s\n", buf);
// 写驱动
write(fd, "Hello app!", 10);
// 关闭
close(fd);
return 0;
}
5. 完整测试命令
# 编译
make
# 加载驱动
sudo insmod helloDrv.ko
# 创建设备节点
sudo mknod /dev/helloDrv c 123 0
// 这里的123 对应 #define MAJOR_NUM 123
// c是 char device(字符设备)
# 运行应用程序
sudo ./app
# 看内核打印
dmesg | tail
# 卸载
sudo rmmod helloDrv
# 删除设备节点
sudo rm /dev/helloDrv
//
#define DEV_NAME "helloDrv"
// 这个名字 和
sudo mknod /dev/xxxx c 123 0
// 这个名字
它们之间:没有任何关联、没有任何绑定、没有任何要求必须一样
#define DEV_NAME "helloDrv"
这个名字是给:内核看的!
mknod /dev/xxx
这个名字是给:应用程序看的
open("/dev/xxx", O_RDWR);