1、設(shè)備號(hào)
主設(shè)備號(hào)標(biāo)識(shí)設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序,次設(shè)備號(hào)由內(nèi)核使用,用于確定設(shè)備文件所指的設(shè)備。
通過(guò)次設(shè)備號(hào)獲得一個(gè)指向內(nèi)核設(shè)備的直接指針,也可將此設(shè)備號(hào)當(dāng)作設(shè)備本地?cái)?shù)組的索引。
設(shè)備編號(hào)用dev_t表示(Linux/types.h? 32位,其中12位表示主設(shè)備號(hào),20位表示次設(shè)備號(hào))。
由dev_t獲得主設(shè)備號(hào)或次設(shè)備號(hào):MAJOR(dev_t dev); MINOR(dev_t dev)
已知主設(shè)備號(hào)和次設(shè)備號(hào)來(lái)獲取dev_t類型:MKDEV(int? major,? int? minor)
獲取一個(gè)或多個(gè)設(shè)備編號(hào):int? register_chrdev_region(dev_t first,? unsigned int? count,? char? *name);(靜態(tài)分配,事先已知道設(shè)備號(hào))
動(dòng)態(tài)分配設(shè)備編號(hào):int? alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);調(diào)用成功后dev會(huì)保存已分配的第一個(gè)編號(hào)。
釋放設(shè)備編號(hào):void unregister_chrdev_region(dev_t first, unsigned int count);
接下來(lái),驅(qū)動(dòng)程序需要將設(shè)備編號(hào)和內(nèi)部函數(shù)連接起來(lái)。
注:(下一步可嘗試采用動(dòng)態(tài)分配設(shè)備號(hào))
?
動(dòng)態(tài)分配設(shè)備號(hào)缺點(diǎn):不能預(yù)先創(chuàng)建設(shè)備節(jié)點(diǎn)(因?yàn)榉峙涞脑O(shè)備號(hào)不能保證始終一致)。
2、文件操作file_operations:
這些操作將與設(shè)備編號(hào)連接起來(lái)。
__user:用于文檔,表明該指針是一個(gè)用戶空間地址。
主要成員:open, ?ioctl,? read,? write,? llseek
3、struct file結(jié)構(gòu)? linux/fs.h文件描述符
每打開(kāi)一個(gè)文件,內(nèi)核就會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的file結(jié)構(gòu),在open()時(shí)創(chuàng)建,同時(shí)會(huì)傳遞給在該文件上進(jìn)行操作的所有函數(shù)(因?yàn)?span style="font-family:Calibri">file結(jié)構(gòu)中包含file_operations結(jié)構(gòu),而該結(jié)構(gòu)包含了所有驅(qū)動(dòng)操作的函數(shù))。
內(nèi)核中用filp作為執(zhí)行file結(jié)構(gòu)的指針。
主要成員:
Mode_t? f_mode;? loff_t? f_pos;? ?struct file_operations *f_pos;? void? private_data;?
4、inode結(jié)構(gòu)
對(duì)單個(gè)文件只有一個(gè)inode,而可能有多個(gè)file(由于fork,dup操作)。
主要成員:
dev_t? i_rdev; 對(duì)表示設(shè)備文件的inode結(jié)構(gòu),該字段包含真正的設(shè)備編號(hào)。
struct cdev?*i_cdev;? ?該結(jié)構(gòu)表示字符設(shè)備的內(nèi)核的內(nèi)部結(jié)構(gòu),當(dāng)inode指向一個(gè)字符設(shè)備文件時(shí),該字段包含指向struct cdev結(jié)構(gòu)的指針。
從inode中獲取設(shè)備號(hào): iminor(struct inode *inode);? imajor(inode);
5、字符設(shè)備注冊(cè)? /linux/cdev.h
內(nèi)核使用struct cdev結(jié)構(gòu)表示字符設(shè)備,所以在內(nèi)核調(diào)用該設(shè)備操作之前,需要分配并注冊(cè)一個(gè)或者多個(gè)該結(jié)構(gòu)。
注冊(cè)有兩種方式:
新方法:
1)定義字節(jié)的結(jié)構(gòu):
struct my_dev {
???????? struct? cdev? cdev; //此處如果定義的是指針類型,則需要申請(qǐng)分配內(nèi)存
}my_dev;
//my_dev ->cdev = cdev_alloc();? //如果cdev是指針則需要這一步
my_dev->cdev.ops = &my_fops;
my_dev->cdev.owner = THIS_MODULE;
2)再調(diào)用cdev_init(struct cdev *cdev,? struct file_operations *fops);
3)調(diào)用cdev_add(struct cdev *dev,? dev_t num,? unsigned int count);
上面每一步都要判斷函數(shù)調(diào)用是否出錯(cuò)。
舊辦法(老接口):
注冊(cè):int register_chrdev();移除:int unregister_chrdev()
?
6、各操作函數(shù)實(shí)現(xiàn)
1)open?? int (*open) (struct? inode? *inode,? struct? file? *filp)
完成以下工作:傳入一個(gè)inode,創(chuàng)建一個(gè)file結(jié)構(gòu)
n? 檢查設(shè)備特定錯(cuò)誤(如未就緒);
n? 如果設(shè)備首次打開(kāi),則進(jìn)行初始化;
n? 必要時(shí)更新f_op指針;
n? 分配并填寫filp->private_data;
注:inode結(jié)構(gòu)是傳入的參數(shù),對(duì)應(yīng)一個(gè)特定的設(shè)備(這就是為什么要在/dev下mknodde原因),而file結(jié)構(gòu)的filp是要修改的參數(shù)(傳出),對(duì)應(yīng)該設(shè)備的一個(gè)文件描述符,也就是一個(gè)inode可能有多個(gè)file描述符,而每個(gè)描述符需要保存inode的信息,即存放在filp->private_data中。
2)release? int (*release) (struct inode *inode,? struct file *filp)
完成工作:傳入一個(gè)inode,釋放這個(gè)file結(jié)構(gòu)
n? 釋放由open分配的、保存在filp->private_data中的內(nèi)容;
n? 在最后一次close時(shí)關(guān)閉設(shè)備
dup 和fork都會(huì)在不調(diào)用open時(shí)創(chuàng)建新的file結(jié)構(gòu)(對(duì)應(yīng)同一個(gè)inode)。
注:并不是每個(gè)close調(diào)用都會(huì)調(diào)用release;只有真正釋放設(shè)備數(shù)據(jù)結(jié)構(gòu)的close調(diào)用才會(huì)調(diào)用release。內(nèi)核對(duì)每個(gè)file結(jié)構(gòu)維護(hù)其被使用次數(shù)的計(jì)數(shù)器,無(wú)論是fork還是dup,都不會(huì)創(chuàng)建新的數(shù)據(jù)結(jié)構(gòu)(只會(huì)有open創(chuàng)建),它們只是增加已有結(jié)構(gòu)中的計(jì)數(shù)器而已。只有在file結(jié)構(gòu)的計(jì)數(shù)為0時(shí),close才會(huì)調(diào)用release。
3)read? ssize_t ?read(struct ?file? *filp,? char __user *buf, ?count,? loff_t *offp)
完成工作:傳入file,將count個(gè)字節(jié)數(shù)據(jù)寫入用戶地址buf,修改loff_t
由copy_to_user()實(shí)現(xiàn)
返回值說(shuō)明:
n? 等于count:所請(qǐng)求的字節(jié)數(shù)讀取成功;
n? 返回值為正,但小于count:只讀取了部分?jǐn)?shù)據(jù);
n? 為0:已經(jīng)達(dá)到文件尾;
n? 負(fù)值:出錯(cuò)
4)write? ssize_t? write(struct ?file? *filp,? char? __user *buf,? count,? offp);
由copy_from_user()實(shí)現(xiàn)
返回值同上。
?
[cpp] view plaincopy
1)驅(qū)動(dòng)代碼??Demo.h??#ifndef?_DEMO_H_??#define?_DEMO_H_??#include?<linux/ioctl.h>????#undef?PDEBUG??#ifdef?DEMO_DEBUG??????#ifdef?__KERNEL__??????????#define?PDEBUG(fmt,?args...)?printk(KERN_DEBUG?"DEMO:"?fmt,##?args)???????#else??????????#define?PDEBUG(fmt,?args...)?fprintf(stderr,?fmt,?##?args)??????#endif??#else??#define?PDEBUG(fmt,?args...)???#endif????#define?DEMO_MAJOR?224??#define?DEMO_MINOR?0??#define?COMMAND1?1??#define?COMMAND2?2????struct?demo_dev?{??????struct?cdev?cdev;??};????ssize_t?demo_read(struct?file?*filp,?char?__user?*buf,?size_t?count,?loff_t?*f_pos);??ssize_t?demo_write(struct?file?*filp,?const?char?__user?*buf,?size_t?count,?loff_t?*f_pos);??loff_t?demo_llseek(struct?file?*filp,?loff_t?off,?int?whence);??int?demo_ioctl(struct?inode?*inode,?struct?file?*filp,?unsigned?int?cmd,?unsigned?long?arg);????#endif??demo.c??#include?<linux/module.h>??#include?<linux/kernel.h>??#include?<linux/fs.h>??#include?<linux/errno.h>??#include?<linux/types.h>??#include?<linux/fcntl.h>??#include?<linux/cdev.h>??#include?<linux/version.h>??#include?<linux/vmalloc.h>??#include?<linux/ctype.h>??#include?<linux/pagemap.h>??#include?"demo.h"????MODULE_AUTHOR("Yangjin");??MODULE_LICENSE("Dual?BSD/GPL");????struct?demo_dev?*demo_devices;????static?unsigned?char?demo_inc?=?0;????static?u8?demo_buffer[256];????int?demo_open(struct?inode?*inode,?struct?file?*filp)??{??????struct?demo_dev?*dev;????????????if?(demo_inc?>?0)?return?-ERESTARTSYS;??????demo_inc++;??????dev?=?container_of(inode->i_cdev,?struct?demo_dev,?cdev);??????filp->private_data?=?dev;????????return?0;??}????int?demo_release(struct?inode?*inode,?struct?file?*filp)??{?????????demo_inc--;??????return?0;??}????ssize_t?demo_read(struct?file?*filp,?char?__user?*buf,?size_t?count,?loff_t?*f_pos)??{??????int?result;??????loff_t?pos?=?*f_pos;?????????if?(pos?>=?256)?{??????????result?=?0;??????????goto?out;????????????????????????????????????????????????????????????????????????????????????????????????????}??????if?(count?>?(256?-?pos))??????????count?=?256?-?pos;??????pos?+=?count;????????if?(copy_to_user(buf,?demo_buffer+*f_pos,?count))?{??????????count?=?-EFAULT;??????????goto?out;?????????}????????????*f_pos?=?pos;??out:??????return?count;??}????ssize_t??demo_write(struct?file?*filp,?const?char?__user?*buf,?size_t?count,?loff_t?*f_pos)??{??????ssize_t?retval?=?-ENOMEM;??????loff_t?pos?=?*f_pos;????????if?(pos?>?256)??????????goto?out;??????if?(count?>?(256?-?pos))???????????count?=?256?-?pos;????????pos?+=?count;??????if?(copy_from_user(demo_buffer+*f_pos,?buf,?count))?{??????????retval?=?-EFAULT;??????????goto?out;?????????}????????????*f_pos?=?pos;??????retval?=?count;??out:??????return?retval;??}????int??demo_ioctl(struct?inode?*inode,?struct?file?*filp,?unsigned?int?cmd,?unsigned?long?arg)??{??????if?(cmd?==?COMMAND1)?{??????????printk("ioctl?command?1?successfully\n");??????????return?0;?????????}??????if?(cmd?==?COMMAND2)?{??????????printk("ioctl?command?2?successfully\n");??????????return?0;?????????}??????printk("ioctl?error\n");??????return?-EFAULT;??}????loff_t?demo_llseek(struct?file?*filp,?loff_t?off,?int?whence)??{??????loff_t?pos;????????????pos?=?filp->f_pos;??????switch?(whence)?{??????case?0:??????????pos?=?off;??????????break;??????case?1:??????????pos?+=?off;??????????break;??????case?2:??????default:??????????return?-EINVAL;???????}????????????if?((pos?>?256)?||?(pos?<?0))??????????return?-EINVAL;????????????return?filp->f_pos?=?pos;??}????struct?file_operations?demo_fops?=?{??????.owner?=?THIS_MODULE,??????.llseek?=?demo_llseek,??????.read?=?demo_read,??????.write?=?demo_write,??????.ioctl?=?demo_ioctl,??????.open?=?demo_open,??????.release?=?demo_release,??};????void?demo_cleanup_module(void)??{??????dev_t?devno?=?MKDEV(DEMO_MAJOR,?DEMO_MINOR);????????????if?(demo_devices)?{??????????cdev_del(&demo_devices->cdev);??????????kfree(demo_devices);??????}??????unregister_chrdev_region(devno,?1);??}????Init?module流程:??1)注冊(cè)設(shè)備號(hào)MKDEV;??2)注冊(cè)設(shè)備驅(qū)動(dòng)程序,即初始化cdev結(jié)構(gòu)(嵌入到demo_devices結(jié)構(gòu)中)??int?demo_init_module(void)??{??????int?result;??????dev_t?dev?=?0;????????????dev?=?MKDEV(DEMO_MAJOR,?DEMO_MINOR);??????result?=?register_chrdev_region(dev,?1,?"DEMO");??????if?(result?<?0)?{??????????printk(KERN_WARNING?"DEMO:?can't?get?major?%d\n",?DEMO_MAJOR);??????????return?result;??????}??????demo_devices?=?kmalloc(sizeof(struct?demo_dev),?GFP_KERNEL);??????if?(!demo_devices)?{??????????result?=?-ENOMEM;??????????goto?fail;??????}??????memset(demo_devices,?0,?sizeof(struct?demo_dev));??????cdev_init(&demo_devices->cdev,?&demo_fops);????demo_devices->cdev.owner?=?THIS_MODULE;??????demo_devices->cdev.ops?=?&demo_fops;?????????result?=?cdev_add(&demo_devices->cdev,?dev,?1);??????if?(result)?{??????????printk(KERN_NOTICE?"error?%d?adding?demo\n",?result);??????????goto?fail;??????}??????return?0;??fail:??????demo_cleanup_module();??????return?result;??}????module_init(demo_init_module);??module_exit(demo_cleanup_module);??
?
2)加載驅(qū)動(dòng)insmod demo.ko,再使用lsmod或cat /proc/modules查看驅(qū)動(dòng)是否安裝;
3)創(chuàng)建設(shè)備節(jié)點(diǎn):mknod? /dev/yangjin c 224 0;注意:此處的節(jié)點(diǎn)設(shè)備號(hào)要與驅(qū)動(dòng)程序中的注冊(cè)的設(shè)備號(hào)相同。
4)再編寫應(yīng)用程序測(cè)試代碼:
用戶測(cè)試代碼:
[cpp] view plaincopy
#include?<sys/types.h>??#include?<unistd.h>??#include?<fcntl.h>??#include?<linux/rtc.h>??#include?<linux/ioctl.h>??#include?<stdio.h>??#include?<stdlib.h>????#define?COMMAND1?1??#define?COMMAND2?2????int?main()??{??????int?fd;??????int?i;??????char?data[256]?=?{0};??????int?retval;????????????fd?=?open("/dev/yangjin",?O_RDWR);??????if?(fd?==?1)?{??????????perror("open?error\n");??????????exit(-1);??????}??????printf("open?/dev/yangjin?successfully\n");??????retval?=?ioctl(fd,?COMMAND1,?0);??????if?(retval?==?-1)?{??????????perror("ioctl?error\n");??????????exit(-1);??????}??????printf("ioctl?command?1?successfully\n");??????retval?=?write(fd,?"yangjin",?7);??????if?(retval?==?-1)?{??????????perror("write?error\n");??????????exit(-1);??????}??????retval?=?lseek(fd,?0,?0);??????if?(retval?==?-1)?{??????????perror("lseek?error\n");??????????exit(-1);??????}??????retval?=?read(fd,?data,?10);??????if?(retval?==?-1)?{??????????perror("read?error\n");??????????exit(-1);??????}??????printf("read?successfully:?%s\n",?data);??????close(fd);??????return?0;??}??
總結(jié)
以上是生活随笔為你收集整理的linux驱动文件操作简单介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。