原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275372.html

大部分驱动除了需要具备读写设备的能力之外,还需要具备对硬件控制的能力。

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

  1. int ioctl(int fd,unsigned long cmd,...);
    /*
    fd:文件描述符
    cmd:控制命令
    ...:可选参数:插入*argp,具体内容依赖于cmd
    */

  用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情

二、驱动ioctl方法

  1. int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
    /*
    inode与filp两个指针对应于应用程序传递的文件描述符fd,这和传递open方法的参数一样。
    cmd 由用户空间直接不经修改的传递给驱动程序
    arg 可选。
    */

  在驱动程序中实现的ioctl函数体内,实际上是有一个switch {case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。

  在Linux核心中是这样定义一个命令码的:
____________________________________

| 设备类型  | 序列号 |  方向 | 数据尺寸 |

|----------|--------|------|-------- |

| 8 bit   |  8 bit   | 2 bit |8~14 bit|

|----------|--------|------|-------- |

  这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。

1、定义命令:
  内核提供了一些宏来帮助定义命令:

  1. //nr为序号,datatype为数据类型,如int
    _IO(type, nr ) //没有参数的命令
    _IOR(type, nr, datatype) //从驱动中读数据
    _IOW(type, nr, datatype) //写数据到驱动
    _IOWR(type,nr, datatype) //双向传送

  定义命令例子:

  1. #define MEM_IOC_MAGIC 'm' //定义类型
    #define MEM_IOCSET _IOW(MEM_IOC_MAGIC,0,int)
    #define MEM_IOCGQSET _IOR(MEM_IOC_MAGIC, 1, int)

2、实现命令:
  定义好了命令,下一步就是要实现ioctl函数了,ioctl的实现包括三个技术环节:
1)返回值;
  ioctl函数的实现是根据命令执行的一个switch语句,但是,当命令不能匹配任何一个设备所支持的命令时,通常返回-EINVAL(非法参数);
2)参数使用;
  用户使用  int ioctl(int fd,unsinged long cmd,...)  时,...就是要传递的参数;
  再通过  int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)  中的arg传递;
  如果arg是一个整数,可以直接使用;
  如果是指针,我们必须确保这个用户地址是有效的,因此,使用之前需要进行正确检查。
  内部有检查的,不需要检测的:

  1. copy_from_user
    copy_to_user
    get_user
    put_user

  需要检测的:

  1. __get_user
    __put_user

 检测函数access_ok():

  1. static inline int access_ok(int type, const void *addr, unsigned long size)
    /*
    type :是VERIFY_READ 或者VERIFY_WRITE用来表明是读用户内存还是写用户内存;
    addr:是要操作的用户内存地址;
    size:是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数就等于sizeof(int);
  2.  
  3. 返回值:Access_ok返回一个布尔值:1,是成功(存取没问题);0,是失败,ioctl返回-EFAULT;
  4.  
  5. */

3)命令操作;

  1. switch(cmd)
    {
    case:
    ... ...
    }

三、ioctl实例分析

(1)memdev.h:

  1. /*mem设备描述结构体*/
    struct mem_dev
    {
    char *data;
    unsigned long size;
    };
  2.  
  3. /* 定义幻数 */
    #define MEMDEV_IOC_MAGIC 'k'
  4.  
  5. /* 定义命令 */
    #define MEMDEV_IOCPRINT _IO(MEMDEV_IOC_MAGIC, 1)
    #define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)
    #define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)
  6.  
  7. #define MEMDEV_IOC_MAXNR 3
  8.  
  9. #endif /* _MEMDEV_H_ */

 

