1. 基本步驟
(1)確定主設備號和次設備號
(2)實現字符驅動程序
- 實現file_operations結構體;
- 實現初始化函數,註冊字符設備;
- 實現銷燬函數,釋放字符設備;
- 實現字符設備其他基本成員函數。
(3)創建設備文件節點
2. 什麼是主設備號/次設備號
主設備號是內核識別一個設備的標識。它是一個整數(佔12位),通常使用1~255。
次設備號由內核使用,用於正確確定設備文件所指的設備。它也是一個整數(佔20位),通常使用0~255。
注:同一類設備的主設備號相同,不同的是次設備號。如多個串口的主設備號是相同的,次設備號不同。
3. 設備編號的內部表達
(1)dev_t類型(32位):用來保存設備編號
(2)從dev_t獲得主、次設備號:
- MAJOR(dev_t): //主
- MINOR(dev_t); //次
(3)將主、次設備號轉換成dev_t類型:
- MKDEV(int major, int minor);
4. 分配設備號
通常在模塊加載函數中調用。
(1)手工分配:找一個內核沒有使用的主設備號來使用
#include<linux/fs.h>
int register_chrdev_region(dev_t first, unsigned int count, char *name);
//first是設備號,次設備號通常爲0;count是要分配設備號的個數,即次設備號個數;name是此類設備的名字。
(2)動態分配:
#include<linux/fs.h>
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
//dev是輸出的設備號;firstminor是要申請第一個次設備號;count是要申請的設備號個數;name是此類設備的名字。
5. 釋放設備號
通常在模塊清理函數中調用。
#include<linux/fs.h>
void unregister_chrdev_region(dev_t dev, unsigned int count);
6. 重要結構體
(1)cdev結構體
struct cdev
{
struct kobject kobj; //內嵌的kobject對象
struct module *owner; //所屬模塊
struct file_operations *ops; //文件操作結構體
struct list_head list;
dev_t dev; //設備號
unsigned int count;
};
操作cdev結構體的函數:
void cdev_init(struct cdev *cdev, struct file_operations *ops);
//用於初始化已分配的cdev結構,並建立cdev和file_operations之間的連接
struct cdev *cdev_alloc(void);
//用於動態申請一個cdev內存
int cdev_add(struct cdev *cdev, dev_t num, unsigned int count);
//向內核添加一個cdev,完成字符設備的註冊,通常在模塊加載函數中調用
void cdev_del(struct cdev *cdev);
//刪除一個cdev,完成字符設備的註銷,通常在模塊卸載函數中調用
(2)file_operations結構體
- 是字符驅動和內核的接口,在include/linux/fs.h中定義;
- 字符驅動都要實現一個file_operations結構體;
file_operations的主要成員:
struct module *owner:指向模塊自身(THIS_MODULE)
open:打開設備
release:關閉設備
read:從設備上讀數據
write:向設備上寫數據
ioctl:I/O控制函數
llseek:定位當前讀寫位置指針
mmap:映射設備空間到進程的地址空間
(3)file結構體
- file_operations結構相關的一個結構體,描述一個正在打開的設備文件。
file的成員:
loff_t f_pos:當前讀寫位置
unsigned int f_flags:標識文件打開時是否可讀或可寫,O_RDONLY、O_NONBLOCK、O_SYNC
struct file_operations *f_op:文件相關的操作,指向所實現的struct file_operations
void *private_data:私有數據指針,驅動程序可以將這個字段用於任何目的或者忽略(設爲NULL)這個字段
(4)inode結構體
- 內核用inode結構在內部表示文件;
- inode與file的區別:file表示打開的文件描述符,多個表示打開的文件描述符的file結構可以指向一個inode結構。
inode的重要成員:
dev_t i_rdev:對錶示設備文件的inode結構,該字段包含了真正的設備號
struct cdev *i_cdev:表示字符設備在內核中的內部結構
7. 字符設備驅動程序模版
//字符設備驅動模塊加載函數
static int __init xxx_init(void)
{
...
cdev_init(&xxx_dev.cdev, &xxx_fops); //初始化cdev
xxx_dev.cdev.owner = THIS_MODULE;
//獲取字符設備編號
if(xxx_major)
{
register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
}
else
{
alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
}
ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); //註冊設備
...
}
//字符設備驅動模塊卸載函數
static void __exit xxx_exit(void)
{
unregister_chrdev_region(xxx_dev_no, 1); //釋放佔用的設備號
cdev_del(&xxx_dev.cdev); //註銷設備
...
}