? ? linux將設(shè)備驅(qū)動(dòng)分成幾大類:字符設(shè)備、雜項(xiàng)設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備······
本篇文章介紹雜項(xiàng)設(shè)備驅(qū)動(dòng)的編寫,雜項(xiàng)設(shè)備與字符設(shè)備本質(zhì)上沒(méi)什么區(qū)別,但是寫法和相關(guān)函數(shù)的使用上有區(qū)別。
除此之外雜項(xiàng)設(shè)備主設(shè)備號(hào)都為10,設(shè)備間通過(guò)次設(shè)備號(hào)來(lái)進(jìn)行區(qū)分,與字符設(shè)備相比節(jié)約了主設(shè)備號(hào)。
雜項(xiàng)設(shè)備驅(qū)動(dòng)編寫模式一般如下:
在linux系統(tǒng)下一切皆文件,設(shè)備驅(qū)動(dòng)同樣秉承此“”大法“”。
對(duì)文件操作就少不了打開(kāi)、讀寫、關(guān)閉等操作。
所以雜項(xiàng)設(shè)備驅(qū)動(dòng)第一步就是進(jìn)行文件操作函數(shù)的編寫。
驅(qū)動(dòng)模塊最終需要加載到內(nèi)核上運(yùn)行,內(nèi)核對(duì)文件的操作在內(nèi)部有標(biāo)準(zhǔn)的系統(tǒng)調(diào)用,
那么怎樣將自己寫的文件操作函數(shù)掛接到系統(tǒng)標(biāo)準(zhǔn)的函數(shù)調(diào)用上呢?
這就是文件操作集合這個(gè)結(jié)構(gòu)體所要做的工作了。
首先看一下該結(jié)構(gòu)體的結(jié)構(gòu)
[cpp]?view plain?copy
struct?file_operations?{??
struct?module?*owner;??
loff_t?(*llseek)?(struct?file?*,?loff_t,?int);??
ssize_t?(*read)?(struct?file?*,?char?__user?*,?size_t,?loff_t?*);??
ssize_t?(*write)?(struct?file?*,?const?char?__user?*,?size_t,?loff_t?*);??
ssize_t?(*aio_read)?(struct?kiocb?*,?const?struct?iovec?*,?unsigned?long,?loff_t);??
ssize_t?(*aio_write)?(struct?kiocb?*,?const?struct?iovec?*,?unsigned?long,?loff_t);??
int?(*readdir)?(struct?file?*,?void?*,?filldir_t);??
unsigned?int?(*poll)?(struct?file?*,?struct?poll_table_struct?*);??
long?(*unlocked_ioctl)?(struct?file?*,?unsigned?int,?unsigned?long);??
long?(*compat_ioctl)?(struct?file?*,?unsigned?int,?unsigned?long);??
int?(*mmap)?(struct?file?*,?struct?vm_area_struct?*);??
int?(*open)?(struct?inode?*,?struct?file?*);??
int?(*flush)?(struct?file?*,?fl_owner_t?id);??
int?(*release)?(struct?inode?*,?struct?file?*);??
int?(*fsync)?(struct?file?*,?loff_t,?loff_t,?int?datasync);??
int?(*aio_fsync)?(struct?kiocb?*,?int?datasync);??
int?(*fasync)?(int,?struct?file?*,?int);??
int?(*lock)?(struct?file?*,?int,?struct?file_lock?*);??
ssize_t?(*sendpage)?(struct?file?*,?struct?page?*,?int,?size_t,?loff_t?*,?int);??
unsigned?long?(*get_unmapped_area)(struct?file?*,?unsigned?long,?unsigned?long,?unsigned?long,?unsigned?long);??
int?(*check_flags)(int);??
int?(*flock)?(struct?file?*,?int,?struct?file_lock?*);??
ssize_t?(*splice_write)(struct?pipe_inode_info?*,?struct?file?*,?loff_t?*,?size_t,?unsigned?int);??
ssize_t?(*splice_read)(struct?file?*,?loff_t?*,?struct?pipe_inode_info?*,?size_t,?unsigned?int);??
int?(*setlease)(struct?file?*,?long,?struct?file_lock?**);??
long?(*fallocate)(struct?file?*file,?int?mode,?loff_t?offset,??
loff_t?len);??
};??
其實(shí)我們仔細(xì)觀察,該結(jié)構(gòu)體里除了第一個(gè)成員外,其余的成員都是函數(shù)指針,當(dāng)我們?cè)谶M(jìn)一步觀察。原來(lái)他們當(dāng)中很多都很熟悉。
有許多跟原來(lái)經(jīng)常調(diào)用的文件操作函數(shù)很類似,其實(shí)他們作用就是將自己書(shū)寫的文件操作函數(shù)與系統(tǒng)標(biāo)準(zhǔn)文件操作函數(shù)調(diào)用銜接起來(lái)。
也可以把他們說(shuō)成是文件操作的函數(shù)的聲明,只有在這個(gè)結(jié)構(gòu)體里聲明過(guò)的函數(shù),在對(duì)驅(qū)動(dòng)操作時(shí)才能使用該函數(shù)。
什么意思呢?
如果我們沒(méi)有在這個(gè)結(jié)構(gòu)體中聲明read,也就是讀取文件的函數(shù),我們?cè)谡{(diào)用該驅(qū)動(dòng)模塊時(shí)就不能對(duì)該模塊進(jìn)行讀操作。
該結(jié)構(gòu)體成員很多,我們不必全部都要聲明,我們?cè)趯?shí)際使用時(shí),用到那個(gè)寫哪個(gè)就可以了。
例如我們只寫一個(gè)簡(jiǎn)單的LED燈的驅(qū)動(dòng),只涉及簡(jiǎn)單的IO控制,因此其余的函數(shù)對(duì)我們沒(méi)有意義,我們就可以不寫。
對(duì)于第一個(gè)成員owner,它的定義有很多,在早期簡(jiǎn)單的驅(qū)動(dòng)編寫中,我們統(tǒng)一將它賦值“”THIS_MODULE“”。
可以防止驅(qū)動(dòng)在運(yùn)行中被意外卸載。至于其他用法自己可以在以后深入學(xué)習(xí)中慢慢探索。
寫完文件操作集合,還有另外一個(gè)重要結(jié)構(gòu)體miscdevice,它記錄了該驅(qū)動(dòng)設(shè)備的相關(guān)信息,為設(shè)備注冊(cè)提供必要信息。
[cpp]?view plain?copy
struct?miscdevice??{??
int?minor;??
const?char?*name;??
const?struct?file_operations?*fops;??
struct?list_head?list;??
struct?device?*parent;??
struct?device?*this_device;??
const?char?*nodename;??
umode_t?mode;??
};??
對(duì)于我們比較關(guān)注的就是前三項(xiàng)
minor 次設(shè)備號(hào),若果讓系統(tǒng)自動(dòng)分配設(shè)備號(hào) 應(yīng)賦值為MISC_DUNAMIC_MINOR
name 設(shè)備名稱
fops 操作集合的聲明 將文件操作集合結(jié)構(gòu)體地址賦值給它即可
將以上內(nèi)容完成后,就是驅(qū)動(dòng)的初始化和卸載函數(shù)的書(shū)寫。
模塊初始化函數(shù)中主要完成的內(nèi)容,主要是進(jìn)行雜項(xiàng)設(shè)備的注冊(cè)。
模塊卸載函數(shù),主要是對(duì)雜項(xiàng)設(shè)備的卸載。
最后聲明模塊的初始化、卸載函數(shù)的入口。聲明遵循的開(kāi)源協(xié)議。
最后可編寫一個(gè)控制函數(shù)驗(yàn)證驅(qū)動(dòng)的正確性
下面附上一段示例代碼,此段并沒(méi)有實(shí)質(zhì)性操作,只簡(jiǎn)單演示雜項(xiàng)設(shè)備驅(qū)動(dòng)編寫流程
[cpp]?view plain?copy
#include??
#include??
#include??
#include??
#include??
//打開(kāi)函數(shù)??
static?int??misc_open(struct?inode?*node,?struct?file?*fp)??
{??
printk("this?dev?is?open ");??
return?0;??
}??
//關(guān)閉函數(shù)??
static?int?misc_close(struct?inode?*node,?struct?file?*fp)??
{??
printk("this?dev?is?close ");??
return?0;??
}??
//讀函數(shù)??
ssize_t?misc_read(struct?file?*fp,?char?__user?*buf,?size_t?size,?loff_t?*loff)??
{??
printk("this?dev?is?read ");??
return?0;??
}??
//寫函數(shù)??
ssize_t?misc_write(struct?file?*fp,?const?char?__user?*buf,?size_t?size,?loff_t?*loff)??
{??
printk("this?dev?is?write ");??
return?0;??
}??
[cpp]?view plain?copy
//文件操作集合??
struct?file_operations?fops={??
.owner=THIS_MODULE,??
.open=misc_open,??
.read=misc_read,??
.write=misc_write,??
.release=misc_close,??
};??
//設(shè)備相關(guān)信息??
struct?miscdevice?mymisc={??
.minor=MISC_DYNAMIC_MINOR,??
.name="mymisc",??
.fops=fops,??
};??
//驅(qū)動(dòng)初始化??
static?int?__init?misc_init(void)??
{??
if((misc_register(&mymisc))??
{??
printk("this?module?is?insmod?fail ");??
return?-1;??
}??
printk("this?module?is?success ");??
return?0;??
}??
//驅(qū)動(dòng)卸載??
static?void?__exit?misc_exit(void)??
{??
printk("this?module?is?exit ");??
}??
module_init(misc_init);??
module_exit(misc_exit);??
MODULE_LICENSE("GPL");??
驅(qū)動(dòng)控制函數(shù)代碼
[cpp]?view plain?copy
#include??
#include??
#include??
#include??
#include??
#include??
//主函數(shù)??參數(shù)為:傳入設(shè)備名稱(含有路徑)??
int?main(int?argc,char?*argv[])????
{??
int?fd?=?open(argv[1],O_RDWR);??
if(fd?==?-1)??
{??
printf("打開(kāi)失敗 ");??
return?0;??
}??
read(fd,buf,0);??
write(fd,NULL,0);??
close(fd);??
return?0;??
}??
驅(qū)動(dòng)控制代碼的作用主要是將,設(shè)備節(jié)點(diǎn)打開(kāi)對(duì)其進(jìn)行讀寫關(guān)閉等操作(這里沒(méi)有實(shí)質(zhì)操作,只打印一句話),只是來(lái)驗(yàn)證驅(qū)動(dòng)框架的正確性
makefile代碼如下:
[cpp]?view plain?copy
KERN_DIR?=?/zhangchao/linux3.5/linux-3.5??
all:??
make?-C?$(KERN_DIR)?M=`pwd`?modules??
clean:??
make?-C?$(KERN_DIR)?M=`pwd`?modules?clean??
obj-m?+=?misc.o??
編譯模塊
[cpp]?view plain?copy
[root@CentOS?zhangchao]#?make??
make?-C?/zhangchao/linux3.5/linux-3.5?M=`pwd`?modules??
make[1]:?進(jìn)入目錄“/zhangchao/linux3.5/linux-3.5”??
Building?modules,?stage?2.??
MODPOST?1?modules??
make[1]:?離開(kāi)目錄“/zhangchao/linux3.5/linux-3.5”??
[root@CentOS?zhangchao]#?ls??
Makefile????misc.c???misc.mod.c??misc.o?????????Module.symvers??
misc_app.c??misc.ko??misc.mod.o??modules.order??
[root@CentOS?zhangchao]#???
可以看到.ko模塊文件已經(jīng)生成
安裝模塊
[cpp]?view plain?copy
[root@ZC/zhangchao]#insmod?misc.ko???
[??857.560000]?this?module?is?success??
打印相關(guān)信息模塊安裝成功
查看模塊
[cpp]?view plain?copy
[root@ZC/zhangchao]#lsmod??
Module??????????????????Size??Used?by????Tainted:?G????
misc????????????????????1440??0???
模塊信息存在,模塊已經(jīng)成功安裝
編譯驅(qū)動(dòng)控制代碼
[cpp]?view plain?copy
[root@CentOS?zhangchao]#?arm-linux-gcc?misc_app.c?-o?run??
[root@CentOS?zhangchao]#?ls??
main.c????man?????????misc.c???misc.mod.c??misc.o?????????Module.symvers??
Makefile??misc_app.c??misc.ko??misc.mod.o??modules.order??run??
編譯沒(méi)有報(bào)錯(cuò),生成了可執(zhí)行文件 run
查看設(shè)備
[cpp]?view plain?copy
[root@ZC/zhangchao]#ls?-al?/dev/mymisc???
crw-rw----????1?root?????root???????10,??47?Mar?16?17:00?/dev/mymisc??
設(shè)備已經(jīng)存在,證明雜項(xiàng)設(shè)備注冊(cè)成功,在設(shè)備列表內(nèi)部能夠查看到設(shè)備。主設(shè)備號(hào)10 次設(shè)備號(hào)47
運(yùn)行控制 程序,傳入?yún)?shù)為注冊(cè)的設(shè)備節(jié)點(diǎn),控制程序可以通過(guò)設(shè)備節(jié)點(diǎn)對(duì)設(shè)備進(jìn)行讀寫等操作。(在開(kāi)發(fā)板上執(zhí)行)
[cpp]?view plain?copy
[root@ZC/zhangchao]#./run?/dev/mymisc???
[?1487.635000]?this?dev?is?open??
[?1487.635000]?this?dev?is?read??
[?1487.635000]?this?dev?is?write??
[?1487.635000]?this?dev?is?close??
[root@ZC/zhangchao]#??
驅(qū)動(dòng)卸載
[cpp]?view plain?copy
[root@ZC/zhangchao]#rmmod?misc.ko???
[?1577.995000]?this?module?is?exit??
模塊編譯,控制程序的編譯在宿主機(jī)上進(jìn)行,開(kāi)發(fā)板上只有內(nèi)核,沒(méi)有編譯工具,模塊的裝載、查看、卸載在開(kāi)發(fā)板上運(yùn)行。
宿主機(jī): VMware12版本下的CentOS7
開(kāi)發(fā)板: 友善之臂Tiny4412開(kāi)發(fā)板
交叉編譯器: arm-linux-gcc 4.5.1
內(nèi)核版本:linux-3.5
?
評(píng)論
查看更多