linux驱动-file_operations之ioctl

简述:
ioctl是linux应用程序用来向设备发送特殊命令。如果,设备要响应应用程序的ioctl调用,那么设备驱动必须编写响应ioctl的接口,这个接口就是file_operations的unlocked_ioctl。

应用程序ioctl接口:

声明头文件:

#include 

如是ubuntu系统,可以在/usr/include/sys/下面查看。
定义ioctl命令需要包含头文件:

#include 

命令定义规则和驱动一样的,见下。

file_operations的定义:

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **);
    long (*fallocate)(struct file *file, int mode, loff_t offset,
              loff_t len);
    int (*show_fdinfo)(struct seq_file *m, struct file *f);
};

其中:

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

就是响应应用程序ioctl调用的接口。

主要用unlocked_ioctl,
compat_ioctl用来兼容老版本。

第一个参数:打开的文件指针
第二个参数:命令
第三个参数:可以是一个整数,也可以是数据起始地址。

ioctl命令定义(第2个参数):
定义命令需要包含头文件:

#include 

命令定义分4个段,如下:
type:魔数,占8位(_IOC_TYPEBITS)
number:序号,占8位(_IOC_NRBITS)
direction:方向(读写),占2位(_IOC_DIRBITS)
size:数据大小,占14位(_IOC_SIZEBITS),当第三个参数是地址的时候,就可以启用这个size来表示数据大小(单位字节),一般情况下不用就是0。(假想下,当同一type下超过256个序号的命令,其实可以启用这个,只需要驱动与应用程序配合好,当然,一般情况也没有超过256的命令)

正好32位。命令类型主要由type和number决定,先看type,然后再看number。

  • 用如下的宏来编码命令:
/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),       (_IOC_TYPECHECK(size)))
#define _IOR_BAD(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW_BAD(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

_IOC_NONE:代表没有读写的命令,只执行动作。
_IOC_READ:代表读,即要返回数据给用户空间(地址是unlocked_ioctl第三个参数),数据大小为size。
_IOC_WRITE:代表写,即用户空间有送数据下来,数据起始地址是unlocked_ioctl第三个参数,大小size。

  • 解码命令用下面宏:
/* used to decode ioctl numbers.. */
#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)       (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

/* ...and for the drivers/sound files... */

#define IOC_IN      (_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT     (_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT   ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK    (_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT   (_IOC_SIZESHIFT)

如,要判断一个命令是不是读:

_IOC_DIR(nr)==IOC_OUT       

定义命令例子:

#include 
#include 
#define YU_CMD  'A'    //其实就是大写A的ascii值,也可以直接写数字
#define TR_CMD  32

#define YU_CMD0 _IO(YU_CMD,0)
#define YU_CMD1 _IO(YU_CMD,1)
#define YU_CMD2 _IOW(YU_CMD,2,sizeof(int))

#define TR_CMD0 _IO(TR_CMD  ,0)
#define TR_CMD1 _IO(TR_CMD  ,1)
#define TR_CMD2 _IOR(TR_CMD ,2,20)

long exioctl(struct file *f, unsigned int cmd, unsigned long data);

struct file_operations fo = {
    ...
    .unlocked_ioctl = exioctl,
    .compat_ioctl = exioctl,    //这行可要可不要,不考虑兼容老版本就不需要
    ...
}

long exioctl(struct file *f, unsigned int cmd, unsigned long data)
{
    int err;
    int ac;
    char ch[20] = {'t'};
    switch(_IOC_TYPE(cmd)|_IOC_NR(cmd))
    {
        case YU_CMD0:
            ...
            break;
        case YU_CMD1:
            ...
            break;
        case YU_CMD2:
            if(_IOC_DIR(cmd) == IOC_IN)
            {
                err = get_user(ac,data);
                printk("ac = %d\n",ac)
            }
            break;
        case TR_CMD0:
            ...
            break;
        case TR_CMD1:
            ...
            break;
        case TR_CMD2:
            if(_IOC_DIR(cmd) == IOC_OUT)
            {
                err = copy_to_user(data,ch,20); 
            }
            break;
        default:
            err = -EINVAL;      
    }
    ...
    return err;
}

unlocked_ioctl 返回值,当没有匹配到命令时一般返回-EINVAL(”Invalid argument”),没有带读写的一般返回0,有读写的,返回读写字节数。

对于内核和用户空间数据交互,尽量用提供的api:
单个数用:
get_user:从用户空间获取一个数
put_user:推送一个数到用户空间
多个数据用:
copy_from_user:从用户空间获取数据
copy_to_user:推送数据到用户空间

如果,不用这些API,要考虑用access_ok来检查用户空间地址的有效性:
access_ok(VERIFY_READ, ptr, sizeof(*ptr))
VERIFY_READ:表示对地址有没有读权限,写用权限VERIFY_WRITE
ptr:用户空间地址
sizeof(*ptr):地址空间大小

其实,命令也可以不按照这么定义,直接用数字代替也是可以的,只要驱动和应用程序配合好,但是兼容性和可读性,就没按照规定好,不建议。

还有些特殊的预定于命令,在:

#include 

很少看到有用的。

你可能感兴趣的:(linux驱动)