美高梅开户-美高梅官方网站开户

您的位置:美高梅开户 > 美高梅官方网站开户 > Linux设备驱动之Ioctl控制

Linux设备驱动之Ioctl控制

发布时间:2019-10-22 06:00编辑:美高梅官方网站开户浏览(186)

    一、 什么是ioctl。

     

    转自:

      大多数使得除了要求具有读写设备的力量之外,还索要全体对硬件调控的技艺。

     大器晚成、在客户空间,使用ioctl系统调用来决定设施,原型如下:

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

      客商程序所作的只是因而命令码告诉驱动程序它想做如何,至于怎么解释这个命令和怎么落到实处这个命令,那都以驱动程序要做的事务

    二、驱动ioctl方法

    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、定义命令:
      内核提供了某些宏来帮忙定义命令:

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

      定义命令例子:

    #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是一个整数,能够一贯利用;
      如果是指针,大家不能够不保证这几个客户地址是行得通的,因而,使用从前供给举行正确检查。
      内部有自己研究的,不必要检查实验的:

    copy_from_user
    copy_to_user
    get_user
    put_user
    

      急需检查实验的:

    __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);
    
    返回值:Access_ok返回一个布尔值:1,是成功(存取没问题);0,是失败,ioctl返回-EFAULT;
    
    */
    

    图片 2

    3)命令操作;

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

     

    三、ioctl实例深入分析

    (1)memdev.h:

    图片 3View Code

    图片 4

    /*mem设备描述结构体*/
    struct mem_dev                                     
    {                                                        
      char *data;                      
      unsigned long size;       
    };
    
    /* 定义幻数 */
    #define MEMDEV_IOC_MAGIC  'k'
    
    /* 定义命令 */
    #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)
    
    #define MEMDEV_IOC_MAXNR 3
    
    #endif /* _MEMDEV_H_ */
    

    图片 5

     

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

    图片 6View Code

    图片 7

    static int mem_major = MEMDEV_MAJOR;
    
    module_param(mem_major, int, S_IRUGO);
    
    struct mem_dev *mem_devp; /*设备结构体指针*/
    
    struct cdev cdev; 
    
    /*文件打开函数*/
    int mem_open(struct inode *inode, struct file *filp)
    {
        struct mem_dev *dev;
    
        /*获取次设备号*/
        int num = MINOR(inode->i_rdev);
    
        if (num >= MEMDEV_NR_DEVS) 
                return -ENODEV;
        dev = &mem_devp[num];
    
        /*将设备描述结构指针赋值给文件私有数据指针*/
        filp->private_data = dev;
    
        return 0; 
    }
    
    /*文件释放函数*/
    int mem_release(struct inode *inode, struct file *filp)
    {
      return 0;
    }
    
    /*IO操作*/
    int memdev_ioctl(struct inode *inode, struct file *filp,
                     unsigned int cmd, unsigned long arg)
    {
    
        int err = 0;
        int ret = 0;
        int ioarg = 0;
    
        /* 检测命令的有效性 */
        if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC) 
            return -EINVAL;
        if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR) 
            return -EINVAL;
    
        /* 根据命令类型,检测参数空间是否可以访问 */
        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;
    
        /* 根据命令,执行相应的操作 */
        switch(cmd) {
    
          /* 打印当前设备信息 */
          case MEMDEV_IOCPRINT:
              printk("<--- CMD MEMDEV_IOCPRINT Done--->nn");
            break;
    
          /* 获取参数 */
          case MEMDEV_IOCGETDATA: 
            ioarg = 1101;
            ret = __put_user(ioarg, (int *)arg);
            break;
    
          /* 设置参数 */
          case MEMDEV_IOCSETDATA: 
            ret = __get_user(ioarg, (int *)arg);
            printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->nn",ioarg);
            break;
    
          default:  
            return -EINVAL;
        }
        return ret;
    
    }
    
    /*文件操作结构体*/
    static const struct file_operations mem_fops =
    {
      .owner = THIS_MODULE,
      .open = mem_open,
      .release = mem_release,
      .ioctl = memdev_ioctl,
    };
    
    /*设备驱动模块加载函数*/
    static int memdev_init(void)
    {
      int result;
      int i;
    
      dev_t devno = MKDEV(mem_major, 0);
    
      /* 静态申请设备号*/
      if (mem_major)
        result = register_chrdev_region(devno, 2, "memdev");
      else  /* 动态分配设备号 */
      {
        result = alloc_chrdev_region(&devno, 0, 2, "memdev");
        mem_major = MAJOR(devno);
      }  
    
      if (result < 0)
        return result;
    
      /*初始化cdev结构*/
      cdev_init(&cdev, &mem_fops);
      cdev.owner = THIS_MODULE;
      cdev.ops = &mem_fops;
    
      /* 注册字符设备 */
      cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
    
      /* 为设备描述结构分配内存*/
      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));
    
      /*为设备分配内存*/
      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);
      }
    
      return 0;
    
      fail_malloc: 
      unregister_chrdev_region(devno, 1);
    
      return result;
    }
    
    /*模块卸载函数*/
    static void memdev_exit(void)
    {
      cdev_del(&cdev);   /*注销设备*/
      kfree(mem_devp);     /*释放设备结构体内存*/
      unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
    }
    
    MODULE_AUTHOR("David Xie");
    MODULE_LICENSE("GPL");
    
    module_init(memdev_init);
    module_exit(memdev_exit);
    

    图片 8

     

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

    图片 9

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

    图片 10

     

    ioctl是设备驱动程序中对道具的I/O通道进行管制的函数。所谓对I/O通道举办田间管理,正是对设施的片段性情开展支配,比方串口的传输Porter率、马达的转向等等。它的调用个数如下:

    int ioctl(int fd, ind cmd, …);

    在那之中fd正是顾客程序张开设备时行使open函数重回的文件标示符,cmd正是客商程序对设备的支配命令,至于前边的省略号,那是部分补充参数,经常最多三个,有或从不是和cmd的意思相关的。

    ioctl函数是文本结构中的叁本性质分量,正是说借使您的驱动程序提供了对ioctl的支撑,客户就足以在客户程序中动用ioctl函数调控设施的I/O通道。

    二、 ioctl的须要性

    风度翩翩旦不用ioctl的话,也足以兑现对设施I/O通道的垄断,但那便是蛮拧了。比方,我们得以在驱动程序中贯彻write的时等候检查查一下是不是有新鲜约定的数目流通过,假设有的话,那么后边就随之调控命令(通常在socket编程中时时那样做)。然而生气勃勃旦这么做的话,会招致代码分工不明,程序结构混乱,技师本人也会头昏脑胀的。

    所以,大家就应用ioctl来贯彻调节的成效。要铭记,客户程序所作的只是由此命令码告诉驱动程序它想做什么样,至于怎么解释这么些命令和怎么贯彻这么些命令,那都以驱动程序要做的业务。

    三、 ioctl如何促成

    在驱动程序中贯彻的ioctl函数体内,实际上是有四个switch{case}结构,每一个case对应一个命令码,做出一些八方呼应的操作。怎么落实那个操作,那是每一种程序猿自个儿的专门的职业,因为设备都以特定的,这里也无法说。关键在于怎么着协会命令码,因为在ioctl中命令码是独步天下联系客户程序命令和驱动程序支持的门路。

    命令码的团伙是有部分青眼的,因为我们必供给达成命令和装置是逐大器晚成对应的,那样才不会将科学的下令发给错误的配备,也许是把错误的命令发给正确的设施,可能是把错误的指令发给错误的设备。那么些错误都会促成不可预料的事情时有发生,而当技士开掘了那几个离奇的事体的时候,再来调节和测验程序查找错误,那将是格外难堪的政工。

    于是在Linux主旨中是如此定义一个命令码的:

    ____________________________________

    | 设备等级次序 | 类别号 | 方向 |数据尺寸|

    本文由美高梅开户发布于美高梅官方网站开户,转载请注明出处:Linux设备驱动之Ioctl控制

    关键词: 美高梅开户

上一篇:Linux入门教程,文件管理

下一篇:没有了