(2)memdev.c:(驱动程序)

  1. static int mem_major = MEMDEV_MAJOR;
  2.  
  3. module_param(mem_major, int, S_IRUGO);
  4.  
  5. struct mem_dev *mem_devp; /*设备结构体指针*/
  6.  
  7. struct cdev cdev;
  8.  
  9. /*文件打开函数*/
    int mem_open(struct inode *inode, struct file *filp)
    {
    struct mem_dev *dev;
  10.  
  11. /*获取次设备号*/
    int num = MINOR(inode->i_rdev);
  12.  
  13. if (num >= MEMDEV_NR_DEVS)
    return -ENODEV;
    dev = &mem_devp[num];
  14.  
  15. /*将设备描述结构指针赋值给文件私有数据指针*/
    filp->private_data = dev;
  16.  
  17. return 0;
    }
  18.  
  19. /*文件释放函数*/
    int mem_release(struct inode *inode, struct file *filp)
    {
    return 0;
    }
  20.  
  21. /*IO操作*/
    int memdev_ioctl(struct inode *inode, struct file *filp,
    unsigned int cmd, unsigned long arg)
    {
  22.  
  23. int err = 0;
    int ret = 0;
    int ioarg = 0;
  24.  
  25. /* 检测命令的有效性 */
    if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC)
    return -EINVAL;
    if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR)
    return -EINVAL;
  26.  
  27. /* 根据命令类型,检测参数空间是否可以访问 */
    if (_IOC_DIR(cmd) & _IOC_READ)
    err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
    err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
    if (err)
    return -EFAULT;
  28.  
  29. /* 根据命令,执行相应的操作 */
    switch(cmd) {
  30.  
  31. /* 打印当前设备信息 */
    case MEMDEV_IOCPRINT:
    printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");
    break;
  32.  
  33. /* 获取参数 */
    case MEMDEV_IOCGETDATA:
    ioarg = 1101;
    ret = __put_user(ioarg, (int *)arg);
    break;
  34.  
  35. /* 设置参数 */
    case MEMDEV_IOCSETDATA:
    ret = __get_user(ioarg, (int *)arg);
    printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);
    break;
  36.  
  37. default:
    return -EINVAL;
    }
    return ret;
  38.  
  39. }
  40.  
  41. /*文件操作结构体*/
    static const struct file_operations mem_fops =
    {
    .owner = THIS_MODULE,
    .open = mem_open,
    .release = mem_release,
    .ioctl = memdev_ioctl,
    };
  42.  
  43. /*设备驱动模块加载函数*/
    static int memdev_init(void)
    {
    int result;
    int i;
  44.  
  45. dev_t devno = MKDEV(mem_major, 0);
  46.  
  47. /* 静态申请设备号*/
    if (mem_major)
    result = register_chrdev_region(devno, 2, "memdev");
    else /* 动态分配设备号 */
    {
    result = alloc_chrdev_region(&devno, 0, 2, "memdev");
    mem_major = MAJOR(devno);
    }
  48.  
  49. if (result < 0)
    return result;
  50.  
  51. /*初始化cdev结构*/
    cdev_init(&cdev, &mem_fops);
    cdev.owner = THIS_MODULE;
    cdev.ops = &mem_fops;
  52.  
  53. /* 注册字符设备 */
    cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
  54.  
  55. /* 为设备描述结构分配内存*/
    mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
    if (!mem_devp) /*申请失败*/
    {
    result = - ENOMEM;
    goto fail_malloc;
    }
    memset(mem_devp, 0, sizeof(struct mem_dev));
  56.  
  57. /*为设备分配内存*/
    for (i=0; i < MEMDEV_NR_DEVS; i++)
    {
    mem_devp[i].size = MEMDEV_SIZE;
    mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
    memset(mem_devp[i].data, 0, MEMDEV_SIZE);
    }
  58.  
  59. return 0;
  60.  
  61. fail_malloc:
    unregister_chrdev_region(devno, 1);
  62.  
  63. return result;
    }
  64.  
  65. /*模块卸载函数*/
    static void memdev_exit(void)
    {
    cdev_del(&cdev); /*注销设备*/
    kfree(mem_devp); /*释放设备结构体内存*/
    unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
    }
  66.  
  67. MODULE_AUTHOR("David Xie");
    MODULE_LICENSE("GPL");
  68.  
  69. module_init(memdev_init);
    module_exit(memdev_exit);

 

(3)app-ioctl.c(应用程序)

  1. #include <stdio.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
  2.  
  3. #include "memdev.h" /* 包含命令定义 */
  4.  
  5. int main()
    {
    int fd = 0;
    int cmd;
    int arg = 0;
    char Buf[4096];
  6.  
  7. /*打开设备文件*/
    fd = open("/dev/memdev0",O_RDWR);
    if (fd < 0)
    {
    printf("Open Dev Mem0 Error!\n");
    return -1;
    }
  8.  
  9. /* 调用命令MEMDEV_IOCPRINT */
    printf("<--- Call MEMDEV_IOCPRINT --->\n");
    cmd = MEMDEV_IOCPRINT;
    if (ioctl(fd, cmd, &arg) < 0)
    {
    printf("Call cmd MEMDEV_IOCPRINT fail\n");
    return -1;
    }
  10.  
  11. /* 调用命令MEMDEV_IOCSETDATA */
    printf("<--- Call MEMDEV_IOCSETDATA --->\n");
    cmd = MEMDEV_IOCSETDATA;
    arg = 2007;
    if (ioctl(fd, cmd, &arg) < 0)
    {
    printf("Call cmd MEMDEV_IOCSETDATA fail\n");
    return -1;
    }
  12.  
  13. /* 调用命令MEMDEV_IOCGETDATA */
    printf("<--- Call MEMDEV_IOCGETDATA --->\n");
    cmd = MEMDEV_IOCGETDATA;
    if (ioctl(fd, cmd, &arg) < 0)
    {
    printf("Call cmd MEMDEV_IOCGETDATA fail\n");
    return -1;
    }
    printf("<--- In User Space MEMDEV_IOCGETDATA Get Data is %d --->\n\n",arg);
  14.  
  15. close(fd);
    return 0;
    }

