Linux驱动程序学习笔记(6)——ioctl函数的用法

一,函数原型

用户空间的ioctl函数原型

int ioctl(int fd, unsigned long cmd, ...); 
驱动程序里的ioctl函数原型
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned  long arg);

二,实现ioctl函数的方法

1,定义命令:
定义 ioctl 命令号的正确方法使用 4 个位段, 它们有下列的含义. 这个列表中介绍的新符号定义在 <linux/ioctl.h>.
type 
    魔数. 只是选择一个数(在参考了 ioctl-number.txt之后)并且使用它在整个驱动中. 这个成员是 8 位宽(_IOC_TYPEBITS). 
number
    序(顺序)号. 它是 8 位(_IOC_NRBITS)宽. 
direction 
    数据传送的方向,如果这个特殊的命令涉及数据传送. 可能的值是 _IOC_NONE(没有数据传输), _IOC_READ, _IOC_WRITE, 和 _IOC_READ|_IOC_WRITE (数据在2个方向被传送). 数据传送是从应用程序的观点来看待的; _IOC_READ 意思是从设备读, 因此设备必须写到用户空间. 注意这个成员是一个位掩码, 因此 _IOC_READ 和 _IOC_WRITE 可使用一个逻辑 AND 操作来抽取.
size 
    涉及到的用户数据的大小. 这个成员的宽度是依赖体系的, 但是常常是 13 或者 14 位.

头文件 <asm/ioctl.h>, 它包含在 <linux/ioctl.h> 中, 定义宏来帮助建立命令号, 如下:  
   _IO(type,nr)  (给没有参数的命令)
   _IOR(type, nre, datatype)  (给从驱动中读数据的)
    _IOW(type,nr,datatype)  (给写数据) 
    _IOWR(type,nr,datatype)(给双向传送). 

type 和 number 成员作为参数被传递, 并且 size 成员通过应用 sizeof 到 datatype 参数而得到.


这个头文件还定义宏, 可被用在你的驱动中来解码这个号: _IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr), 和 _IOC_SIZE(nr).


例子
/* 定义幻数 */
#define MEMDEV_IOC_MAGIC  'k'

/* 定义命令 */
#define MEMDEV_IOCPRINT   _IO(MEMDEV_IOC_MAGIC, 1)
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)

2,ioctl函数实现
(1)返回值
当命令号不能匹配switch语句中设备所支持的命令时候,应该返回-EINVAL
(2)参数的使用

如果arg参数是一个整数则可直接使用,但是如果是一个指针,则可能需要检查用户空间是否可读或可写


不需要检测的函数:

copy_from_user ,copy_to_user,put_user,get_user

需要检测的函数

__put_user,__get_user

注意以下函数参数方向:
put_user(datum, ptr),__put_user(datum, ptr)这些宏定义写 datum 到指针ptr用户空间

get_user(local, ptr),__get_user(local, ptr)这些宏定义用来从ptr用户空间接收单个数据给local。


检测方法:

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

第一个参数应当是 VERIFY_READ 或者 VERIFY_WRITE, 依据这个要进行的动作是否是读用户空间内存区或者写它. addr 参数 持有一个用户空间地址, size 是一个字节量成功返回布尔值1,失败返回0,如果返回失败,则ioctl函数应当返回-EFAULT


例子
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)
        return -EFAULT;
(3)命令实现

switch(cmd)

 {


      /* 打印当前设备信息 */
      case MEMDEV_IOCPRINT:
      printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");
        break;
      
      /* 获取参数 */
      case MEMDEV_IOCGETDATA: 
        ioarg = 1101;
        ret = __put_user(ioarg, (int *)arg);
        break;
      
      /* 设置参数 */
      case MEMDEV_IOCSETDATA: 
        ret = __get_user(ioarg, (int *)arg);
        printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);
        break;


      default:  
        return -EINVAL;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值