ioctl()函数学习 【综合贴】

目录:

参考文献原址:... 1

第一篇:用户空间的ioctl()函数... 1

第二篇:内核空间的ioctl----用于驱动程序... 2

第三篇:头文件ioctl.h----关于ioctl的宏定义... 6

第四篇:ioctl-number.txt文件----魔数(即幻数)定义... 11

第五篇:Ioctl-----内核空间与用户空间的数据交换... 16

第六篇:Ioctl使用实例(用户空间和内核空间均有)... 20

 

参考文献原址:

    1、百度百科:http://baike.baidu.com/link?url=E_dgSillRPMfgkVVAUadSxRcR11HoIDFF3OH6STVr2zxfwSRa92RX43y8jCL4G4G_jjWfKNUo4okJYfZpCaY-K

    2、http://blog.163.com/chenhongswing@126/blog/static/13359244320119541655905/

3、http://www.cnblogs.com/hnrainll/archive/2011/06/21/2086347.html

    4、http://blog.csdn.net/flyingdon/article/details/5096203

    5、http://hi.baidu.com/shentuhongfeng/item/1da8c21275d0ca021994ec62

    6、linux系统文件

说明:本文为上述文章的综合,但有些地方根据自己的理解做了编辑更改。虽有些许原创,但可忽略不计。本文只为学习更方便,不保留版权。

 

第一篇:用户空间的ioctl()函数

头文件: #include<sys/ioctl.h>

功  能:  控制I/O设备 ,提供了一种获得设备信息和向设备发送控制参数的手段。用于向设备发控制和配置命令,有些命令需要控制参数,这些数据是不能用read / write 读写的,称为Out-of-band数据。也就是说,read / write 读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。

用  法:  int ioctl(int handle, unsigned long cmd,[int *argdx, int argcx]);

返回值: 成功为0,出错为-1

     实际上在我的linux系统(redhat9,linux2.4内核和升级到的linux2.6)中,有三处ioctl.h头文件:

     /usr/include/asm/ioctl.h:这个文件定义了关于ioctl要用到的许多至关重要的宏定义,在后边要讲到。

     /usr/include/linux/ioctl.h:包含的上一个文件

     /usr/include/sys/ioctl.h:除了包含了好几个文件外,就是一个ioctl的extern声明,形式如下:externint ioctl(int __fd, unsigned long int __request, ...)。这个文件就是提供给了用户空间一个ioctl控制函数。由于这些点而凸现于 Unix 系统调用列表, 这些点常常表示函数有数目不定的参数. 在实际系统中, 但是, 一个系统调用不能真正有变数目的参数. 系统调用必须有一个很好定义的原型, 因为用户程序可存取它们只能通过硬件的"门". 因此, 原型中的点不表示一个变数目的参数, 而是一个单个可选的参数, 传统上标识为 char *argp. 这些点在那里只是为了阻止在编译时的类型检查. 第 3 个参数的实际特点依赖所发出的特定的控制命令( 第 2 个参数 ). 一些命令不用参数, 一些用一个整数值, 以及一些使用指向其他数据的指针. 使用一个指针就可以传递任意数据到 ioctl 调用的方法; 设备接着可与用户空间交换任何数量的数据.

 

 

第二篇:内核空间的ioctl----用于驱动程序

函数形式:

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);


这是驱动程序设备控制接口函数(ioctl函数)的内核原型定义,struct inode * struct file* 描述了操作的文件,unsigned int 描述了ioctl命令号,这是一个重要的参数,我们稍后会对它做详细介绍。最后一个参数是unsigned long数据类型,描述了ioctl命令可能带有的参数,它可能是一个整数或指针数据。

  • ioctl命令号

ioctl命令号是这个函数中最重要的参数,它描述的ioctl要处理的命令。Linux中使用一个32位的数据来编码ioctl命令,它包含四个部分:dir:type:nr:size。

·        dir

代表数据传输的方向,占2位,可以是_IOC_NONE(无数据传输,0U),_IOC_WRITE(向设备写数据,1U)或_IOC_READ(从设备读数据,2U)或他们的逻辑或组合,当然只有_IOC_WRITE和_IOC_READ的逻辑或才有意义。

·        type

描述了ioctl命令的类型,8位。每种设备或系统都可以指定自己的一个类型号,ioctl用这个类型来表示ioctl命令所属的设备或驱动。一般用ASCII码字符来表示,如 'a'。

·        nr

ioctl命令序号,一般8位。对于一个指定的设备驱动,可以对它的ioctl命令做一个顺序编码,一般从零开始,这个编码就是ioctl命令的序号。

·        size

ioctl命令的参数大小,一般14位。ioctl命令号的这个数据成员不是强制使用的,你可以不使用它,但是我们建议你指定这个数据成员,通过它我们可以检查用户空间数据的大小以避免错误的数据操作,也可以实现兼容旧版本的ioctl命令。

我们可以自己来直接指定一个ioctl命令号,它可能仅仅是一个整数集,但Linux中的ioctl命令号都是有特定含义的,因此通常我们不推荐这么做。其实Linux内核已经提供了相应的宏来自动生成ioctl命令号:

_IO(type,nr)
_IOR(type,nr,size)
_IOW(type,nr,size)
_IOWR(type,nr,size)


宏_IO用于无数据传输,宏_IOR用于从设备读数据,宏 _IOW用于向设备写数据,宏_IOWR用于同时有读写数据的IOCTL命令。相对的,Linux内核也提供了相应的宏来从ioctl命令号种解码相应的域值:

