linux字符設備驅動學習筆記1

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                《常識》
¥應用程序----->系統內核----->設備驅動----->硬件設備
¥設備驅動既是系統內核的下屬,又是硬件設備的老大。 
¥在inux系統中用一個文件來代表一個設備。這個文件就叫設備文件。設備驅動的責任是將應用程序對設備文件
的打開、讀、寫、定位等操作轉化爲對硬件設備的打開、讀、寫、定位等操作。而對於任何硬件設備,應用程序
只需利用這些基本操作就可以完全控制它!
¥編寫linux設備驅動需要的知識結構:
1、40%的設計模式相關知識。設計模式是系統內核限定的,做別人的下屬就得按照別人的規矩辦事。
2、30%的內核工作原理相關知識。內核是你領導,領會領導意圖才能把事情辦好。
3、30%的硬件相關知識。控制好硬件是你的的本質工作,你得把你的小弟管理好.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
***********************************************************華麗的分割線***************************************************************
一、設備文件
-------------------------------------------------------------------------------------------------------
什麼是設備文件?設備文件有什麼用?
linux設備文件就是一個實實在在看得見的文件(什麼是文件?一個txt文本就是一個文件、一個word文檔就是一個文件、一張圖片就是一個文件),只不過這個文件是“設備類型”,它是用來代表一個設備的,一般在設備驅動加載之後創建,在設備驅動卸載後移除。有了設備文件,如果想對設備進行等操作。只需對設備文件進行等操作即可。這是如何實現的呢?就是通過驅動程序實現的:設備文件操作----->系統內核----->設備驅動----->硬件設備設備文件也是一個文件,c庫中對文件的操作包括打開、寫、讀、定位等,分別是通過fopen、fwrite、fread、fseek等c庫函數實現的。設備驅動的作用就是將 這些對設備文件的打開、讀、寫、定位等操作轉化爲對硬件設備的打開、讀、寫、定位等操作。那麼僅僅是實現對設備進行打開、讀、寫、定位等操作就能完全控制好它麼?對於能與微控制器接口的設備都可以!!!!!!
------------------------------------------------------------------------------------------------------
如何創建設備文件?
有兩種方法:1.手工創建 mknod方法:mknok filename type major minor【major-主設備號;minor-次設備號;type-設備類型可取:c(字符設備),b(塊設備)】執行此命令之後就可以看到在當前目錄下生成了一個名爲filename 的文件。2.自動創建: 暫時不知
創建設備文件需要用到主次設備號,那麼什麼是主次設備號呢?
***********************************************************華麗的分割線***************************************************************
二、主次設備號:
------------------------------------------------------------------------------------------------------
什麼是主次設備號?主次設備號的作用是什麼?
設備號,就是系統爲設備分配的一個編號。在/dev目錄下-ll,可以看到有每個設備文件都有兩個號,他們就是主次設備號。
主設備號是用來標識與設備文件(什麼是設備文件?上面講過)相連的驅動程序,主設備號用來反映設備類型;次設備號被驅動程序用來辨別操作的是哪個設備,次設備號用來區分同類型的設備。設備文件需要設備號才能創建;設備驅動也需要設備號才能裝載。
設備文件正是通過主設備號找到它的驅動;設備驅動正是利用次設備號才知道他要操作的具體是哪個設備。
------------------------------------------------------------------------------------------------------
內核中如何描述設備號?
內核中用  dev_t  類型來描述,其實質是是unsigned int 32位整數,其中高12位爲主設備號,低20位爲次設備號用宏 MAJOR(dev_t dev)解出主設備號,MINOR(dev_t  dev)分解出次設備號。
------------------------------------------------------------------------------------------------------
linux內核如何分配主次設備號?
靜態申請:1.根據/documentation/devices.txt,確定一個沒有使用的主設備號
          2.使用register_chrdev_region函數註冊設備號:int register_chrdev_region(dev_t from,
          unsigned count,const char *name)【from-希望使用的設備號;count- 望申請使用的設備號數目;
          nanme-shebeiming(體現在/proc/devices)】
