linux驱动24:ioctl

本文详细介绍了Linux内核空间和用户空间之间的ioctl通信机制,包括ioctl命令的构造、数据传输、权限检查以及错误处理。通过示例展示了如何在驱动程序中实现ioctl函数,同时给出了用户空间调用ioctl的代码片段。内容涵盖ioctl命令的位字段解析、数据传输函数的使用以及设备控制的权限验证。
摘要由CSDN通过智能技术生成

用户空间:int ioctl(int fd, unsigned long cmd, ...);

内核空间:int (unlocked_ioctl)(struct file *filep, unsigned int cmd, unsigned long arg);

ioctl命令定义:

<linux/ioctl.h>

4个位字段:

type设备类型(幻数),8bit

number(序数),8bit

direction(方向),2bit,包括:_IOC_NONE(没有数据传输)、_IOC_READ、_IOC_WRITE、及_IOC_READ | _IOC_WRITE(双向数据传输)

size(用户数据大小)8~14bit,通常13或14bit

构造命令编号宏:

<linux/ioctl.h>

_IO(type, nr) 用于构造无参数的命令编号;

_IOR(type, nr, datatype) 用于构造从驱动程序中读取数据的命令编号;

_IOW(type, nr, datatype) 用于构造写入数据的命令编号;

_IOWR(type, nr, datatype) 用于构造写双向传输命令编号;

type:幻数,nr:序数,datatype:数据类型

解开位字段的宏:

_IOC_DIR(cmd) 获取方向;

_IOC_TYPE(cmd) 获取幻数;

_IOC_NR(cmd) 获取序数;

_IOC_SIZE(cmd) 获取数据大小;

使用ioctl参数:

验证地址:int access_ok(int type, const void *addr, unsigned long size);

type:VERIFY_READ或VERIFY_WRITE,取决于要执行的动作是读取还是写入用户空间内存区。

addr:用户空间的地址

size:数据大小

返回值:1成功,0失败

如果在指定地址既要读取又要写入,则type为VERIFY_WRITE,它是VERIFY_READ的超集。

数据传输:

<linux/uaccess.h>

使用为最常用的数据大小(1、2、4、8个字节)优化过的一组函数。

把数据写到用户空间:

put_user(dataum, ptr)   (调用access_ok进行地址检查)

__put_user(dataum, ptr)   (不调用access_ok,使用前要调用access_ok)

成功返回0,失败返回-EFAULT

从用户空间接收一个数据:

get_user(local, ptr)

__get_user(local, ptr)

除了传输方向不同,和put_user、__put_user差不多。

上面函数只适合传输1、2、4、8字节大小的数据,如果需要传输其他大小的数据必须使用copy_to_user或copy_from_user

权能与受限操作:

对设备的访问由设备文件的权限控制,驱动程序通常不进行权限检查。

权能操作定义在<linux/capacity.h>

如:CAP_SYS_ADMIN 截获的能力,提供了访问许多系统管理操作的途径

检查权能:int capable(int capacity);     声明在<linux/sched.h>,失败返回0

非ioctl的设备控制:

通过向设备写入控制序列控制设备,适合不传送数据而只响应命令的设备,如机器人。

ioctl例程:

#ifndef _HELLOCMD_H_
#define _HELLOCMD_H_

#define HELLO_IOC_MAGIC	'Q'
#define HELLO_MAX_NR 2
#define HELLO_IOC_GET_VAL _IOR(HELLO_IOC_MAGIC, 0, int)
#define HELLO_IOC_SET_VAL _IOW(HELLO_IOC_MAGIC, 1, int)

#endif
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/sched.h>
#include "helloCmd.h"

static int major = 277;
static int minor = 0;
static dev_t helloNum;
// static struct cdev helloCdev;
struct class *helloClass = NULL;
struct device *helloDev = NULL;
static int helloVal = 7;

int hello_open(struct inode *pinode, struct file *pfile)
{
	printk("hello_open, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
	return 0;
}

int hello_release(struct inode *pinode, struct file *pfile)
{
	printk("hello_release, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));	
	return 0;
}

long hello_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
	int err = 0;

	if (HELLO_IOC_MAGIC != _IOC_TYPE(cmd))
	{
		pr_err("bad magic.\n");	
		return -ENOTTY;
	}

	if (_IOC_NR(cmd) > HELLO_MAX_NR) 
	{
		pr_err("bad nr.\n");	
		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)
	{
		pr_err("bad address.\n");	
		return -EFAULT;
	}

	switch(cmd)
	{
		case HELLO_IOC_GET_VAL:
			printk("HELLO_IOC_GET_VAL.\n");
			err = put_user(helloVal, (int __user *)arg);
			break;
		case HELLO_IOC_SET_VAL:
			if (!capable(CAP_SYS_ADMIN))
			{
				pr_err("without permission.\n");
				return -EPERM;
			}
			printk("HELLO_IOC_SET_VAL.\n");
			err = get_user(helloVal, (int __user *)arg);
			break;
		default:
			pr_err("bad cmd.\n");
			err = -ENOTTY;	
			break;
	}

	return err;
}

static struct file_operations hello_ops = {
	.open = hello_open,
	.release = hello_release,
	.unlocked_ioctl = hello_ioctl,
};

static int hello_init(void)
{
	int ret = 0;
	printk("hello_init\n");

	ret = register_chrdev( major, "hello", &hello_ops);
	if(ret < 0)
	{
		printk("register_chrdev failed.\n");
		return ret;
	}

	helloClass = class_create(THIS_MODULE, "hellocls");
	if (IS_ERR(helloClass)) 
	{
		printk("class_create failed.\n");
		ret = PTR_ERR(helloClass);
		goto error_exit1;
	}

	helloNum = MKDEV(major,minor);
	printk("major:%d, minor:%d\n", MAJOR(helloNum), MINOR(helloNum));

	helloDev = device_create(helloClass, NULL, helloNum, NULL, "hello0");
	if (IS_ERR(helloDev)) 
	{
		printk("device_create failed.\n");
		ret = PTR_ERR(helloDev);
		goto error_exit2;
	}
	
	return 0;

error_exit2:
	class_destroy(helloClass);

error_exit1:
	unregister_chrdev(major,"hello");

	return ret;
}

static void hello_exit(void)
{
	printk("hello_exit\n");
	device_destroy(helloClass, helloNum);
	class_destroy(helloClass);
	unregister_chrdev(major,"hello");
}

MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "helloCmd.h"

int main(int argc, char * argv [ ])
{
	int fd = open("/dev/hello0", O_RDWR, 666);
	if (fd < 0)
	{
		perror("open faied");
		return -1;
	}

	printf("open successed: %d\n", fd);

	int num = 0;
	int ret = ioctl(fd, HELLO_IOC_GET_VAL, &num);
	if (ret < 0)
	{
		printf("HELLO_IOC_GET_VAL failed\n");
	}
	printf("HELLO_IOC_GET_VAL:%d\n", num);

	num = 77;
	ret = ioctl(fd, HELLO_IOC_SET_VAL, &num);
	if (ret < 0)
	{
		printf("HELLO_IOC_SET_VAL failed\n");
	}
	printf("HELLO_IOC_SET_VAL:%d\n", num);

	ret = ioctl(fd, HELLO_IOC_GET_VAL, &num);
	if (ret < 0)
	{
		printf("HELLO_IOC_GET_VAL failed\n");
	}
	printf("HELLO_IOC_GET_VAL:%d\n", num);

	sleep(2);
	close(fd);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值