linux驱动学习4:scull驱动

要点:学习ioctl()驱动编写,如何传入命令来控制硬件。

1ioctl知识

用户空间的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,根据命令cmdarg来修改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_NONEsize的值为0

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

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

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

定义命令(范例)

#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是验证内核可以向用户空间的指针写数据(主体是内核)。

 

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

5ioctl代码:先检验

 

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,驱动)