?一、如何對設(shè)備操作
linux中對設(shè)備進(jìn)行操作是通過文件的方式進(jìn)行的,包括open、read、write。
對于設(shè)備文件,一般稱其為設(shè)備節(jié)點(diǎn),
節(jié)點(diǎn)有一個(gè)屬性是設(shè)備號(主設(shè)備號、次設(shè)備號),其中主設(shè)備號將設(shè)備文件與驅(qū)動(dòng)模塊對應(yīng)起來
當(dāng)我們open一個(gè)設(shè)備節(jié)點(diǎn)時(shí),告訴了kernel要操作的是是主設(shè)備號為XX的節(jié)點(diǎn),然后kernel會(huì)通過過XX來尋找合適的內(nèi)存模塊,進(jìn)而調(diào)用內(nèi)存模塊中定義的open函數(shù)
由于操作節(jié)點(diǎn)之前kernel就需要有主設(shè)備號的信息,因此主設(shè)備號的申請、具有該主設(shè)備號的字符設(shè)備的添加都需要在驅(qū)動(dòng)模塊的初始化函數(shù)中執(zhí)行
二、主設(shè)備號的申請
建議采用動(dòng)態(tài)申請的主設(shè)備號的方式,linux中有很多設(shè)備,每一個(gè)設(shè)備對應(yīng)著一個(gè)主設(shè)備號,動(dòng)態(tài)申請是由內(nèi)核分配一個(gè)沒用的主
設(shè)備號,
動(dòng)態(tài)申請函數(shù)為alloc_chrdev_region,相對應(yīng)的釋放函數(shù)為unregister_chrdev_region。
申請完后,可以從/proc/devices中讀到分配的主設(shè)備號,后面建立設(shè)備節(jié)點(diǎn)時(shí)還需要用到
三、向kernle添加字符設(shè)備
上一步向內(nèi)核申請了主設(shè)備號,就可以向kernel中添加字符設(shè)備了
kennel中一個(gè)字符設(shè)備對應(yīng)了一個(gè)結(jié)構(gòu)體cdev,這個(gè)結(jié)構(gòu)體中定義了對字符設(shè)備的操作方式file_operations(包括open、read、write),這些操作方式也需要在驅(qū)動(dòng)模塊中事先定義好。
字符設(shè)備結(jié)構(gòu)體cdev的添加步驟:
cdev初始化:cdev_init,該函數(shù)將file_operations與cdev對應(yīng)起來
向kernel添加:cdev_add,該函數(shù)將主設(shè)備號與cdev結(jié)構(gòu)體對應(yīng)起來
當(dāng)對open設(shè)備節(jié)點(diǎn)時(shí),首先通過節(jié)點(diǎn)找到主設(shè)備號,然后再kernel中搜索與主設(shè)備號相對應(yīng)的字符設(shè)備cdev,然后動(dòng)過cdev中file_operations結(jié)構(gòu)體定義的open方法(這個(gè)open是需要自己實(shí)現(xiàn)的)
四、3個(gè)重要的結(jié)構(gòu)體
一個(gè)是file_operations,這里面主要包含了驅(qū)動(dòng)的主要實(shí)現(xiàn)方法
一個(gè)是inode,這個(gè)是節(jié)點(diǎn)的信息,包含了主設(shè)備號和cdev結(jié)構(gòu)體
一個(gè)是file,當(dāng)節(jié)點(diǎn)首次被打開時(shí),就會(huì)在內(nèi)核中創(chuàng)建一個(gè)file結(jié)構(gòu)體,file結(jié)構(gòu)其充當(dāng)了file_operations中方法的紐帶,要不然read和wirte方法怎么知道操作的是那個(gè)設(shè)備的數(shù)據(jù)。
file中的自定義內(nèi)容(驅(qū)動(dòng)需要的數(shù)據(jù))一般是在open中定義,然后read和write就可以操作自定義的數(shù)據(jù)了。
下面是一個(gè)簡單的實(shí)例,可以看到驅(qū)動(dòng)是怎樣把自定義的open方法和主設(shè)備號對應(yīng)起來的
#include /*它定義了模塊的 API、類型和宏(MODULE_LICENSE、MODULE_AUTHOR等等),所有的內(nèi)核模塊都必須包含這個(gè)頭文件。*/
#include
#include //設(shè)備號相關(guān)函數(shù)
#include //內(nèi)存分配相關(guān)函數(shù)
#include
#include //設(shè)備號相關(guān)函數(shù)
#include //字符設(shè)備頭文件
#include
struct char_dev
{
int size;
char *data;
struct cdev cdev;//內(nèi)核中的字符設(shè)備
};
int major = 0;
int minor = 0;
struct char_dev char_devices;
int char_open(struct inode *inode, struct file *filep)
{
int Major = 0;
Major = MAJOR(inode-》i_rdev);
printk(“open my_char_dev major: %d\n”, Major);
return 0;
}
struct file_operations char_fops = {
.owner = THIS_MODULE,
.open = char_open,
};
static void char_exit(void) //如果init函數(shù)中調(diào)用了該函數(shù),則不應(yīng)有 __exit
{
dev_t dev;
printk(“char device driver exit \n”);
//釋放設(shè)備號
dev = MKDEV(major, minor);
unregister_chrdev_region(dev, 1);
printk(“release major %d\n”, major);
//釋放內(nèi)存
if(char_devices.data){
kfree(char_devices.data);
}
//從內(nèi)核中刪除字符設(shè)備
cdev_del(&(char_devices.cdev));
}
static int __init char_init(void)//__init一個(gè)標(biāo)記,表明是初始化函數(shù)
{
//初始化的代碼
dev_t dev;
int result;
printk(“char device driver init \n”);
//動(dòng)態(tài)向內(nèi)核申請?jiān)O(shè)備號
result = alloc_chrdev_region(&dev, 0, 1, “my_char_dev”);
major = MAJOR(dev);
minor = MINOR(dev);
printk(“alloc major %d\n”, major);
if (result 《 0) {
printk(KERN_WARNING “my_char_dev: can‘t get major %d\n”, major);
return result;
}
//為設(shè)備分配一塊內(nèi)存
char_devices.size = 100;
char_devices.data = (char*)kmalloc(char_devices.size, GFP_KERNEL);
if (!char_devices.data) {
result = -ENOMEM;
goto fail; //不能直接退出函數(shù),需要釋放設(shè)備號
}
//向內(nèi)核中添加字符設(shè)備cdev
cdev_init(&(char_devices.cdev), &char_fops);
char_devices.cdev.owner = THIS_MODULE;
char_devices.cdev.ops = &char_fops;
result = cdev_add(&(char_devices.cdev), dev, 1);
if((result 《 0)) {
printk(KERN_WARNING “Error %d adding my_char_dev\n”, result);
goto fail;
}
return 0; //成功
fail:
char_exit();
return result;
}
MODULE_LICENSE(“Dual BSD/GPL”);
//當(dāng)模塊被加載時(shí),執(zhí)行moudle_init函數(shù),該函數(shù)會(huì)調(diào)用初始化函數(shù)
module_init(char_init);
//模塊卸載時(shí),調(diào)用,釋放資源
module_exit(char_exit);
KDIR=/usr/src/linux-headers-$(shell uname -r)
PWD=$(shell pwd)
obj-m = CharDevice.o
all:
$(MAKE) -C $(KDIR) M=$(PWD)
注:驅(qū)動(dòng)insmod后,通過/proc/devices查看主設(shè)備號,然后通過mknod在/dev下創(chuàng)建設(shè)備節(jié)點(diǎn),注意保持主設(shè)備好的一致,當(dāng)前的節(jié)點(diǎn)只支持open方法,可以在demsg中進(jìn)行驗(yàn)證。
評論
查看更多