這三個結構體均在文件linux-2.6.22.6>include>linux>fs.h中定義,大部分驅動程序操作都涉及三個重要的內核數據結構,分別是file_operations,file,inode。第一個是文件操作,file_operations結構就是用來連接驅動程序操作連接到我們前面給自己保留的編號的工作的。結構定義在<linux/fs.h>中,其中包含一組指針,每個打開的文件和一組函數關聯(通過包含指向一個file_operations結構的f_op字段)。這些操作主要用來實現系統調用,也可以說文件可以認為是一個對象,而操作它的函數是方法。
慣例,file_operations 結構或者指向這類結構的指針稱為fops,這個結構中每個字段都必須指向驅動中實現特定操作的函數。
對於不支持的操作,可以對應的字段設置為NULL。
而file結構是設備驅動程序所使用的第二個重要的數據結構。它是一個內核結構,不會出現在用戶程序中。它不僅僅代表一個打開的文件。它由內核在open時創建,並傳遞給該文件上進行操作的所有函數,知道最后的close函數,在文件的所有實例都被關閉后,內核會釋放這個數據結構。
inode結構它表示打開的文件描述符。他包含了大量有關文件的信息。而只有 dev_t i_rdev; struct cdev *i_cdev 與驅動程序代碼有關用。
前者表示了設備文件的inode結構,包含了真正的設備編號,而后者表示字符設備的內核的內部結構,當其指向一個字符設備文件時,則包含了指向struct cdev結構的指針。
三者之間關系:
struct file結構體中包含有struct file_operations結構體,struct file_operations是struct file的一個域;我們在使用系統調用open()打開一個設備節點struct inode時,我們會得到一個文件struct file,同時返回一個文件描述符,該文件描述符是一個整數,我們稱之為句柄,通過訪問句柄我們能夠訪問設備文件struct file,描述符是一個有着特殊含義的整數,特定位都有一定的意義或屬性。
---------------------
作者:SkyHandy
來源:CSDN
原文:https://blog.csdn.net/u010944778/article/details/45077565
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
1. struct file_operations{ ... }結構體
1 /* 2 * NOTE: 3 * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl 4 * can be called without the big kernel lock held in all filesystems. 5 */ 6 struct file_operations { 7 struct module *owner; 8 loff_t (*llseek) (struct file *, loff_t, int); 9 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 10 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 11 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 12 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); 13 int (*readdir) (struct file *, void *, filldir_t); 14 unsigned int (*poll) (struct file *, struct poll_table_struct *); 15 int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 16 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); 17 long (*compat_ioctl) (struct file *, unsigned int, unsigned long); 18 int (*mmap) (struct file *, struct vm_area_struct *); 19 int (*open) (struct inode *, struct file *); 20 int (*flush) (struct file *, fl_owner_t id); 21 int (*release) (struct inode *, struct file *); 22 int (*fsync) (struct file *, struct dentry *, int datasync); 23 int (*aio_fsync) (struct kiocb *, int datasync); 24 int (*fasync) (int, struct file *, int); 25 int (*lock) (struct file *, int, struct file_lock *); 26 ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *); 27 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); 28 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); 29 int (*check_flags)(int); 30 int (*dir_notify)(struct file *filp, unsigned long arg); 31 int (*flock) (struct file *, int, struct file_lock *); 32 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); 33 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); 34 };
2.struct file{ ... }結構體
1 struct file 2 { 3 /* 4 * fu_list becomes invalid after file_free is called and queued via 5 * fu_rcuhead for RCU freeing 6 */ 7 union 8 { 9 struct list_head fu_list; 10 struct rcu_head fu_rcuhead; 11 } f_u; 12 struct path f_path; 13 #define f_dentry f_path.dentry 14 #define f_vfsmnt f_path.mnt 15 const struct file_operations *f_op; 16 atomic_t f_count; 17 unsigned int f_flags; 18 mode_t f_mode; 19 loff_t f_pos; 20 struct fown_struct f_owner; 21 unsigned int f_uid, f_gid; 22 struct file_ra_state f_ra; 23 24 unsigned long f_version; 25 #ifdef CONFIG_SECURITY 26 void *f_security; 27 #endif 28 /* needed for tty driver, and maybe others */ 29 void *private_data; 30 31 #ifdef CONFIG_EPOLL 32 /* Used by fs/eventpoll.c to link all the hooks to this file */ 33 struct list_head f_ep_links; 34 spinlock_t f_ep_lock; 35 #endif /* #ifdef CONFIG_EPOLL */ 36 struct address_space *f_mapping; 37 };
3. struct inode{ ... }結構體
1 /* 2 * Use sequence counter to get consistent i_size on 32-bit processors. 3 */ 4 #if BITS_PER_LONG==32 && defined(CONFIG_SMP) 5 #include <linux/seqlock.h> 6 #define __NEED_I_SIZE_ORDERED 7 #define i_size_ordered_init(inode) seqcount_init(&inode->i_size_seqcount) 8 #else 9 #define i_size_ordered_init(inode) do { } while (0) 10 #endif 11 12 struct inode { 13 struct hlist_node i_hash; 14 struct list_head i_list; 15 struct list_head i_sb_list; 16 struct list_head i_dentry; 17 unsigned long i_ino; 18 atomic_t i_count; 19 unsigned int i_nlink; 20 uid_t i_uid; 21 gid_t i_gid; 22 dev_t i_rdev; 23 unsigned long i_version; 24 loff_t i_size; 25 #ifdef __NEED_I_SIZE_ORDERED 26 seqcount_t i_size_seqcount; 27 #endif 28 struct timespec i_atime; 29 struct timespec i_mtime; 30 struct timespec i_ctime; 31 unsigned int i_blkbits; 32 blkcnt_t i_blocks; 33 unsigned short i_bytes; 34 umode_t i_mode; 35 spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ 36 struct mutex i_mutex; 37 struct rw_semaphore i_alloc_sem; 38 const struct inode_operations *i_op; 39 const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ 40 struct super_block *i_sb; 41 struct file_lock *i_flock; 42 struct address_space *i_mapping; 43 struct address_space i_data; 44 #ifdef CONFIG_QUOTA 45 struct dquot *i_dquot[MAXQUOTAS]; 46 #endif 47 struct list_head i_devices; 48 union { 49 struct pipe_inode_info *i_pipe; 50 struct block_device *i_bdev; 51 struct cdev *i_cdev; 52 }; 53 int i_cindex; 54 55 __u32 i_generation; 56 57 #ifdef CONFIG_DNOTIFY 58 unsigned long i_dnotify_mask; /* Directory notify events */ 59 struct dnotify_struct *i_dnotify; /* for directory notifications */ 60 #endif 61 62 #ifdef CONFIG_INOTIFY 63 struct list_head inotify_watches; /* watches on this inode */ 64 struct mutex inotify_mutex; /* protects the watches list */ 65 #endif 66 67 unsigned long i_state; 68 unsigned long dirtied_when; /* jiffies of first dirtying */ 69 70 unsigned int i_flags; 71 72 atomic_t i_writecount; 73 #ifdef CONFIG_SECURITY 74 void *i_security; 75 #endif 76 void *i_private; /* fs or device private pointer */ 77 };
以下為摘錄的幾篇博客:
linux驅動程序中最重要的涉及3個重要的內核數據結構,分別為file_operations,file和inode。
鏈接:http://blog.163.com/seven_7_one/blog/static/16260641220112710311323/
在linux中inode結構用於表示文件,而file結構則表示打開的文件的描述,因為對於單個文件而言可能會有許多個表示打開的文件的描述符,因而就可能會的對應有多個file結構,但是都指向單個inode結構。
在系統內部,I/O設備的存取操作通過特定的的入口來進行,而這組特定的入口由驅動程序來提供的。通常這組設備驅動的接口是由結構體file_operations向系統說明的,它定義在include/linux/fs.h中。
file_operations的數據結構如下:(2.6內核)
struct file_operations{
struct module *owner;//擁有該模塊的指針,一般THIS_MODULE
loff_t (*llseek) (struct file*, loff_t,int);//修改文件當前的讀寫位置
ssize_t (*read)(struct file *,char *, size_t, loff_t *);
//從設備同步讀取數據
ssize_t (*write)(struct file *,const char *, size_t, loff_t *);
//向設備發送數據
int (*readdir) (struct file *, void *, filldir_t);
//僅用於讀取目錄,對於設備文件,該字段為NULL
unsigned int (*poll)(struct file *, struct poll_table_struct *);
//輪詢函數,判斷目前是否可以進行非阻塞的讀取或寫入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
//執行設備IO控制命令
int (*mmap) (sturct file *, struct vm_area_struct*);
//用於請求將設備內存映射到進程地址空間
int (*open) (struct inode *, struct file *);//打開
int (*flush)(struct file *);
int (*release)(struct inode *, struct file *);//關閉
int (*synch)(struct file *, struct dentry *, int datasync);//刷新待處理的數據
int (*fasync)(int, struct file *, int);//通知設備fasync標志發生變化
int (*lock)(struct file *, int, struct file_lock);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long*, loff_t * );
sszie_t(*writev)(struct file *, const struct iovec *, unsigned long *,
loff_t *);//分散聚集型的讀寫
ssize_t (*sengpage)(struct file *, struct page *, int, size_t,loff_t *, int);
unsigned long (*get_unmaapped_area)(struct file *,unsigned long, unsigned long, unsigned long, unsigned long );
long (*fcntl)(int fd, unsigned int cmd,unsigned arg, struct file *filp);
};
隨着內核的不斷升級,file_operations結構也會越來越大,不同版本會稍有不同。
一般的設備驅動程序只需用到上面的藍色部分的五個函數!
Linux的設備文件是同硬件一一對應的,因而對設備的操作可以通過對設備文件的操作來實現。而這些操作的實現其實就是對一些標准的系統調用,如open(),read(),write(),close()等。實際上file_operations就是把系統調用和驅動程序 關聯起來的關鍵數據結構。這個結構的每一成員都對應着一個系統調用。當用戶進程利用系統調用對設備進行讀寫操作的時候,這些系統調用通過設備的主設備號和 次設備號來確定相應的驅動程序,然后讀取file_operations中相應的函數指針,接着把控制權交給函數,從而完成linux設備驅動程序的工 作。
struct file提供關於被打開的文件信息,主要供與文件系統對應的設備文件驅動程序使用。結構如下:
struct file{
mode_t f_mode;//表示文件是否可讀或可寫,FMODE_READ或FMODE_WRITE
dev_ t f_rdev ;// 用於/dev/tty
off_t f_ops;//當前文件位移
unsigned short f_flags;//文件標志,O_RDONLY,O_NONBLOCK和O_SYNC
unsigned short f_count;//打開的文件數目
unsigned short f_reada;
struct inode *f_inode;//指向inode的結構指針
struct file_operations *f_op;//文件索引指針
}
還有 struct device_struct
在系統啟動過程中的塊設備和字符設備管理表的定義在文件fs/device.h中
struct device_struct
{
const char *name;
struct file_operations *fops;
}
static struct device_struct chrdevs[MAX_CHRDEV];
static struct device_struct blkdevs[MAX_BLKDEV];
其實塊設備表和字符設備表使用了相同的數據結構。這些設備表也稱作設備開關表,不同的是他們定義了一組函數指針對設 備進行管理。而這里系統用文件操作(file_operations)代替了那組開關。文件操作是文件系統與驅動程序之間的接口,系統特殊文件在建立的時 候並沒有把兩者對應起來,只是把設備的默認文件結構和i節點結構賦給設備文件,而真正的對應定義在系統啟動后,當設備被打開時才進行的。
linux驅動編寫之二(字符設備驅動程序)

