《驅動學習 - 字符設備驅動》


1.1字符設備驅動基礎

字符設備驅動:設備對數據的處理是按照字節流的形式進行的。

 

在linux中,“一切皆文件”(除了網絡設備),這表示設備最終都會體現為一個文件。設備文件通常位於/dev目錄下、

內核通常用主設備號區別一類設備,次設備號用於區分同一類設備的不同個人或不同分區。

 

手動創建設備文件

mknod   /dev/vser0 c 256 0

mknod是make node的縮寫。用於創建一個節點(設備文件也叫設備節點)、在linux系統中,一個節點代表一個文件。

 

1.2 字符設備驅動框架

 example 1.1

 1 #include <linux/init.h>
 2 #include <linux/kernel.h>
 3 #include <linux/module.h>
 4 
 5 #include <linux/fs.h>
 6 
 7 
 8 #define VSER_MAJOR     256
 9 #define VSER_MINOR     0
10 #define VSER_DEV_CNT   1
11 #define VSER_DEV_NAME  "vser"
12 
13 static int __init vser_init(void)
14 {
15     int ret;
16     dev_t dev;
17 
18     /* MKDEV宏將主設備號和次設備號合並成一個設備號 */
19     dev = MKDEV(VSER_MAJOR, VSER_MINOR);
20 
21     /* 將構造的設備號注冊到內核中(靜態) */
22     ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
23     if(ret)
24         goto reg_err;
25     return 0;
26 
27 reg_err:
28     return ret;
29 }
30 
31 static void __exit vser_exit(void)
32 {
33     dev_t dev;
34 
35     dev = MKDEV(VSER_MAJOR, VSER_MINOR);
36 
37     /* 從內核中注銷設備號 */
38     unregister_chrdev_region(dev, VSER_DEV_CNT);
39 }
40 
41 module_init(vser_init);
42 module_exit(vser_exit);
43 
44 MODULE_LICENSE("GPL");

  注意:使用goto函數進行集中錯誤處理在驅動中非常常見,雖然這和一般的C語言編程規則相悖(因為C語言面向程序的語言,要求模塊單入口,單出口,但是goto破壞了這種結構)。 

  register_chrdev_region注冊設備號的方式稱為靜態注冊設備號,這樣如果兩個驅動都是用同樣的設備號,那么后加載的驅動將會失敗,因為設備號沖突。可以使用動態分配設備號的方式避免這個問題。alloc_chrdev_region函數。

 

  MKDEV該宏的作用是將主設備號左移20位和次設備號相或。

  MAJOR和MINOR分別是從設備號中取出主設備號和次設備號。

#define MINORBITS            20
#define MINORMASK           ((1U << MINORBITS) - 1)


#define MAJOR(dev)         ((unsigned int) ((dev) >> MINORBITS))       
#define MINOR(dev)         ((unsigned int) ((dev) & MINORMASK)) 
#define MKDEV(ma, mi)      ((ma) << MINORBITS | (mi))

 

example 1.2

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>


#define VSER_MAJOR     256
#define VSER_MINOR     0
#define VSER_DEV_CNT   1
#define VSER_DEV_NAME  "vser"

/* 代表一個具體得字符設備 */ static struct cdev vsdev;
/* 操作該設備得一些方法 */
static struct file_operations vser_ops = { .owner = THIS_MODULE, }; static int __init vser_init(void) { int ret; dev_t dev; /* MKDEV宏將主設備號和次設備號合並成一個設備號 */ dev = MKDEV(VSER_MAJOR, VSER_MINOR); /* 將構造的設備號注冊到內核中(靜態) */ ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME); if(ret) goto reg_err; cdev_init(&vsdev, &vser_ops); vsdev.owner = THIS_MODULE;
   /* 初始化vsdev中得部分成員,將vsdev中得ops指針指向vser_ops */ ret
= cdev_add(&vsdev, dev, VSER_DEV_CNT); if(ret) goto add_err; return 0; add_err: unregister_chrdev_region(dev, VSER_DEV_CNT); reg_err: return ret; } static void __exit vser_exit(void) { dev_t dev; dev = MKDEV(VSER_MAJOR, VSER_MINOR); /* 刪除cdev對象 */ cdev_del(&vsdev); /* 從內核中注銷設備號 */ unregister_chrdev_region(dev, VSER_DEV_CNT); } module_init(vser_init); module_exit(vser_exit); MODULE_LICENSE("GPL");

  cdev_init函數初始化了vsdev中得部分成員。另外一個最重要得操作就是將vsdev中得ops指針指向了vser_ops,這樣通過設備號找到vsdev對象后,就能找到相關得操作方法集合,並調用其中的方法。

  owner是一個指向struct module類型變量的指針,THIS_MODULE是包含驅動的模塊中的struct module類型對象的地址,類似於C++中的this指針。這樣就能夠通過vsdev或vser_fops找到對應的模塊。

  cdev_add函數將cdev對象初始化后添加到內核中的cdev_map散列表中。

  在例1.2中的cdev對象是靜態定義的,我們也可以進行動態分配,對應的函數是cdev_alloc。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM