主要的文件操作方法实现

文件操作函数有很多的操作接口,驱动编程需要实现这些接口,在用户编程时候系统调用时候会调用到这些操作

[cpp] view plaincopy
  1. struct file_operations {
  2. ...
  3. loff_t (*llseek) (struct file *, loff_t, int);
  4. ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  5. ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  6. int (*open) (struct inode *, struct file *);
  7. int (*release) (struct inode *, struct file *);
  8. ...
  9. };

以上只列出了主要的操作,下面会依次介绍:
本次的测试代码上传在:char_step2

结构体:

首先 我们会模拟写一个不操作任何设备,而仅仅是存储的一个驱动。
定义自己的一个结构体为:

[cpp] view plaincopy
  1. struct simple_dev{
  2. char data[MAX_SIMPLE_LEN];
  3. loff_t count;
  4. struct semaphore semp;
  5. };

data 保存数据, count表示文件的数据有效的位置, semp是一个信号量锁,在以后的编程中使用,
之后的程序中结构体也会做相应的变化,以适应linux编写驱动的习惯

open方法:

打开设备并进一步初始化工作,在没有定义open方法时内核以一种默认的方式打开设备,保证每次都能正确打开。
open方法中有有struct inode参数,包含了设备号,程序中可以使用次设备号得到正操作的设备
在struct file中主要的操作是private_data指针,他可以传递任何自己创建的结构。
总得说来open方法的作用有3
1、获得操作的设备(通过设备号)
2、进一步的初始化设备
3、初始化file结构体的private_data

[cpp] view plaincopy
  1. static int simple_open(struct inode *inodp, struct file *filp)
  2. {
  3. struct simple_dev *temp_dev = NULL;
  4. int minor = 0;
  5. #if SIMPLE_DEBUG
  6. printk(KERN_INFO "In %s \n", __func__);
  7. #endif
  8. minor = iminor(inodp);//获得操作的设备的次设备号
  9. if(minor > DEV_COUNT-1){
  10. printk(KERN_ERR "the char dev in invalid \n");
  11. return -ENODEV;
  12. }
  13. #if SIMPLE_DEBUG
  14. printk(KERN_INFO "the minor is  %d \n", minor);
  15. #endif
  16. temp_dev = &char2_dev[minor];//获得真正操作的设备
  17. /* 进一步 初始化设备 因为是操作一个模拟的设备 故省去*/
  18. filp->private_data = temp_dev; //初始化 private_data
  19. return 0;
  20. }

release方法:

主要是对open进一步初始化的操作的反操作
比如open时候分配了内存,在release时就需要释放它等
例子中因为操作内存设备,故在release时无需做什么事

read方法:

read 是把设备中的数据传递给调用者
主要步骤
1、检测偏移量有效(有些设备驱动不需要检测)
2、检测用户空间地址有效
3、将数据传给用户(在此步骤中调用的函数可能会自己检测步骤2)
4、调整偏移量
5、返回读到的数据长度
(read write 用法相对灵活,不要依赖上边的步骤,设备驱动程序要根据设备特性去设计此方法)
这里先介绍一个会检测用户空间地址是否有效的copy函数
用户调用read读设备,而在内核空间就是将数据传给用户,是一个to的操作

[cpp] view plaincopy
  1. unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)

__must_check表述必须检测其返回值,操作成功返回0,不成功返回负的错误码
to是用户空间指针 也就是read函数传入的用户空间的指针,
from指向设备要传送的数据

n标识传入长度

上图是 摘自LDD3上的经典视图, 应该比较能说明read的方法

[cpp] view plaincopy
  1. static ssize_t simple_read(struct file *filp, char __user *userstr, size_t count, loff_t *loff)
  2. {
  3. struct simple_dev *dev = NULL;
  4. int data_remain = 0;
  5. int err;
  6. #if SIMPLE_DEBUG
  7. printk(KERN_INFO "In %s \n", __func__);
  8. #endif
  9. dev         = filp->private_data;
  10. data_remain = dev->count - *loff;
  11. if(MAX_SIMPLE_LEN < *loff)//检测偏移量
  12. {
  13. printk(KERN_ERR "the offset is illegal in func %s \n",__func__ );
  14. return -EINVAL;
  15. }
  16. else if(data_remain <= 0)
  17. {
  18. printk(KERN_WARNING "there was not much data in the device\n");
  19. return 0;
  20. }
  21. else
  22. {
  23. if(count > data_remain)
  24. {
  25. #if SIMPLE_DEBUG
  26. printk(KERN_INFO "the data is less than the user want to read\n");
  27. #endif
  28. count = data_remain;
  29. }
  30. else
  31. {
  32. }
  33. }
  34. err = copy_to_user(userstr, (dev->data)+(*loff), count); //调用内核函数进行数据拷贝,它会检测用户地址是否有效
  35. if(err != 0)
  36. {
  37. printk(KERN_ERR "an error occured when copy data to user\n");
  38. return err;
  39. }
  40. else
  41. {
  42. #if SIMPLE_DEBUG
  43. printk(KERN_INFO "data copy to user OK\n");
  44. #endif
  45. *loff = *loff + count; //调整偏移量
  46. return count; //返回写入的数据量
  47. }
  48. }

