• 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! ");//这里通过虚拟设备来模拟  
    131.             return 0;  
    132.         case MEM_SET: //如果传入的是设置参数命令  
    133.             printk(KERN_WARNING"arg is : %d ", 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! ");  
    165.   printk("Hello Memdev! ");  
    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! ");  
    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了!

  • 相关阅读:
    xmake v2.2.2, 让C/C++拥有包依赖自动构建
    xmake新增对WDK驱动编译环境支持
    记一次博客页面美化过程,分享代码.
    Java容器集合经典面试题集
    学习Redis好一阵了,我对它有了一些新的看法
    Java讲解RPC的基本实现
    项目中用到了Redis分布式锁,了解一下背后的原理
    为什么Mysql的常用引擎都默认使用B+树作为索引?
    关于浮点数与精确小数计算的理解
    通过模拟Mybatis动态代理生成Mapper代理类,讲解Mybatis核心原理
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/6255064.html
Copyright © 2020-2023  润新知