本文转载自:http://blog.csdn.net/coding__madman/article/details/51356313

字符设备的控制

1. 字符设备控制理论

1.1 作用

大部分驱动程序除了需要提供读写设备的能力外,还需要具备控制设备的能力。比如:改变波特率

1.2 应用程序接口

在用户空间,使用ioctl系统调用来控制设备,原型如下:

int  ioctl(int fd, unsigned long cmd, ...)

fd: 要控制的设备文件描述符

cmd: 发送给设备的控制命令

...: 第三个参数是可选的参数,存在与否依赖于控制命令(第二个参数)

1.3 设备驱动方法

2. 字符设备控制实现

2.1 定义命令

命令其实质而言就是一个整数,但为了让这个整数具备更好的可读性,我们通常会把这个整数分为几个段,类             型(8位),序号, 参数传送方向,参数长度

type(类型/幻数):表明这是属于哪个设备的命令

number(序号):用来区分同一设备的不同命令

direction: 参数传送的方向,可能的值是_IOC_NONE(没有数据传输),_IOC_READ, _IOC_WRITE(向设备读写           参数)

size : 参数长度

Linux系统提供了下面的宏来帮助定义命令:

* _IO(type, nr) : 不带参数的命令

*_IOR(type, nr, datatype) : 从设备中读取参数的命令

*_IOW(type, nr, datatype) : 向设备写入参数的命令

例如:

#define MEM_MAGIC  'm' //定义幻数

#define MEM_SET  _IOW(MEM_MAGIC, 0, int)

2.2 实现设备方法

unlocked_ioctl函数的实现通常是根据命令执行的一个switch语句。但是,当命令号不能匹配任何一个设备所支           持的命令时, 返回 -EINVAL

编程模型:

switch cmd:

case  命令A:

//执行A所对应的操作

case  命令B:

//执行B对应的操作

defaule:

return -EINVAL;

实例代码分析来实现ioctl:

这里的memdev.c还是接着上篇的基础上改的

  1. #include <linux/module.h>
  2. #include <linux/types.h>
  3. #include <linux/fs.h>
  4. #include <linux/errno.h>
  5. #include <linux/init.h>
  6. #include <linux/cdev.h>
  7. #include <asm/uaccess.h>
  8. #include <linux/slab.h>
  9. #include "memdev.h"
  10. int dev1_registers[5];
  11. int dev2_registers[5];
  12. struct cdev cdev;
  13. dev_t devno;
  14. /*文件打开函数*/
  15. int mem_open(struct inode *inode, struct file *filp)
  16. {
  17. /*获取次设备号*/
  18. int num = MINOR(inode->i_rdev);
  19. if (num==0)
  20. filp->private_data = dev1_registers;
  21. else if(num == 1)
  22. filp->private_data = dev2_registers;
  23. else
  24. return -ENODEV;  //无效的次设备号
  25. return 0;
  26. }
  27. /*文件释放函数*/
  28. int mem_release(struct inode *inode, struct file *filp)
  29. {
  30. return 0;
  31. }
  32. /*读函数*/
  33. static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
  34. {
  35. unsigned long p =  *ppos;
  36. unsigned int count = size;
  37. int ret = 0;
  38. int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/
  39. /*判断读位置是否有效*/
  40. if (p >= 5*sizeof(int))
  41. return 0;
  42. if (count > 5*sizeof(int) - p)
  43. count = 5*sizeof(int) - p;
  44. /*读数据到用户空间*/
  45. if (copy_to_user(buf, register_addr+p, count))
  46. {
  47. ret = -EFAULT;
  48. }
  49. else
  50. {
  51. *ppos += count;
  52. ret = count;
  53. }
  54. return ret;
  55. }
  56. /*写函数*/
  57. static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
  58. {
  59. unsigned long p =  *ppos;
  60. unsigned int count = size;
  61. int ret = 0;
  62. int *register_addr = filp->private_data; /*获取设备的寄存器地址*/
  63. /*分析和获取有效的写长度*/
  64. if (p >= 5*sizeof(int))
  65. return 0;
  66. if (count > 5*sizeof(int) - p)
  67. count = 5*sizeof(int) - p;
  68. /*从用户空间写入数据*/
  69. if (copy_from_user(register_addr + p, buf, count))
  70. ret = -EFAULT;
  71. else
  72. {
  73. *ppos += count;
  74. ret = count;
  75. }
  76. return ret;
  77. }
  78. /* seek文件定位函数 */
  79. static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
  80. {
  81. loff_t newpos;
  82. switch(whence) {
  83. case SEEK_SET:
  84. newpos = offset;
  85. break;
  86. case SEEK_CUR:
  87. newpos = filp->f_pos + offset;
  88. break;
  89. case SEEK_END:
  90. newpos = 5*sizeof(int)-1 + offset;
  91. break;
  92. default:
  93. return -EINVAL;
  94. }
  95. if ((newpos<0) || (newpos>5*sizeof(int)))
  96. return -EINVAL;
  97. filp->f_pos = newpos;
  98. return newpos;
  99. }
  100. <span style="color:#ff0000;">//设备控制函数
  101. long mem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  102. {
  103. switch(cmd)
  104. {
  105. case MEM_RESTART: //假如这里传入的是重启的命令
  106. printk(KERN_WARNING"restart device!\n");//这里通过虚拟设备来模拟
  107. return 0;
  108. case MEM_SET: //如果传入的是设置参数命令
  109. printk(KERN_WARNING"arg is : %d\n", arg);//打印出传入的参数
  110. return 0;
  111. default:
  112. return -EINVAL;//其他的打印错误
  113. }
  114. return 0;
  115. }</span>
  116. /*文件操作结构体*/
  117. static const struct file_operations mem_fops =
  118. {
  119. .llseek = mem_llseek,
  120. .read = mem_read,
  121. .write = mem_write,
  122. .open = mem_open,
  123. .release = mem_release,
  124. <span style="color:#ff0000;">.unlocked_ioctl = mem_ioctl,</span>
  125. };
  126. /*设备驱动模块加载函数*/
  127. static int memdev_init(void)
  128. {
  129. /*初始化cdev结构*/
  130. cdev_init(&cdev, &mem_fops);
  131. /* 注册字符设备 */
  132. alloc_chrdev_region(&devno, 0, 2, "memdev");
  133. cdev_add(&cdev, devno, 2);
  134. //printk(KERN_WARNING"Hello Memdev!\n");
  135. printk("Hello Memdev!\n");
  136. }
  137. /*模块卸载函数*/
  138. static void memdev_exit(void)
  139. {
  140. cdev_del(&cdev);   /*注销设备*/
  141. unregister_chrdev_region(devno, 2); /*释放设备号*/
  142. printk(KERN_WARNING"exit Memdev!\n");
  143. }
  144. MODULE_LICENSE("GPL");
  145. module_init(memdev_init);
  146. module_exit(memdev_exit);

