ioctl--字符设备的控制技术【转】

本文转载自: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还是接着上篇的基础上改的

 

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

memdev.h

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include<sys/types.h>  
  2. #include<sys/stat.h>  
  3. #include<fcntl.h>  
  4. #include<sys/ioctl.h>  
  5. #include "memdev.h"  
  6.   
  7. int main()  
  8. {  
  9.     int fd;  
  10.       
  11.     fd = open("/dev/memdev0", O_RDWR);//可读可写打开文件  
  12.       
  13.     ioctl(fd, MEM_SET, 115200);//第一个参数fd,第二个是我们要发送的命令,第三个是要传入的参数  
  14.       
  15.     ioctl(fd, MEM_RESTART);//重启  
  16.       
  17.     close(fd);  
  18. }  


Makefile

 

 

[cpp] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  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了!

posted @ 2017-01-06 09:40  请给我倒杯茶  阅读(396)  评论(0编辑  收藏  举报