嵌入式Linux熱門培訓內容之Linux 驅動之Ioctl

時間:2018-12-13 17:35:38

一、在用戶空間,使用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中提供了一些宏,這些宏可根據便于理解的字符串生成命令碼,或者是從命令碼得到一些用戶可以理解的字符串以標明這個命令對應的設備類型、設備序列號、數據傳送方向和數據傳輸尺寸。

三、使用命令定義ioctl幻數

  內核提供了一些宏來幫助定義命令:

//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)

四、實例分析

驅動層:

#include <linux/init.h>

#include <linux/module.h>

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/device.h>

#include <linux/module.h>

#include <linux/types.h>

#include <linux/errno.h>

#include <linux/mm.h>

#include <linux/sched.h>

#include <asm/io.h>

#include <asm/system.h>

#include <asm/uaccess.h>

/* 定義幻數 */

#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   /*一共定義了三條命令*/

MODULE_LICENSE("WQFDual BSD/GPL");

MODULE_AUTHOR("WQFDriver Monkey");

struct mmap_dev_t

{

    dev_t dev_num;

    char *dev_name;

    struct class* cls;

    char * cls_name;

    struct cdev cdev;

};

struct mmap_dev_t mmap_dev = 

{

    .dev_name = "test_dev",

    .cls_name = "test_class",

};

int my_open(struct inode *inode, struct file *filp)

{

    printk(KERN_INFO"WQFmy_open()++\n");

    printk(KERN_INFO"WQFmy_open()--\n");

    printk("wQF===============================\n");

    return 0;

}

ssize_t my_read(struct file *filp, char *buff, size_t count, loff_t *f_pos)

{

    printk(KERN_INFO"my_read()++\n");

    printk(KERN_INFO"my_read()--\n");

    return 0;

}

ssize_t my_write(struct file *filp, const char *buff, size_t count, loff_t *f_pos)

{

    printk(KERN_INFO"buff is :\n %s", buff);

    printk(KERN_INFO"my_write()--\n");

    return count;

}

int my_ioctl(struct inode *inode, struct file *filp,  \

                 unsigned int cmd, unsigned long arg)   /*IOCTL的驅動函數*/

{

        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--->\n\n");

        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 --->\n\n",ioarg);

        break;

      default:  

        return -EINVAL;

    }

    return ret;    

}

struct file_operations mmap_opt = 

{

    .owner = THIS_MODULE,

    .open = my_open,

    .read = my_read,

    .write = my_write,

    .unlocked_ioctl  = my_ioctl,

    /* linux2.6.29和linux2.6.38的內核在file_operations結構發生了變化,

    否則在linux2.6.38內核中,繼續使用.ioctl成員,編譯時就會報錯:

    error: unknown field 'ioctl' specified in initializer,struct file_operations結構體定義在include/linux/fs.h文件中。*/

};

static int wqfmmap_init(void)

{

    int err = 0;

    printk("mmap_init()++\n");

    //dynamic alloc device number

    if(0 != (err = alloc_chrdev_region

            (&mmap_dev.dev_num, 0,3, mmap_dev.dev_name)))  /*自動分配主次設備號*/

        goto alloc_chrdev_region_err;

    printk("Wmajor = %d, Wminor = %d\n",

           MAJOR(mmap_dev.dev_num),MINOR(mmap_dev.dev_num));/*從設備編號里抽取出主次設備號*/

    //create class

    mmap_dev.cls = class_create(THIS_MODULE,mmap_dev.cls_name);

    if(IS_ERR(mmap_dev.cls))

        goto class_create_err;

    //create character device

    device_create(mmap_dev.cls,NULL,mmap_dev.dev_num,&mmap_dev,"wfdevice");/*創建字符設備 在dev/下面會生成這個設備文件*/

    //activate character driver

    cdev_init(&mmap_dev.cdev, &mmap_opt);/*file_operations 字符設備操作指針*/

    mmap_dev.cdev.owner = THIS_MODULE;

    err = cdev_add(&mmap_dev.cdev,mmap_dev.dev_num,1);

    if(err)

        goto cdev_add_err;

    printk("mmap_init()--\n");

    return 0;

cdev_add_err:    

    device_destroy(mmap_dev.cls, mmap_dev.dev_num);

    class_destroy(mmap_dev.cls);

class_create_err:

    unregister_chrdev_region(mmap_dev.dev_num,3);

alloc_chrdev_region_err:

    return (-1);

}

static void wqfmmap_exit(void)

{

    printk(KERN_INFO"mmap_exit()++\n");

    cdev_del(&mmap_dev.cdev);

    device_destroy(mmap_dev.cls, mmap_dev.dev_num);

    class_destroy(mmap_dev.cls);

    unregister_chrdev_region(mmap_dev.dev_num,3);

    printk(KERN_INFO"mmap_exit()--\n");

}

module_init(wqfmmap_init);

module_exit(wqfmmap_exit);

應用層:

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/ioctl.h>

#include <unistd.h> /*加入這個頭 close才不會出錯*/

#include "memdev.h"  /* 包含命令定義 */

int main()

{

    int fd = 0;

    int cmd;

    int arg = 0;

    char Buf[4096];

    /*打開設備文件*/

    fd = open("/dev/wfdevice",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 fail\n");

            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 fail\n");

            return -1;

    }

    /* 調用命令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);    

    close(fd);

    return 0;    

}

#ifndef _MEMDEV_H_

#define _MEMDEV_H_

#include "linux/ioctl.h"

#ifndef MEMDEV_MAJOR

#define MEMDEV_MAJOR 0   /*預設的mem的主設備號*/

#endif

#ifndef MEMDEV_NR_DEVS

#define MEMDEV_NR_DEVS 2    /*設備數*/

#endif

#ifndef MEMDEV_SIZE

#define MEMDEV_SIZE 4096

#endif

/*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_ */

注意注意!!

有時候會發現ioctl沒有調用到驅動層

注意下面的問題

1、linux-2.6.36 后由unlocked_ioctl 代替了ioctl

2、在你的機器下面運行 uname -a 

127|rk3399_mid:/ # uname -a Linux localhost 4.4.83

 #111 SMP PREEMPT Thu Mar 29 23:54:51 EDT 2018 aarch64 

說明你的是64位系統 所以要用compat_ioctl 代替unlocked_ioctl 

? 江苏快3号码表