IOCTL
的系统调用1、应用程序中的ioctl
(系统IO的内容)
#include
int ioctl(int d, int request, ...);
应用程序向驱动程序发送命令(cmd
),然后应用程序可以向驱动程序发送数据(args
),也可以从驱动程序中读数据。
2、驱动程序中,对应的ioctl
struct file_operations {
struct module *owner;
int (*ioctl) (struct inode *inode, struct file *filep, unsigned int cmd, unsigned long args);
long (*unlocked_ioctl) (struct file *filep, unsigned int cmd, unsigned long args);
......
}
在驱动程序中,ioctl
和unlocked_ioctl
的区别
在2.6.36以后linux
的内核中,只支持unlocked_ioctl()
,不支持ioctl()
。2.6.35.7
内核中,两个函数都可以使用。
为了高效的使用cmd
参数传递更多的控制信息,一个unsigned int cmd
被分为了4个段,每一段都有各自的意义,cmd
的定义在
。
unsigned int cmd
位域拆分如下:
cmd[31:30]
—数据(args
)的传输方向
cmd[29:16]
—数据(args
)的大小
cmd[15:8]
—>命令的类型,可以理解成命令的密钥,一般为ASCII
码
cmd[7:0]
—>命令的序号,是一个8bits
的数字
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
例:
#define ADC_INPUT_PIN _IOW('S', 0x0c, unsigned long)
幻数( 幻数(TYPE ):0~0xff
,占8bit
(_IOC_TYPEBITS
)。为一个ASCII
字符,内核
文档给出一些推荐的或者已经被使用的幻数,也可以叫做魔数
序数(NR
):为功能号,给自己的命令编号,占8bit
(_IOC_NRBITS
)。
数据传输方向( 数据传输方向(DIR
):占2bit
(_IOC_DIRBITS
)。如果涉及到要传参,内
核要求描述一下传输的方向,传输的方向是以应用层的角度来描述的。
#include
_IO(type,nr) //没有参数的命令
_IOR(type,nr,size) //该命令是从驱动读取数据
_IOW(type,nr,size) //该命令是从驱动写入数据
_IOWR(type,nr,size) //双向数据传输
上面的命令已经定义了方向,我们要传的是幻数(type
)
、序号(nr
)和大小(size
)。在这里szie的参数只需要填参
数的类型,如int
,上面的命令就会帮你检测类型的正确
然后赋值sizeof(int)
。
数据大小( 数据大小(SIZE
):arg
参数大小,占14bit(_IOC_SIZEBITS)
。
针对unsigned int cmd
的四段位域,linux
中提供以下用于位域操作的宏:
cmd
的函数 ##/* 定义幻数 */
#define DATA_MAGIC 'B‘
/* 定义命令 */
#define NODATA_CMD _IO(MAGIC_DATA, 0x20)
#define GETDATA_CMD _IOR(MAGIC_DATA, 0x21, int)
#define SETDATA_CMD _IOW(MAGIC_DATA, 0x22, int)
cmd
的函数1、应用程序和驱动程序之间不传递参数
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
2、应用程序从驱动程序中读参数
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
3、应用程序想驱动程序写参数
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
4、参数的传递是双向的。
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
参数说明:
type
—>命令的类型
nr
—>命令的序号
size
—>参数的大小
1.系统调用号定义
arch/arm/include/asm/unistd.h
#define __NR_ioctl (__NR_SYSCALL_BASE+ 54)
2.系统调用入口
arch/arm/kernel/calls.S
/* 55 */ CALL(sys_ioctl)
3.声明系统调用sys_ioctl()
/include/linux.h
asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
4.系统调用安装
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
其中:
/include/linux/syscalls.h
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...) __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...) asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
5、内核系统调用接口
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
struct file *filp;
int error = -EBADF;
int fput_needed;
filp = fget_light(fd, &fput_needed);//由fd得带filp指针
if (!filp)
goto out;
error = security_file_ioctl(filp, cmd, arg);
if (error)
goto out_fput;
error = do_vfs_ioctl(filp, fd, cmd, arg);
out_fput:
fput_light(filp, fput_needed);
out:
return error;
}
6、do_vfs_ioctl函数
int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
unsigned long arg)
{
int error = 0;
int __user *argp = (int __user *)arg;
struct inode *inode = filp->f_path.dentry->d_inode;
switch (cmd) {
case FIOCLEX:
set_close_on_exec(fd, 1);
break;
case FIONCLEX:
set_close_on_exec(fd, 0);
break;
case FIONBIO:
error = ioctl_fionbio(filp, argp);
break;
case FIOASYNC:
error = ioctl_fioasync(fd, filp, argp);
break;
case FIOQSIZE:
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) ||
S_ISLNK(inode->i_mode)) {
loff_t res = inode_get_bytes(inode);
error = copy_to_user(argp, &res, sizeof(res)) ?
-EFAULT : 0;
} else
error = -ENOTTY;
break;
case FIFREEZE:
error = ioctl_fsfreeze(filp);
break;
case FITHAW:
error = ioctl_fsthaw(filp);
break;
case FS_IOC_FIEMAP:
return ioctl_fiemap(filp, arg);
case FIGETBSZ:
return put_user(inode->i_sb->s_blocksize, argp);
default:
if (S_ISREG(inode->i_mode))//是否为常规文件若是常规文件
error = file_ioctl(filp, cmd, arg);
else
error = vfs_ioctl(filp, cmd, arg);//调用vfs_ioctl
break;
}
return error;
}
7、调用vfs_ioctl()
static long vfs_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int error = -ENOTTY;
if (!filp->f_op || !filp->f_op->unlocked_ioctl)
goto out;
unlocked_ioctl
error = filp->f_op->unlocked_ioctl(filp, cmd, arg);//调用unlocked_ioctl()
if (error == -ENOIOCTLCMD)
error = -EINVAL;
out:
return error;
}