linux驱动-file_operations之ioctl

来源:互联网 发布:农村淘宝合伙人的收入 编辑:程序博客网 时间:2024/05/21 08:53

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

应用程序ioctl接口:

声明头文件:

#include <sys/ioctl.h>

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

#include <asm/ioctl.h>

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

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 <asm/ioctl.h>

命令定义分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 <asm/uaccess.h>#include <asm/ioctl.h>#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 <uapi/asm-generic/ioctls.h>

很少看到有用的。

0 0