1、設備號
主設備號標識設備對應的驅動程序,次設備號由內核使用,用于確定設備文件所指的設備。
通過次設備號獲得一個指向內核設備的直接指針,也可將此設備號當作設備本地數組的索引。
設備編號用dev_t表示(Linux/types.h? 32位,其中12位表示主設備號,20位表示次設備號)。
由dev_t獲得主設備號或次設備號:MAJOR(dev_t dev); MINOR(dev_t dev)
已知主設備號和次設備號來獲取dev_t類型:MKDEV(int? major,? int? minor)
獲取一個或多個設備編號:int? register_chrdev_region(dev_t first,? unsigned int? count,? char? *name);(靜態分配,事先已知道設備號)
動態分配設備編號:int? alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);調用成功后dev會保存已分配的第一個編號。
釋放設備編號:void unregister_chrdev_region(dev_t first, unsigned int count);
接下來,驅動程序需要將設備編號和內部函數連接起來。
注:(下一步可嘗試采用動態分配設備號)
?
動態分配設備號缺點:不能預先創建設備節點(因為分配的設備號不能保證始終一致)。
2、文件操作file_operations:
這些操作將與設備編號連接起來。
__user:用于文檔,表明該指針是一個用戶空間地址。
主要成員:open, ?ioctl,? read,? write,? llseek
3、struct file結構? linux/fs.h文件描述符
每打開一個文件,內核就會創建一個對應的file結構,在open()時創建,同時會傳遞給在該文件上進行操作的所有函數(因為file結構中包含file_operations結構,而該結構包含了所有驅動操作的函數)。
內核中用filp作為執行file結構的指針。
主要成員:
Mode_t? f_mode;? loff_t? f_pos;? ?struct file_operations *f_pos;? void? private_data;?
4、inode結構
對單個文件只有一個inode,而可能有多個file(由于fork,dup操作)。
主要成員:
dev_t? i_rdev; 對表示設備文件的inode結構,該字段包含真正的設備編號。
struct cdev?*i_cdev;? ?該結構表示字符設備的內核的內部結構,當inode指向一個字符設備文件時,該字段包含指向struct cdev結構的指針。
從inode中獲取設備號: iminor(struct inode *inode);? imajor(inode);
5、字符設備注冊? /linux/cdev.h
內核使用struct cdev結構表示字符設備,所以在內核調用該設備操作之前,需要分配并注冊一個或者多個該結構。
注冊有兩種方式:
新方法:
1)定義字節的結構:
struct my_dev {
???????? struct? cdev? cdev; //此處如果定義的是指針類型,則需要申請分配內存
}my_dev;
//my_dev ->cdev = cdev_alloc();? //如果cdev是指針則需要這一步
my_dev->cdev.ops = &my_fops;
my_dev->cdev.owner = THIS_MODULE;
2)再調用cdev_init(struct cdev *cdev,? struct file_operations *fops);
3)調用cdev_add(struct cdev *dev,? dev_t num,? unsigned int count);
上面每一步都要判斷函數調用是否出錯。
舊辦法(老接口):
注冊:int register_chrdev();移除:int unregister_chrdev()
?
6、各操作函數實現
1)open?? int (*open) (struct? inode? *inode,? struct? file? *filp)
完成以下工作:傳入一個inode,創建一個file結構
n? 檢查設備特定錯誤(如未就緒);
n? 如果設備首次打開,則進行初始化;
n? 必要時更新f_op指針;
n? 分配并填寫filp->private_data;
注:inode結構是傳入的參數,對應一個特定的設備(這就是為什么要在/dev下mknodde原因),而file結構的filp是要修改的參數(傳出),對應該設備的一個文件描述符,也就是一個inode可能有多個file描述符,而每個描述符需要保存inode的信息,即存放在filp->private_data中。
2)release? int (*release) (struct inode *inode,? struct file *filp)
完成工作:傳入一個inode,釋放這個file結構
n? 釋放由open分配的、保存在filp->private_data中的內容;
n? 在最后一次close時關閉設備
dup 和fork都會在不調用open時創建新的file結構(對應同一個inode)。
注:并不是每個close調用都會調用release;只有真正釋放設備數據結構的close調用才會調用release。內核對每個file結構維護其被使用次數的計數器,無論是fork還是dup,都不會創建新的數據結構(只會有open創建),它們只是增加已有結構中的計數器而已。只有在file結構的計數為0時,close才會調用release。
3)read? ssize_t ?read(struct ?file? *filp,? char __user *buf, ?count,? loff_t *offp)
完成工作:傳入file,將count個字節數據寫入用戶地址buf,修改loff_t
由copy_to_user()實現
返回值說明:
n? 等于count:所請求的字節數讀取成功;
n? 返回值為正,但小于count:只讀取了部分數據;
n? 為0:已經達到文件尾;
n? 負值:出錯
4)write? ssize_t? write(struct ?file? *filp,? char? __user *buf,? count,? offp);
由copy_from_user()實現
返回值同上。
?
[cpp] view plaincopy
1)驅動代碼??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)注冊設備號MKDEV;??2)注冊設備驅動程序,即初始化cdev結構(嵌入到demo_devices結構中)??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)加載驅動insmod demo.ko,再使用lsmod或cat /proc/modules查看驅動是否安裝;
3)創建設備節點:mknod? /dev/yangjin c 224 0;注意:此處的節點設備號要與驅動程序中的注冊的設備號相同。
4)再編寫應用程序測試代碼:
用戶測試代碼:
[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;??}??
總結
以上是生活随笔為你收集整理的linux驱动文件操作简单介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。