學習Linux設備驅動已經有一段時間了,但是發現學習後很多的知識點記憶模糊了,因此對學習過的知識進行了梳理和總結。
1.一般的驅動都是以模塊的形式存在的,那麼在字符設備的模塊加載函數中要完成設備號的申請(靜態、動態兩種方法)和cdev的註冊,而在卸載函數中實現設備號的釋放和cdev的註銷。
2. 驅動編寫模板:
/*
這個設備結構體是自己定義的結構體,包含字符設備結構體和自己定義的其他的成員。
*/
// 設備結構體
struct xxx_dev_t{
struct cdev cdev;
...
}xxx_dev;
// 設備驅動模塊加載函數
static int __init xxx_init(void)
{
...
cdev_init(&xxx_dev.cdev,&xxx_fops);// 這個字符設備初始化函數是最關鍵的設備和驅動的綁定,這樣才能使用驅動函數來操作字符設備。
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);
....
}
3.驅動操作成員函數的編寫,例如,open(),close(),read(),write(),release()等函數,先單獨的編寫在文件中,再賦給struct file_operations xxx_fops 結構體成員中。
// 讀設備
ssize_t xxx_read(strcut file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
...
copy_to_user(buf,...,..);
}
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
...
copy_from_user(..., buf, ...);
...
}
/*
long xxx_ioctl() 函數等,根據自己的設備操作需求編寫函數;
**/
// 關鍵的驅動文件操作結構體
struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.read = xxx_read,
.write = xxx_write,
.unlocked_ioctl= xxx_ioctl,
};
注意:(1)文件操作函數中從讀函數是從從內核空間讀到用戶空間讀數據,因此使用copy_to_user()函數,to 到用戶空間。
(2)copy_from_user() 是把用戶空間的數據複製到內核空間,from。
( 3 ) 下面是字符設備的示意圖。
(4)這個字符設備的模板是基本的字符設備、驅動的模板。一般的設備和驅動是以總線的形式來編寫的,例如虛擬總線,這個我們在下一次筆記中記錄學習。