除了读和写设备外,大部分驱动还需要另一种能力,即通过设备驱动程序执行各种类型的硬件控制。这些操作通常通过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中大多实现了一个switch语句,根据不同的cmd执行不同的代码。不同的cmd有不同的值,为了方便创建唯一的ioctl的cmd,Linux在<linux/ioctl.h>中包含的<asm.ioctl.h>中提供了一些创建cmd的宏,如下:
_IO(type,nr);
_IOR(type,nr,datatype);
_IOW(type,nr,datatype);
_IOWR(type,nr,datatype);
type是表示一个magic number,一般可以如下定义:
#define XXX_IO_MAGIC ‘k’
nr代表命令序数,一般从1开始,随你设置,不能重复。datatype是数据类型。下面例子,我们定义一个命令cmd:
#define XXX_IOCMD_O _IO(XXX_IO_MAGIC,1)
#define XXX_IOCMD_C _IO(XXX_IO_MAGIC,2)
XXX代表任意,自己定吧,反正是宏定义,自己看着办。
定义了上述命令,我们即刻通过XXX_IOCMD_O传入ioctl,由用户空间到内核空间的联系了。注意,上面定义命令部分,要在用户空间和内核空间进行相同的操作,即在驱动程序和用户程序里面是相同的命令设置。
在驱动程序部分,大概是这样一个switch语句:
switch(cmd)
{
case XXX_IOCMD_O:
……//O对应open操作
case XXX_IOCMD_C:
……//C对应close操作
}
在用户程序部分,就像是ioctl()的调用了。假设一个设备描述符为dev_fd,打开这个设备(用户态),调用ioctl(dev_fd,XXX_IOCMD_O);
就这么简单啦。