1、设备号

主设备号标识设备对应的驱动程序,次设备号由内核使用,用于确定设备文件所指的设备。

通过次设备号获得一个指向内核设备的直接指针,也可将此设备号当作设备本地数组的索引。

设备编号用dev_t表示(Linux/types.h  32位,其中12位表示主设备号,20位表示次设备号)。

由dev_t获得主设备号或次设备号:MAJOR(dev_t dev); MINOR(dev_t dev)

已知主设备号和次设备号来获取dev_t类型:MKDEV(int  major,  int  minor)

获取一个或多个设备编号:int  register_chrdev_region(dev_t first,  unsigned int  count,  char  *name);(静态分配,事先已知道设备号)

动态分配设备编号:int  alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);调用成功后dev会保存已分配的第一个编号。

释放设备编号:void unregister_chrdev_region(dev_t first, unsigned int count);

接下来,驱动程序需要将设备编号和内部函数连接起来。

注:(下一步可尝试采用动态分配设备号

动态分配设备号缺点:不能预先创建设备节点(因为分配的设备号不能保证始终一致)。

2、文件操作file_operations:

这些操作将与设备编号连接起来。

__user:用于文档,表明该指针是一个用户空间地址。

主要成员:open,  ioctl,  read,  write,  llseek

3、struct file结构  linux/fs.h文件描述符

每打开一个文件,内核就会创建一个对应的file结构,在open()时创建,同时会传递给在该文件上进行操作的所有函数(因为file结构中包含file_operations结构,而该结构包含了所有驱动操作的函数)。

内核中用filp作为执行file结构的指针。

主要成员:

Mode_t  f_mode;  loff_t  f_pos;   struct file_operations *f_pos;  void  private_data;

4、inode结构

对单个文件只有一个inode,而可能有多个file(由于fork,dup操作)。

主要成员:

dev_t  i_rdev; 对表示设备文件的inode结构,该字段包含真正的设备编号。

struct cdev *i_cdev;   该结构表示字符设备的内核的内部结构,当inode指向一个字符设备文件时,该字段包含指向struct cdev结构的指针。

从inode中获取设备号: iminor(struct inode *inode);  imajor(inode);

5、字符设备注册  /linux/cdev.h

内核使用struct cdev结构表示字符设备,所以在内核调用该设备操作之前,需要分配并注册一个或者多个该结构。

注册有两种方式:

新方法:

1)定义字节的结构:

struct my_dev {

struct  cdev  cdev; //此处如果定义的是指针类型,则需要申请分配内存

}my_dev;

//my_dev ->cdev = cdev_alloc();  //如果cdev是指针则需要这一步

my_dev->cdev.ops = &my_fops;

my_dev->cdev.owner = THIS_MODULE;

2)再调用cdev_init(struct cdev *cdev,  struct file_operations *fops);

3)调用cdev_add(struct cdev *dev,  dev_t num,  unsigned int count);

上面每一步都要判断函数调用是否出错。

旧办法(老接口):

注册:int register_chrdev();移除:int unregister_chrdev()

6、各操作函数实现

1)open   int (*open) (struct  inode  *inode,  struct  file  *filp)

完成以下工作:传入一个inode,创建一个file结构

n  检查设备特定错误(如未就绪);

n  如果设备首次打开,则进行初始化;

n  必要时更新f_op指针;

n  分配并填写filp->private_data;

注:inode结构是传入的参数,对应一个特定的设备(这就是为什么要在/dev下mknodde原因),而file结构的filp是要修改的参数(传出),对应该设备的一个文件描述符,也就是一个inode可能有多个file描述符,而每个描述符需要保存inode的信息,即存放在filp->private_data中。

2)release  int (*release) (struct inode *inode,  struct file *filp)

完成工作:传入一个inode,释放这个file结构

n  释放由open分配的、保存在filp->private_data中的内容;

n  在最后一次close时关闭设备

dup 和fork都会在不调用open时创建新的file结构(对应同一个inode)。

注:并不是每个close调用都会调用release;只有真正释放设备数据结构的close调用才会调用release。内核对每个file结构维护其被使用次数的计数器,无论是fork还是dup,都不会创建新的数据结构(只会有open创建),它们只是增加已有结构中的计数器而已。只有在file结构的计数为0时,close才会调用release。

