ioctl 函数.docx
《ioctl 函数.docx》由会员分享,可在线阅读,更多相关《ioctl 函数.docx(10页珍藏版)》请在冰豆网上搜索。
ioctl函数
Linux驱动之ioctl
分类:
ARM+Linux2010-05-0917:
10537人阅读评论(0)收藏举报
在用户空间,使用ioctl系统调用来控制设备,原型如下:
intioctl(intfd,unsignedlongcmd,...);
第三个参数不表示一个变数目的参数,而是一个类型可选的参数。
第三个参数依赖于控制命令。
一些命令不用参数,一些用一个整数值,一些使用指针
1、ioctl函数定义命令
在编写ioctl代码之前,首先需要定义命令。
为了防止对错误的设备使用正确的命令,命令号应该在系统范围内事唯一的
ioctl命令编码被划分为几个段,include/asm/ioctl.h中定义了这些字段:
类型(幻数),基数,传送方向,参数大小等。
Documentation/ioctl-number.txt文件中罗列了再内核中已经使用了的幻数。
定义ioctl命令的正确方法是使用4个位段,这个列表中介绍的符号定义在中:
Type:
幻数(类型):
表明哪个设备的命令,在参考了ioctl-number.txt之后选出,8位宽。
Number:
序号,表明设备命令中的第几个,8位宽
内核提供了下列宏来帮助定义命令号,如下
_IO(type,nr):
没有参数的命令
_IOR(type,nr,datatype):
从驱动中读数据
_IOW(type,nr,datatype):
写数据到驱动
_IOWR(type,nr,datatype):
双向传送,type和number作为参数被传递
2、ioctl函数的实现
分三个技术环节:
1、返回值
ioctl函数的实现通常是根据命令执行的一个switch语句。
但是当命令不能匹配任何一个设备所支持的命令时,通常返回-EINVAL(“非法参数”)
2、参数使用
如果是一个整数,可以直接使用。
如果是指针,我们必须确保这个用户地址是有效的,因此使用前需进行正确的检查
使用intaccess_ok(inttype,constvoid*addr,unsignedlongsize)来检查一个指针地址是否可以访问
第一个参数是VERIFY_READ或者是VERIFY_WRITE,用来表明读用户内存还是写用户内存。
addr参数时要操作的用户内存地址,size是操作的长度。
如果ioctl需要从用户空间读一个整数,那么size=sizeof(int)。
如果需要同时读和写,则使用VERIFY_WRITE。
access_ok返回一个布尔值:
1是成功(存取没问题)和0是失败(存取有问题),如果该函数返回失败,则ioctl应当返回-EFAULT,如:
if(_IOC_DIR(cmd)&_IOC_READ)//为什么_IOC_READ对应VERIFY_WRITE?
?
?
err=!
access_ok(VERIFY_WRITE,(void __user*)arg,_IOC_SIZE(cmd));
elseif(_IOC_DIR(cmd)&_IOC_WRITE)
err=!
access_ok(VERIFY_READ,(void __user*)arg,_IOC_SIZE(cmd));
if(err)
return–EFAULT
在access_ok检测通过后,驱动可以使用cope_from_user和cope_to_user等函数进行参数传递了
3、命令操作
switch(cmd)
{
caseMEMDEV_IOCPRINT:
printk("<---CMDMEMDEV_IOCPRINTDone--->/n/n");
break;
caseMEMDEV_IOCGETDATA:
ioarg=1101;
ret=__put_user(ioarg,(int*)arg);
break;
caseMEMDEV_IOCSETDATA:
ret=__get_user(ioarg,(int*)arg);
printk("<---InKernelMEMDEV_IOCSETDATAioarg=%d--->/n/n",ioarg);
break;
default:
return-ENOTTY;
}
实例分析,源码如下(最好先看《第一个嵌入式Linux的驱动程序》中的源码)
memdev.h:
#ifndef_MEMDEV_H_
#define_MEMDEV_H_
#include
#ifndefMEMDEV_MAJOR
#defineMEMDEV_MAJOR0
#endif
#ifndefMEMDEV_NR_DEVS
#defineMEMDEV_NR_DEVS3
#endif
#ifndefMEMDEV_SIZE
#defineMEMDEV_SIZE4096
#endif
typedefstructMem_Dev{
char*data;
structMem_Dev*next;
unsignedlongsize;
}Mem_Dev;
#defineMEMDEV_IOC_MAGIC 'k'
#defineMEMDEV_IOCPRINT _IO(MEMDEV_IOC_MAGIC,1)
#defineMEMDEV_IOCGETDATA_IOR(MEMDEV_IOC_MAGIC,2,int)
#defineMEMDEV_IOCSETDATA_IOW(MEMDEV_IOC_MAGIC,3,int)
#defineMEMDEV_IOC_MAXNR3
#endif
memdev.c:
#ifndef__KERNEL__
# define__KERNEL__
#endif
#ifndefMODULE
# defineMODULE
#endif
//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"memdev.h"
MODULE_LICENSE("GPL");
Mem_Dev*mem_devices;
intmemdev_major=MEMDEV_MAJOR;
intmemdev_open(structinode*inode,structfile*filp)
{
Mem_Dev*dev;
intnum=MINOR(inode->i_rdev);
dev=(Mem_Dev*)filp->private_data;
if(!
dev)
{
if(num>=MEMDEV_NR_DEVS)
return-ENODEV;
dev=&mem_devices[num];
filp->private_data=dev;
}
//MOD_INC_USE_COUNT;
return0;
}
intmemdev_release(structinode*inode,structfile*filp)
{
//MOD_DEC_USE_COUNT;
return0;
}
intmemdev_ioctl(structinode*inode,structfile*filp,
unsignedintcmd,unsignedlongarg)
{
interr=0;
intret=0;
intioarg=0;
if(_IOC_TYPE(cmd)!
=MEMDEV_IOC_MAGIC)
return-ENOTTY;
if(_IOC_NR(cmd)>MEMDEV_IOC_MAXNR)
return-ENOTTY;
if(_IOC_DIR(cmd)&_IOC_READ)
err=!
access_ok(VERIFY_WRITE,(void*)arg,_IOC_SIZE(cmd));
elseif(_IOC_DIR(cmd)&_IOC_WRITE)
err=!
access_ok(VERIFY_READ,(void*)arg,_IOC_SIZE(cmd));
if(err)
return-EFAULT;
switch(cmd)
{
caseMEMDEV_IOCPRINT:
printk("<---CMDMEMDEV_IOCPRINTDone--->/n/n");
break;
caseMEMDEV_IOCGETDATA:
ioarg=1101;
ret=__put_user(ioarg,(int*)arg);
break;
caseMEMDEV_IOCSETDATA:
ret=__get_user(ioarg,(int*)arg);
printk("<---InKernelMEMDEV_IOCSETDATAioarg=%d--->/n/n",ioarg);
break;
default:
return-ENOTTY;
}
returnret;
}
structfile_operationsmemdev_fops={
ioctl:
memdev_ioctl,
open:
memdev_open,
release:
memdev_release,
};
voidmemdev_cleanup_module(void)
{
inti;
unregister_chrdev(memdev_major,"memdev");
if(mem_devices)
{
for(i=0;i kfree(mem_devices[i].data);
kfree(mem_devices);
}
}
intmemdev_init_module(void)
{
intresult,i;
//SET_MODULE_OWNER(&memdev_fops);
result=register_chrdev(memdev_major,"memdev",&memdev_fops);
if(result<0)
{
printk(KERN_WARNING"mem:
can'tgetmajor%d/n",memdev_major);
returnresult;
}
if(memdev_major==0)
memdev_major=result;
mem_devices=kmalloc(MEMDEV_NR_DEVS*sizeof(Mem_Dev),GFP_KERNEL);
if(!
mem_devices)
{
result=-ENOMEM;
gotofail;
}
memset(mem_devices,0,MEMDEV_NR_DEVS*sizeof(Mem_Dev));
for(i=0;i {
mem_devices[i].size=MEMDEV_SIZE;
mem_devices[i].data=kmalloc(MEMDEV_SIZE,GFP_KERNEL);
memset(mem_devices[i].data,0,MEMDEV_SIZE);
}
return0;
fail:
memdev_cleanup_module();
returnresult;
}
module_init(memdev_init_module);
module_exit(memdev_cleanup_mo
app-ioctl.c:
#include
#include
#include
#include
#include"memdev.h"
intmain()
{
intfd=0;
intcmd;
intarg=0;
charBuf[4096];
fd=open("/dev/memdev0",O_RDWR);
if(fd<0)
{
printf("OpenDevMem0Error!
/n");
return-1;
}
printf("<---CallMEMDEV_IOCPRINT--->/n");
cmd=MEMDEV_IOCPRINT;
if(ioctl(fd,cmd,&arg)<0)
{
printf("CallcmdMEMDEV_IOCPRINTfail/n");
return-1;
}
printf("<---CallMEMDEV_IOCSETDATA--->/n");
cmd=MEMDEV_IOCSETDATA;
arg=2007;
if(ioctl(fd,cmd,&arg)<0)
{
printf("CallcmdMEMDEV_IOCSETDATAfail/n");
return-1;
}
printf("<---CallMEMDEV_IOCGETDATA--->/n");
cmd=MEMDEV_IOCGETDATA;
if(ioctl(fd,cmd,&arg)<0)
{
printf("CallcmdMEMDEV_IOCGETDATAfail/n");
return-1;
}
printf("<---InUserSpaceMEMDEV_IOCGETDATAGetDatais%d--->/n/n",arg);
close(fd);
return0;
}
Makefile和《第一个嵌入式Linux的驱动程序》中的Makefile一样。