memdev.h

  1. #define MEM_MAGIC 'm' //定义一个幻数,而长度正好和ASC码长度一样为8位,所以这里定义个字符
  2. #define MEM_RESTART _IO(MEM_MAGIC, 0)   //第一个命令是重启的命令,重启的命令不带参数 第一命令这里序号定义成0
  3. #define MEM_SET  _IOW(MEM_MAGIC, 1, int) //设置参数命令 序号为1, 类型为int

测试应用程序mem_ctl.c

  1. #include<sys/types.h>
  2. #include<sys/stat.h>
  3. #include<fcntl.h>
  4. #include<sys/ioctl.h>
  5. #include "memdev.h"
  6. int main()
  7. {
  8. int fd;
  9. fd = open("/dev/memdev0", O_RDWR);//可读可写打开文件
  10. ioctl(fd, MEM_SET, 115200);//第一个参数fd,第二个是我们要发送的命令,第三个是要传入的参数
  11. ioctl(fd, MEM_RESTART);//重启
  12. close(fd);
  13. }

Makefile

  1. obj-m := memdev.o
  2. KDIR := /home/kernel/linux-ok6410
  3. all:
  4. make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
  5. clean:
  6. rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order

这里ioctl函数原型可以查看内核源码Fs.h(/include/linux目录下)文件


然后这次采用静态编译mem_ctl.c文件 arm-linux-gcc -static -o memctl (这里采用静态编译上面博文有说道)

由于挂载了整个rootfs目录,这里编译产生的文件会同步到我的开发板

安装设备驱动模块,然后cat /proc/device 查看设备驱动程序的主设备号

然后创建设备文件,这样我们的应用程序就可以通过这个设备文件来访问设备了(当然这里我们的设备是虚拟出来的字符设备,不过麻雀虽小五脏俱全)

mknod memdev0 c 252 0 (执行该命令)

memdevo是我们给字符设备取的名字, 252是该字符设备的设备号,