3)read  ssize_t  read(struct  file  *filp,  char __user *buf,  count,  loff_t *offp)

完成工作:传入file,将count个字节数据写入用户地址buf,修改loff_t

由copy_to_user()实现
返回值说明:

n  等于count:所请求的字节数读取成功;

n  返回值为正,但小于count:只读取了部分数据;

n  为0:已经达到文件尾;

n  负值:出错

4)write  ssize_t  write(struct  file  *filp,  char  __user *buf,  count,  offp);

由copy_from_user()实现

返回值同上。

[cpp] view plaincopy
  1. 1)驱动代码
  2. Demo.h
  3. #ifndef _DEMO_H_
  4. #define _DEMO_H_
  5. #include <linux/ioctl.h>
  6. /*Macros to help debuging*/
  7. #undef PDEBUG
  8. #ifdef DEMO_DEBUG
  9. #ifdef __KERNEL__
  10. #define PDEBUG(fmt, args...) printk(KERN_DEBUG "DEMO:" fmt,## args)
  11. #else
  12. #define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
  13. #endif
  14. #else
  15. #define PDEBUG(fmt, args...)
  16. #endif
  17. #define DEMO_MAJOR 224
  18. #define DEMO_MINOR 0
  19. #define COMMAND1 1
  20. #define COMMAND2 2
  21. struct demo_dev {
  22. struct cdev cdev;
  23. };
  24. ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
  25. ssize_t demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
  26. loff_t demo_llseek(struct file *filp, loff_t off, int whence);
  27. int demo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
  28. #endif
  29. demo.c
  30. #include <linux/module.h>
  31. #include <linux/kernel.h>
  32. #include <linux/fs.h>
  33. #include <linux/errno.h>
  34. #include <linux/types.h>
  35. #include <linux/fcntl.h>
  36. #include <linux/cdev.h>
  37. #include <linux/version.h>
  38. #include <linux/vmalloc.h>
  39. #include <linux/ctype.h>
  40. #include <linux/pagemap.h>
  41. #include "demo.h"
  42. MODULE_AUTHOR("Yangjin");
  43. MODULE_LICENSE("Dual BSD/GPL");
  44. struct demo_dev *demo_devices;
  45. static unsigned char demo_inc = 0;//全局变量,每次只能打开一个设备
  46. static u8 demo_buffer[256];
  47. int demo_open(struct inode *inode, struct file *filp)
  48. {
  49. struct demo_dev *dev;
  50. if (demo_inc > 0) return -ERESTARTSYS;
  51. demo_inc++;
  52. dev = container_of(inode->i_cdev, struct demo_dev, cdev);
  53. filp->private_data = dev;
  54. return 0;
  55. }
  56. int demo_release(struct inode *inode, struct file *filp)
  57. {
  58. demo_inc--;
  59. return 0;
  60. }
  61. ssize_t demo_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
  62. {
  63. int result;
  64. loff_t pos = *f_pos; //pos: offset
  65. if (pos >= 256) {
  66. result = 0;
  67. goto out;
  68. }
  69. if (count > (256 - pos))
  70. count = 256 - pos;
  71. pos += count;
  72. if (copy_to_user(buf, demo_buffer+*f_pos, count)) {
  73. count = -EFAULT;
  74. goto out;
  75. }
  76. *f_pos = pos;
  77. out:
  78. return count;
  79. }
  80. ssize_t  demo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
  81. {
  82. ssize_t retval = -ENOMEM;
  83. loff_t pos = *f_pos;
  84. if (pos > 256)
  85. goto out;
  86. if (count > (256 - pos))
  87. count = 256 - pos;
  88. pos += count;
  89. if (copy_from_user(demo_buffer+*f_pos, buf, count)) {
  90. retval = -EFAULT;
  91. goto out;
  92. }
  93. *f_pos = pos;
  94. retval = count;
  95. out:
  96. return retval;
  97. }
  98. int  demo_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
  99. {
  100. if (cmd == COMMAND1) {
  101. printk("ioctl command 1 successfully\n");
  102. return 0;
  103. }
  104. if (cmd == COMMAND2) {
  105. printk("ioctl command 2 successfully\n");
  106. return 0;
  107. }
  108. printk("ioctl error\n");
  109. return -EFAULT;
  110. }
  111. loff_t demo_llseek(struct file *filp, loff_t off, int whence)
  112. {
  113. loff_t pos;
  114. pos = filp->f_pos;
  115. switch (whence) {
  116. case 0:
  117. pos = off;
  118. break;
  119. case 1:
  120. pos += off;
  121. break;
  122. case 2:
  123. default:
  124. return -EINVAL;
  125. }
  126. if ((pos > 256) || (pos < 0))
  127. return -EINVAL;
  128. return filp->f_pos = pos;
  129. }
  130. struct file_operations demo_fops = {
  131. .owner = THIS_MODULE,
  132. .llseek = demo_llseek,
  133. .read = demo_read,
  134. .write = demo_write,
  135. .ioctl = demo_ioctl,
  136. .open = demo_open,
  137. .release = demo_release,
  138. };
  139. void demo_cleanup_module(void)
  140. {
  141. dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINOR);
  142. if (demo_devices) {
  143. cdev_del(&demo_devices->cdev);
  144. kfree(demo_devices);
  145. }
  146. unregister_chrdev_region(devno, 1);
  147. }
  148. Init module流程:
  149. 1)注册设备号MKDEV;
  150. 2)注册设备驱动程序,即初始化cdev结构(嵌入到demo_devices结构中)
  151. int demo_init_module(void)
  152. {
  153. int result;
  154. dev_t dev = 0;
  155. dev = MKDEV(DEMO_MAJOR, DEMO_MINOR);
  156. result = register_chrdev_region(dev, 1, "DEMO");
  157. if (result < 0) {
  158. printk(KERN_WARNING "DEMO: can't get major %d\n", DEMO_MAJOR);
  159. return result;
  160. }
  161. demo_devices = kmalloc(sizeof(struct demo_dev), GFP_KERNEL);
  162. if (!demo_devices) {
  163. result = -ENOMEM;
  164. goto fail;
  165. }
  166. memset(demo_devices, 0, sizeof(struct demo_dev));
  167. cdev_init(&demo_devices->cdev, &demo_fops);
  168. demo_devices->cdev.owner = THIS_MODULE;
  169. demo_devices->cdev.ops = &demo_fops; //将创建的字符设备与file_operations中各函数操作连接起来
  170. result = cdev_add(&demo_devices->cdev, dev, 1);
  171. if (result) {
  172. printk(KERN_NOTICE "error %d adding demo\n", result);
  173. goto fail;
  174. }
  175. return 0;
  176. fail:
  177. demo_cleanup_module();
  178. return result;
  179. }
  180. module_init(demo_init_module);
  181. module_exit(demo_cleanup_module);

