ioctl命令详解

昨天复习了一下对于ioctl 的使用和实现

举个例子 吧。
/* ioctl command for BMA220 device file */
static long bma220_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
unsigned char data[6];
struct bma220_data *sensor_data = i2c_get_clientdata(bma220_client);


/* check cmd */
if (_IOC_TYPE(cmd) != BMA220_IOC_MAGIC) {
#if DEBUG
printk(KERN_ERR "cmd magic type error\n");
#endif
return -ENOTTY;
}
if (_IOC_NR(cmd) > BMA220_IOC_MAXNR) {
#if DEBUG
printk(KERN_ERR "cmd number error\n");
#endif
return -ENOTTY;
}


if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,
(void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ,
(void __user *)arg, _IOC_SIZE(cmd));
if (err) {
#if DEBUG
printk(KERN_ERR "cmd access_ok error\n");
#endif
return -EFAULT;
}
/* check bam120_client */
if (bma220_client == NULL) {
#if DEBUG
printk(KERN_ERR "I2C driver not install\n");
#endif
return -EFAULT;
}


/* cmd mapping */
switch (cmd) {
case BMA220_IOCTL_EVENT_CTRL:
if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
#if DEBUG
printk(KERN_ERR "copy_from_user error\n");
#endif
return -EFAULT;
}
printk(KERN_INFO "%s: set bma220_no_event = %d\n",
__func__, data[0]);
bma220_no_event = (int)data[0];
return err;
case BMA220_IOCTL_CALIBRATION:
if (sensor_data->enabled == 0) {
bma220_set_suspend_mode(1);
bma220_i2c_delay(30);
err = bma220_reset_offset_xyz();
if (err)
printk(KERN_ERR "%s: reset offset error!\n",
__func__);
else
err = bma220_calibration();
bma220_i2c_delay(30);
bma220_set_suspend_mode(0);
return err;
}
err = bma220_reset_offset_xyz();
if (err)
printk(KERN_ERR "%s: reset offset error!\n", __func__);
else
err = bma220_calibration();
return err;
case BMA220_IOCTL_WRITE:
if (copy_from_user(data, (unsigned char *)arg, 2) != 0) {
#if DEBUG
printk(KERN_ERR "copy_from_user error\n");
#endif
return -EFAULT;
}
printk(KERN_INFO "%s: write = %x %x\n",
__func__, data[1], data[0]);
err = bma220_i2c_write(BMA220_I2C_ADDR, data[1], &data[0], 1);
return err;


case BMA220_IOCTL_READ:
if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
#if DEBUG
printk(KERN_ERR "copy_from_user error\n");
#endif
return -EFAULT;
}
err = bma220_i2c_read(BMA220_I2C_ADDR, data[0], &data[0], 1);
printk(KERN_INFO "%s: read = %d\n", __func__, data[0]);
if (copy_to_user((unsigned char *)arg, data, 1) != 0) {
#if DEBUG
printk(KERN_ERR "copy_to_user error\n");
#endif
return -EFAULT;
}
return err;


case BMA220_SOFT_RESET:
err = bma220_soft_reset();
return err;


case BMA220_SET_SUSPEND:
if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
#if DEBUG
printk(KERN_ERR "copy_from_user error\n");
#endif
return -EFAULT;
}
bma220_set_suspend_mode((int)data[0]);
return err;
 
(signed char *)data, 1) != 0) {
#if DEBUG
printk(KERN_ERR "copy_to_user error\n");
#endif
return -EFAULT;
}
return err;


case BMA220_READ_ACCEL_Y:
err = bma220_read_accel_y((signed char *)data);
if (copy_to_user((signed char *)arg,
(signed char *)data, 1) != 0) {
#if DEBUG
printk(KERN_ERR "copy_to_user error\n");
#endif
return -EFAULT;
}
return err;


case BMA220_READ_ACCEL_Z:
err = bma220_read_accel_z((signed char *)data);
if (copy_to_user((signed char *)arg,
(signed char *)data, 1) != 0) {
#if DEBUG
printk(KERN_ERR "copy_to_user error\n");
#endif
return -EFAULT;
}
return err;


case BMA220_SET_EN_LOW:
if (copy_from_user(data, (unsigned char *)arg, 1) != 0) {
#if DEBUG
printk(KERN_ERR "copy_from_user error\n");
#endif
return -EFAULT;
}
err = bma220_set_en_low(*data);
return err;


case BMA220_SET_EN_HIGH_XYZ:
 
printk(KERN_ERR "copy_from_user error\n");
#endif
return -EFAULT;
}
err = bma220_set_en_tt_xyz(*data);
return err;
 
 命令号的定义


#define BMA220_IOC_MAGIC 'B'


