Linux字符設備驅動框架(一):cdev接口

/************************************************************************************html

*本文爲我的學習記錄,若有錯誤,歡迎指正。node

*本文參考資料: 
linux

*        http://www.cnblogs.com/xiaojiang1025/p/6367061.html數據結構

*        http://www.cnblogs.com/xiaojiang1025/p/6367910.html框架

*        http://www.cnblogs.com/xiaojiang1025/p/6369065.html函數

*        http://www.javashuo.com/article/p-dnlpbddz-bh.html學習

*        http://www.javashuo.com/article/p-rzrvxiho-by.htmlspa

************************************************************************************/.net

1. 字符設備

字符設備是指在I/O傳輸過程當中以字節爲單位進行傳輸的設備。其特色是,不能隨機讀取字符設備的某一數據,只能按照字節的前後順序讀取數據。常見的字符設備有串口、鍵盤、鼠標等。指針

字符設備相關數據結構詳見Linux設備管理(二):內核中字符設備的管理

 2. 字符設備驅動框架

2.1 註冊驅動

(1)動態申請cdev內存

/* 
*所在文件:/kernel/fs/char_dev.c
*參數:無
*返回值:
*    成功: cdev 對象首地址
*    失敗:NULL 
*/
struct cdev *cdev_alloc(void);  

(2)初始化cdev

初始化cdev,創建cdev與file_operations之間的關聯。

/* 
*所在文件:/kernel/fs/char_dev.c
*參數:
*        struct cdev *p:                 被初始化的cdev對象  
*        const struct file_operations *p:字符設備操做方法集
*返回值:無 
*/
void cdev_init(struct cdev *p, const struct file_operations *p); 

(3)申請設備號

1)指定設備號

/*
*所在文件:/kernel/fs/char_dev.c
*參數:
*    dev_t from:        起始設備號
*    unsigned count:    需註冊的次設備個數
*    const char *name:  設備名稱
*返回值:
*    0:   註冊成功
*    負數: 註冊失敗
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);

2)系統自動分配設備號

/*
*所在文件:/kernel/fs/char_dev.c
*參數:
*    dev_t *dev:         輸出型參數,系統自動分配的設備號
*    unsigned baseminor: 次設備的起始編號
*    unsigned count:     次設備的個數
*    const char *name:   設備名稱
*返回值:
*    0:   註冊成功
*    負數: 註冊失敗
*/

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

(4)註冊cdev設備對象

將cdev添加至系統字符設備鏈表中。

/* 
*所在文件:/kernel/fs/char_dev.c
*參數:
*        struct cdev *p:被初始化的cdev對象  
*        dev_t dev:     起始設備號
*        unsigned count:該設備連續的次設備號的個數
*返回值:
*         成功:返回0
*         失敗:返回負數
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

2.2 建立設備文件

建立設備文件的細節詳見Linux設備文件的建立

(1)在/sys中導出設備類信息

/* 
*所在文件:/kernel/include/linux/device.h
*參數:
*        owner:類所屬模塊,通常爲THIS_MODULE
*        name: 類名
*返回值:
*        struct class *:指向所建立的類
*/
#define class_create(owner, name)        \
({                                       \
    static struct lock_class_key __key;  \
    __class_create(owner, name, &__key); \
})

(2)建立設備文件

/* 
*所在文件:/kernel/driver/base/core.c
*參數:
*        struct class *class:  被初始化的cdev對象  
*        struct device *parent:父設備
*        dev_t dev:            設備號
*        void *drvdata:     設備的回調數據
*        const char *fmt:      設備名
*返回值:
*        struct device *:      指向所建立的設備
*/
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);

2.3 實現設備操做

根據須要實現file_operations中的函數,經常使用的函數以下:

(1)read、write

Linux下各個進程都有本身獨立的進程空間,即便是將內核的數據映射到用戶進程,該數據的PID也會自動轉變爲該用戶進程的PID,因爲這種機制的存在,咱們不能直接將數據從內核空間和用戶空間進行拷貝,而須要專門的拷貝數據函數/宏:

long copy_from_user(void *to, const void __user * from, unsigned long n)

long copy_to_user(void __user *to, const void *from, unsigned long n)

有了這兩個函數,內核中的read,write就能夠實現內核空間和用戶空間的數據拷貝。

(2)ioctl

雖然在文件操做結構體"structfile_operations"中有不少對應的設備操做函數,可是有些命令是實在找不到對應的操做函數。如CD-ROM的驅動,想要一個彈出光驅的操做,這種操做並非全部的字符設備都須要的,因此文件操做結構體也不會有對應的函數操做。

出於這樣的緣由,ioctl就有它的用處了——一些沒辦法歸類的函數就統一放在ioctl這個函數操做中,經過指定的命令來實現對應的操做。因此,ioctl函數裏面都實現了多個的對硬件的操做,經過應用層傳入的命令來調用相應的操做。

ioctl函數使用的詳細方法,詳見Linux驅動程序中ioctl函數的用法

/* 
*參數:
*    struct inode *inode和struct file *filp: ioctl的操做有多是要修改文件的屬性,或者訪問硬件。要修改文件屬性的話,就要用到這兩個結構體了,因此這裏傳來了它們的指針。
*       unsigned int cmd: 命令
*       unsigned long arg:參數
*返回值: 無 
*/

int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

2.4 註銷驅動

(1)註銷cdev

將cdev對象從系統字符設備鏈表中剔除,並清理cdev所佔的內存資源。

/* 
*所在文件:/kernel/fs/char_dev.c
*參數:
*        struct cdev *p:被註銷的cdev對象  
*返回值:    無
*/
void cdev_del(struct cdev *p);

(2)釋放設備號

/* 
*所在文件:/kernel/fs/char_dev.c
*參數:
*        dev_t from:被註銷設備的起始設備號
*        unsigned count:該設備連續的次設備號個數
*返回值:    無
*/
void unregister_chrdev_region(dev_t from, unsigned count);

(3)刪除設備類

/* 
*所在文件:/kernel/driver/base/class.c
*參數:
*        struct class *cls:設備類
*返回值:    無
*/
void class_destroy(struct class *cls);

(4)刪除設備文件

/* 
*所在文件:/kernel/driver/base/core.c
*參數:
*        struct class *class:設備類
*        dev_t devt:            設備號
*返回值:    無
*/
void device_destroy(struct class *class, dev_t devt);

3. 實例

 詳見驅動程序實例(二):LED設備驅動程序( platform + cdev)

相關文章
相關標籤/搜索