内核驱动编译,简单例子

// 首先需要明白,驱动分为,普通字符设备驱动 和 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);

posted @ 2026-04-17 00:18  一见无始道成空  阅读(12)  评论(0)    收藏  举报