ioctl

实现对设备的控制,例如针对串口设备 驱动层除了需要提供对串口的读写之外还需提供对串口 波特率 、 奇偶校验位 、 终止位 的设置这些配置信**息需要从应用层传递一些基本数据** 仅仅是**数据类型不同。**
ioctl接口,可以传递数据并区分类型。

应用层:ioctl

#include<sys/ioctl.h>

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

cmd:给驱动层传递的命令,需要注意的时候,驱动层的命令和应用层的命令一定要统一
第三个参数:在 C 语言中,很多时候都被理解成可变参数。
返回值:失败-1,同时设置errno


内核层:unlocke_ioctl

long (*unlocked_ioctl)(struct file *,unsigned int,unsigned long);

file:vfs 层为打开字符设备文件的进程创建的结构体 用于存放文件的动态信息
cmd : 用户空间传递的命令 可以根据不同的命令做不同的事情

第三个参数 : 用户空间的数据,这个数据可能是一个地址值,用户空间传递的是一个地址;也可能是一个数值,也可能没值。
如果应用层传递的是一个整型值,内核层接收到也是整型值;
如果传递的是一个指针变量的地址,内核层需要把整型值强转成地址,再去读取地址里的值。


两者的调用关系

在这里插入图片描述应用层fd对应内核态的file,cmd对应cmd。

cmd

在这里插入图片描述
设备类型——type
设备号——nr

封装命令

因为实现cmd命令,是需要进行位操作的,内核里提供宏定义便于实现
在这里插入图片描述

举个栗子

在这里插入图片描述
在documentation下有已经被占用的序列号,需要注意自己写的cmd不能和之前的重复。

如何检查命令、地址正确性?
•可以通过宏 _IOC_TYPE(nr)来判断应用程序传下来的命令 type 是否正确;
•可以通过宏 _IOC_DIR(nr)来得到命令是读还是写,然后再通过宏 access_ok(type,addr,size)来判断用户层传递的内存地址是否合法。

头文件:#include "beep.h"
#ifndef _BEFF_H
#define _BEFF_H

#define DEV_FIFO_TYPE 'l'

#define DEV_FIFO_CLEAN _IO(DEV_FIFO_TYPE,0)
#define DEV_FIFO_GETVALUE _IOR(DEV_FIFO_TYPE,1,int)
#define DEV_FIFO_PUTVALUE _IOW(DEV_FIFO_TYPE,2,int)

#endif

#ifndef
#define

#endif  用于防止头文件被重复包含
宏定义分三部分:设备类型,这是该组的第几个命令,操作值的类型


驱动:#include <linux/io.h>
static int knum = 10010;

long hello_ioctl (struct file *filep, unsigned int cmd, unsigned long arg)
{
	/* 需要和用户层协调:所传的第三个参数需要为指针;但在内核中它以整型值形式接受这个指针;
	   因此需要强行装换类型*/
	void __user *argp = (void __user *)arg;
	int __user *p = argp;
	int err = 0;
	/* 以上的地址都是用户层地址,但是在内核中操作的 */

	switch(cmd)
		{
			case DEV_FIFO_CLEAN: 
				printk("DEV_FIFO_CLEAN \n");
				break;

			case DEV_FIFO_PUTVALUE:	
				err = put_user(knum, p);
				printk("DEV_FIFO_PUTVALUE  %d\n",knum);
				break;
				
			case DEV_FIFO_GETVALUE:
				err = get_user(knum,p);
				printk("DEV_FIFO_GETVALUE  %d\n",knum);
				break;
			default:
					return -EINVAL;
		}
	return 0;
}

1、在定义cmd宏中,第三个参数已经定义了数据类型;但是内核中传参用的却是用unsigned long arg,因此需要进行格式的转换。
由于可以约定了第三个的数据类型(int),且参数arg一般代表指针,它现在就是指针的值(不是解指针的值),因此直接转换成int 指针类型。

void __user *argp = (void __user *)arg;
int __user *p = argp;

2、由于arg是由用户层传来的,所以为了保持类型一致,用户层也要和cmd保持一致。


用户层:
int num = 0ioctl(fd,DEV_FIFO_PUTVALUE,&num);
ioctl(fd,DEV_FIFO_GETVALUE,&num);

需要和cmd命令中格式保持一致;一般习惯传指针。

部分问题

result = register_chrdev(major, "hello", &hello_drv);

在注册设备时,register_chrdev的返回值不代表设备号,设备号始终只储存在major中。


如果模块没有被重装,里面存储的值可以一直被修改且不会重置。APP层程序可以不断开打关闭重启重置,但是对驱动的修改并不会重启重置;除非你重装驱动。所以,可以通过APP层去实时修改驱动。


“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值