您的位置:首页 > 运维架构 > Linux

linux驱动学习4:scull驱动

2012-11-25 15:06 204 查看
要点:学习ioctl()驱动编写,如何传入命令来控制硬件。

1)ioctl知识

用户空间的ioctl()调用如下:

int  ioctl(int fd,  unsigned long  cmd, …);

2)驱动中ioctl:

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

主要工作:传入设备对应的inode,根据命令cmd和arg来修改filp。

3)实现

分为两步:

n  定义命令(一般在头文件中, scull.h);

n  实现命令, switch。

i.             
定义命令


编写ioctl之前先需要定义命令,命令号在系统范围内必须是唯一的。命令cmd被划分为4个位段:类型type(幻数:确保唯一)、序号(对应该设备驱动的命令的序号)、传送方向、参数大小。

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

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

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

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

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

_IO(type, nr):没有参数传递的命令。(那么direction的值为_IOC_NONE,size的值为0)

_IOR(type, nr, datatype):从驱动中读数据(4个值已经确定)

_IOW(type, nr, datatype):写数据到驱动

_IOWR(type, nr, datatype):双向传送,type和number成员作为参数被传递

定义命令(范例)

#define   MEM_IOC_MAGIC  'm' //定义幻数,一个字母刚好是8位,必须唯一

#define          MEM_IOCSET       _IOW(MEM_IOC_MAGIC, 0, int)

#define       MEM_IOCGQSET     _IOR(MEM_IOC_MAGIC, 1, int)

ii.             
实现

1) 
返回值:switch;

2) 
传入参数:如果是整数,则可直接使用;如果是指针,则需要先检验该指针是否有效(用access_ok()检验该指针是否是用户空间合法的)。

注:

不需要检测的函数:copy_to_user(),  copy_from_user(),  get_user(),  put_user();

需要用access_ok检验的:__get_user()内核从用户空间读数据,   __put_user()内核向用户空间写数据.(此处要注意access_ok()
与_IOC_READ的反向问题)即如下语句:

if (_IOC_DIR(cmd) & _IOC_READ)

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

_IOC_READ:表示用户读取内核设备的数据(主体是用户程序);

access_ok()的write是验证内核可以向用户空间的指针写数据(主体是内核)。

 

4)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.

5)ioctl代码:先检验

 

int scull_ioctl(struct inode *inode,  struct file *filp,  unsigned int cmd,  unsigned long arg)
{
int err = 0, tmp;
int retval = 0;
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
*/
if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;

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;

switch(cmd) {
case SCULL_IOCRESET:
scull_quantum = SCULL_QUANTUM;
scull_qset = SCULL_QSET;
break;

case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
retval = __get_user(scull_quantum, (int __user *)arg);
break;

case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
scull_quantum = arg;
break;

case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
retval = __put_user(scull_quantum, (int __user *)arg);
break;
default:  /* redundant, as cmd was checked against MAXNR */
return -ENOTTY;
}
return retval;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 驱动