驅動程式架構
- qudong.c:核心驅動代碼(open/read/write/release/file_operations)
- Makefile:核心外建構腳本(x86 交叉編譯與 ARM 原生編譯)
- test_app.c:用戶空間測試程式
核心驅動代碼 (qudong.c)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#define DEV_NAME "rwtest"
#define MAX_BUF_LEN 1024
static int major = 0;
static char *drv_buf;
static int buf_len = 0;
static ssize_t drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
if (count > MAX_BUF_LEN) count = MAX_BUF_LEN;
if (copy_from_user(drv_buf, buf, count)) return -EFAULT;
buf_len = count;
return count;
}
static ssize_t drv_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
if (count > buf_len) count = buf_len;
if (copy_to_user(buf, drv_buf, count)) return -EFAULT;
return count;
}
static int drv_open(struct inode *inode, struct file *filp) {
printk(KERN_INFO "%s: device opened\n", DEV_NAME);
return 0;
}
static int drv_release(struct inode *inode, struct file *filp) {
printk(KERN_INFO "%s: device closed\n", DEV_NAME);
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.write = drv_write,
.read = drv_read,
.open = drv_open,
.release = drv_release,
};
static int __init drv_init(void) {
major = register_chrdev(0, DEV_NAME, &fops);
if (major < 0) return major;
drv_buf = kmalloc(MAX_BUF_LEN, GFP_KERNEL);
if (!drv_buf) { unregister_chrdev(major, DEV_NAME); return -ENOMEM; }
printk(KERN_INFO "%s: registered with major %d\n", DEV_NAME, major);
return 0;
}
static void __exit drv_exit(void) {
kfree(drv_buf);
unregister_chrdev(major, DEV_NAME);
printk(KERN_INFO "%s: unregistered\n", DEV_NAME);
}
module_init(drv_init);
module_exit(drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Developer");
Makefile (x86 宿主機交叉編譯 ARM)
KDIR := /usr/src/linux-headers-$(shell uname -r)
# ARM 交叉編譯時取消註解下兩行並註解上行
# KDIR := /path/to/arm-linux-kernel
# ARCH := arm
# CROSS_COMPILE := arm-linux-gnueabihf-
obj-m := qudong.o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules $(ARCH) $(CROSS_COMPILE)
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
rm -f *.ko *.o *.mod.o *.mod.c *.symvers modules.order
用戶空間測試程式 (test_app.c)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define DEV_PATH "/dev/rwtest"
int main() {
int fd = open(DEV_PATH, O_RDWR);
if (fd < 0) { perror("open"); return 1; }
char wbuf[] = "Hello from user space!";
char rbuf[128] = {0};
if (write(fd, wbuf, strlen(wbuf)) < 0) perror("write");
if (read(fd, rbuf, sizeof(rbuf)-1) < 0) perror("read");
printf("Read back: %s\n", rbuf);
close(fd);
return 0;
}
部署與驗證步驟
- 編譯:
make 產生 qudong.ko
- 載入:
sudo insmod qudong.ko,查看 dmesg 確認 major number
- 建立裝置節點:
sudo mknod /dev/rwtest c 0
- 編譯測試程式:
gcc test_app.c -o test_app (ARM 交叉編譯加 arm-linux-gnueabihf-gcc)
- 執行測試:
./test_app
- 卸載:
sudo rmmod qudong
進階擴展方向
- 支援
ioctl 命令控制裝置參數
- 實作
poll/fasync 非同步通知
- 使用
cdev + device_create 自動建立 /dev 節點 (驅動核心模型)
- DMA 緩衝區零拷貝傳輸