字符设备驱动一旦注册完后就会进入file_operations结构体对设备操作进行定义。
file_operations:是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,把系统调用与驱动程序关联起来的关键数据结构,这个结构体的每个成员都对应着一个系统调用。
传统上, 一个 file_operations结构或者其一个指针称为 fops( 或者它的一些变体). 结构中的每个成员必须指向驱动中的函数, 这些函数实现一个特别的操作, 或者对于不支持的操作留置为 NULL. 当指定为 NULL 指针时内核的确切的行为是每个函数不同的。
file_operations中不少参数包含字串”__user”,这种注解是一种文档形式, 注意, 一个指针是一个不能被直接解引用的用户空间地址. 对于正常的编译”__user”没有效果, 但是它可被外部检查软件使用来找出对用户空间地址的错误使用。
下面看看file_operations中的注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | struct file_operations { struct module *owner; //所属模块,新注册的设备必须由"THIS_MODULE"这个值来指定 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); //仅用于读取目录,对于设备文件,该字段为NULL unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数,判断目前是否可以进行非阻塞的读取或者写入操作 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统,将使用此钟函数指针代替ioctl long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替 int (*mmap) (struct file *, struct vm_area_struct *); //用于请教设备内存映射到进程地址空间 int (*open) (struct inode *, struct file *); //打开函数 int (*flush) (struct file *, fl_owner_t id); //发生在进程关闭设备文件描述符副本,执行并等待,若设置为NULL,内核将忽略用户应用程序的请求 int (*release) (struct inode *, struct file *); //关闭函数 int (*fsync) (struct file *, int datasync); //刷新等待处理的数据 int (*aio_fsync) (struct kiocb *, int datasync); //异步的fsync int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化 int (*lock) (struct file *, int, struct file_lock *); // 实现文件锁,设备驱动常不去实现此lock ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); //内核调用将其数据发送到对应文件,每次一个数据页,设备驱动通常将其设置为NULL unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); //允许模块检查船体给fcntl(F_SETEL...)调用的标志 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); //readv和weritev:分散/聚集型的读写操作 int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); }; |
一般比较常用的有read,write,open,llseek,unlocked_ioctl,poll和mmap。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | int (*open)(struct inode *inode, struct file *filp); 功能:打开文件指针为filp,文件节点为inode的文件 int (*release) (struct inode *inode, struct file *filp); 功能:关闭文件指针为filp,文件节点为inode的文件 ssize_t (*read) (struct file *filp, char __user *buff, size_t count, loff_t *offp); 功能:把文件指针filp所指定文件的当前位置offp中读取count大小的数据到buff所指向的缓冲区 ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp); 功能:把buff所指向的缓冲区中count大小的数据写入到文件指针filp所指定文件的当前位置offp中 unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait); 功能:如果进程发生阻塞则则使用poll_wait()函数把指定的等待队列添加到wait中,并返回描述设备的掩码 掩码取值: POLLIN 设备可读 POLLRDNORM 数据可读 POLLOUT 设备可写 POLLWRNORM 数据可写 loff_t (*llseek) (struct file *filp, loff_t p, int where); 功能:定位文件指针filp的起始地址where中目标偏移P的位置 where取值: SEEK_SET 0 文件开头 SEEK_CUR 1 当前位置 SEEK_END 2 文件末尾 |
然活我们通过一个驱动列子来对比这些成员的使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | #include <linux/module.h> #include <linux/fs.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/irq.h> #include <linux/interrupt.h> #include <mach/regs-gpio.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/gpio.h> #define DEVICE_NAME "buttons" static struct cdev *buttons_cdev; static dev_t devno; static volatile char keystatus[]= {'0', '0', '0', '0', '0', '0'}; static volatile int press = 0; static DECLARE_WAIT_QUEUE_HEAD(buttons_wait); struct buttons_irq_desc { int irq; int pin; int pin_set; int number; char *name; }; static struct buttons_irq_desc buttons_irq[]= { {IRQ_EINT8 ,S3C2410_GPG(0),S3C2410_GPG0_EINT8 ,0,"KEY0"}, {IRQ_EINT11,S3C2410_GPG(3),S3C2410_GPG3_EINT11,1,"KEY1"}, {IRQ_EINT13,S3C2410_GPG(5),S3C2410_GPG5_EINT13,2,"KEY2"}, {IRQ_EINT14,S3C2410_GPG(6),S3C2410_GPG6_EINT14,3,"KEY3"}, {IRQ_EINT15,S3C2410_GPG(7),S3C2410_GPG7_EINT15,4,"KEY4"}, {IRQ_EINT19,S3C2410_GPG(11),S3C2410_GPG11_EINT19,5,"KEY5"}, }; static irqreturn_t buttons_interrupt(int irq,void *dev_id) { struct buttons_irq_desc *buttons_irq = (struct buttons_irq_desc *)dev_id; int down; down=!s3c2410_gpio_getpin(buttons_irq->pin); if(down!=(keystatus[buttons_irq->number] & 1)) { keystatus[buttons_irq->number]='0'+down; press=1; wake_up_interruptible(&buttons_wait); } return IRQ_RETVAL(IRQ_HANDLED); } static int buttons_open(struct inode *inode,struct file *filp) { int i; int err=0; for (i = 0; i < 6; i++) { if(buttons_irq[i].irq<0) { continue; } err=request_irq(buttons_irq[i].irq,buttons_interrupt,IRQ_TYPE_EDGE_BOTH,buttons_irq[i].name,(void *)&buttons_irq[i]); if(err) break; } if(err) { i--; for(; i>=0; i--) { if (buttons_irq[i].irq < 0) { continue; } disable_irq(buttons_irq[i].irq); free_irq(buttons_irq[i].irq,(void *)&buttons_irq[i]); } return -EBUSY; } press = 1; return 0; } static int buttons_read(struct file *filp,char __user *buff,size_t count, loff_t *offp) { unsigned long err; if (!press) { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; else wait_event_interruptible(buttons_wait, press); } press = 0; err = copy_to_user(buff, (const void *)keystatus, min(sizeof(keystatus), count)); return err ? -EFAULT : min(sizeof(keystatus), count); } static unsigned int buttons_poll( struct file *filp, struct poll_table_struct *wait) { unsigned int mask = 0; poll_wait(filp, &buttons_wait, wait); if (press) mask |= POLLIN | POLLRDNORM; return mask; } static int buttons_close(struct inode *inode, struct file *filp) { int i; for (i = 0; i < 6; i++) { if (buttons_irq[i].irq < 0) { continue; } free_irq(buttons_irq[i].irq, (void *)&buttons_irq[i]); } return 0; } static struct file_operations buttons_fops= { .owner = THIS_MODULE, .read = buttons_read, .open = buttons_open, .release = buttons_close, .poll = buttons_poll, }; static int __init buttons_init(void) { int ret,err; int major_no,monir_no; ret=alloc_chrdev_region(&devno,0,1,DEVICE_NAME); if(ret) return ret; buttons_cdev=cdev_alloc(); cdev_init(buttons_cdev,&buttons_fops); err=cdev_add(buttons_cdev,devno,1); if(err) { unregister_chrdev_region(devno,1); return -EFAULT; } major_no=MAJOR(devno); monir_no=MINOR(devno); printk(KERN_NOTICE "Major is: %d , Monir is: %d.\n", major_no, monir_no); printk(DEVICE_NAME "\tinitialized!\n"); return 0; } static void __exit buttons_exit(void) { cdev_del(buttons_cdev); unregister_chrdev_region(devno,1); printk(DEVICE_NAME "\tunloaded!\n"); } module_init(buttons_init); module_exit(buttons_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yayi"); |
粤ICP备09088251号