int scull_init_module(void)
{
int result;
dev_t dev = 0;
//分配設備號,register函數如果在事先知道所需要的設備號,則會很合適,可是很多情況是不知道的,因此使用alloc進行動態設備編號
if(scull_major)
{
dev = MKDEV(scull_major,scull_minor);//此功能是將設備號和次設備號轉換成dev_t類型,此類型是32位數,前12主,后次。
result = register_chrdev_region(dev,scull_nr_devs,"scull");
//register的第一個參數是設備編號的范圍的起始值,而scull_nr_devs則是連續請求設備編號的個數,而scull則是和該編號范圍關聯的設備名稱,將出現在/proc/devices和sysfs中
}
else
{
result = alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");
//動態分配設備號,內核將為我們恰當分配所需要的主設備號。此函數第一個參數是用於輸出的參數,成功完成調用后,將保存已分配范圍的第一個編號,而第二個參數是要使用的被請求的第一個次設備號,通常是0。其余兩個與register后兩個參數作用相同。
scull_major = MAJOR(dev);//獲得主設備號
printk(KERN_ALERT"THE scull is ok\n");
}
if(result<0)
{
printk(KERN_ALERT"scull:can't get major %d\n",scull_major);
return result;
}
return 0;
}
void scull_cleanup_module(void)
{
dev_t dev = MKDEV(scull_major,scull_minor);//獲得設備號
unregister_chrdev_region(dev,scull_nr_devs);//釋放設備編號,第一個設備號,第二個設備數。
}
module_init(scull_init_module);
module_exit(scull_cleanup_module);
****************************************************************************************************************************************
大部分驅動程序操作都涉及三個重要的內核數據結構,分別是file_operations,file,inode。第一個是文件操作,file_operations結構就是用來連接驅動程序操作連接到我們前面給自己保留的編號的工作的。結構定義在<linux/fs.h>中,其中包含一組指針,每個打開的文件和一組函數關聯(通過包含指向一個file_operations結構的f_op字段)。這些操作主要用來實現系統調用,也可以說文件可以認為是一個對象,而操作它的函數是方法。
慣例,file_operations 結構或者指向這類結構的指針稱為fops,這個結構中每個字段都必須指向驅動中實現特定操作的函數。
對於不支持的操作,可以對應的字段設置為NULL。
而file結構是設備驅動程序所使用的第二個重要的數據結構。它是一個內核結構,不會出現在用戶程序中。它不僅僅代表一個打開的文件。它由內核在open時創建,並傳遞給該文件上進行操作的所有函數,知道最后的close函數,在文件的所有實例都被關閉后,內核會釋放這個數據結構。
inode結構它表示打開的文件描述符。他包含了大量有關文件的信息。而只有dev_t i_rdev; struct cdev *i_cdev與驅動程序代碼有關用。
前者表示了設備文件的inode結構,包含了真正的設備編號,而后者表示字符設備的內核的內部結構,當其指向一個字符設備文件時,則包含了指向struct cdev結構的指針。
//scull設備驅動所實現的只是最重要的設備方法,所以file_operation結構被初始化為如下形式
struct file_operations scull_fops =
{
.owner = THIS_MODULE,//所有者字段,應該設置為THIS_MODULE
// .read = scull_read,
// .write = scull_write,
// .open = scull_open,
// .release = scull_release,
};
//scull中的設備注冊,scull_dev結構來表示每個設備,定義在scull.h中
static void scull_setup_cdev(struct scull_dev *dev,int index)
{
int err,devno = MKDEV(scull_major,scull_minor+index);
cdev_init(&dev->cdev,&scull_fops);//用此函數來初始化已分配到的結構,因為前面已經分配設備編號,故接下來初始化
dev->cdev.owner = THIS_MODULE;//所有者字段,應該設置為THIS_MODULE
dev->cdev.ops = &scull_fops;
err = cdev_add(&dev->cdev,devno,1);
//此調用告訴內核該結構的信息,devno是該設備對應的第一個設備編號,第三個參數是和該設備關聯的設備編號的數量,經常取1。
if(err)
{
printk(KERN_NOTICE"Error %d adding scull %d ",err,index);
}
//cdev_add這個調用有可能會失敗,如果返回的是一個為負的錯誤碼,則設備不會被添加到系統中。同時需要注意一點,在驅動程序還沒有完全准備好設備上的操作時,不能調用cdev_add.
}
**********************************************************************************************************************
//提供給驅動程序初始化的能力,為以后完成初始化作准備,大部分open都需要完成以下工作。1檢查設備特定的錯誤2如果設備首次打開,則初始化3如果必要,更新f_op指針。分配並填寫置於filp->private_data里的數據結構。
int scull_open(struct inode *inode ,struct file *filp)
{
struct scull_dev *dev;
dev = container_of(inode->i_cdev,struct scull_dev,cdev);//這個宏用來找到適當的設備結構
filp->private_data = dev;//用這個字段指向已分配的數據
if((filp->f_flags & O_ACCMODE)==O_WRONLY)
{
scull_trim(dev);//簡單的遍歷列表,並釋放所有找到的量子和量子集
}
return 0;
}
int scull_release(struct inode *inode,struct file *filp)
{
return 0;
}
//簡單的遍歷鏈表,並釋放所有找到的量子和量子集
int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next,*dptr;
int qset=dev->qset;//當前數組大小
int i;
for(dptr=dev->data;dptr;dptr=next)
{
if(dptr->data)
{
for(i=0;i<qset;i++)
{
kfree(dptr->data[i]);//釋放當前集中的一個量
}
kfree(dptr->data);
dptr->data=NULL;
}
next=dptr->next;//指向下一個
kfree(dptr);
}
//初始化
dev->size=0;
dev->quantum=scull_quantum;
dev->qset=scull_qset;
dev->data=NULL;
return 0;
}
//********************************************************************************************************************
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>//定義dev_t類型,用來保存設備編號,包括主設備號,和從設備號
#include <linux/kdev_t.h>//獲得dev_t中的主設備號和從設備號需要動用此中定義的宏
#include <linux/fs.h>//要獲得一個或者多個設備編號需要使用從中聲明的函數
#include <linux/slab.h> /* kmalloc() */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/cdev.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include "scull.h"//里面定義了設備號的初始化數值
//*********************************************************************************************************************
struct scull_dev *scull_devices;
int scull_major = SCULL_MAJOR;
int scull_minor = 0;//此數值為0時,采用動態分配
int scull_nr_devs = SCULL_NR_DEVS;//scull0-scull3
int scull_quantum = SCULL_QUANTUM;
int scull_qset = SCULL_QSET;
void scull_cleanup_module(void);
int scull_open(struct inode *inode ,struct file *filp);
int scull_release(struct inode *inode,struct file *filp);
ssize_t scull_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos);
ssize_t scull_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos);
module_param(scull_major, int, S_IRUGO);
module_param(scull_minor, int, S_IRUGO);
module_param(scull_nr_devs, int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
module_param(scull_qset, int, S_IRUGO);
//*********************************************************************************************************************
//scull設備驅動所實現的只是最重要的設備方法,所以file_operation結構被初始化為如下形式
struct file_operations scull_fops =
{
.owner = THIS_MODULE,//所有者字段,應該設置為THIS_MODULE
.read = scull_read,
.write = scull_write,
.open = scull_open,
.release = scull_release,
};
//**********************************************************************************************************************
//scull中的設備注冊,scull_dev結構來表示每個設備,定義在scull.h中
static void scull_setup_cdev(struct scull_dev *dev,int index)
{
int err,devno = MKDEV(scull_major,scull_minor+index);
cdev_init(&dev->cdev,&scull_fops);//用此函數來初始化已分配到的結構,因為前面已經分配設備編號,故接下來初始化
dev->cdev.owner = THIS_MODULE;//所有者字段,應該設置為THIS_MODULE
dev->cdev.ops = &scull_fops;
err = cdev_add(&dev->cdev,devno,1);
//此調用告訴內核該結構的信息,devno是該設備對應的第一個設備編號,第三個參數是和該設備關聯的設備編號的數量,經常取1。
if(err)
{
printk(KERN_NOTICE"Error %d adding scull %d ",err,index);
}
//cdev_add這個調用有可能會失敗,如果返回的是一個為負的錯誤碼,則設備不會被添加到系統中。同時需要注意一點,在驅動程序還沒有完全准備好設備上的操作時,不能調用cdev_add.
//printk(KERN_ALERT"SETUP OK\n");
}
//************************************************************************************************************
struct scull_qset *scull_follow(struct scull_dev *dev, int n)//順次遍歷
{
struct scull_qset *qs = dev->data;
if (! qs) {
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
return NULL;
memset(qs, 0, sizeof(struct scull_qset));
}
while (n--) {
if (!qs->next) {
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs->next == NULL)
return NULL;
memset(qs->next, 0, sizeof(struct scull_qset));
}
qs = qs->next;
continue;
}
return qs;
}
ssize_t scull_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)//讀取
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr;
int quantum = dev->quantum,qset = dev->qset;
int itemsize = quantum*qset;
int item,s_pos,q_pos,rest;
ssize_t retval = 0;
if(down_interruptible(&dev->sem))
{
return -ERESTARTSYS;
}
if(*f_pos >= dev->size)
{
goto out;
}
if(*f_pos + count>dev->size)
{
count = dev->size - *f_pos;
}
item = (long)*f_pos / itemsize; //當前位置
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest & quantum;
dptr = scull_follow(dev,item);
if(dptr == NULL || !dptr->data || !dptr->data[s_pos])
{
goto out;
}
if(count > quantum - q_pos)
{
count = quantum - q_pos;
}
if(copy_to_user(buf,dptr->data[s_pos]+q_pos,count))
{
retval = -EFAULT;
goto out;
}
*f_pos +=count;
retval = count;
out:
up(&dev->sem);
return retval;
}
ssize_t scull_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)//寫入
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr;
int quantum = dev->quantum,qset = dev->qset;
int itemsize = quantum * qset;
int item,s_pos,q_pos,rest;
ssize_t retval = -ENOMEM;
if(down_interruptible(&dev->sem))
{
return -ERESTARTSYS;
}
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
dptr = scull_follow(dev,item);
if(dptr == NULL)
{
goto out;
}
if(!dptr->data)
{
dptr->data = kmalloc(qset * sizeof(char *),GFP_KERNEL);
if(!dptr->data)
{
goto out;
}
memset(dptr->data,0,qset*sizeof(char *));
}
if(!dptr->data[s_pos])
{
dptr->data[s_pos]=kmalloc(quantum,GFP_KERNEL);
if(!dptr->data[s_pos])
{
goto out;
}
}
if(count>quantum - q_pos)
{
count = quantum - q_pos;
}
if(copy_from_user(dptr->data[s_pos]+q_pos,buf,count))
{
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
if(dev -> size < *f_pos)
{
dev ->size = *f_pos;
}
out:
up(&dev->sem);
return retval;
}
//************************************************************************************************************
//提供給驅動程序初始化的能力,為以后完成初始化作准備,大部分open都需要完成以下工作。1檢查設備特定的錯誤2如果設備首次打開,則初始化3如果必要,更新f_op指針。分配並填寫置於filp->private_data里的數據結構。
int scull_open(struct inode *inode ,struct file *filp)
{
struct scull_dev *dev;
dev = container_of(inode->i_cdev,struct scull_dev,cdev);//這個宏用來找到適當的設備結構
filp->private_data = dev;//用這個字段指向已分配的數據
if((filp->f_flags & O_ACCMODE)==O_WRONLY)
{
scull_trim(dev);//簡單的遍歷列表,並釋放所有找到的量子和量子集
up(&dev->sem);
}
return 0;
}
int scull_release(struct inode *inode,struct file *filp)
{
return 0;
}
//簡單的遍歷鏈表,並釋放所有找到的量子和量子集
int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next,*dptr;
int qset=dev->qset;//當前數組大小
int i;
for(dptr=dev->data;dptr;dptr=next)
{
if(dptr->data)
{
for(i=0;i<qset;i++)
{
kfree(dptr->data[i]);//釋放當前集中的一個量
}
kfree(dptr->data);
dptr->data=NULL;
}
next=dptr->next;//指向下一個
kfree(dptr);
}
//初始化
dev->size=0;
dev->quantum=scull_quantum;
dev->qset=scull_qset;
dev->data=NULL;
return 0;
}
//************************************************************************************************************
int scull_init_module(void)
{
int result,i;
dev_t dev = 0;
//分配設備號,register函數如果在事先知道所需要的設備號,則會很合適,可是很多情況是不知道的,因此使用alloc進行動態設備編號
if(scull_major)
{
dev = MKDEV(scull_major,scull_minor);//此功能是將設備號和次設備號轉換成dev_t類型,此類型是32位數,前12主,后次。
result = register_chrdev_region(dev,scull_nr_devs,"scull");
//register的第一個參數是設備編號的范圍的起始值,而scull_nr_devs則是連續請求設備編號的個數,而scull則是和該編號范圍關聯的設備名稱,將出現在/proc/devices和sysfs中
}
else
{
result = alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");
//動態分配設備號,內核將為我們恰當分配所需要的主設備號。此函數第一個參數是用於輸出的參數,成功完成調用后,將保存已分配范圍的第一個編號,而第二個參數是要使用的被請求的第一個次設備號,通常是0。其余兩個與register后兩個參數作用相同。
scull_major = MAJOR(dev);//獲得主設備號
printk(KERN_ALERT"THE scull is ok\n");
}
if(result<0)
{
printk(KERN_ALERT"scull:can't get major %d\n",scull_major);
return result;
}
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_devices) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));
/* Initialize each device. */
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem);
scull_setup_cdev(&scull_devices[i], i);
}
return 0;
fail:
scull_cleanup_module();
return result;
}
void scull_cleanup_module(void)
{
int i;
dev_t devno = MKDEV(scull_major,scull_minor);//獲得設備號
if (scull_devices)
{
for (i = 0; i < scull_nr_devs; i++)
{
scull_trim(scull_devices + i);
cdev_del(&scull_devices[i].cdev);
}
kfree(scull_devices);
}
unregister_chrdev_region(devno,scull_nr_devs);//釋放設備編號,第一個設備號,第二個設備數。
}
module_init(scull_init_module);
module_exit(scull_cleanup_module);
MODULE_AUTHOR("THISISGOOD");
MODULE_LICENSE("Dual BSD/GPL");
應用層和驅動的銜接,一直是一個老大難問題,若弄不清楚,總覺得驅動寫起來似是而非的。下面就說說我對他們的理解,還有就是如何實現一個驅動支持多個上設備的問題。最主要涉及兩個機制:inode和file
在驅動中:
(1)、我們先找到一個設備號devno,可以動態申請,也可以靜態設定,假設靜態設定為major,minor,通過宏MKDEV(major,minor)來生成devno
(2)、構建對設備的操作函數file_opreation結構體,里面包含了的設備的操作:open、read、write、release、ioctl等
(3)、構建cdev結構體,里面填充兩個主要成員dev(設備號)、file_operation(對設備的操作)
(4)、把cdev添加的cdev鏈表中:cdev_init、cdev_add
應用程序中:
fd=open("/dev/hello",O_RDWR)來打開設備文件,此設備節點對應有一個設備號,這是我們識別驅動和設備的橋梁。
打開 /dev/hello時,根據設備號,在cdev鏈表中找到cdev這個結構體,cdev里面包含了file_operation結構體,有設備的各種操作,打開時就調用里面的.open 函數。在這里要完成幾件事:
(1)inode節點 每一個文件都對應有一個inode節點,inode結構體里.i_fop由cdev的file_operation填充,i_dev由cdev的設備號填充
(2)file結構體中的file_operation也同樣由cdev中對應項填充,還有一項fd,對應於打開文件的文件描述符,fd和file一一對應,文件每打開一次,就有一個file結構it。所以file里面的.private就很重要,下面會說到。
還有一個問題,那就是多個相同的設備,會公用同一個驅動,所以要把每一個設備的私有數據封裝起來,構成一個私有數據結構體。對設備的每一次讀寫,都通過操作設備的私有數據結構體中的資源來完成。也就是說,驅動在加載的時候,會申請多個設備私有資源結構體,每個結構體中包含了設備的所有私有資源,雖然公用一個驅動,可是通過設備號找到此設備號對應設備的私有資源,說的有點拗口。這可以通過file結構體的.private來指向。
例如封裝私有數據的結構體為:
struct hello_device{
char buf[128]; //設備的私有資源,譬如buf
struct cdev cdev;//設備結構體,里面有devno和file_operation
……
};
前面應經提到inode中的i_cdev會指向cdev結構,所以可以由container宏來得到hello_device的地址。
所以,在驅動的open函數中有兩個參數,inode和file
int open(structc inode *inode,struct file *file){
struct hello_device *p =container(inode->i_cdev,hello_struct,cdev)
file->private=p;
}
這樣file中就包含了設備的私有數據。
驅動read函數中:
ssize_t read(fd,char __user *buf,count)
fd和file一一對應,每打開一次設備,雖然有不同的fd,但他們的file.private是一樣的。
前面主要說了一個驅動如何可以支持多個設備的問題,以及應用層和驅動之間的聯系。還有一個問題就是,如何處理過個進程訪問同一個設備的問題。