write方法:

与read类似 它是从用户传数据给设备驱动
从内核空间看就是一个从用户空间取数据 是一个from操作

[cpp] view plaincopy
  1. long __must_check strncpy_from_user(char *dst, const char __user *src, long count)

dst 驱动保存数据的地址
src 用户空间传入的数据
count 标识数据长度

[cpp] view plaincopy
  1. static ssize_t simple_write(struct file *filp, const char __user *userstr, size_t count, loff_t *loff)
  2. {
  3. struct simple_dev *dev = NULL;
  4. int err;
  5. int remain_space = 0;
  6. #if SIMPLE_DEBUG
  7. printk(KERN_INFO "In %s\n",__func__);
  8. #endif
  9. dev          = filp->private_data;
  10. if(MAX_SIMPLE_LEN <= *loff) //检测偏移量
  11. {
  12. printk(KERN_ERR "the offset is illegal in func %s\n", __func__);
  13. return -EINVAL;
  14. }
  15. else
  16. {
  17. remain_space = MAX_SIMPLE_LEN - *loff;
  18. if(count > remain_space)
  19. {
  20. #if SIMPLE_DEBUG
  21. printk(KERN_WARNING "the data is to long to write to the device\n");
  22. #endif
  23. count = remain_space;
  24. }
  25. else
  26. {
  27. }
  28. }
  29. err = copy_from_user((dev->data)+(*loff),userstr,count);//取得数据
  30. if(err != 0)
  31. {
  32. printk(KERN_ERR "an error occured when copy data from user\n");
  33. return err;
  34. }
  35. else
  36. {
  37. #if SIMPLE_DEBUG
  38. printk(KERN_INFO "data copy from user OK\n");
  39. #endif
  40. *loff = *loff + count; //跳着偏移
  41. if(*loff > dev->count)
  42. {
  43. dev->count = *loff;
  44. }
  45. else
  46. {
  47. }
  48. return count; //返回写入的数据量
  49. }
  50. }

lseek方法:

根据用户传入的参数调整文件偏移
mode

SEEK_SET 从文件起始处开始偏移
SEEK_CUR 从文件当前位置计算偏移
SEEK_END 从文件末尾计算偏移

file结构的f_pos保存了文件的偏移量
在调整文件偏移后需要 更新file中得f_pos成员

[cpp] view plaincopy
  1. static loff_t simple_llseek(struct file *filp, loff_t loff, int mode)
  2. {
  3. struct simple_dev *dev = NULL;
  4. loff_t tmp_len;
  5. #if SIMPLE_DEBUG
  6. printk(KERN_INFO "In %s\n",__func__);
  7. #endif
  8. dev          = filp->private_data;
  9. switch ( mode )
  10. {
  11. case SEEK_SET:
  12. if( loff < 0 )
  13. {
  14. printk(KERN_ERR "can't move above file line %d \n", __LINE__);
  15. return -1;
  16. }
  17. else if(loff > dev->count)
  18. {
  19. printk(KERN_ERR "offset is too long line %d\n", __LINE__);
  20. return -1;
  21. }
  22. else
  23. {
  24. filp->f_pos = loff;
  25. }
  26. break;
  27. case SEEK_CUR:
  28. if((tmp_len = filp->f_pos+loff) < 0)
  29. {
  30. printk(KERN_ERR "can't move above file line %d \n", __LINE__);
  31. return -1;
  32. }
  33. else if(tmp_len > dev->count)
  34. {
  35. printk(KERN_ERR "offset is too long line %d\n", __LINE__);
  36. return -1;
  37. }
  38. else
  39. {
  40. filp->f_pos = tmp_len;
  41. }
  42. break;
  43. case SEEK_END:
  44. if((tmp_len = dev->count+loff ) < 0)
  45. {
  46. printk(KERN_ERR "can't move above file line %d \n", __LINE__);
  47. return -1;
  48. }
  49. else if(tmp_len > dev->count)
  50. {
  51. printk(KERN_ERR "offset is too long line %d\n", __LINE__);
  52. return -1;
  53. }
  54. else
  55. {
  56. filp->f_pos = tmp_len;
  57. }
  58. break;
  59. default :
  60. printk(KERN_INFO "illigal lseek mode! \n");
  61. return -1;
  62. break;
  63. }
  64. return filp->f_pos;
  65. }

