/********************
* 字符設(shè)備驅(qū)動(dòng)
********************/
(1)字符設(shè)備驅(qū)動(dòng)介紹
字符設(shè)備是指那些按字節(jié)流訪問(wèn)的設(shè)備,針對(duì)字符設(shè)備的驅(qū)動(dòng)稱為字符設(shè)備驅(qū)動(dòng)。
此類驅(qū)動(dòng)適合于大多數(shù)簡(jiǎn)單的硬件設(shè)備。比如并口打印機(jī),我們通過(guò)在/dev下建立一個(gè)設(shè)備文件(如/dev/printer)來(lái)訪問(wèn)它。
用戶應(yīng)用程序用標(biāo)準(zhǔn)的open函數(shù)打開(kāi)dev/printer,然后用write向文件中寫入數(shù)據(jù),用read從里面讀數(shù)據(jù)。
調(diào)用流程:
- write(): 用戶空間 -->
- sys_write(): VFS -->
- f_op->write: 特定設(shè)備的寫方法
所謂驅(qū)動(dòng),就是提供最后的write函數(shù),通過(guò)訪問(wèn)打印機(jī)硬件的寄存器直接和打印機(jī)對(duì)話
(2)主設(shè)備號(hào)和次設(shè)備號(hào)
a.設(shè)備編號(hào)介紹
對(duì)字符設(shè)備的訪問(wèn)是通過(guò)文件系統(tǒng)內(nèi)的設(shè)備文件進(jìn)行的。這些文件位于/dev。用"ls -l"查看。
設(shè)備通過(guò)設(shè)備號(hào)來(lái)標(biāo)識(shí)。設(shè)備號(hào)分兩部分,主設(shè)備號(hào)和次設(shè)備號(hào)。
通常,主設(shè)備號(hào)標(biāo)示設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序,linux允許多個(gè)驅(qū)動(dòng)共用一個(gè)主設(shè)備號(hào);
而次設(shè)備號(hào)用于確定設(shè)備文件所指的設(shè)備。
在內(nèi)核中,用dev_t類型<linux/types.h>保存設(shè)備編號(hào)。
2.4內(nèi)核中采用16位設(shè)備號(hào)(8位主,8位從),而2.6采用32位,12位主,20位從。
在驅(qū)動(dòng)中訪問(wèn)設(shè)備號(hào)應(yīng)該用<linux/kdev_t.h>中定義的宏。
獲取設(shè)備號(hào):
- MAJOR(dev_t dev)
- MINOR(dev_t dev)
- MKDEV(int major, int minor)
b.分配和釋放設(shè)備編號(hào)
在建立一個(gè)字符設(shè)備前,驅(qū)動(dòng)需要先獲得設(shè)備編號(hào)。
分配:
#include <linux/fs.h>
int register_chrdev_region(dev_t first, unsigned int count, char *name);
//first:要分配的設(shè)備編號(hào)范圍的起始值(次設(shè)備號(hào)常設(shè)為0)
//count: 所請(qǐng)求的連續(xù)編號(hào)范圍
//name: 和編號(hào)關(guān)聯(lián)的設(shè)備名稱(見(jiàn)/proc/devices)
也可以要求內(nèi)核動(dòng)態(tài)分配:
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
//firstminor: 通常為0
//*dev: 存放內(nèi)核返回的設(shè)備號(hào)
釋放:
void unregister_chrdev_region(dev_t first, unsigned int count);
//在模塊的清除函數(shù)中調(diào)用
在Documentation/devices.txt中可以找到內(nèi)核已經(jīng)分配的設(shè)備號(hào)。
c.建立設(shè)備文件
當(dāng)設(shè)備驅(qū)動(dòng)模塊向系統(tǒng)申請(qǐng)了主設(shè)備號(hào)和次設(shè)備號(hào),并且已經(jīng)通過(guò)insmod加載到內(nèi)核中后,我們就可以通過(guò)在/dev下創(chuàng)建設(shè)備文件來(lái)訪問(wèn)這個(gè)設(shè)備了。
字符設(shè)備的創(chuàng)建:$>mknod /dev/mychar c major minor
我們?cè)隍?qū)動(dòng)中常常采用動(dòng)態(tài)分配主次設(shè)備號(hào)的方法,這樣不會(huì)和系統(tǒng)中已有的設(shè)備號(hào)沖突。
動(dòng)態(tài)分配時(shí),/dev下的設(shè)備文件也需要通過(guò)分析/proc/devices動(dòng)態(tài)建立。
見(jiàn)char_load和char_unload腳本。
(3)字符設(shè)備的基本數(shù)據(jù)結(jié)構(gòu)
和字符設(shè)備驅(qū)動(dòng)關(guān)系最緊密的3個(gè)基本的數(shù)據(jù)結(jié)構(gòu)是:file, file_oepeations和inode
a.file_operations數(shù)據(jù)結(jié)構(gòu)
結(jié)構(gòu)中包含了若干函數(shù)指針。這些函數(shù)就是實(shí)際和硬件打交道的函數(shù)。
用戶空間調(diào)用的open,write等函數(shù)最終會(huì)調(diào)用這里面的指針?biāo)赶虻暮瘮?shù)。每個(gè)打開(kāi)的文件和一組函數(shù)關(guān)聯(lián)。
見(jiàn)<linux/fs.h>和驅(qū)動(dòng)書(shū)的p54
2.6內(nèi)核結(jié)構(gòu)的初始化:
struct file_operations my_fops = {
.owner = THIS_MODULE,
.llseek = my_llseek,
.read = my_read,
.write = my_write,
.ioctl = my_ioctl,
.open = my_open,
.release = my_release,
}
2.4內(nèi)核結(jié)構(gòu)的初始化:
struct file_operations my_fops = {
owner: THIS_MODULE,
llseek: my_llseek,
...
}
b.file結(jié)構(gòu)<linux/fs.h>
file是一個(gè)內(nèi)核結(jié)構(gòu)體,實(shí)際上和用戶open文件后返回的文件描述符fd對(duì)應(yīng)。
file結(jié)構(gòu)代表一個(gè)打開(kāi)的文件,系統(tǒng)中每個(gè)打開(kāi)的文件在內(nèi)核空間都有一個(gè)對(duì)應(yīng)的file結(jié)構(gòu)。
它由內(nèi)核在open時(shí)創(chuàng)建,并傳遞給在該文件上進(jìn)行操作的所有函數(shù),直到最后的close函數(shù),在文件的所有實(shí)例都被關(guān)閉后,內(nèi)核會(huì)釋放這個(gè)結(jié)構(gòu)。
用戶空間進(jìn)程fork一個(gè)新進(jìn)程后,新老進(jìn)程會(huì)共享打開(kāi)的文件描述符fd,這個(gè)操作不會(huì)在內(nèi)核空間創(chuàng)建新的file結(jié)構(gòu),只會(huì)增加已創(chuàng)建file結(jié)構(gòu)的計(jì)數(shù)。
見(jiàn)<linux/fs.h>
mode_t f_mode; 通過(guò)FMODE_READ和FMODE_WRITE標(biāo)示文件是否可讀或可寫。
loff_t f_pos; 當(dāng)前的讀寫位置,loff_t為64位
unsigned int f_flags; 文件標(biāo)志,如O_RDONLY, O_NONBLOCK, O_SYNC。標(biāo)志都定義在<linux/fcntl.h>
struct file_operations *f_op; 與文件相關(guān)的操作。內(nèi)核在執(zhí)行open時(shí)對(duì)這個(gè)指針賦值??梢栽隍?qū)動(dòng)的open方法中根據(jù)次設(shè)備號(hào)賦予不同的f_op
void *private; 通常將表示硬件設(shè)備的結(jié)構(gòu)體賦給private.
struct dentry *f_dentry; 文件對(duì)應(yīng)的目錄項(xiàng)(dentry)結(jié)構(gòu)??赏ㄟ^(guò)filp->f_dentry->d_inode訪問(wèn)索引節(jié)點(diǎn)。
file中其他的內(nèi)容和驅(qū)動(dòng)關(guān)系不大。
c.inode結(jié)構(gòu)
內(nèi)核用inode結(jié)構(gòu)表示一個(gè)實(shí)際的文件,可以是一個(gè)普通的文件,也可以是一個(gè)設(shè)備文件。
每個(gè)文件只有一個(gè)inode結(jié)構(gòu),而和文件描述符對(duì)應(yīng)的file結(jié)構(gòu)可以有多個(gè)(多次進(jìn)行open調(diào)用)。這些file都指向同一個(gè)inode。
inode定義在<linux/fs.h>
dev_t i_rdev; 對(duì)于表示設(shè)備文件的inode結(jié)構(gòu),i_rdev里包含了真正的設(shè)備編號(hào)
struct cdev *i_cdev cdev是表示字符設(shè)備的內(nèi)核的內(nèi)部結(jié)構(gòu)。當(dāng)inode表示一個(gè)字符設(shè)備時(shí),i_cdev指向內(nèi)核中的struct cdev.
其他結(jié)構(gòu)和設(shè)備驅(qū)動(dòng)關(guān)系不大。
用如下宏從inode獲取設(shè)備號(hào):
- unsigned int iminor(struct inode *inode)
- unsigned int imajor(struct inode *inode)
(4)字符設(shè)備的注冊(cè)
內(nèi)核內(nèi)部使用struct cdev結(jié)構(gòu)來(lái)表示一個(gè)字符設(shè)備。
我們的驅(qū)動(dòng)要把自己的cdev注冊(cè)到內(nèi)核中去。見(jiàn) <linux/cdev.h>
a.通常在設(shè)備的結(jié)構(gòu)中加入cdev
struct scull_dev{
...
struct cdev cdev; /* 字符設(shè)備結(jié)構(gòu) */
}
b.初始化
void cdev_init(struct cdev *cdev, struct file_operations *fops)
c.設(shè)定cdev中的內(nèi)容
- dev->cdev.owner = THIS_MODULE;
- dev->cdev.ops = &scull_fops;
d.向內(nèi)核添加設(shè)定好的cdev
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
//num: 設(shè)備對(duì)應(yīng)的第一個(gè)編號(hào)
//count: 和設(shè)備關(guān)聯(lián)的設(shè)備編號(hào)的數(shù)量,常取1
//一旦cdev_add返回,內(nèi)核就認(rèn)為設(shè)備可以使用了,所以要在調(diào)用之前完成設(shè)備的硬件初始化。
(5)老式的注冊(cè)函數(shù)
2.4中的老式注冊(cè)函數(shù)仍然在驅(qū)動(dòng)函數(shù)中大量存在,但新的代碼不應(yīng)該使用這些代碼。
注冊(cè):
int register_chrdev(unsigned int major,
const char *name,
struct file_operations *fops);
//為給定的主設(shè)備號(hào)注冊(cè)0~255作為次設(shè)備號(hào),并為每個(gè)設(shè)備建立一個(gè)對(duì)應(yīng)的默認(rèn)cdev結(jié)構(gòu)
注銷:
int unregister_chrdev(unsigned int major,
const char *name);
(6)open和release
a.open
在驅(qū)動(dòng)的open方法中完成設(shè)備的初始化工作,open完成后,硬件就可以使用,用戶程序可以通過(guò)write等訪問(wèn)設(shè)備,open的工作有:
- *檢查設(shè)備的特定錯(cuò)誤
- *如果設(shè)備首次打開(kāi),則對(duì)其進(jìn)行初始化(有可能多次調(diào)用open)
- *如有必要,更新f_op指針
- *分配并填寫置于filp->private_data中的數(shù)據(jù)
open原型;
int (*open) (struct inode *inode, struct file *filp);
//在open中通過(guò)inode獲得dev指針,并將其賦給file->private_data
//struct scull_dev *dev;
//dev = contain_of(inode->i_cdev, struct scull_dev, cdev);
//filp->private_data = dev;
//(如果dev是靜態(tài)分配的,則在open或write等方法中可以直接訪問(wèn)dev,但如果dev是在module_init時(shí)動(dòng)態(tài)分配的,則只能通過(guò)上面的方法獲得其指針)
b.release
并不是每個(gè)close調(diào)用都會(huì)引起對(duì)release方法的調(diào)用,只有當(dāng)file的計(jì)數(shù)器歸零時(shí),才會(huì)調(diào)用release,從而釋放dev結(jié)構(gòu))
(7)read和write
read和write的工作是從用戶空間拷貝數(shù)據(jù)到內(nèi)核,或是將內(nèi)核數(shù)據(jù)拷貝到用戶空間。其原型為:
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: 用戶空間的緩沖區(qū)指針
//offp: 用戶在文件中進(jìn)行存取操作的位置
//在read和write中,拷貝完數(shù)據(jù)后,應(yīng)該更新offp,并將實(shí)際完成的拷貝字節(jié)數(shù)返回。
(8)和用戶空間交換數(shù)據(jù)
read和write中的__user *buff 是用戶空間的指針,內(nèi)核不能直接引用其中的內(nèi)容(也就是不能直接對(duì)buff進(jìn)行取值操作),需要通過(guò)內(nèi)核提供的函數(shù)進(jìn)行數(shù)據(jù)拷貝。其原因是:
- a.在不同架構(gòu)下,在內(nèi)核模式中運(yùn)行時(shí),用戶空間的指針可能是無(wú)效的。
- b.用戶空間的內(nèi)存是分頁(yè)的,系統(tǒng)調(diào)用執(zhí)行時(shí),buff指向的內(nèi)存可能根本不在RAM中(被交換到磁盤中了)
- c.這可能是個(gè)無(wú)效或者惡意指針(比如指向內(nèi)核空間)
內(nèi)核和用戶空間交換數(shù)據(jù)的函數(shù)見(jiàn)<asm/uaccess.h>
如:
1. unsigned long copy_to_user(
void __user *to,
const void *from,
unsigned long count);
//向用戶空間拷貝數(shù)據(jù)
2. unsigned long copy_from_user(
void *to,
const void __user *from,
unsigned long count);
//從用戶空間獲得數(shù)據(jù)
3. int put_user(datum, ptr)
//向用戶空間拷貝數(shù)據(jù)。字節(jié)數(shù)由sizeof(*ptr)決定
//返回值為0成功,為負(fù)錯(cuò)誤。
4. int get_user(local, ptr);
//從用戶空間獲得數(shù)據(jù)。字節(jié)數(shù)由sizeof(*ptr)決定
//返回值和local都是從用戶空間獲得的數(shù)據(jù)
任何訪問(wèn)用戶空間的函數(shù)都必須是可睡眠的,這些函數(shù)需要可重入。
copy_to_user等函數(shù)如果返回值不等于0,則read或write應(yīng)向用戶空間返回-EFAULT
主設(shè)備號(hào)用來(lái)表示設(shè)備驅(qū)動(dòng), 次設(shè)備號(hào)表示使用該驅(qū)動(dòng)的設(shè)備
在內(nèi)核dev_t 表示設(shè)備號(hào), 設(shè)備號(hào)由主設(shè)備號(hào)和次設(shè)備號(hào)組成
#include <linux/kdev_t.h>
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) //根據(jù)設(shè)備號(hào)獲取主設(shè)備號(hào)
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) //獲取次設(shè)備號(hào)
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) //根據(jù)指定的主設(shè)備和次設(shè)備號(hào)生成設(shè)備號(hào)
#include <linux/fs.h>
//靜態(tài):申請(qǐng)指定的設(shè)備號(hào), from指設(shè)備號(hào), count指使用該驅(qū)動(dòng)有多少個(gè)設(shè)備(次設(shè)備號(hào)), 設(shè)備名
int register_chrdev_region(dev_t from, unsigned count, const char *name);
//name的長(zhǎng)度不能超過(guò)64字節(jié)
//動(dòng)態(tài)申請(qǐng)?jiān)O(shè)備號(hào), 由內(nèi)核分配沒(méi)有使用的主設(shè)備號(hào), 分配好的設(shè)備存在dev, baseminor指次設(shè)備號(hào)從多少開(kāi)始, count指設(shè)備數(shù), name設(shè)備名
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
//釋放設(shè)備號(hào), from指設(shè)備號(hào), count指設(shè)備數(shù)
void unregister_chrdev_region(dev_t from, unsigned count)
//cat /proc/devices 可查看設(shè)備使用情況
在內(nèi)核源碼的documentations/devices.txt可查看設(shè)備號(hào)的靜態(tài)分配情況
///內(nèi)核里使用struct cdev來(lái)描述一個(gè)字符設(shè)備驅(qū)動(dòng)
#include <linux/cdev.h>
struct cdev {
struct kobject kobj; //內(nèi)核用于管理字符設(shè)備驅(qū)動(dòng)
struct module *owner; //通常設(shè)為THIS_MODULE, 用于防止驅(qū)動(dòng)在使用中時(shí)卸載驅(qū)動(dòng)模塊
const struct file_operations *ops; //怎樣操作(vfs)
struct list_head list; //因多個(gè)設(shè)備可以使用同一個(gè)驅(qū)動(dòng), 用鏈表來(lái)記錄
dev_t dev; //設(shè)備號(hào)
unsigned int count; //設(shè)備數(shù)
};
////////字符設(shè)備驅(qū)動(dòng)//////////
1. 申請(qǐng)?jiān)O(shè)備號(hào)
2. 定義一個(gè)cdev的設(shè)備驅(qū)動(dòng)對(duì)象
struct cdev mycdev;
//定義一個(gè)file_operations的文件操作對(duì)象
struct file_operations fops = {
.owner = THIS_MODULE,
.read = 讀函數(shù)
....
};
3. 把fops對(duì)象與mycdev關(guān)聯(lián)起來(lái)
cdev_init(&mycdev, &fops); //mycdev.ops = &fops;
mycdev.owner = THIS_MODULE;
4. 把設(shè)備驅(qū)動(dòng)加入內(nèi)核里, 并指定該驅(qū)動(dòng)對(duì)應(yīng)的設(shè)備號(hào)
cdev_add(&mycdev, 設(shè)備號(hào), 次設(shè)備號(hào)的個(gè)數(shù));
5. 卸載模塊時(shí), 要把設(shè)備驅(qū)動(dòng)從內(nèi)核里移除, 并把設(shè)備號(hào)反注冊(cè)
cdev_del(&mycdev);
///////////創(chuàng)建設(shè)備文件
mknod /dev/設(shè)備文件名 c 主設(shè)備號(hào) 次設(shè)備號(hào)
////////inode節(jié)點(diǎn)對(duì)象描述一個(gè)文件/設(shè)備文件, 包括權(quán)限,設(shè)備號(hào)等信息
struct inode {
...
dev_t i_rdev; //設(shè)備文件對(duì)應(yīng)的設(shè)備號(hào)
struct cdev *i_cdev; //指向?qū)?yīng)的設(shè)備驅(qū)動(dòng)對(duì)象的地址
...
};
////file對(duì)象描述文件描述符, 在文件打開(kāi)時(shí)創(chuàng)建, 關(guān)閉時(shí)銷毀
struct file {
...
const struct file_operations *f_op; //對(duì)應(yīng)的文件操作對(duì)象的地址
unsigned int f_flags; //文件打開(kāi)的標(biāo)志
fmode_t f_mode; //權(quán)限
loff_t f_pos; //文件描述符的偏移
struct fown_struct f_owner; //屬于哪個(gè)進(jìn)程
unsigned int f_uid, f_gid;
void *private_data; //給驅(qū)動(dòng)程序員使用
...
};
通file里的成員f_path.dentry->d_inode->i_rdev可以獲取到設(shè)備文件的設(shè)備號(hào)
///錯(cuò)誤碼在<asm/errno.h> ////
/////////struct file_operations ////
inode表示應(yīng)用程序打開(kāi)的文件的節(jié)點(diǎn)對(duì)象, file表示打開(kāi)文件獲取到的文件描述符
成功返回0, 失敗返回錯(cuò)誤碼
int (*open) (struct inode *, struct file *);
buf指向用戶進(jìn)程里的緩沖區(qū), len表示buf的大小(由用戶調(diào)用read時(shí)傳進(jìn)來(lái)的)
off表示fl文件描述符的操作偏移, 返回值為實(shí)際給用戶的數(shù)據(jù)字節(jié)數(shù).
ssize_t (*read) (struct file *fl, char __user *buf, size_t len, loff_t *off);
用戶進(jìn)程把數(shù)據(jù)給驅(qū)動(dòng)
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
to指用戶進(jìn)程的緩沖區(qū), from指驅(qū)動(dòng)里裝數(shù)據(jù)的緩沖區(qū), n多少字節(jié), 返回值是0
extern inline long copy_to_user(void __user *to, const void *from, long n)
to指驅(qū)動(dòng)的... from用戶... n多少字節(jié), ....
static inline unsigned long __must_check copy_to_user(void __user *to, const
void *from, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
n = __copy_to_user(to, from, n);
return n; //返回值為剩下多少字節(jié)沒(méi)拷貝
}
extern inline long copy_from_user(void *to, const void __user *from, long n)
- 如果與用戶進(jìn)程交互的數(shù)據(jù)是1,2,4,8字節(jié)的話, 可用put_user(x,p) //x為值, p為地址
- 如果從用戶進(jìn)程獲取1,2,4字節(jié)的話, 可用get_user(x,p)
///////////
///動(dòng)態(tài)申請(qǐng)內(nèi)存, 并清零. size為申請(qǐng)多大(不要超過(guò)128K),
//flags為標(biāo)志(常為GFP_KERNEL). 成功返回地址, 失敗返回NULL
// GFP_ATOMIC, 使用系統(tǒng)的內(nèi)存緊急池
void *kmalloc(size_t size, gfp_t flags);//申請(qǐng)后要內(nèi)存要清零
void *kzalloc(size_t size, gfp_t flags); //申請(qǐng)出來(lái)的內(nèi)存已清零
void kfree(const void *objp); //回收kmalloc/kzalloc的內(nèi)存
void *vmalloc(unsigned long size); //申請(qǐng)大內(nèi)存空間
void vfree(const void *addr); //回收vmalloc的內(nèi)存
// kmalloc申請(qǐng)出來(lái)的內(nèi)存是物理地址連續(xù)的, vmalloc不一定是連續(xù)的
///// container_of(ptr, type, member) type包括member成員的結(jié)構(gòu)體,
//ptr是type類型 結(jié)構(gòu)體的member成員的地址.
//此宏根據(jù)結(jié)構(gòu)體成員的地址獲取結(jié)構(gòu)體變量的首地址
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
15 typedef struct led_dev_t {
16 dev_t mydevid;
17 unsigned int *rLEDCON;
18 unsigned int *rLEDDAT;
19 struct cdev mycdev;
20 }LED_DEV;
LED_DEV myled;
//ind->i_cdev是指向myled.mycdev成員的地址
//結(jié)構(gòu)體變量myled首地址可由container_of(ind->i_cdev, LED_DEV, mycdev)獲取;
/////// 自動(dòng)創(chuàng)建設(shè)備文件 ////
#include <linux/device.h>
1.
struct class *cl;
cl = class_create(owner, name) ; //owner指屬于哪個(gè)模塊, name類名
//創(chuàng)建出來(lái)后可以查看 /sys/class/類名
void class_destroy(struct class *cls); //用于銷毀創(chuàng)建出來(lái)的類
2. 創(chuàng)建設(shè)備文件
struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...)
__attribute__((format(printf, 5, 6)));
device_create(所屬的類, NULL, 設(shè)備號(hào), NULL, "mydev%d", 88); //在/dev/目錄下產(chǎn)生名字為mydev88的設(shè)備文件
void device_destroy(struct class *cls, dev_t devt); //用于銷毀創(chuàng)建出來(lái)的設(shè)備文件
////////
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops) ; //注冊(cè)設(shè)備號(hào)并創(chuàng)建驅(qū)動(dòng)對(duì)象
void unregister_chrdev(unsigned int major, const char *name); //反注冊(cè)設(shè)備號(hào)并刪除驅(qū)動(dòng)對(duì)象
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
cd = __register_chrdev_region(major, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
cdev = cdev_alloc();
if (!cdev)
goto out2;
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接