1. 主設(shè)備號和次設(shè)備號
Linux下一個設(shè)備都有主設(shè)備號和次設(shè)備號跌前。主設(shè)備和次設(shè)備號統(tǒng)稱設(shè)備號站欺,而VxWorks中沒有設(shè)備號這種用法,Linux下主設(shè)備號用來表示一個特定的驅(qū)動子刮。次設(shè)備號用來表示該驅(qū)動程序所驅(qū)動的各個設(shè)備淌友。
2. 初識cdev結(jié)構(gòu)
在Linux內(nèi)核中使用cdev結(jié)構(gòu)體來描述字符設(shè)備,該結(jié)構(gòu)體是所有字符設(shè)備的抽象骗卜,其中包含了大量的字符設(shè)備的共性宠页,如下:
struct cdev { struct kobject kobj; //內(nèi)嵌的kobject結(jié)構(gòu),用于內(nèi)核設(shè)備驅(qū)動模型的管理 struct module *owner; //指向包含該結(jié)構(gòu)的模塊的指針寇仓,用于引用計數(shù) const struct file_operations *ops; //指向字符設(shè)備操作函數(shù)集合 struct list_head list; //該結(jié)構(gòu)將使用驅(qū)動的字符設(shè)備連接成一個鏈表 dev_t dev; //該字符設(shè)備的起始設(shè)備號举户,一個設(shè)備可能有多個設(shè)備號 unsigned int count; //使用該字符設(shè)備驅(qū)動的設(shè)備數(shù)量 };
- kobject結(jié)構(gòu)用于內(nèi)核管理字符設(shè)備,驅(qū)動開發(fā)人員一般不使用該成員遍烦。
- ops是指向file_operations結(jié)構(gòu)的指針俭嘁,該結(jié)構(gòu)定義了操作字符設(shè)備的函數(shù)。
- dev用來存儲字符設(shè)備所申請的設(shè)備號服猪。
- count表示目前有多少個字符設(shè)備在使用該驅(qū)動供填。當使用rmmod卸載模塊的時候,若count成員不為0罢猪,那么系統(tǒng)不允許卸載模塊
- list是一個雙向的鏈表捕虽,用于將其他的結(jié)構(gòu)體連接成一個雙向鏈表。
struct list_head{struct list_head *prev, *next;}
2.2 file_operations結(jié)構(gòu)體
file_operations是一個對設(shè)備進行操作的抽象結(jié)構(gòu)體坡脐,Linux內(nèi)核的設(shè)計非常巧妙泄私,內(nèi)核允許為設(shè)備建立一個設(shè)備文件,對設(shè)備文件的所有操作,就相當于對設(shè)備進行操作晌端。這一點Vxworks與Linux相似捅暴,也是建立一個設(shè)備文件,然后將對設(shè)備的操作映射到這個設(shè)備文件上來咧纠。Linux中file_operations的功能很多蓬痒,該結(jié)構(gòu)的定義目前比較龐大,而Vxworks中則要小的多漆羔。Vxworks使用iosDrvInstall()函數(shù)向I/O子系統(tǒng)注冊驅(qū)動的函數(shù)梧奢。對于file_operations中的功能不必全部實現(xiàn)。
2.3 cedv和file_operations結(jié)構(gòu)體的關(guān)系
通常驅(qū)動開發(fā)人員會將特定的數(shù)據(jù)結(jié)構(gòu)放到cdev結(jié)構(gòu)體之后演痒,在定義一個新的結(jié)構(gòu)體亲轨,自定義字符設(shè)備結(jié)構(gòu)中就包含cdev這個特定的結(jié)構(gòu)(vxWorks中類似的定義就是會為每一個設(shè)備定義一個設(shè)備頭DEV_HDR結(jié)構(gòu)體,該結(jié)構(gòu)體必須是自定義結(jié)構(gòu)體的第一個成員)鸟顺。cdev結(jié)構(gòu)體中有一個指向file_operations的指針惦蚊,這個指針指向的函數(shù)就可以用來操作硬件,或者是自定義字符設(shè)備中的其他數(shù)據(jù)讯嫂,從而起到控制設(shè)備的作用蹦锋。
3. 字符設(shè)備驅(qū)動的組成
3.1 字符設(shè)備加載和卸載
在字符設(shè)備的加載中,應(yīng)該要實現(xiàn)字符設(shè)備號的申請和cdev的注冊欧芽。在字符設(shè)備的卸載時莉掂,應(yīng)該要實現(xiàn)字符設(shè)備號的釋放和cedv的注銷。
4.設(shè)備實例:系統(tǒng)CMOS
下面實現(xiàn)的是一個字符設(shè)備驅(qū)動程序用來訪問系統(tǒng)的CMOS千扔。在PC兼容的硬件(如下如)上的BIOS使用CMOS存儲系統(tǒng)信息憎妙,如啟動選項、引導(dǎo)順序昏鹃、系統(tǒng)數(shù)據(jù)等,我們可以通過BIOS設(shè)置菜單對其進行配置诀诊。借助CMOS設(shè)備驅(qū)動程序洞渤,你可以像訪問普通文件一樣訪問兩個PC CMOS存儲體。應(yīng)用程序可以在/dev/cmos/0和/dev/coms/1上運行属瓣,使用I/O系統(tǒng)調(diào)用訪問兩個存儲體當中的數(shù)據(jù)载迄。因為BIOS分配給CMOS域的存儲粒度是比特級別的,所以驅(qū)動程序能夠進行比特級的訪問抡蛙,因此护昧,read()可以獲得指定數(shù)目的比特,并根據(jù)讀取的比特數(shù)移動內(nèi)部文件的指針粗截。
通過兩個I/O地址(一個索引寄存器惋耙,一個數(shù)據(jù)寄存器)訪問CMOS,如下所示,必須要在索引寄存器中指定準備訪問的CMOS存儲器的偏移绽榛,并通過數(shù)據(jù)寄存器來交換數(shù)據(jù)湿酸。
先進行一些基本的定義:
設(shè)備的初始化過程如下:
初始化首先調(diào)用alloc_chrdev_region()
- 動態(tài)申請一個未使用的主設(shè)備號,若調(diào)用功灭美,dev_number中存放的就是主設(shè)備號推溃。第二個參數(shù)是設(shè)備的起始次設(shè)備號,第三個參數(shù)是支持的次設(shè)備號數(shù)目届腐,最后一個參數(shù)是和CMOS關(guān)聯(lián)的設(shè)備名稱铁坎,它將會出現(xiàn)在/proc/devices中.
- class_create()為此設(shè)備構(gòu)建sysfs的入口點。
- cdev_init()將文件操作(cmos_fops)和cdev關(guān)聯(lián).
- cdev_add()將通過alloc_chrdev_region()分配的主/次設(shè)備號和cdev連接在一起犁苏。