2)加载驱动insmod demo.ko,再使用lsmod或cat /proc/modules查看驱动是否安装;

3)创建设备节点:mknod  /dev/yangjin c 224 0;注意:此处的节点设备号要与驱动程序中的注册的设备号相同。

4)再编写应用程序测试代码:

用户测试代码:

[cpp] view plaincopy
  1. #include <sys/types.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. #include <linux/rtc.h>
  5. #include <linux/ioctl.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #define COMMAND1 1
  9. #define COMMAND2 2
  10. int main()
  11. {
  12. int fd;
  13. int i;
  14. char data[256] = {0};
  15. int retval;
  16. fd = open("/dev/yangjin", O_RDWR);
  17. if (fd == 1) {
  18. perror("open error\n");
  19. exit(-1);
  20. }
  21. printf("open /dev/yangjin successfully\n");
  22. retval = ioctl(fd, COMMAND1, 0);
  23. if (retval == -1) {
  24. perror("ioctl error\n");
  25. exit(-1);
  26. }
  27. printf("ioctl command 1 successfully\n");
  28. retval = write(fd, "yangjin", 7);
  29. if (retval == -1) {
  30. perror("write error\n");
  31. exit(-1);
  32. }
  33. retval = lseek(fd, 0, 0);
  34. if (retval == -1) {
  35. perror("lseek error\n");
  36. exit(-1);
  37. }
  38. retval = read(fd, data, 10);
  39. if (retval == -1) {
  40. perror("read error\n");
  41. exit(-1);
  42. }
  43. printf("read successfully: %s\n", data);
  44. close(fd);
  45. return 0;
  46. }