靜態註冊的缺點是移植時容易發生衝突,但是簡單。
動態分配:使用allo_chrdev_region分配設備號:int allo_chrdev_region(dev_t *dev,unsigned baseminor,
unsigned count,const char name)【dev 非配到的設備號(是獲取到得,不用填);baseminor-起始次設備號
;count-需要分配的設備號數目;name-設備名】
------------------------------------------------------------------------------------------------------
如何註銷設備號?
設備號是寶貴的資源,都應該在不再使用它們時釋放這些設備號
void unregister_chrdev_region(dev_t from,unsigned cout|):釋放從from開始的count個設備號
***********************************************************華麗的分割線***************************************************************
三、字符設備驅動中的三種重要數據結構
------------------------------------------------------------------------------------------------------
哪三種?有什麼作用
Struct  inode,struct file_operation,Struct file,他們是linux內核定義的。很顯然,他們就是c語言中的結構體類型。
這裏需要區分下“結構體類型”與“結構體變量”:結構體類型相當於生產產品的“模子”,而“結構體變量”就是用這些模子生產出來的產品。不同的模子生產出來的產品的樣式不同:生產人的模子造出來的是長兩隻手兩條腿一個腦袋的人,生產馬的模子造出來的是四條腿一個腦袋的馬。同一個模子可以用來生產很多產品,同一個結構體類型可以定義很多變量。
linux內核爲什麼要定義這些“模子”呢?linux內核定義他們是爲了描述不同的類。不同的類具有不同的特徵,需要用不同的結構去描述:人長兩隻手兩條腿一個腦袋,馬是長四條腿一個腦袋,雞長兩條腿一個腦袋。
------------------------------------------------------------------------------------------------------
Struct inode結構用來描述什麼?(這個模子用來生產什麼的?)重要成員有哪些?
作用:struct inode結構是用來描述設備文件屬性的,它的內容由內核來填寫的。每創建一個設備文件時,就會定義一個struct inode結構體類型的變量(用這個模子造一個產品),用來存放該文件的位置,設備號等物理信息。
重要成員:dev_t  i_rdev:設備號。在創建設備文件時,把命令中的設備號賦給它。
struct cdev *i_cdev:struct cdev 也是一個模子,他是用來描述一類設備的,用這個模子生產出來的變量都是用來描述設備一類的,它的內容是由驅動程序來填寫的。那i_cdev變量描述的是哪類設備的呢?i_cdev變量描述的就是inode結構變量所描述的這個設備文件的想關聯的這類設備。設備驅動程序在創建的時候會定義一個struct cdev結構的變量,把自己相關的信息的值賦給cdev變量的成員,在設備驅動裝載的時候它會被註冊到內核中。 當設備文件沒有被打開的時候structinode中的i_cdev變量值爲NULL;當設備文件被打開時,內核就會在已註冊到內核中的所有struct cdev 變量中找出與當前打開的設備文件具有相同主設備號的那個,然後把這個struct cdev變量的指針賦給i_cdev。具體的過程需要進行內核分析。由此可見,設備文件 是通過設備號與設備驅動關聯起來的。
------------------------------------------------------------------------------------------------------
Struct file_peration結構用來描述什麼的?重要成員有哪些?
作用:Struct file_peration用來描述能在設備上進行的操作,它的內容需要驅動程序來填寫的。它是一個函數指針(指向函數的指針!!!)的集合。結構中的成員指向驅動函數中的函數,這些函數實現一個特定的操作,對於不支持的操作保留爲NULL。實際是一張對應表,把應用程序中的操作轉換成驅動程序操作。內核爲什麼只定義這些操作呢?因爲這些操作足以完全控制任何一個硬件設備。驅動程序只需完成這些操作的是實現,其他的問題交給應用程序來處理。每個驅動程序都會定義一個struct file_operation類型的變量,並把編寫好的操作函數(讀寫打開等函數)的指針賦給這個結構變量的成員,而這個變量的指針會被賦給 在驅動程序定義的struct cdev類型的變量,然後隨struct cdev一同註冊到內核中。
重要成員:struct module *owner:它並不是一個操作,它是指向“擁有”該結構的內核模塊的指針,內核使用這個字段以避免在模塊的操作正在使用時被卸載。
loff_t (*llseek)(struct file *,loff_t ,int):llseek方法用來修改文件當前讀寫位置,並且返回新位置
ssize_t (*read)(struct file *,char_ _user * ,size_t,loff_t*):用來從設備中讀取數據
ssize_t (*write)(struct file *,char_ _user * ,size_t,loff_t*):用來向設備中寫取數據
int (*open)(struct inode*,struct file *):一般爲對設備文件執行的第一個操作,但不是必須的。
------------------------------------------------------------------------------------------------------
Struct file結構用來描述什麼?重要成員有哪些?
作用:struct file結構用來描述打開的文件,存放與文件操作有關的數據,它的內容由內核填寫的。每個文件(當然包括設備文件)在打開的時候,內核都會爲他創建一個struct file類型的變量,存放與該文件相關的操作。
重要成員:loff_t  f_pos:文件讀寫的位置(loff是內核定義的一種數據類型,其實是整型)
struct  file_operations *f_op:對的,就是上面剛講的!!!!!!struct file又是如何獲得這個成員的值的呢?文件在打開的時候,內核會根據設備號(設備號從哪兒來的呢?從文件的對應inode結構變量中找到)找到在內核中找到具有相同設備號的那個驅動,從而找到指向指向驅動file_operation變量的指針,具體的過程很複雜,設計到內核分析。以後再說。
***********************************************************華麗的分割線***************************************************************
四、字符設備的註冊
------------------------------------------------------------------------------------------------------
字符設備在內核中是如何被描述的?
在linux2.6內核中,字符設備使用struct  cdev結構來描述的,一個cdev描述的是一類設備,而這類設備可以有很多個,在設備註冊的時候需要告訴這類設備關聯的設備的個數。實質上在內核中字符設備的真正模型是struct probe結構。在內核中有一個probe結構體數組,具有255個元素,每個元素代表一個驅動,probe結構中當然包含cdev結構。至於說cdev結構是字符設備的描述,我想是因爲cdev結構能夠唯一的描述一個字符設備,並不代表內核中就是採用這個結構來描述字符設備。因此,應該把“設備”和“設備模型”兩個概念分別開來。
------------------------------------------------------------------------------------------------------
字符設備驅動是如何註冊的?
分爲以下三個步驟:
前提是你在驅動中首先定義了一個cdev類型變量和一個file_operation類型變量。
1、給cdev變量分配內存:使用cdev_alloc函數完成,struct cdev *cdec_alloc(void),目的是爲cdev變量分配動態內存。當然,如果你把它定義成靜態變量,不用再用這個
函數專門再分配內存,因爲他已經有了。
2、初始化cdev變量:使用cdev_init函數完成,void cdev_init(struct cdev *cdev,const struct file_operations *fops)【cdev-待初始化的cdev結構;fops-設備對應的操作 函數集(上面講到得)】,初始化的作用暫時還不清楚。
3、註冊cdev:使用cdev_add函數完成,int cdev_add(struct cdev *p,dev_t dev,unsigned count)【p-待添加到內核的字符設備結構;dev-設備號;count-添加的設備個數)】。cdev_add()用來將設備驅動添加到設備驅動模型(上面經過設備驅動模型)中。前面講過,內核中有一個probe結構體數組,他有255個元素,每個元素代表一個驅動,因此cdev_add()的作用實際上是將設備驅動struct cdev *p,添加到這個結構體數組的某一項中去。
***********************************************************華麗的分割線***************************************************************
五、字符設備的操作
------------------------------------------------------------------------------------------------------
字符設備的操作指的是什麼?
就是struct  file_operation中包含的那些方法(函數),這些方法的實現也正是驅動程序的本職任務。
------------------------------------------------------------------------------------------------------
字符設備的基本操作包括哪些?
就是上面講的struct  file_operation中的那些重要成員。
open:int (*open)(struct inode *,struct file *),一般爲設備的第一個操作,可以不實現,如果該項是NULL,
設備的打開操作永遠成功。
release:void (*release)(struct inode *,struct file *),當文件關閉時調用這個操作,這個操作也可以不實現。
read:ssize_t (*read)(struct file *,char__user *,size_t,loff_t *),從設備中讀數據
write:ssize_t(*write)(struct file *,char__user *,size_t,loff_t *),向設備寫數據。
lseek::off_t(*llseek)(structfile *,loff_t,int),修改文件的當前讀寫位置,並將新位置座位返回值。
要注意的是,上述操作函數的接口(什麼是接口?在這裏就是函數的參數的類型!)是不能被修改的!因爲這是內核規定的標準接口。這些就是驅動程序需要實現的操作!每種
操作對對應着一種系統調用!
上述函數被調用的時候,它的那些參數自然是系統函數(屬於內核)傳遞給它的,那系統內核又是怎麼知道這些參數的呢?我們知道在這些函數被調用之前,驅動已經被加載到
內核之中了(module_init()已經執行了),也就是說設備驅動cdev已經註冊到內核之中了!所以內核當然知道這些參數的信息了。
------------------------------------------------------------------------------------------------------
驅動程序(內核模塊)的open方法如何實現?
open方法是驅動程序用來爲以後的操作完初始化準備工作的:初始化設備(寄存器設置),標明次設備號等等。open方
法的原型如下:
int (*open)(struct inode *inode,struct file *filp)
函數名可以改,函數參數的類型一定不能改!爲什麼呢?上面提到過,open方法對應着“系統調用(什麼是系統調用?)的open函數(此open非彼open),系統調用的
open函數有標準的格式,所以這個open方法應該保持着對應,不然你的驅動函數顯然不能被系統函數正常調用。
------------------------------------------------------------------------------------------------------
read和write方法的原型又是什麼?
ssize_t  read(struct file *filp,char__user *buff,size_t count,loff_t *offp)
ssize_t write(struct file *filp, const  char__user *buff,size_t count,loff_t *offp)
其中buff是用戶空間(什麼是用戶空間和內核空間?參看,參看操作系統原理)的指針,內核代碼不能直接引用其中
的內容!!!(爲什麼?暫時不懂)。然而驅動程序(位於內核空間,屬於內核代碼)又必須訪問用戶空間的緩衝去以完成自己的工作,爲了確保安全,必須使用內核提供的
專用函數來完成。例如:
int long copy_to_user(void _ _user  *to,const void *from,unsigned long count);
int long copy_from_user(void *to,const void _ _user *from,unsigned long count)
***********************************************************華麗的分割線***************************************************************
六、字符設備的註銷
------------------------------------------------------------------------------------------------------
爲什麼要註銷設備?
因爲不需要再使用。。。。。做事要有頭有尾。
如何註銷字符設備?
void cdev_del(struct cdev *cdev)
***********************************************************華麗的分割線***************************************************************
本人享有博客文章的版權,轉載請標明出處:http://blog.csdn.net/qingyu2431

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章