Linux驱动中字符设备file的偏移

Linux中file文件的研究☠️

编者:weirdo

编著时间:2020-6-12

编者QQ:2651293248

编写说明:AT24C256这样的字符设备需要一个指针来指明读写地址,这里主要研究file结构体中的f_ops所代表的地址偏移。


1. 文件结构定义

struct file {
        struct list_head        f_list;
        struct dentry           *f_dentry;
        struct vfsmount         *f_vfsmnt;
        struct file_operations  *f_op;			//设备文件操作结构体
        atomic_t                f_count;
        unsigned int            f_flags;
        mode_t                  f_mode;			//文件打开模式
        loff_t                  f_pos;			//文件对应的读写偏移地址
        unsigned long           f_reada, f_ramax, f_raend, f_ralen, f_rawin;
        struct fown_struct      f_owner;
        unsigned int            f_uid, f_gid;
        int                     f_error;

        size_t                  f_maxcount;
         unsigned long           f_version;

         /* needed for tty driver, and maybe others */
        void                    *private_data;			//用于读报临时保存设备结构体指针
 
         /* preallocated helper kiobuf to speedup O_DIRECT */
         struct kiobuf           *f_iobuf;
         long                    f_iobuf_lock;
 };

2. 实验测试

实验结果

result

实验代码:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>

#define DEV_NAME	"myfile"
#define DEV_SIZE	0x1000

static int file_open(struct inode *inode, struct file *filp)
{
	printk("Open: filp->f_pos = %lld \n", filp->f_pos);
	return 0;
}

static int file_close(struct inode *inode, struct file *filp)
{
	printk("Close: filp->f_pos = %lld \n", filp->f_pos);
	return 0;
}

static ssize_t file_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	unsigned char dat;
	int ret;
	
	printk("Write: The value filp->f_pos = %lld, *ppos = %lld !\n", filp->f_pos, *ppos);
	
	ret = copy_from_user(&dat, buf, 1);
	
	printk("Write: The value = %c \n", dat);
	printk("Write: The value filp->f_pos = %lld, *ppos = %lld !\n", filp->f_pos, *ppos);
	
	return ret;
}


static ssize_t file_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	unsigned char dat = 0x31;
	int ret;
	
	printk("Read: The value filp->f_pos = %lld, *ppos = %lld !\n", filp->f_pos, *ppos);
	
	ret = copy_to_user(buf, &dat, 1);
	
	printk("Read: The value filp->f_pos = %lld, *ppos = %lld !\n", filp->f_pos, *ppos);
	
	return ret;
}


loff_t file_llseek (struct file *filp, loff_t offset, int whence)
 {
	loff_t new_pos; 				//新偏移量
	loff_t old_pos = filp->f_pos; 	//旧偏移量

	printk("<kernel>: file llseek !\n");
	switch(whence){
	case SEEK_SET:
		new_pos = offset;
		break;
	case SEEK_CUR:
		new_pos = old_pos + offset;
		break;
	case SEEK_END:
		new_pos = DEV_SIZE + offset;
		break;
	default:
		printk("<kernel>: Unknow whence !\n");
		return - EINVAL;
	}
	
	//偏移量的合法检查
	if(new_pos < 0 || new_pos > DEV_SIZE){ 	
		printk("Llseek: <kernel>: Set offset error !\n");
		return - EINVAL;
	}

	filp->f_pos = new_pos;
	printk("Llseek: <kernel>: The new pos = %lld and offset = %lld!\n", new_pos, offset);
	
	return new_pos; 					//正确返回新的偏移量
	
 }

static const struct file_operations file_fops= {
		.owner = THIS_MODULE,
		.open = file_open,
		.release = file_close,
		.read = file_read,
		.write = file_write,
		.llseek = file_llseek,
};


static struct miscdevice file_misc = {
		.minor = MISC_DYNAMIC_MINOR,
		.name = DEV_NAME,
		.fops = &file_fops,
};

static int __init file_init(void)
{
	int ret_val;
	pr_info("File Test Init\n");
	
	/* Register the device with the kernel */
	ret_val = misc_register(&file_misc);
	if (ret_val != 0) {
		pr_err("could not register the misc device mydev");
		return ret_val;
	}
	
	pr_info("mydev: got minor %i\n",file_misc.minor);
	return 0;
}

static void __exit file_exit(void)
{
	pr_info("File Test Exit\n");
	/* unregister the device with the Kernel */
	misc_deregister(&file_misc);
}

module_init(file_init);
module_exit(file_exit);

MODULE_AUTHOR("WEIRDO");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("This driver for test struct file");

测试程序:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<termios.h>
#include<string.h>

int main()
{	
	
	int i,fd, ret;
	char dat;

	fd = open("/dev/myfile", O_RDONLY);
	if (fd == -1) 
	{
		printf("Can not open /dev/myfile!\n");
		exit(0);
	}
	
	printf("Open file success ! \n");

	for(i=0;i<10;i++)	
	{
		read(fd,&dat,1);

		ret = lseek(fd, 5, SEEK_CUR);

		printf("AD0 = %d\n",ret);
	}

		
	close(fd);

	return 0;

}

实验注意:filp->f_ops和参数*ppos是一直的,我们需要修改filp->f_ops来使得文件指针做出改变,普通的读写操作不会使得文件指针自动的增加。

猜你喜欢

转载自www.cnblogs.com/weirdo-xo/p/13210225.html