linux驱动文件操作简单介绍相关推荐

  1. Linux内核文件操作

    Linux内核文件操作 前言 一.文件操作结构体 二.VFS之file_operations对象 1.文件打开filp_open 2.文件关闭filp_close 3.文件读取vfs_read 4.文 ...

  2. linux拷贝文件函数,linux下文件操作的各个函数

    作者:HoytEmail:hoytluo@21cn.com 前言: 我们在这一节将要讨论linux下文件操作的各个函数. 文件的创建和读写 文件的各个属性 目录文件的操作 管道文件 --------- ...

  3. linux修改目录block信息,linux下文件操作inode,block的变化

    在"浅谈linux性能调优之三:分区格式化之前的考虑" http://my.oschina.net/sharelinux/blog/143522    一文中我说了inode与bl ...

  4. Linux的文件操作

    1. 文件操作系统的调用 Linux的文件操作系统调用(在Windows编程领域,操作系统提供的接口称为 API)涉及创建(create).打开(open).读写(read/write)和关闭(clo ...

  5. 服务器LINUX查看文件操作

    LINUX查看文件操作 步骤命令如下: grep # 显示 key 所在行及前后5行grep -5 'key' filegrep -C 5 'key' file # 显示 key 所在行及前5行gre ...

  6. linux awk数组长度,linux awk数组操作详细介绍

    linux awk数组操作详细介绍 用awk进行文本处理,少不了就是它的数组处理.那么awk数组有那些特点,一般常见运算又会怎么样呢.我们先看下下面的一些介绍,结合例子我 们会讲解下它的不同之处.在 ...

  7. Linux coredump文件的简单使用

    Linux coredump文件的简单使用 一.简述 记-在linux端生成core文件, 及其简单利用. 二.设置可以生成core文件 2.1 查看当前系统是否已开启core文件记录 ulimit ...

  8. linux .pc文件make,简单的驱动makefile详解

    简单的驱动makefile详解 一个工程中的源文件不计数,其按类型.功能.模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编 ...

  9. linux c文件操作,Linux C 文件的输入/输出操作

    10.1 文件I/O操作概述 在Linux系统中,文件I/O操作可以分为两类,一类是基于文件描述符的I/O操作,另一类是基于数据流的I/O操作. 10.1.1 文件描述符简介 在文件操作一章中,也经常 ...

最新文章

  1. Django(part17)--form表单提交数据
  2. 删掉被2345篡改的IE起始页
  3. Struts2和Struts1.x的全面比较
  4. 域名服务器(DNS)工作原理
  5. R语言基础入门(7)之数据类型的性质
  6. MySQL逻辑架构以及调优篇
  7. 用opencsv文件读写CSV文件
  8. java私塾架构二,小弟我在Java私塾学习期间的学习源码
  9. 怎么在电脑上录制qq音乐
  10. 关于COM类工厂80070005和8000401a错误分析及解决办法
  11. 有Web认证情况下的路由器设置
  12. 数学建模学习(93):方差分析、T检验、卡方分析(检验)
  13. 附近的人实现原理详细剖析!
  14. 6 模型的属性与功能
  15. Word表格外的第一个空行如何删除
  16. 第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛题解
  17. python 图片文字识别orc
  18. 五分钟you-get入门
  19. 根据身份证号判断性别
  20. 解决svn: E170001报错

热门文章

  1. promise之我见
  2. MySQL 关联表批量修改(数据同步)
  3. oracle中的视图详解
  4. Tomcat4/5连接池的设置及简单应用示例
  5. 【许晓笛】 EOS 智能合约案例解析(2)
  6. 传统数据中心如何实现向云的平滑升级
  7. 提升你的开发效率,10 个 NPM 使用技巧
  8. 手机屏幕适配原理及实现
  9. VirtualBox 扩展虚拟硬盘容量
  10. Mysql 中 delete 与 left join 的问题