这里可以看到/dev目录下就会产生memdev0这个设备文件了,运行mem_clt应用程序

至此ioctl字符设备的控制就OVER了!

ioctl--字符设备的控制技术【转】的更多相关文章

  1. Linux 字符设备控制技术

    字符设备控制技术 一.字符设备控制理论 1.作用 大部分设备除了具有<读写设备>的能力,还应该有<控制设备>的能力.比如改变 波特率 2. 应用程序接口 在用户空间中使用 Io ...

  2. 转:内核空间与用户空间数据交换的方式之一 --ioctl(通过字符设备演示)

    对于linux而言,内核程序和用户程序分别运行在内核空间和用户空间,要实现两者的数据交换,主要有以下几种方式:系统调用,读写系统文件(procfs,sysfs, seq_file,debugfs等), ...

  3. linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】

    转自:http://my.oschina.net/u/274829/blog/285014 1,ioctl介绍 ioctl控制设备读写数据以及关闭等. 用户空间函数原型:int ioctl(int f ...

  4. Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...

  5. 字符设备驱动之Led驱动学习记录

    一.概述 Linux内核就是由各种驱动组成的,内核源码中大约有85%的各种渠道程序的代码.一般来说,编写Linux设备驱动大致流程如下: 1.查看原理图,数据手册,了解设备的操作方法. 2.在内核中找 ...

  6. NeuSoft(4)编写字符设备驱动

    1.要求:实现简单的字符设备驱动程序 2.源码清单 #include <linux/module.h> #include <linux/types.h> #include &l ...

  7. Linux字符设备

    一.linux系统将设备分为3类:字符设备.块设备.网络设备. 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的 ...

  8. 深入理解Linux字符设备驱动

    文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...

  9. Smart210学习记录----beep linux字符设备驱动

    今天搞定了beep linux字符设备驱动,心里还是很开心的,哈哈...但在完成的过程中却遇到了一个非常棘手的问题,花费了我大量的时间,,,, 还是把问题描述一下吧,好像这个问题很普遍的,网上许多解决 ...

随机推荐

  1. CSS 学习笔记

    0.CSS概念层叠样式表(Cascading Style Sheets),CSS的来历就不必多说了.可以简单的理解为万维网联盟(w3c)为了丰富HTML页面的布局和外观而指定的一种标准. 1.CSS实 ...

  2. python - socket - client端指定ip和端口

    问题描述: 在设备中有3个NI, ip分别为192.168.1.5/6/7.其中本端192.168.1.6同对端192.168.1.10建立了一个tunnel. 我希望测试tunnel连通性, 对端起 ...

  3. 树状结构Java模型、层级关系Java模型、上下级关系Java模型与html页面展示

    树状结构Java模型.层级关系Java模型.上下级关系Java模型与html页面展示 一.业务原型:公司的组织结构.传销关系网 二.数据库模型 很简单,创建 id 与 pid 关系即可.(pid:pa ...

  4. poj1753

    #include <stdio.h> char s[100][100]; int s1[100][100]; int ax[5]={0,-1,0,1,0}; int ay[5]={0,0, ...

  5. VC 6中配置OpenGL开发环境

    2010,2012中配置类似 http://hi.baidu.com/yanzi52351/item/f9a600dffa4caa4ddcf9be1d VC 6中配置OpenGL开发环境 这里,我习惯 ...

  6. iOS中的线程安全问题

    为了保证线程安全,不会因为多个线程访问造成资源抢夺,出现的运行结果的偏差问题,我们需要使用到线程同步技术,最常用的就是 @synchronized互斥锁(同步锁).NSLock.dispatch_se ...

  7. django--forms

    forms模块的功能 1 表单提交验证 2 生成HTML标签 其他 提交后保留页面数据 创建forms类 首先从django中引入forms,一般会在application中新建一个文件专门保存for ...

  8. chm转换为html

    在Windows下chm转换为html的超简单方法(反编译CHM文件的方法) 通过调用Windows命令,将chm 文件转换为html 文件. 方法: 命令行(cmd),输入hh -decompile ...

  9. 什么是ValueStack

    转载自:http://www.cnblogs.com/zyw-205520/archive/2012/09/12/2681346.html Strut2的Action类通过属性可以获得所有相关的值,如 ...

  10. 在asp.net WebForms中使用路由Route

    1.新建WebForms应用程序 2.打开Global.asax文件代码如下: public class Global : System.Web.HttpApplication { protected ...