字元裝置驅動是 Linux 驅動開發的入門基礎。本文系統介紹裝置驅動角色、字元/區塊裝置差異、裝置檔案與主/次裝置號、file_operations 統一 I/O 介面,並提供完整的三個檔案範本(test.h、test.c、ledtest.c)實作 LED 控制驅動與用戶測試程式。
/dev/xxx,應用程式以檔案操作存取硬體
struct file_operations {
struct module *owner;
loff_t (*llseek)(struct file *, loff_t, int);
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long);
int (*open)(struct inode *, struct file *);
int (*release)(struct inode *, struct file *);
// ... 其他操作
};
不同檔案類型實作不同 file_operations,應用程式統一呼叫 open/read/write/ioctl/close,VFS 自動分派至對應驅動。
insmod)open() → 驅動 open()ioctl()/read()/write() → 驅動對應函數close() → 驅動 release()rmmod)
#ifndef __CONFIG_H
#define __CONFIG_H
#include
#include
#include
#include
#include
#include
#include
#define LED_MAJOR_NR 231
#define DEVICE_NAME "led"
#define SET_LED_OFF 0
#define SET_LED_ON 1
#endif
#include "test.h"
static int led_open(struct inode *inode, struct file *filp) {
// 初始化硬體、模組計數器+1
MOD_INC_USE_COUNT;
return 0;
}
static int led_release(struct inode *inode, struct file *filp) {
// 清理硬體、模組計數器-1
MOD_DEC_USE_COUNT;
return 0;
}
static int led_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg) {
switch (cmd) {
case SET_LED_ON:
// 寫入暫存器點亮 LED
break;
case SET_LED_OFF:
// 寫入暫存器熄滅 LED
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.ioctl = led_ioctl,
};
int init_module(void) {
int result = register_chrdev(LED_MAJOR_NR, DEVICE_NAME, &led_fops);
if (result < 0) return result;
printk("LED driver loaded\n");
return 0;
}
void cleanup_module(void) {
unregister_chrdev(LED_MAJOR_NR, DEVICE_NAME);
printk("LED driver unloaded\n");
}
#include
#include
#include
#include "test.h"
int main() {
int fd = open("/dev/led", O_RDWR);
if (fd < 0) {
perror("open /dev/led");
return 1;
}
printf("Turn ON LED\n");
ioctl(fd, SET_LED_ON, 0);
sleep(2);
printf("Turn OFF LED\n");
ioctl(fd, SET_LED_OFF, 0);
close(fd);
return 0;
}
編譯:gcc ledtest.c -o ledtest,執行前需 mknod /dev/led c 231 0 建立裝置節點。
printk,用戶空間用 printfcopy_to_user/copy_from_usertry_module_get()/module_put()