#define BMA220_SET_SLEEP_EN \
_IOWR(BMA220_IOC_MAGIC, 0, unsigned char)
#define BMA220_SET_SUSPEND \
_IOW(BMA220_IOC_MAGIC, 1, unsigned char)
#define BMA220_READ_ACCEL_X \
_IOWR(BMA220_IOC_MAGIC, 2, signed char)
#define BMA220_READ_ACCEL_Y \
_IOWR(BMA220_IOC_MAGIC, 3, signed char)
#define BMA220_READ_ACCEL_Z \
_IOWR(BMA220_IOC_MAGIC, 4, signed char)
#define BMA220_SET_MODE \
_IOWR(BMA220_IOC_MAGIC, 5, unsigned char)
#define BMA220_GET_MODE \
_IOWR(BMA220_IOC_MAGIC, 6, unsigned char)
#define BMA220_SET_RANGE \
_IOWR(BMA220_IOC_MAGIC, 7, unsigned char)
#define BMA220_GET_RANGE \
_IOWR(BMA220_IOC_MAGIC, 8, unsigned char)
#define BMA220_SET_BANDWIDTH \
_IOWR(BMA220_IOC_MAGIC, 9, unsigned char)
#define BMA220_GET_BANDWIDTH \
_IOWR(BMA220_IOC_MAGIC, 10, unsigned char)
#define BMA220_SET_SC_FILT_CONFIG \
_IOWR(BMA220_IOC_MAGIC, 11, unsigned char)
#define BMA220_RESET_INTERRUPT \
_IO(BMA220_IOC_MAGIC, 12)
#define BMA220_GET_DIRECTION_STATUS_REGISTER \
_IOWR(BMA220_IOC_MAGIC, 13, unsigned char)
#define BMA220_GET_INTERRUPT_STATUS_REGISTER \
_IOWR(BMA220_IOC_MAGIC, 14, unsigned char)
 
 ..................
#define BMA220_IOC_MAXNR 100












功能:




大部分驱动除了需要具备读写设备的能力外,还需要具备对硬件控制的能力。例如,要求设备报告错误信息,改变波特率,这些操作常常通过ioctl方法来实现。




用户使用方法:




在用户空间,使用ioctl系统调用来控制设备,原型如下:




int ioctl(int fd,unsigned long cmd,...);
原型中的点表示这是一个可选的参数,存在与否依赖于控制命令(第二个参数)是否涉及到与设备的数据交互。




驱动ioctl方法:




ioctl驱动方法有和用户空间版本不同的原型:




int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)




cmd参数从用户空间传下来,可选的参数 arg以一个unsigned long 的形式传递,不管它是一个整数或一个指针。如果cmd命令不涉及数据传输,则第3个参数arg的值无任何意义。




ioctl实现




如何实现ioctl方法?




1.定义命令
2.实现命令








定义命令




在编写ioctl代码之前,首先需要定义命令。为了防止对错误的设备使用正确的命令,命令号应该在系统范围内是唯一的中定义。ioctl命令编码被划分为几个位段,include/asm/ioctl.h定义了这些位字段:类型(幻数),序号,传送方向,参数的大小。




Documentation/ioctl-number.txt文件中罗列了在内核中已经使用了的幻数。








定义命令




定义ioctl命令的正确方法是使用4 个位段,这个列表中介绍的符号定义在<linux/ioctl.h>中:




Type




幻数(类型):表明哪个设备的命令,在参考了ioctl-number.txt之后选出,8位宽。




Number
序号,表明设备命令中的第几个,8位宽。




Direction
数据传送的方向,可能的值是_IOC_NONE(没有数据传输),_IOC_READ,_IOC_WRITE。数据传送是从应用程序的观点来看待的,_IOC_READ意思是从设备读。




size
用户数据的大小。(13/14位宽,视处理器而定)








内核提供了下列宏来帮助定义命令:




_IO(type,nr);
没有 参数的命令




_IOR(type,nr,datatype)
从驱动中读取数据




_IOW(type,nr,datatype)
写数据到驱动




_IOWR(type,nr,datatype)
双向传送,type和number成员作为参数被传递。








列子(example)




#define MEM_IOC_MAGIC 'm' //定义幻数




#define MEM_IOCSET _IOW(MEM_IOC_MAGIC,0,int)




#define MEM_IOCGQSET  _IOR(MEM_IOC_MAGIC,1,int)








ioctl函数实现
定义好了命令,下一步就是要实现ioctl函数了,ioctl函数的实现包括如下3个技术环节:




1.返回值
2. 参数使用
3.命令操作




ioctl函数实现(返回值)
ioctl函数的实现通常是根据命令执行的一个 switch语句。但是,当命令号不能匹配任何一个设备所支持的命令时, 通常返回-EINVAL("非法参数")








ioctl函数实现(参数)




如何使用ioctl中的参数?
如果是一个整数,可以直接使用。如果是指针,我们必须确保这个用户地址是有效的,因此使用前需进行正确的检查。








ioctl函数实现(参数检查)
不需要检测:
copy_from_user
copy_to_user
get_user
put_user




需要检测:
__get_user
__put_user








参数检查函数:




int access_ok(int type,const void *addr,unsigned long size);




第一个参数是VERIFY_READ或者VERIFY_WRITE,用来表明是读用户内存还是写用户内存。addr参数是要操作的用户内存地址,size是操作的长度。如果ioctl需要从用户空间读一个整数,那么size参数等于sizeof(int)。




access_ok返回一个布尔值:1是成功(存取没问题)和0是失败(存取有问题),如果该函数返回失败,则ioctl应当返回-EFAULT。








ioctl 函数实现(参数检查)




if(_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,(void __user *)arg,_IOC_SIZE(cmd));




//为什么_IOC_READ对应VERIFY_WRITE??
else if(_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ,(void _user)*arg,_IOC_SEIZE(cmd));




if(err)
   return -EFAULT;








ioctl函数实现(命令操作)




switch(cmd)
{
   case MEM_IOCSQUANTUM:
retval = _get_user(scull_quantum,(int *)arg);
break;




   case MEM_IOCGQUANTUM:
retval = _put_user(scull_quantum,(int *)arg);
break;
   
   default:
return -EINVAL;
}

转载于:https://www.cnblogs.com/yuzaipiaofei/archive/2012/10/12/4124158.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值