linux驱动ioctl数据传递机制

ioctl是linux中一种除read和write之外的数据传递机制, 通信双方是应用层和内核层。
简单描述这个机制就是,

  1. 应用程序调用ioctl函数发送一个数字给内核层驱动程序
  2. 驱动程序接收到数字, 执行对应的操作。

难点:

  1. ioctl所发送的数字, 是有一定规则的, 必须符合这个规则, 驱动层才能正确解析这个数字指令。 这个数字也叫指令码
  2. 应用层的ioctl是个变参函数, 但是变参并不意味着它可以传任意多个参数进去,它的意思是第三个参数可传可不传

既然是机制, 那么ioctl的机制如下

// >>> 应用层应用程序 test.c
int fd = open("/dev/xxx", O_RDWR);
// 与驱动沟通方式一: 
//  read(fd, buf, bufsize);
//  write(fd, buf, bufsize);

// 与驱动沟通方式二:
ioctl(fd,CMD1); 

----------------------
// >>> ioctl.h 一个发挥通信协议作用的头文件
#define CMD1 _IO('r', 10)

----------------------
// >>> 内核层驱动程序 test_dev.c
// 这里是驱动中自定义的ioctl函数, 通过结构体fileoperations结构体建立的映射
long dev_ioctl(struct file *file, unsigned int cmd, unsigned long ptr)
{
	switch (cmd){
		case CMD1:
				// do something ...
			break;
	}
}

关于指令码

  1. 指令码的构成
指令码是一个共32位的无符号整型数
direction [31:30]   size [29:16]   device type [15:8]  number [7:0]

direction: 00--没有参数 10--读数据 01--写数据 11--读写双向
size: 如果应用层的ioctl传递了第三个参数, 那么这个段存储第三个参数的字节数取值范围0-4096
type: 一个任意的字符即可, 取值范围0-255
number: 用户自定义个一个数字, 用来构成唯一的指令码,取值范围0-255
  1. linux内核提供了生成指令码的宏函数, 以及解析指令码的宏函数
// 生成指令码的宏函数
_IO(type, nr)			// 生成一个无参数的指令码
_IOR(type, nr, size)	// 生成一个读数据的指令码
_IOW(type, nr, size)	// 生成一个写数据的指令码
_IOWR(type, nr, size)	// 生成一个既要读又要写的指令码
// 解析指令码的宏函数
_IOC_DIR(nr)	// 解析出指令码的读写方式direction
_IOC_SIZE(nr)	// 解析出指令码中的size
_IOC_TYPE(nr)	// 解析出指令码中的type字符
_IOC_NR(nr)		// 解析出指令码中的用户自定义的number

关于ioctl的第三个参数
在man手册中,对第三个参数的描述是可以传递一个指针。但是在驱动层对这个参数并没有非常严格的限制。任何小于long类型的数据都可以通过第三个参数完成传递。也就是说, 我可以给第三个参数传递一个字符, 或者一个short型数据, 或者一个int型数据都是可以的。如果是指针, 那这个指针可以传递几乎任何数据类型, 只要在指令码的size段指明这个数据类型的大小即可。举例如下

传递char

// >>> 应用层应用程序 test.c
ioctl(fd,CMD1, data); 

----------------------
// >>> ioctl.h 一个发挥通信协议作用的头文件
char data = 'x'; // 要传递的数据
#define CMD1 _IOR('r', 10, data)

---------------------
// >>> 内核层驱动程序 test_dev.c
long dev_ioctl(struct file *file, unsigned int cmd, unsigned long ptr)
{
	switch (cmd){
		case CMD1:
				printk("received a char : %c\n", (char)ptr);
			break;
	}
}

传递int

// >>> 应用层应用程序 test.c
ioctl(fd,CMD1, data); 

----------------------
// >>> ioctl.h 一个发挥通信协议作用的头文件
int data = 12345; // 要传递的数据
#define CMD1 _IOR('r', 10, data)

---------------------
// >>> 内核层驱动程序 test_dev.c
long dev_ioctl(struct file *file, unsigned int cmd, unsigned long ptr)
{
	switch (cmd){
		case CMD1:
				printk("received a int: %d\n", (int)ptr);
			break;
	}
}

传递字符串

// >>> 应用层应用程序 test.c
ioctl(fd,CMD1, data); 

----------------------
// >>> ioctl.h 一个发挥通信协议作用的头文件
char *data = "this is string data to transfer."; // 要传递的数据
#define CMD1 _IOR('r', 10, data)

---------------------
// >>> 内核层驱动程序 test_dev.c
long dev_ioctl(struct file *file, unsigned int cmd, unsigned long ptr)
{
	switch (cmd){
		case CMD1:
				printk("received a string : %s\n", (char *)ptr);
			break;
	}
}

传递结构体

// >>> 应用层应用程序 test.c
ioctl(fd,CMD1, data); 

----------------------
// >>> ioctl.h 一个发挥通信协议作用的头文件
typedef struct {
	char name[20];
	int age;
} stu_t;
stu_t data; // 要传递的数据
#define CMD1 _IOR('r', 10, data)

---------------------
// >>> 内核层驱动程序 test_dev.c
long dev_ioctl(struct file *file, unsigned int cmd, unsigned long ptr)
{
	switch (cmd){
		case CMD1:
				memcpy(&data, (stu_t *)ptr, _IOC_SIZE(cmd));
				printk("student name: %s age:%d\n", data.name, data.age);
			break;
	}
}
  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__万波__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值