linux 字符设备实例 ioctl,linux高级字符设备驱动(一 设备Ioctl控制)

1. Ioctl 用来做什么?

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

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

int ioctl(int fd,unsigned long cmd,...)

原型中的点表示这是一个可选的参数,存在与否依赖于控制命令(第2 个参数)是否涉及到与设备的数据交互。

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

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

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

2. Ioctl实现

2.1 实现Ioctl方法的步骤:      1)  定义命令

2.)  实现命令

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

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

1) Type

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

2)  Number

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

3) Direction

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

4) Size

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

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

1)  _IO(type,nr)

没有参数的命令

2)  _IOR(type,nr,datatype)

从驱动中读数据

3)  _IOW(type,nr,datatype)

写数据到驱动

4) _IOWR(type,nr,datatype)

双向传送,type 和number 成员作为参数被传递。

定义命令(范例)

#define MEM_IOC_MAGIC ‘m’ //定义幻数

#define MEM_IOCSET

_IOW(MEM_IOC_MAGIC, 0, int)

#define MEM_IOCGQSET

_IOR(MEM_IOC_MAGIC, 1, int)

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

1)  返回值

2) 参数使用

3) 命令操作

2.3.1 Ioctl函数实现(返回值)

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

2.3..2 Ioctl函数实现(参数)

如何使用Ioctl中的参数?

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

2.3.3 Ioctl函数实现(参数检查)

不需要检测:

1) copy_from_user

2) copy_to_user

3) get_user

4) put_user

需要检测:

1) __get_user

2) __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 。

3. Ioctl函数实现范例

if (_IOC_DIR(cmd) & _IOC_READ)

err = !access_ok(VERIFY_WRITE, (void __user *)arg,_IOC_SIZE(cmd));   //why _IOC_READ 对应VERIFY_WRITE ???

else if (_IOC_DIR(cmd) & _IOC_WRITE)

err = !access_ok(VERIFY_READ, (void __user *)arg,_IOC_SIZE(cmd));

if (err)

return -EFAULT;

switch(cmd)

{

case MEM_IOCSQUANTUM: /* Set: arg points to the value */

retval = __get_user(scull_quantum, (int *)arg);

break;

case MEM_IOCGQUANTUM: /* Get: arg is pointer to result */

retval = __put_user(scull_quantum, (int *)arg);

break;

default:

return –EINVAL;

}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值