? ? linux將設備驅動分成幾大類:字符設備、雜項設備、塊設備、網絡設備······
本篇文章介紹雜項設備驅動的編寫,雜項設備與字符設備本質上沒什么區別,但是寫法和相關函數的使用上有區別。
除此之外雜項設備主設備號都為10,設備間通過次設備號來進行區分,與字符設備相比節約了主設備號。
雜項設備驅動編寫模式一般如下:
在linux系統下一切皆文件,設備驅動同樣秉承此“”大法“”。
對文件操作就少不了打開、讀寫、關閉等操作。
所以雜項設備驅動第一步就是進行文件操作函數的編寫。
驅動模塊最終需要加載到內核上運行,內核對文件的操作在內部有標準的系統調用,
那么怎樣將自己寫的文件操作函數掛接到系統標準的函數調用上呢?
這就是文件操作集合這個結構體所要做的工作了。
首先看一下該結構體的結構
[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);??
};??
其實我們仔細觀察,該結構體里除了第一個成員外,其余的成員都是函數指針,當我們在進一步觀察。原來他們當中很多都很熟悉。
有許多跟原來經常調用的文件操作函數很類似,其實他們作用就是將自己書寫的文件操作函數與系統標準文件操作函數調用銜接起來。
也可以把他們說成是文件操作的函數的聲明,只有在這個結構體里聲明過的函數,在對驅動操作時才能使用該函數。
什么意思呢?
如果我們沒有在這個結構體中聲明read,也就是讀取文件的函數,我們在調用該驅動模塊時就不能對該模塊進行讀操作。
該結構體成員很多,我們不必全部都要聲明,我們在實際使用時,用到那個寫哪個就可以了。
例如我們只寫一個簡單的LED燈的驅動,只涉及簡單的IO控制,因此其余的函數對我們沒有意義,我們就可以不寫。
對于第一個成員owner,它的定義有很多,在早期簡單的驅動編寫中,我們統一將它賦值“”THIS_MODULE“”。
可以防止驅動在運行中被意外卸載。至于其他用法自己可以在以后深入學習中慢慢探索。
寫完文件操作集合,還有另外一個重要結構體miscdevice,它記錄了該驅動設備的相關信息,為設備注冊提供必要信息。
[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;??
};??
對于我們比較關注的就是前三項
minor 次設備號,若果讓系統自動分配設備號 應賦值為MISC_DUNAMIC_MINOR
name 設備名稱
fops 操作集合的聲明 將文件操作集合結構體地址賦值給它即可
將以上內容完成后,就是驅動的初始化和卸載函數的書寫。
模塊初始化函數中主要完成的內容,主要是進行雜項設備的注冊。
模塊卸載函數,主要是對雜項設備的卸載。
最后聲明模塊的初始化、卸載函數的入口。聲明遵循的開源協議。
最后可編寫一個控制函數驗證驅動的正確性
下面附上一段示例代碼,此段并沒有實質性操作,只簡單演示雜項設備驅動編寫流程
[cpp]?view plain?copy
#include??
#include??
#include??
#include??
#include??
//打開函數??
static?int??misc_open(struct?inode?*node,?struct?file?*fp)??
{??
printk("this?dev?is?open ");??
return?0;??
}??
//關閉函數??
static?int?misc_close(struct?inode?*node,?struct?file?*fp)??
{??
printk("this?dev?is?close ");??
return?0;??
}??
//讀函數??
ssize_t?misc_read(struct?file?*fp,?char?__user?*buf,?size_t?size,?loff_t?*loff)??
{??
printk("this?dev?is?read ");??
return?0;??
}??
//寫函數??
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,??
};??
//設備相關信息??
struct?miscdevice?mymisc={??
.minor=MISC_DYNAMIC_MINOR,??
.name="mymisc",??
.fops=fops,??
};??
//驅動初始化??
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;??
}??
//驅動卸載??
static?void?__exit?misc_exit(void)??
{??
printk("this?module?is?exit ");??
}??
module_init(misc_init);??
module_exit(misc_exit);??
MODULE_LICENSE("GPL");??
驅動控制函數代碼
[cpp]?view plain?copy
#include??
#include??
#include??
#include??
#include??
#include??
//主函數??參數為:傳入設備名稱(含有路徑)??
int?main(int?argc,char?*argv[])????
{??
int?fd?=?open(argv[1],O_RDWR);??
if(fd?==?-1)??
{??
printf("打開失敗 ");??
return?0;??
}??
read(fd,buf,0);??
write(fd,NULL,0);??
close(fd);??
return?0;??
}??
驅動控制代碼的作用主要是將,設備節點打開對其進行讀寫關閉等操作(這里沒有實質操作,只打印一句話),只是來驗證驅動框架的正確性
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]:?進入目錄“/zhangchao/linux3.5/linux-3.5”??
Building?modules,?stage?2.??
MODPOST?1?modules??
make[1]:?離開目錄“/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模塊文件已經生成
安裝模塊
[cpp]?view plain?copy
[root@ZC/zhangchao]#insmod?misc.ko???
[??857.560000]?this?module?is?success??
打印相關信息模塊安裝成功
查看模塊
[cpp]?view plain?copy
[root@ZC/zhangchao]#lsmod??
Module??????????????????Size??Used?by????Tainted:?G????
misc????????????????????1440??0???
模塊信息存在,模塊已經成功安裝
編譯驅動控制代碼
[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??
編譯沒有報錯,生成了可執行文件 run
查看設備
[cpp]?view plain?copy
[root@ZC/zhangchao]#ls?-al?/dev/mymisc???
crw-rw----????1?root?????root???????10,??47?Mar?16?17:00?/dev/mymisc??
設備已經存在,證明雜項設備注冊成功,在設備列表內部能夠查看到設備。主設備號10 次設備號47
運行控制 程序,傳入參數為注冊的設備節點,控制程序可以通過設備節點對設備進行讀寫等操作。(在開發板上執行)
[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]#??
驅動卸載
[cpp]?view plain?copy
[root@ZC/zhangchao]#rmmod?misc.ko???
[?1577.995000]?this?module?is?exit??
模塊編譯,控制程序的編譯在宿主機上進行,開發板上只有內核,沒有編譯工具,模塊的裝載、查看、卸載在開發板上運行。
宿主機: VMware12版本下的CentOS7
開發板: 友善之臂Tiny4412開發板
交叉編譯器: arm-linux-gcc 4.5.1
內核版本:linux-3.5
?
評論
查看更多