字符设备(3)lseek
2014-03-15 16:56
267 查看
/* 改进:读写时,新添加了利用llseek()函数,修改偏移量属性 */ /* 对设备属性进行了封装,读写时重要的改变 */ #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/errno.h> #include <linux/slab.h> #define P_DEBUG(fmt, args...) printk("<kernel>[%s]:"fmt, __FUNCTION__, ##args) #define DEVSIZE 512 //将设备属性封装成一个结构体,表示一个字符设备 struct test_dev_t { char kbuf[DEVSIZE]; int curr_size; int major; int minor; dev_t devno; struct cdev test_cdev; }*dev; /* static dev_t devno; static int major = 0; static int minor = 0; struct cdev test_cdev; */ //设备驱动的操作方法 static int test_open(struct inode *,struct file *); static int test_close(struct inode *,struct file *); static ssize_t test_read(struct file *, char __user *, size_t , loff_t *); static ssize_t test_write(struct file *, const char __user *, size_t, loff_t *); static off_t test_llseek(struct file *, off_t, int); struct file_operations test_fops = { .open = test_open, .release = test_close, .read = test_read, .write = test_write, .llseek = test_llseek, }; //打开设备 static int test_open(struct inode *inode,struct file *filp) { P_DEBUG("dev open!\n"); //***重要改变,打开设备时将设备属性结构体赋值给打开设备文件的似有数据 struct test_dev_t *dev;//驱动程序获取设备的通用方法 dev = container_of(inode->i_cdev,struct test_dev_t,test_cdev); filp->private_data = dev;//利用设备文件的私有数据结构,保存设备相关的数据结构 return 0; } //释放设备 static int test_close(struct inode *inode,struct file *filp) { P_DEBUG("dev close!\n"); return 0; } //从设备读取 static ssize_t test_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) { int ret; //***重要改变,现在从设备读取 struct test_dev_t *dev = filp->private_data; //比较上一版本有修改(对偏移量进行判断) if (*offset > DEVSIZE) {//如果偏移量已经超过了设备大小 return count ? -ENXIO : 0; } //count不为0则分会错误号,地址越界 if (*offset + count > DEVSIZE) { //如果读取字节数超过了最大偏移量 count = DEVSIZE - *offset; } if (copy_to_user(buf, dev->kbuf + *offset, count)) {//安全性检查 return -EFAULT; } else { P_DEBUG("offset is [%d]\n", *offset); ret = count; //修改设备文件当前大小 //dev->curr_size -= count; *offset += count;//偏移量增加 P_DEBUG("buf is [%s]\n", buf); } return ret; } //写入设备 static ssize_t test_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset) { int ret; //***重要改变,现在写到设备 struct test_dev_t *dev = filp->private_data; P_DEBUG("dev->curr_size[%d]\n",dev->curr_size); //比较上一版本有修改 if (*offset > DEVSIZE) {//如果偏移量已经超过了设备大小 return count ? -ENXIO : 0; } //count不为0则分会错误号,地址越界 if (*offset + count > DEVSIZE) { //如果写入字节数超过了最大偏移量 count = DEVSIZE - *offset; } if (copy_from_user(dev->kbuf,buf,count)) {//有问题,总是写在kbuf开始字节 return -EFAULT; } else { ret = count; //修改设备文件当前偏移量 dev->curr_size += count; *offset += count;//偏移量增加 P_DEBUG("dev->curr_size[%d]\n",dev->curr_size); P_DEBUG("kbuf is [%s]\n", dev->kbuf); } return ret; } //修改设备文件偏移量 static off_t test_llseek(struct file *filp, off_t offset, int whence) { off_t new_pos; off_t old_pos = filp->f_pos; switch (whence) { case SEEK_SET : new_pos = offset; break; case SEEK_CUR : new_pos = old_pos + offset; break; case SEEK_END : new_pos = old_pos + DEVSIZE; break; default: P_DEBUG("unknown whence!\n"); return -EINVAL; } if (new_pos < 0 || new_pos > DEVSIZE) { P_DEBUG("f_ops over failed!\n"); return -EINVAL; } filp->f_pos = new_pos; return new_pos;//返回新的偏移量 } //初始化设备属性 static struct test_dev_t* dev_init(struct test_dev_t *dev) { dev = kmalloc(sizeof(struct test_dev_t),GFP_KERNEL); memset(dev->kbuf,0,DEVSIZE); dev->curr_size = 0; dev->major = 0; dev->minor = 0; return dev; } static int __init test_init(void) { int ret; //很重要的部分,定义并初始化一个设备 dev = dev_init(dev); //申请字符设备号 if (dev->major) { dev->devno = MKDEV(dev->major,dev->minor); ret = register_chrdev_region(dev->devno,1,"test_dev"); } else {//动态分配 ret = alloc_chrdev_region(&dev->devno,dev->minor,1,"test_dev"); dev->major = MAJOR(dev->devno); dev->minor = MINOR(dev->devno); } if (ret < 0) { P_DEBUG("register chrdev devno err!\n"); goto err_regDevno; } printk("major[%d] minor[%d]\n",dev->major,dev->minor); //注册设备 cdev_init(&dev->test_cdev,&test_fops);//初始化cdev结构体 dev->test_cdev.owner = THIS_MODULE; ret = cdev_add(&dev->test_cdev,dev->devno,1);//将设备加到字符设备数据库 if (ret < 0) { P_DEBUG("add cdev error!\n"); goto err_addCdev; } return 0; err_addCdev: unregister_chrdev_region(dev->devno,1); err_regDevno: return ret; } static int __exit test_exit(void) { cdev_del(&dev->test_cdev);//从字符设备数据库删除cdev unregister_chrdev_region(dev->devno,1);//释放设备号 return 0; } module_init(test_init); module_exit(test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("LL"); MODULE_VERSION("1.4");
相关文章推荐
- struts2自学笔记之二
- 结构体内存分配问题
- 虚拟IP技术-VIP
- IIS
- LLDB - GDB的区别
- 字符设备(2)面向设备对象
- 开始我的编程之旅
- Sql2008安全审计
- 查看占用进程
- WPF之依赖属性
- documentElement与body
- 30条经典的SQL语句
- Docker跟一般的虚拟机有什么区别?
- 【LeetCode】Search in Rotated Sorted Array && Search in Rotated Sorted Array II
- gcd 最小公约数
- poj 3176 Cow Bowling dp
- c++指针和引用的区别
- COCOS2D-X 3.0BETA2 版本LabelTTF中文显示乱码的解决办法
- poj 3176 Cow Bowling dp
- lync用户无法托管到池中