【转】Linux设备驱动之Ioctl控制的更多相关文章

  1. Linux设备驱动之Ioctl控制

    大部分驱动除了需要具备读写设备的能力之外,还需要具备对硬件控制的能力. 一.在用户空间,使用ioctl系统调用来控制设备,原型如下: int ioctl(int fd,unsigned long cm ...

  2. Linux设备驱动之Ioctl控制【转】

    转自:http://www.cnblogs.com/geneil/archive/2011/12/04/2275372.html 大部分驱动除了需要具备读写设备的能力之外,还需要具备对硬件控制的能力. ...

  3. linux设备驱动归纳总结(三):4.ioctl的实现【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59419.html linux设备驱动归纳总结(三):4.ioctl的实现 一.ioctl的简介: 虽 ...

  4. linux设备驱动归纳总结(三):4.ioctl的实现

    linux设备驱动归纳总结(三):4.ioctl的实现 一.ioctl的简单介绍: 尽管在文件操作结构体"struct file_operations"中有非常多相应的设备操作函数 ...

  5. 【Linux开发】linux设备驱动归纳总结(三):4.ioctl的实现

    linux设备驱动归纳总结(三):4.ioctl的实现 一.ioctl的简介: 虽然在文件操作结构体"struct file_operations"中有很多对应的设备操作函数,但是 ...

  6. linux设备驱动归纳总结(十一):写个简单的看门狗驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-112879.html linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxx ...

  7. Smart210学习记录-------Linux设备驱动结构

    cdev结构体 1 struct cdev { 2 struct kobject kobj; /* 内嵌的 kobject 对象 */ 3 struct module *owner; /*所属模块*/ ...

  8. Linux 设备驱动 Edition 3

    原文网址:http://oss.org.cn/kernel-book/ldd3/index.html Linux 设备驱动 Edition 3 By Jonathan Corbet, Alessand ...

  9. linux 设备驱动概述

    linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer):       主要利用C库函数和 ...

随机推荐

  1. hdu 4768 Flyer 二分

    思路:由于最多只有一个是奇数,所以二分枚举这个点,每次判断这个点的左边区间段所有点的和作为 二分的依据. 代码如下: #include<iostream> #include<cstd ...

  2. OpenCV4Android开发之旅(一)----OpenCV2.4简介及 app通过Java接口调用OpenCV的示例

    转自:  http://blog.csdn.net/yanzi1225627/article/details/16917961 开发环境:windows+ADT Bundle+CDT+OpenCV-2 ...

  3. poj 2349(最小生成树应用)

    题目链接:http://poj.org/problem?id=2349 思路:由于有S个专门的通道,我们可以先求一次最小生成树,然后对于最小生成树上的边从大到小排序,前S-1条边用S-1个卫星通道连接 ...

  4. 使用var声明的变量 和 直接赋值并未声明的变量的区别

    在看JS高级程序设计时忽然想到这个问题,众所周知,直接赋值一个变量而为声明,会产生一个全局变量(或者说是全局对象的属性),但用var声明的变量 和 直接赋值而并未声明的变量 都有哪些区别呢,这是我在百 ...

  5. android真机自动化测试

    appium执行用例时报错问题: 问题解析: 一般该种情况都是因为来连接了多个设备,验证办法:cmd->执行adb devices  看结果是否是多个devices ,如果是这个问题,停掉多余设 ...

  6. poj3415 Common Substrings(后缀数组,单调栈 | 后缀自动机)

    [题目链接] http://poj.org/problem?id=3415 [题意] A与B长度至少为k的公共子串个数. [思路] 基本思想是将AB各个后缀的lcp-k+1的值求和.首先将两个字符串拼 ...

  7. MongoDB (七) MongoDB 数据类型

    MongoDB支持许多数据类型的列表下面给出: String : 这是最常用的数据类型来存储数据.在MongoDB中的字符串必须是有效的UTF-8. Integer : 这种类型是用来存储一个数值.整 ...

  8. 华为OJ:火车进站

    火车进站 给定一个正整数N代表火车数量,0<N<10,接下来输入火车入站的序列,一共N辆火车,每辆火车以数字1-9编号.要求以字典序排序输出火车出站的序列号. 输入描述: 有多组测试用例, ...

  9. Android核心分析 之一分析方法论探讨之设计意图

    为什么要研究Android,是因为它够庞大,它够复杂,他激起了我作为一个程序员的内心的渴望,渴望理解这种复杂性.我研究的对象是作为手机开发平台的Android软件系统部分,而不是Dalvik虚拟机本身 ...

  10. 【转】SIP初步

    1.什么是SIP SIP(会话发起协议)属于IP应用层协议,用于在IP网上为用户提供会话应用.会话(Session)指两方或多方用户之间的语音.视频.及其他媒体形式的通信,具体可能是IP电话.会议.即 ...