Linux驱动编程 step-by-step (五)主要的文件操作方法实现相关推荐

  1. Linux驱动编程 step-by-step

    第三次看了LDD3了(虽然现在已经是kernel3.0但从这本书商还是能学到很多) 每次都有一些收获 现在终于能够写一写代码了 驱动程序的作用: 简单来说 驱动程序就是使计算机与设备通信的特殊的代码, ...

  2. Linux驱动编程 step-by-step (二) 简单字符设备驱动

    简单字符设备驱动 1.主次设备号 主设备号标识设备连接的的驱动,此设备好由内核使用,标识在相应驱动下得对应的设备 在linux中设备号是一个32位的dev_t类型 typedef __u32    _ ...

  3. Linux驱动编程 step-by-step (二)

    简单字符设备驱动 1.主次设备号 主设备号标识设备连接的的驱动,此设备好由内核使用,标识在相应驱动下得对应的设备 在linux中设备号是一个32位的dev_t类型 typedef __u32    _ ...

  4. linux驱动编程——ch340x驱动移植

    Linux驱动编程--ch340x驱动移植 主要概念: ​ ch340x驱动移植 ​ 作为通用器件,厂商都有提供适配各种平台的驱动.linux一般会提供驱动源码. ​ 一般所谓驱动移植,就是将厂商的驱 ...

  5. Linux驱动编程视频教程

    本视频教程主要介绍字符驱动.杂项设备.中断.调试驱动的基本方法以及驱动的移植等. Linux驱动编程视频教程: 链接:https://pan.baidu.com/s/1Yn5d4w9uudb4tGDT ...

  6. linux驱动read函数 copytouser,Linux驱动编程 step-by-step (五)主要的文件操作方法实现...

    主要的文件操作方法实现 文件操作函数有很多的操作接口,驱动编程需要实现这些接口,在用户编程时候系统调用时候会调用到这些操作 structfile_operations { ... loff_t (*l ...

  7. Linux驱动编程(驱动程序基石)(上)

    一.休眠与唤醒 要休眠的线程,放在 wq 队列里,中断处理函数从 wq 队列里把它取出来唤醒.所以,我们要做这几件事: ① 初始化 wq 队列 ② 在驱动的 read 函数中,调用 wait_even ...

  8. linux 进程 控制终端,linux系统编程之进程(五):终端、作业控制与守护进程

    #include#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); } while(0) int setup_daemon(int, int ...

  9. Linux驱动编程(驱动程序基石)(下)

    一.中断的线程化处理 复杂.耗时的事情,尽量使用内核线程来处理.上节视频介绍的工作队列用起来挺简单,但是它有一个缺点:工作队列中有多个 work,前一个 work 没处理完会影响后面的 work.解决 ...

最新文章

  1. C#中调用Windows API时的数据类型对应关系
  2. 好像最近买了很多东西
  3. postmessage 消息接收延迟_微信为什么会突然延迟接收消息?原来是它们搞的鬼!...
  4. MOS和三极管不同接法对应的性能比较
  5. Leetcode题库 136.只出现一次的数字(异或 C实现)
  6. CNN 神经网络tricks 学习总结
  7. 单件模式(单例模式)
  8. React Native的键盘遮挡问题(input/webview里)
  9. 使用相同的原计算机名新建DDC加入原有站点报错
  10. 多窗口售票:单件模式多线程实现
  11. 用NBSI进行SQL注入***分析及安全解决方案
  12. 2017年微软暑期实习PM产品经理面经
  13. Entersekt欢迎Nicolas Huss加入董事会
  14. amd cpu排行_amd cpu性能排行图_笔记本CPU性能排行天梯图-2012最新笔记本CPU天梯图...
  15. 使用 Moment.js 吧时间戳生成格式化时间
  16. 语音房间实现的一种方式
  17. uni-app 实现手写签名
  18. Linux驱动开发(二)内核符号表
  19. 计算机指纹识别的原理步骤,指纹识别技术的基本原理及过程
  20. [转] 管理学中的各种理论

热门文章

  1. boost::ratio_power相关的测试程序
  2. boost::mpl模块实现map相关的测试程序
  3. boost::mp11::mp_iterate相关用法的测试程序
  4. boost::log::parse_formatter用法的测试程序
  5. boost::hana::second用法的测试程序
  6. boost::fusion::as_set用法的测试程序
  7. C++实现教学信息管理系统
  8. OpenGL Lighting models照明模型的实例
  9. C++ 循环链表circular linked list实现算法(附完整源码)
  10. QT的QSGGeometry类的使用