_IOC_DIR(nr)
_IOC_TYPE(nr)
_IOC_NR(nr)
_IOC_SIZE(nr)

 

这些宏都定义在<asm/ioctl.h>头文件中(一般在<asm-generic.h>头文件中)。一般在使用中,先指定各个IOCTL命令的顺序编号(一般从0开始),然后根据使用的环境用这些宏来自动生成IOCTL命令号,在后面的例子中你可以了解实际的使用场景。

  • ioctl返回值

ioctl函数的返回值是一个整数类型的值,如果命令执行成功,ioctl返回零,如果出现错误,ioctl函数应该返回一个负值。这个负值会作为errno值反馈给调用此ioctl的用户空间程序。关于返回值的具体含义,请参考<linux/errno.h>和<asm/errno.h>头文件。

  • ioctl参数

这里有必要说明一下ioctl命令的参数,因为它很容易犯错误。如果ioctl命令参数仅仅是一个整数,那么事情很简单了,我们可以在ioctl函数中直接使用它。但如果它是一个指针数据,那么使用上就要小心了。首先要说明这个参数是有用户空间的程序传递过来的,因此这个指针指向的地址是用户空间地址,在Linux中,用户空间地址是一个虚拟地址,在内核空间是无法直接使用它的。为了解决在内核空间使用用户空间地址的数据,Linux内核提供了以下函数,它们用于在内核空间访问用户空间的数据,定义在<asm/uaccess.h>头文件中:

unsigned long __must_check copy_to_user(void __user *to,
                const void *from, unsigned long n);
unsigned long __must_check copy_from_user(void *to,
                const void __user *from, unsigned long n);


copy_from_user
和copy_to_user一般用于复杂的或大数据交换,对于简单的数据类型,如int或char,内核提供了简单的宏来实现这个功能:

#define get_user(x,ptr)
#define put_user(x,ptr)


其中,x是内核空间的简单数据类型地址,ptr是用户空间地址指针。
我们需要牢记:在内核中是无法直接访问用户空间地址数据的。因此凡是从用户空间传递过来的指针数据,务必使用内核提供的函数来访问它们。

这里有必要再一次强调的是,在内核模块或驱动程序的编写中,我们强烈建议你使用内核提供的接口来生成并操作ioctl命令号,这样可以对命令号赋予特定的含义,使我们的程序更加的健壮;另一方面也可以提高程序的可移植性。

举例
好了,是时候举个例子了。我们将扩展我们的helloworld驱动添加ioctl函数。

首先,我们添加一个头文件来定义ioctl接口需要用到的数据(hello.h):

#ifndef _HELLO_H
#define _HELLO_H
#include <asm/ioctl.h>
#define MAXBUF  20
typedef struct _buf_data{
        int size;
        char data [MAXBUF];
}buf_data;

#define HELLO_IOCTL_NR_BASE             0
#define HELLO_IOCTL_NR_SET_DATA         (HELLO_IOCTL_NR_BASE + 1)
#define HELLO_IOCTL_NR_MAX              (HELLO_IOCTL_NR_GET_BUFF + 1)

#define HELLO_IOCTL_SET_DATA            _IOR('h', HELLO_IOCTL_NR_SET_DATA, buf_data*)

#endif


然后为我们的驱动程序添加ioctl接口hello_ioctl,并实现这个函数:

static int hello_ioctl (struct inode *inode, struct file *filp,
                           unsigned int cmd, unsigned long arg)
{
    int cmd_nr;
    int err;
    buf_data buff;

    err = 0;
    cmd_nr = _IOC_NR (cmd);
    switch (cmd_nr){
        case HELLO_IOCTL_NR_SET_DATA:
            if (copy_from_user(&buff, (unsigned char *)arg, sizeof(buf_data)))
            {
                err = -ENOMEM;
                goto error;
            }
            memset(hello_buf, 0, sizeof(hello_buf));
            memcpy(hello_buf, buff.data, buff.size);
            break;
        default:
            printk("hello_ioctl: Unknown ioctl command (%d)/n", cmd);
            break;
    }

error:
    return err;
}

static struct file_operations hello_fops = {
        .read = hello_read,
        .write = hello_write,
        .open = hello_open,
        .ioctl = hello_ioctl,
        .release = hello_release,
};

 

这个例子尽管很简单,但已经足够了。到这里你可以写出一个标准的Linux驱动程序了。不过这里还有个问题,那就是我们不得不从/proc/devices文件里读取设备号然后手动创建设备节点。我们是否可以让系统自动的创建这个设备节点文件呢?当然可以。不过在那之前,我们必须深入了解Linux的设备驱动模型。

 

第三篇:头文件ioctl.h----关于ioctl的宏定义

在编写ioctl代码之前,需要选择对应不同命令的编号。为了防止对错误的设备使用正确的命令,命令号应该在系统范围内唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFOaudio等这类非串行设备输入流修改波特率,如果每一个ioctl命令都是唯一的,应用程序进行这种操作时就会得到一个EINVAL错误,而不是无意间成功地完成了意想不到的操作。

   要按Linux内核的约定方法为驱动程序选择ioctl编号,应该首先看看include/asm/ioctl.hDoucumention/ioctl-number.txt这两个文件。头文件定义了要使用的位字段:类型(幻数)、序数、传送方向以及参数大小等。ioctl-number.txt文件中罗列了内核所使用的幻数,选择自己的幻数要避免和内核冲突。以下是对include/asm/ioctl.h中定义的宏的注释:

#define         _IOC_NRBITS          8                               //序数(number)字段的字位宽度,8bits

#define         _IOC_TYPEBITS      8                               //幻数(type)字段的字位宽度,8bits

#define         _IOC_SIZEBITS       14                              //大小(size)字段的字位宽度,14bits

#define         _IOC_DIRBITS         2                               //方向(direction)字段的字位宽度,2bits

#define         _IOC_NRMASK        ((1 << _IOC_NRBITS)-1)    //序数字段的掩码,0x000000FF

#define         _IOC_TYPEMASK   ((1 << _IOC_TYPEBITS)-1)    //幻数字段的掩码,0x000000FF

#define         _IOC_SIZEMASK     ((1 << _IOC_SIZEBITS)-1)   //大小字段的掩码,0x00003FFF

#define         _IOC_DIRMASK      ((1 << _IOC_DIRBITS)-1)    //方向字段的掩码,0x00000003

#define        _IOC_NRSHIFT       0                                                         //序数字段在整个字段中的位移,0

#define        _IOC_TYPESHIFT   (_IOC_NRSHIFT+_IOC_NRBITS)         //幻数字段的位移,8

#define        _IOC_SIZESHIFT    (_IOC_TYPESHIFT+_IOC_TYPEBITS)  //大小字段的位移,16

#define        _IOC_DIRSHIFT      (_IOC_SIZESHIFT+_IOC_SIZEBITS)    //方向字段的位移,30

/*

 * Direction bits.

 */

#define _IOC_NONE     0U     //没有数据传输

#define _IOC_WRITE   1U     //向设备写入数据,驱动程序必须从用户空间读入数据

#define _IOC_READ     2U     //从设备中读取数据,驱动程序必须向用户空间写入数据

/*

*_IOC 宏将dirtypenrsize四个参数组合成一个cmd参数,如下图:

*

*/

#define _IOC(dir,type,nr,size) \

       (((dir)  << _IOC_DIRSHIFT) | \

        ((type) << _IOC_TYPESHIFT) | \

        ((nr)   << _IOC_NRSHIFT) | \

        ((size) << _IOC_SIZESHIFT))

/*

* used to create numbers 

*/

//构造无参数的命令编号

#define _IO(type,nr)             _IOC(_IOC_NONE,(type),(nr),0) 

//构造从驱动程序中读取数据的命令编号

#define _IOR(type,nr,size)     _IOC(_IOC_READ,(type),(nr),sizeof(size)) 

//用于向驱动程序写入数据命令

#define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),sizeof(size))

//用于双向传输

#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

/* 

*used to decode ioctl numbers..

 */

//从命令参数中解析出数据方向,即写进还是读出

#define _IOC_DIR(nr)          (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)

//从命令参数中解析出幻数type

#define _IOC_TYPE(nr)              (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)

//从命令参数中解析出序数number

#define _IOC_NR(nr)           (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)

//从命令参数中解析出用户数据大小

#define _IOC_SIZE(nr)         (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

/* ...and for the drivers/sound files... */

#define IOC_IN            (_IOC_WRITE << _IOC_DIRSHIFT)

#define IOC_OUT         (_IOC_READ << _IOC_DIRSHIFT)

#define IOC_INOUT     ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)

#define IOCSIZE_MASK      (_IOC_SIZEMASK << _IOC_SIZESHIFT)

#define IOCSIZE_SHIFT      (_IOC_SIZESHIFT)

以上内容转自:article.phpfans.net/

我的理解:

       假如我定义了一个命令MY_CMD:

#define MY_CMD_MAGIC0xdf             //type字段,由于字段宽度为8 bits,所以不能大于0xff

#define MY_CMD  _IOW(MY_CMD_MAGIC,0,unsignedint)    

     于是有命令MY_CMD各组成字段(dirsizetypenr)分别为:01(=_IOC_WRITE),00 0000 0000 0100(=sizeof(unsignedint)),1101 1111(=MY_CMD_MAGIC),0000 0000(=0)。用十六进制表示即0x4004df00。这个32 bits整数就是该命令的编号(LDD3原文是command number),也就是命令MY_CMD在系统中的身份证号码了!

     为什么上面我用身份证号码作比喻呢?众所周知,一个国家里所有公民的身份证号都是各不相同的。身份证编号有一定的规则:即把身份证号划分成几个字段,各段位数可不等,每个字段编码都有它的实际意义,例如我们现在用的的身份证前N位(不记得具体是多少了)表示一个具体的省、市、等地区,不同地区的人该字段肯定不同了;另外有个表示出生年月的字段(据说以前整个号码最后一位偶数表示男性,奇数表示女性,现在貌似没这个规则了,此为题外话)。

     类似地,我们要为系统里所有的IOCTL命令编号。我们身份证用的是15(上一代是18位)位十进制数编码(最后一位可能是拉丁字母);我们用32位二进制数为IOCTL命令编码,把它划分成4个字段,每段也有它的实际意义。要保证每个命令编号为系统唯一,主要靠命令的typenr字段。我们称type 字段内容为magic number,即幻数,它表示命令的类型

    看到上段红色这句话,我想可能有细心的人会问:那么命令到底有哪些类型呢?老实说我也不知道正确答案。大概因为大家都知道基本数据类型有整形、浮点型、字符型...人的性格类型有外向型、内向型...这些我们常见的类型都是可以用文字来枚举描述的,所以潜意识就觉得有类型就应该有文字可描述吧。回到正题,我想每个magic number,就像上面的宏定义中:#defineMY_CMD_MAGIC 0xdfMY_CMD_MAGIC就是类型名了吧!不知道我的想法对不对?!反正大家知道一个magic number就对应唯一一种命令类型就是了。LDD原文中有一段:

type
The magic number. Just choose one number (after consulting ioctl-number.txt)
and use it throughout the driver. This field is eight bits wide(_IOC_TYPEBITS).

原文是说type的内容叫幻数(magic number),强调它是一个8位二进制数(number)。

因此不同type的命令就有不同的magic number,因此命令编码自然就不同了。但如果两个命令type相同,它们的magic number就相同,于是就不能仅靠type字段区分了。于是nr字段就起作用了:

number
The ordinal (sequential) number. It’s eight bits (_IOC_NRBITS) wide.

nr
number)即序号,一般地我们从0开始编号。由于nr字段为8位二进制数,所以nr的取值范围为0~255。同一种type的命令每个nr值对应唯一一个命令。

其他两个字段在这里不作深究。

值得一提的是LDD3里这么一段话:

The header alsodefines macros that may be used in your driver to decode the num-
bers:_IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr), and _IOC_SIZE(nr). We won’t go
into any moredetail about these macros because the header file is clear, and sample
code is shownlater in this section.

根据ioctl.h文件的定义,很明显_IOC_DIR(nr), _IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)这几个宏里面的参数就是一个IOCTL命令,即一个32位的二进制数,而文件中参数居然用nr表示!当然用nr表示不是逻辑上的错误。但是别忘了文件中还有_IOW(type,nr,size)这样的定义IOCTL命令的宏,这其中的参数nr IOCTL命令编号中的一个nr字段,一个8位的二进制数!我想很多新人都会对此产生莫大的疑惑!所以我认为把_IOC_DIR(nr), _IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)这几个解码宏的参数改用cmd表示更恰当!
但是,我知道这不是LDD3作者的错,因为内核头文件里面也是这么表示的。我想内核开发者不可能没意识到这个问题。因此,我猜测这其中肯定有个历史原因:大概以前版本的命令不管type是否一样,nr字段的值都是唯一的,于是仅靠nr字段就可以解码出一个IOCTL命令的其他字段吧?!但即使这样 _IOC_DIR(nr), _IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)也没必要保留这种写法啊!到底谁可以告诉我真相?

LDD3没有对_IOC_DIR(nr), _IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)里面的nr作任何解释,只是实例中有如下用法:

if(_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd)> SCULL_IOC_MAXNR) return -ENOTTY;

可见那个nr参数完全就是我所说的32位的IOCTL命令编码。靠,既然这样好歹也对着个confusion作一下简单的解释啊!如果LDD3一书那不承认,也不否认的暧昧态度让我真让我哭笑不得的话,那么国内某书(具体哪本我就不说了)简直令我抓狂,我摘书中的两段话如下:

_IO(type,nr):定义一个没有数据传输的命令编号。type为幻数,nr命令编号...
...
_IOC_DIR(nr): 获得命令编号的命令传输方向(direction)。这个宏的参数就是命令编号。

 

第四篇:ioctl-number.txt文件----魔数(即幻数)定义

由于文件不长,且其中的英文比较简单 这里直接贴原文件内容:

 

If youare adding new ioctl's to the kernel, you should use the _IO

macrosdefined 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

systemcalls 'write' and 'read'.  For example, aSET_FOO ioctl would

be _IOW,although the kernel would actually read data from user space;

a GET_FOOioctl would be _IOR, although the kernel would actually write

data touser space.

 

The firstargument to _IO, _IOW, _IOR, or _IOWR is an identifying letter

or numberfrom the table below.  Because of thelarge number of drivers,

many driversshare a partial letter with other drivers.

 

If youare writing a driver for a new device and need a letter, pick an

unusedblock with enough room for expansion: 32 to 256 ioctl commands.

You canregister the block by patching this file and submitting the

patch toLinus Torvalds.  Or you can e-mail me at<mec@shout.net> and

I'llregister one for you.

 

Thesecond argument to _IO, _IOW, _IOR, or _IOWR is a sequence number

todistinguish ioctls from each other.  Thethird argument to _IOW,

_IOR, or_IOWR is the type of the data going into the kernel or coming

out ofthe kernel (e.g.  'int' or 'struct foo').

 

Somedevices use their major number as the identifier; this is OK, as

long asit is unique.  Some devices are irregularand don't follow any

conventionat all.

 

Followingthis convention is good because:

 

(1)Keeping the ioctl's globally unique helps error checking:

    if a program calls an ioctl on the wrongdevice, it will get an

    error rather than some unexpectedbehaviour.

 

