linux驱动学习之ioctl接口

   这里先用例子介绍一下ioctrl接口的使用, 应用层的ioctl函数传入的cmd和arg参数会直接传入驱动层的ioctl接口,ioctl接口的命令有一定规范详细查看ioctl-number.txt文件,这里命令的定义不在规范内,先看下面测试的例子,驱动只实现ioctrl接口并使用ioctl修改和读取内核中的一个整型参数为例,使用两个不同方式读取(值传递和地址传递)。

 

 应用程序测试代码main.c

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define IOCTL_RESET 100 /*重置命令*/
#define IOCTL_GET1  101 /*读取命令值返回*/
#define IOCTL_GET2  102 /*读取命令地址返回*/
#define IOCTL_SET1  103 /*设置命令值传入*/
#define IOCTL_SET2  104 /*设置命令地址传入*/

int main (int *argc,char**argv)
{
  int  fs;int val;
  fs=open("/dev/moduledev60",O_RDWR);
  if(fs<0)
  {
    printf("open fail\n");
    return -1;
  }

  ioctl(fs,IOCTL_SET1,1000);                               //使用值传入设置参数
  printf("ioctl get1 result:%d\n",ioctl(fs,IOCTL_GET1));   //使用返回值读取参数
  ioctl(fs,IOCTL_GET2,&val);                               //使用地址读取参数 
  printf("ioctl get2 result:%d\n",val);        

  /*当设置参数是负数时 使用返回值读参数会出错 由于ioctl返回负数会被内核认为错误*/
  ioctl(fs,IOCTL_SET1,-100);                               //使用值传入设置参数
  printf("ioctl get1 result:%d\n",ioctl(fs,IOCTL_GET1));   //使用返回值读取参数
  ioctl(fs,IOCTL_GET2,&val);                               //使用地址读取参数 
  printf("ioctl get2 result:%d\n",val);  

  /*使用地址传入设置参数*/
  val=5555;
  ioctl(fs,IOCTL_SET2,&val);
  printf("ioctl get1 result:%d\n",ioctl(fs,IOCTL_GET1));
 
  close(fs);
  return 0;
}


驱动主要部分 fileops.c

#define IOCTL_RESET 100 /*重置命令*/
#define IOCTL_GET1  101 /*读取命令值返回*/
#define IOCTL_GET2  102 /*读取命令地址返回*/
#define IOCTL_SET1  103 /*设置命令值传入*/
#define IOCTL_SET2  104 /*设置命令地址传入*/
int drive_param=0;
int fileops_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
  printk(KERN_ALERT "fileops_ioctl  \n");
  switch(cmd)
  {
    case IOCTL_RESET:
	  drive_param=0;
	  break;
	case IOCTL_GET1:
	  return drive_param;
	  break;
	case IOCTL_GET2:
//	  __put_user(drive_param,(int __user *)arg);
      if(copy_to_user((int __user*)arg,&drive_param,4)) return -ENOTTY;
      break;
	case IOCTL_SET1:
	  drive_param=arg;
	  break;
	case IOCTL_SET2:
//	  __get_user(drive_param,(int __user *)arg);
      if(copy_from_user(&drive_param,(int __user*)arg,4))return -ENOTTY;
      break;  
  }
  return 0;
}

传送单个值时使用 __put_user __get_user要比copy相对快,  注意 __put_user,__get_user 应当只用在已经使用 access_ok 校验过的地址.copy_from_user和copy_to_user跟踪代码会发现已经加了access_ok的校验。


执行结果

ioctl get1 result:1000
ioctl get2 result:1000
ioctl get1 result:-1
ioctl get2 result:-100
ioctl get1 result:5555

会发现使用值返回负数-100时,驱动接口内返回-100应用程序的ioctl返回的是-1,使用地址传递参数则正确读取。


下面是部分ioctl-number.tx的内容

If you are adding new ioctl's to the kernel, you should use the _IO macros defined in <linux/ioctl.h>:

    _IO    an ioctl with no parameters
    _IOW   an ioctl with write parameters (copy_from_user)
    _IOR   an ioctl with read parameters  (copy_to_user)
    _IOWR  an ioctl with both write and read parameters.

'Write' and 'read' are from the user's point of view, just like the system calls 'write' and 'read'.  For example, a SET_FOO ioctl would be _IOW, although the kernel would actually read data from user space;
a GET_FOO ioctl would be _IOR, although the kernel would actually write data to user space.

The first argument to _IO, _IOW, _IOR, or _IOWR is an identifying letter or number from the table below.  Because of the large number of drivers,many drivers share a partial letter with other drivers.

If you are writing a driver for a new device and need a letter, pick an unused block with enough room for expansion: 32 to 256 ioctl commands.
You can register the block by patching this file and submitting the patch to Linus Torvalds.  Or you can e-mail me at <mec@shout.net> and I'll register one for you.

The second argument to _IO, _IOW, _IOR, or _IOWR is a sequence number to distinguish ioctls from each other.  The third argument to _IOW,_IOR, or _IOWR is the type of the data going into the kernel or coming out of the kernel (e.g.  'int' or 'struct foo').  NOTE!  Do NOT use sizeof(arg) as the third argument as this results in your ioctl thinking it passes an argument of type size_t.
Some devices use their major number as the identifier; this is OK, as long as it is unique.  Some devices are irregular and don't follow any convention at all.

Following this convention is good because:
(1) Keeping the ioctl's globally unique helps error checking: if a program calls an ioctl on the wrong device, it will get an  error rather than some unexpected behaviour.
(2) The 'strace' build procedure automatically finds ioctl numbers  defined with _IO, _IOW, _IOR, or _IOWR.
(3) 'strace' can decode numbers back into useful names when the  numbers are unique.
(4) People looking for ioctls can grep for them more easily when  this convention is used to define the ioctl numbers.
(5) When following the convention, the driver code can use generic  code to copy the parameters between user and kernel space.










  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值