linux驱动开发--字符设备:添加文件指针偏移的功能

来源:互联网 发布:马士兵java源代码 编辑:程序博客网 时间:2024/05/18 16:38
设备移位操作llseek
对应lseek系统调用的设备移位操作为llseek
默认情况为允许设备移位操作
大部分字符设备提供的都是数据流而不是一个数据区,比如串口,对于这些设备而言移位操作毫无意义
设备可选择是否支持移位操作

支持设备移位操作
loff_t (*llseek)(struct file *filp, loff_t off, int whence);
filp:待操作的设备文件file结构体指针
off:待操作的定位偏移值(可正可负)
whence:待操作的定位起始位置
返回:返回移位后的新文件读、写位置,并且新位置总为正值
定位起始位置
  SEEK_SET:0,表示文件开头
  SEEK_CUR:1,表示当前位置

  SEEK_END:2,表示文件尾


支持设备移位操作函数
  完成设备移位操作函数
  应该检查用户请求的定位操作合法性,若不合法,应该返回-EINVAL
  在读、写操作中完成读、写更新文件位置

  应该根据当前文件位置检查当前读、写操作合法性


设备移位操作函数
  定义新位置变量
  switch(whence)重新计算新位置
  判断如果新位置为负或超过缓冲区最大值返回-EINVAL
  用新位置更新filp中的f_pos

  返回新位置


在读、写操作中完成读、写后更新文件位置
  定义实际可读、写字节数变量
  根据当前文件位置和缓冲区最大值判断新的实际可读、写字节数变量值
  根据实际可读、写字节数变量读、写数据
  根据实际可读、写字节数变量更新文件位置

  返回实际可读、写字节数变量值