(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 ioctlnumbers.

 

(5) Whenfollowing the convention, the driver code can use generic

    code to copy the parameters between userand kernel space.

 

Thistable lists ioctls visible from user land for Linux/i386.  It contains

mostdrivers up to 2.3.14, but I know I am missing some.

 

Code    Seq#    IncludeFile        Comments

========================================================

0x00    00-1F   linux/fs.h      conflict!

0x00    00-1F   scsi/scsi_ioctl.h   conflict!

0x00    00-1F   linux/fb.h      conflict!

0x00    00-1F   linux/wavefront.h   conflict!

0x02    all linux/fd.h

0x03    all linux/hdreg.h

0x04    all linux/umsdos_fs.h

0x06    all linux/lp.h

0x09    all linux/md.h

0x12    all linux/fs.h

        linux/blkpg.h

0x20    all drivers/cdrom/cm206.h

0x22    all scsi/sg.h

'1' 00-1F   <linux/timepps.h>   PPS kit from Ulrich Windl

                    <ftp://ftp.de.kernel.org/pub/linux/daemons/ntp/PPS/>

'6' 00-10   <asm-i386/processor.h>  Intel IA32 microcode update driver

                    <mailto:tigran@veritas.com>

'8' all             SNP8023advanced NIC card

                    <mailto:mcr@solidum.com>

'A' 00-1F   linux/apm_bios.h

'B' C0-FF               advancedbbus

                    <mailto:maassen@uni-freiburg.de>

'C' all linux/soundcard.h

'D' all asm-s390/dasd.h

'F' all linux/fb.h

'I' all linux/isdn.h

'J' 00-1F   drivers/scsi/gdth_ioctl.h

'K' all linux/kd.h

'L' 00-1F   linux/loop.h

'L' E0-FF   linux/ppdd.h        encrypted disk device driver

                    <http://linux01.gwdg.de/~alatham/ppdd.html>

'M' all linux/soundcard.h   conflict!

'M' 00-1F   linux/isicom.h      conflict!

'N' 00-1F   drivers/usb/scanner.h

'P' all linux/soundcard.h

'Q' all linux/soundcard.h

'R' 00-1F   linux/random.h

'S' all linux/cdrom.h       conflict!

'S' 80-81   scsi/scsi_ioctl.h   conflict!

'S' 82-FF   scsi/scsi.h     conflict!

'T' all linux/soundcard.h   conflict!

'T' all asm-i386/ioctls.h   conflict!

'U' all linux/drivers/usb/usb.h

'V' all linux/vt.h

'W' 00-1F   linux/watchdog.h    conflict!

'W' 00-1F   linux/wanrouter.h   conflict!

'X' all linux/xfs_fs.h

'Y' all linux/cyclades.h

'a' all             ATMon linux

                    <http://lrcwww.epfl.ch/linux-atm/magic.html>

'b' 00-FF               bit3vme host bridge

                    <mailto:natalia@nikhefk.nikhef.nl>

'c' 00-7F   linux/comstats.h    conflict!

'c' 00-7F   linux/coda.h        conflict!

'd' 00-1F   linux/devfs_fs.h    conflict!

'd' 00-DF   linux/video_decoder.h   conflict!

'd' F0-FF   linux/digi1.h

'e' all linux/digi1.h       conflict!

'e' 00-1F   linux/video_encoder.h   conflict!

'e' 00-1F   net/irda/irtty.h    conflict!

'f' 00-1F   linux/ext2_fs.h

'h' 00-7F               Charonfilesystem

                    <mailto:zapman@interlan.net>

'i' 00-3F   linux/i2o.h

'j' 00-3F   linux/joystick.h

'k' all asm-sparc/kbio.h

        asm-sparc64/kbio.h

'l' 00-3F   linux/tcfs_fs.h     transparent cryptographic file system

                    <http://mikonos.dia.unisa.it/tcfs>

'l' 40-7F   linux/udf_fs_i.h    in development:

                    <http://www.trylinux.com/projects/udf/>

'm' all linux/mtio.h        conflict!

'm' all linux/soundcard.h   conflict!

'm' all linux/synclink.h    conflict!

'm' 00-1F   net/irda/irmod.h    conflict!

'n' 00-7F   linux/ncp_fs.h

'n' E0-FF   video/matrox.h          matroxfb

'p' 00-3F   linux/mc146818rtc.h

'p' 40-7F   linux/nvram.h

'p' 80-9F               user-spaceparport

                    <mailto:tim@cyberelk.net>

'q' 00-1F   linux/videotext.h   conflict!

'q' 80-FF               InternetPhoneJACK, Internet LineJACK

                    <http://www.quicknet.net>

'r' 00-1F   linux/msdos_fs.h

's' all linux/cdk.h

't' 00-7F   linux/if_ppp.h

't' 80-8F   linux/isdn_ppp.h

'u' 00-1F   linux/smb_fs.h

'v' 00-1F   linux/ext2_fs.h     conflict!

'v' all linux/videodev.h    conflict!

'w' all             CERNSCI driver

'y' 00-1F               packetbased user level communications

                    <mailto:zapman@interlan.net>

'z' 00-3F               CANbus card

                    <mailto:hdstich@connectu.ulm.circular.de>

'z' 40-7F               CANbus card

                    <mailto:oe@port.de>

0x80    00-1F   linux/fb.h

0x89    00-06   asm-i386/sockios.h

0x89    0B-DF   linux/sockios.h

0x89    E0-EF   linux/sockios.h     SIOCPROTOPRIVATE range

0x89    F0-FF   linux/sockios.h     SIOCDEVPRIVATE range

0x8B    all linux/wireless.h

0x8C    00-3F               WiNRADiOdriver

                    <http://www.proximity.com.au/~brian/winradio/>

0x90    00  drivers/cdrom/sbpcd.h

0x93    60-7F   linux/auto_fs.h

0x99    00-0F               537-Addinboarddriver

                    <mailto:buk@buks.ipn.de>

0xA0    all linux/sdp/sdp.h     Industrial Device Project

                    <mailto:kenji@bitgate.com>

0xA2    00-0F  DVD decoder driver      indevelopment:

                                       <http://linuxtv.org/dvd/api/>

0xA3    00-1F   PhilipsSAA7146 dirver  in development:

                    <mailto:Andreas.Beckmann@hamburg.sc.philips.com>

0xA3    80-8F   PortACL        in development:

                    <mailto:tlewis@mindspring.com>

0xA3    90-9F   linux/dtlk.h

0xAB    00-1F   linux/nbd.h

0xAC    00-1F   linux/raw.h

0xAD    00  Netfilterdevice    in development:

                    <mailto:rusty@rustcorp.com.au> 

0xB0    all RATIOdevices       in development:

                    <mailto:vgo@ratio.de>

0xB1    00-1F   PPPoX           <mailto:mostrows@styx.uwaterloo.ca>

0xCB    00-1F   CBMserial IEC bus  in development:

                    <mailto:michael.klein@puffin.lb.shuttle.de>

 

0xFE    00-9F   LogicalVolume Manager  <mailto:linux-lvm@sistina.com>

 

其中最左边的这一列就是所谓的魔数(即幻数)了。

 

第五篇:Ioctl-----内核空间与用户空间的数据交换

1. 前言

 

使用ioctl系统调用是用户空间向内核交换数据的常用方法之一,从ioctl这个名称上看,本意是针对I/O设备进行的控制操作,但实际并不限制是真正的I/O设备,可以是任何一个内核设备即可。

 

2. 基本过程

 

在 内核空间中ioctl是很多内核操作结构的一个成员函数,如文件操作结构structfile_operations(include/linux/fs.h)、协议操作结构struct proto_ops(include/linux/net.h)等、tty操作结构structtty_driver(include/linux/tty_driver.h)等,而这些操作结构分别对应各种内核设备,只要在用户空间打开这些设备, 如I/O设备可用open(2)打开,网络协议可用socket(2)打开等,获取一个文件描述符后,就可以在这个描述符上调用ioctl(2)来向内核交换数据。

 

3. ioctl(2)

 

ioctl(2)函数的基本使用格式为:

int ioctl(int fd, int cmd,void *data)

第一个参数是文件描述符;cmd是操作命令,一般分为GET、SET以及其他类型命令,GET是用户空间进程从内核读数据,SET是用户空间进程向内核写数据,cmd虽然是一个整数,但是有一定的参数格式的,下面再详细说明;第三个参数是数据起始位置指针,

cmd命令参数是个32位整数,分为四部分:

dir(2b)  size(14b) type(8b) nr(8b)

详细定义cmd要包括这4个部分时可使用宏_IOC(dir,type,nr,size)来定义,而最简单情况下使用_IO(type,nr)来定义就可以了,这些宏都在include/asm/ioctl.h中定义

本文cmd定义为:

#defineNEWCHAR_IOC_MAGIC   'M'

#defineNEWCHAR_SET    _IO(NEWCHAR_IOC_MAGIC, 0)

#defineNEWCHAR_GET    _IO(NEWCHAR_IOC_MAGIC, 1)

#define NEWCHAR_IOC_MAXNR   1

 

要 定义自己的ioctl操作,可以有两个方式,一种是在现有的内核代码中直接添加相关代码进行支持,比如想通过socket描述符进行 ioctl操作,可在net/ipv4/af_inet.c中的inet_ioctl()函数中添加自己定义的命令和相关的处理函数,重新编译内核即可,不过这种方法一般不推荐;第二种方法是定义自己的内核设备,通过设备的ioctl()来操作,可以编成模块,这样不影响原有的内核,这是最通常的做法。

 

4. 内核设备

 

为进行ioctl操作最通常是使用字符设备来进行,当然定义其他类型的设备也可以。在用户空间,可使用mknod命令建立一个字符类型设备文件,假设该设备的主设备号为123,次设备号为0:

mknode /dev/newchar c 123 0

如果是编程的话,可以用mknode(2)函数来建立设备文件。

 

建立设备文件后再将该设备的内核模块文件插入内核,就可以使用open(2)打开/dev/newchar文件,然后调用ioctl(2)来传递数据,最后用close(2)关闭设备。而如果内核中还没有插入该设备的模块,open(2)时就会失败。

 

由于内核内存空间和用户内存空间不同,要将内核数据拷贝到用户空间,要使用专用拷贝函数copy_to_user();要将用户空间数据拷贝到内核,要使用copy_from_user()。

要最简单实现以上功能,内核模块只需要实现设备的open, ioctl和release三个函数即可,

下面介绍程序片断:

staticint newchar_ioctl(struct inode *inode, struct file *filep,

unsignedint cmd, unsigned long arg);

static int newchar_open(structinode *inode, struct file *filep);

static intnewchar_release(struct inode *inode, struct file *filep);

// 定义文件操作结构,结构中其他元素为空

struct file_operationsnewchar_fops =

{

owner:  THIS_MODULE,

ioctl:  newchar_ioctl,

open:  newchar_open,

release: newchar_release,

};

// 定义要传输的数据块结构

struct newchar{

int a;

int b;

};

#define MAJOR_DEV_NUM 123

#define DEVICE_NAME"newchar"

 

打开设备,非常简单,就是增加模块计数器,防止在打开设备的情况下删除模块,

当然想搞得复杂的话可进行各种限制检查,如只允许指定的用户打开等:

static int newchar_open(structinode *inode, struct file *filep)

{

MOD_INC_USE_COUNT;

 return 0;

}

 

关闭设备,也很简单,减模块计数器:

static intnewchar_release(struct inode *inode, struct file *filep)

{

MOD_DEC_USE_COUNT;

 return 0;

}

 

进行ioctl调用的基本处理函数

static int newchar_ioctl(structinode *inode, struct file *filep,

unsigned int cmd, unsignedlong arg)

{

int  ret;

// 首先检查cmd是否合法

if(_IOC_TYPE(cmd) != NEWCHAR_IOC_MAGIC) return -EINVAL;

if(_IOC_NR(cmd) > NEWCHAR_IOC_MAXNR) return -EINVAL;

// 错误情况下的缺省返回值

ret = EINVAL;

 switch(cmd)

{

case KNEWCHAR_SET:

// 设置操作,将数据从用户空间拷贝到内核空间

{

struct  newchar nc;

if(copy_from_user(&nc,(const char*)arg, sizeof(nc)) != 0)

return -EFAULT;

ret = do_set_newchar(&nc);

}

break;

caseKNEWCHAR_GET:

//GET操作通常会在数据缓冲区中先传递部分初始值作为数据查找条件,获取全部

// 数据后重新写回缓冲区

// 当然也可以根据具体情况什么也不传入直接向内核获取数据

{

struct  newchar nc;

if(copy_from_user(&nc,(const char*)arg, sizeof(nc)) != 0)

return -EFAULT;

ret = do_get_newchar(&nc);

if(ret == 0){

if(copy_to_user((unsigned char*)arg, &nc, sizeof(nc))!=0)

return -EFAULT;

}

  }

break;

}

returnret;

}

模块初始化函数,登记字符设备

staticint __init _init(void)

{

int  result;

// 登记该字符设备,这是2.4以前的基本方法,到2.6后有了些变化,

// 是使用MKDEV和cdev_init()来进行,本文还是按老方法

result= register_chrdev(MAJOR_DEV_NUM, DEVICE_NAME, &newchar_fops);

if (result < 0) {

printk(KERN_WARNING__FUNCTION__ ": failed register character device for/dev/newchar/n");

returnresult;

}

return0;

}

 

模块退出函数,登出字符设备

staticvoid __exit _cleanup(void)

{

int  result;

 result = unregister_chrdev(MAJOR_DEV_NUM,DEVICE_NAME);

if (result < 0)

printk(__FUNCTION__ ":failed unregister character device for /dev/newchar/n");

 return;

}

module_init(_init);

module_exit(_cleanup);

 

5. 结论

 

用ioctl()在用户空间和内核空间传递数据是最常用方法之一,比较简单方便,而且可以在同一个ioctl中对不同的命令传送不同的数据结构,本文只是为描述方便而在不同命令中使用了相同的数据结构。

 

第六篇:Ioctl使用实例(用户空间和内核空间均有)

实验内容
分别在 PC 并口的 13 脚和 25 脚引出两条引线,LED 灯长脚(+)接 PC 并口第 2 脚,短脚(-)接 PC 并口 18 脚 。当加载驱动程序后,启动测试应用程序,一开始会启动开启 LED 然后弹出等待输入信息并等待输入。这时接触两根金属引脚,1s 后 LED 关闭,然后再弹出第二次等待输入信息。再一次接触金属引脚,LED 灯以 0.5s 为周期开关 5 次。接着,弹出第三次等待输入信息,在金属引脚接触后,LED 以 0.2s 为间隔开关。此时,如果再次接触金属引线,那么程序终止。

实验目的主要是熟悉 ioctl()函数的使用方法以及相关宏的使用。代码由 3 部分组成,一个公用头文件,一个设备驱程序,一个应用测试程序。

头文件ioctl_test.h 内容
引用 #ifndef_IOCTLTEST_H_
#define_IOCTLTEST_H_


#defineIOCTLTEST_MAGIC    't'

typedef struct {
    unsigned long size;
    unsigned char buff[128];
}__attribute__((packed))ioctl_test_info;

#define   IOCTLTEST_LEDOFF    _IO (IOCTLTEST_MAGIC, 0)
#defineIOCTLTEST_LEDON        _IO (IOCTLTEST_MAGIC,1)
#defineIOCTLTEST_GETSTATE    _IO (IOCTLTEST_MAGIC, 2)

#defineIOCTLTEST_READ        _IOR (IOCTLTEST_MAGIC,3, ioctl_test_info)

#defineIOCTLTEST_WRITE        _IOW(IOCTLTEST_MAGIC, 4, ioctl_test_info)

#defineIOCTLTEST_WRITE_READ    _IOWR (IOCTLTEST_MAGIC, 5,ioctl_test_info)

#defineIOCTLTEST_MAXNR        6

#endif 

上面所用到的宏 _IO,_IOR, _IOW, IOWR 见:http://www.groad.net/bbs/read.php?tid-1212.html

驱动程序代码
引用 #include<linux/init.h>
#include<linux/module.h>

#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/errno.h>
#include<linux/types.h>
#include<linux/fcntl.h>

#include<asm/uaccess.h>
#include<asm/io.h>

#include"ioctl_test.h"

#define   IOCTLTEST_DEV_NAME    "ioctldev"
#define   IOCTLTEST_DEV_MAJOR    240
#defineIOCTLTEST_WRITE_ADDR    0x378
#define   IOCTLTEST_READ_ADDR    0X379

int ioctltest_open (struct inode*inode, struct file*filp)
{    
    return 0;
}

int ioctltest_release (struct inode*inode, struct file*filp)
{
    return 0;
}

int ioctltest_ioctl (struct inode*inode, struct file*filp, unsigned int cmd, unsigned long arg)
{
   ioctl_test_info    ctrl_info;
    int        err,size;
    int        loop;

    if (_IOC_TYPE(cmd)!= IOCTLTEST_MAGIC) /*魔数不一致则出错*/
        
return -EINVAL;
    
    if (_IOC_NR(cmd)>= IOCTLTEST_MAXNR)     /*魔数相同时判断命令的基数是否大于定义的值*/
        
return -EINVAL;

   size = _IOC_SIZE (cmd);

    if (size){
       err = 0;
        if (_IOC_DIR(cmd) & _IOC_READ )
           err = access_ok (VERIFY_WRITE, (void *)arg,size);
        else if (_IOC_DIR(cmd) & _IOC_WRITE) 
           err = access_ok (VERIFY_READ, (void *)arg, size);
        
        if (!err)
          return err;
   }
    
    switch (cmd){
        case IOCTLTEST_LEDOFF:
            outb (0x00, IOCTLTEST_WRITE_ADDR);
             break;

        case IOCTLTEST_LEDON:
           outb (0xff, IOCTLTEST_WRITE_ADDR);
            break;

        case IOCTLTEST_GETSTATE:
            return (inb(IOCTLTEST_READ_ADDR));

        case IOCTLTEST_READ:
           ctrl_info.buff[0] = inb (IOCTLTEST_READ_ADDR);
           ctrl_info.size = 1;
           copy_to_user ((void *)arg,(const void *)&ctrl_info,(unsigned long)size);
            break;
    
        case IOCTLTEST_WRITE:
           copy_from_user ((void *)&ctrl_info,(const void *)arg,size);
            
            for (loop= 0; loop < ctrl_info.size; loop++)
               outb (ctrl_info.buff[loop], IOCTLTEST_WRITE_ADDR);
            
            break;
        case IOCTLTEST_WRITE_READ:
           copy_from_user ((void *)&ctrl_info,(const void *)arg,size);

            for (loop= 0; loop < ctrl_info.size; loop++)
               outb (ctrl_info.buff[loop], IOCTLTEST_WRITE_ADDR);

           ctrl_info.buff[0] = inb (IOCTLTEST_READ_ADDR);
           ctrl_info.size = 1;
           copy_to_user ((void *)arg,(const void *)&ctrl_info,(unsigned long)size);
        
            break;
   }

    return 0;
}

struct file_operations ioctltest_fops = {
   .owner = THIS_MODULE,
   .ioctl = ioctltest_ioctl,
   .open = ioctltest_open,
   .release = ioctltest_release,
};

int ioctltest_init (void)
{
    int result;
    
   result = register_chrdev (IOCTLTEST_DEV_MAJOR, IOCTLTEST_DEV_NAME,&ioctltest_fops);
    
    if (result< 0) return result;
    
    return 0;
}

void ioctltest_exit (void)
{
   unregister_chrdev (IOCTLTEST_DEV_MAJOR, IOCTLTEST_DEV_NAME);
}

module_init(ioctltest_init);
module_exit(ioctltest_exit);

MODULE_LICENSE("Dual BSD/GPL"); 

Makefile 文件
引用 obj-m :=ioctl_dev.o

KDIR:= /lib/modules/$(shell uname -r)/build


PWD := $(shell pwd)

default:
    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
   rm -rf *.ko
   rm -rf *.mod.*
   rm -rf .*.cmd
   rm -rf *.o 

应用程序代码
引用 #include<stdio.h>
#include<stdlib.h>

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

#include"ioctl_test.h"

#defineDEVICE_FILENAME    "/dev/ioctl_dev"

int main()
{
   ioctl_test_info    info;
    int        dev;
    int        state;
    int        cnt;

   dev = open (DEVICE_FILENAME, O_RDWR | O_NDELAY);
    
    if (dev>= 0) {
       printf ("wait...input\n");
       ioctl (dev, IOCTLTEST_LEDON);
    
        while (1){
           state = ioctl (dev, IOCTLTEST_GETSTATE);
            if (!(state& 0x10)) break;
       }

       sleep (1);
       ioctl (dev, IOCTLTEST_LEDOFF);
        
       printf ("wait... input\n");
        
        while (1){
           info.size = 0;
           ioctl (dev, IOCTLTEST_READ, &info);
            if (info.size> 0) {
                if (!(info.buff[0]& 0x10)) break;
           }
       }
       printf ("IOCTLTEST_READ OK!\n");

       info.size = 1;
       info.buff[0] = 0xFF;
        
        for (cnt= 0; cnt < 10; cnt++) {
           ioctl (dev, IOCTLTEST_WRITE, &info);
                
           info.buff[0] = ~info.buff[0];
           usleep (500000);
       }

       printf ("wait... input\n");
       cnt = 0;
       state = 0xFF;

        while (1){
           info.size = 1;
           info.buff[0] = state;
           ioctl (dev, IOCTLTEST_WRITE_READ, &info);

            if (info.size> 0) {
                if (!(info.buff[0]& 0x10)) break;
           }

           cnt++;
            if (cnt>= 2) {
               cnt = 0;
               state = ~state;
           }
        
           usleep (100000);
       }
       ioctl (dev, IOCTLTEST_LEDOFF);
        
       close (dev);
   }
    return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值