/***Copyright (c) 2013.TianYuan*All rights reserved.**文件名称: char_device_driver09.c*文件标识: copy_to_user 、copy_from_user函数的使用。添加偏移的功能**当前版本:1.0*作者:wuyq **取代版本:xxx*原作者:xxx*完成日期:2013-11-29*/#include <linux/init.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/slab.h>#include <asm/uaccess.h>MODULE_LICENSE("GPL");#define CDD_MAJOR200//cat /proc/devices找一个尚未使用的#define CDD_MINOR0#define CDD_COUNT10dev_t dev = 0;u32 cdd_major = 0;u32 cdd_minor = 0;struct class *dev_class = NULL;#define BUF_SIZE100struct cdd_cdev{struct cdev cdev;struct device *dev_device;u8 led;char kbuf[BUF_SIZE];u32 data_len;//记录缓冲区中已经写入数据的长度};struct cdd_cdev *cdd_cdevp = NULL;int cdd_open(struct inode* inode, struct file *filp){struct cdd_cdev *pcdevp = NULL;printk("enter cdd_open!\n");pcdevp = container_of(inode->i_cdev, struct cdd_cdev, cdev);printk("led = %d\n", pcdevp->led);filp->private_data = pcdevp;return 0;}int cdd_read(struct file *filp, char __user *buf, size_t count, loff_t *offset){int ret = 0;u32 pos = *offset;u32 cnt = count;struct cdd_cdev *cdevp = filp->private_data;//printk("enter cdd_read!\n");if(cnt > (cdevp->data_len-pos) ){cnt = cdevp->data_len - pos;}ret = copy_to_user(buf, cdevp->kbuf+pos, cnt);//printk("kernel kbuf content:%s\n", cdevp->kbuf);*offset += cnt;return ret;}int cdd_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset){int ret = 0;struct cdd_cdev *cdevp = filp->private_data;u32 pos = *offset;u32 cnt = count;//printk("enter cdd_write!\n");if(cnt > (BUF_SIZE - pos) ){cnt = BUF_SIZE - pos;}ret = copy_from_user(cdevp->kbuf+pos, buf, cnt);*offset += cnt;if(*offset > cdevp->data_len){cdevp->data_len = *offset;}return ret;}int cdd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data){printk("enter cdd_ioctl!\n");return 0;}int cdd_release(struct inode *inode, struct file *filp){printk("enter cdd_release!\n");return 0;}loff_t cdd_llseek(struct file *filp, loff_t offset, int whence){struct cdd_cdev *pcdevp = filp->private_data;loff_t newpos = 0;switch(whence){case SEEK_SET:newpos = offset;break;case SEEK_CUR:newpos = filp->f_pos + offset;break;case SEEK_END:newpos = pcdevp->data_len + offset;break;default:return -EINVAL;//无效的参数}if( newpos<0 || newpos>= BUF_SIZE ){return -EINVAL;}filp->f_pos = newpos;return newpos;}struct file_operations cdd_fops = {.owner = THIS_MODULE,.open = cdd_open,.read = cdd_read,.write = cdd_write,.ioctl = cdd_ioctl,.release = cdd_release,.llseek = cdd_llseek,};int __init cdd_init(void){int ret = 0;int i = 0;if(cdd_major){dev = MKDEV(CDD_MAJOR, CDD_MINOR);//生成设备号//注册设备号;1、要注册的起始设备号2、连续注册的设备号个数3、名字ret = register_chrdev_region(dev, CDD_COUNT, "cdd_demo");}else{// 动态分配设备号ret = alloc_chrdev_region(&dev, cdd_minor, CDD_COUNT, "cdd_demo02");}if(ret < 0){printk("register_chrdev_region failed!\n");goto failure_register_chrdev;}//获取主设备号cdd_major = MAJOR(dev);printk("cdd_major = %d\n", cdd_major);cdd_cdevp = kzalloc(sizeof(struct cdd_cdev)*CDD_COUNT, GFP_KERNEL);if(IS_ERR(cdd_cdevp)){printk("kzalloc failed!\n");goto failure_kzalloc;}/*创建设备类*/dev_class = class_create(THIS_MODULE, "cdd_class");if(IS_ERR(dev_class)){printk("class_create failed!\n");goto failure_dev_class;}for(i=0; i<CDD_COUNT; i++){/*初始化cdev*/cdev_init(&(cdd_cdevp[i].cdev), &cdd_fops);/*添加cdev到内核*/cdev_add(&(cdd_cdevp[i].cdev), dev+i, 1);/* “/dev/xxx” */device_create(dev_class, NULL, dev+i, NULL, "cdd%d", i);cdd_cdevp[i].led = i;}return 0;failure_dev_class:kfree(cdd_cdevp);failure_kzalloc:unregister_chrdev_region(dev, CDD_COUNT);failure_register_chrdev:return ret;}void __exit cdd_exit(void){/*逆序消除*/int i = 0;for(; i < CDD_COUNT; i++){device_destroy(dev_class, dev+i);cdev_del(&(cdd_cdevp[i].cdev));//cdev_del(&((cdd_cdevp+i)->cdev));}class_destroy(dev_class);kfree(cdd_cdevp);unregister_chrdev_region(dev, CDD_COUNT);}module_init(cdd_init);module_exit(cdd_exit);

/***Copyright (c) 2013.TianYuan*All rights reserved.**文件名称: char_device_driver09_test.c*文件标识: 测试程序:测试内核空间的copy_to_user、copy_frome_user。添加偏移的功能*执行:先./char_device_driver08_testr、w、l、r、*当前版本:1.0*作者:wuyq **取代版本:xxx*原作者:xxx*完成日期:2013-11-29*/#include <stdio.h>#include <fcntl.h>#include <stdlib.h>#include <string.h>/*手工创建设备节点文件mknod /dev/cdd c 248 0*/int fd = 0;char rbuf[100];char wbuf[100] = "nihao!\n";int main(){char ch;fd = open("/dev/cdd5", O_RDWR);if(fd < 0){printf("open failed!\n");return -1;}printf("open successed fd = %d\n", fd);while(1){printf("starting to test /dev/cdd...\n");ch = getchar();getchar();//取走回车if(ch == 'q'){break;}switch(ch){case 'r':memset(rbuf, 0, 100);//清空read(fd, rbuf, 3);printf("user space from kernel: %s\n", rbuf);break;case 'w':write(fd, wbuf, strlen(wbuf) );break;case 'o':ioctl(fd, 0, 0);break;case 'l':lseek(fd, 0, SEEK_SET);//移动的文件的开头break;default:break;}sleep(1);}close(fd);return 0;}



0 0
原创粉丝点击