字符设备驱动程序(ioctl)——开发步骤(6)

字符设备驱动程序(ioctl)

  • ioctl函数模型
    int (*ioctl)(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg){}
    返回值:

    • ioctl函数的实现通常是根据命令执行的switch语句,
    • 但是命令号不能匹配到任何一个设备时,通常返回-EINVAL(非法参数)

    使用参数:

    • 参数如果是一个整数,可以直接使用,
    • 如果是一个指针,需要先确保用户地址的有效性,使用前需要做正确检查
    • 需要检测的函数:__get_user 和 __put_user
    • 不需要检测的函数:copy_from_user & copy_to_user & get_user & put_user

    参数检查:
    int access_ok(int type,const void *addr,unsigned long size){}

    • 第一个参数:VERIFY_READ or VERIFY_WRITE
      • 用来表示读用户内存或者写用户内存
    • 第二个参数:addr是要操作的用户内存地址
    • 第三个参数:size是操作的长度,如果ioctl需要从用户空间读一个整数,size=sizeof(int)
    • 返回值:access_ok返回一个布尔值,1是成功,0是失败,如果返回失败,ioctl应当返回-EFAULT
    //ioctl读取数据,需要向用户空间写入数据
    if(_IOC_DIR(cmd) & _IOC_READ){
    	err = !access_ok(VERIFY_WRITE,(void __user *)arg,_IOC_SIZE(cmd));
    }
    //ioctl设置数据,需要向用户空间读取数据
    else if(_IOC_DIR(cmd) & _IOC_WRITE){
    	err = !access_ok(VERIFY_READ,(void __user *)arg,_IOC_SIZE(cmd));
    }
    if(err){
    	return -EFAULT;
    }
    

定义命令

#define MEM_IOC_MAGIC 	'm'			//定义幻数
#define MEM_IOCSET		_IOW(MEM_IOC_MAGIC,o,int)			//set数据到驱动中
#define MEM_IOCGQSET 	_IOR(MEM_IOC_MAGIC,1,int)			//从驱动中读数据

关于ioctl函数模型

  • 在2.6.36以后ioctl函数已经不存在了,用unlocked_ioctl和compat_ioctl两个函数代替。参数去除了原来ioctl中的struct inode参数,返回值也发生了改变。

  • 在应用程序设计中还是采用ioctl实现访问,而并不是unlocked_ioctl函数,因此我们还可以称之为ioctl函数的实现。

    • 操作集定义
      在这里插入图片描述
    • 函数原型
      在这里插入图片描述
    • 应用程序调用
      在这里插入图片描述

详细步骤

  • 编译Makefile
    在这里插入图片描述

  • 将字符设备驱动模块加载到内核中
    在这里插入图片描述

  • 查看主设备号
    cat /proc/devices
    在这里插入图片描述

  • 去到/dev目录下,创建设备文件memdev_ioctl0
    cd /dev

  • 关于创建设备文件参数解释见上一篇博客(连接:创建设备文件参数解释)
    sudo mknod memdev_ioctl0 c 240 0
    在这里插入图片描述

  • 编译应用程序文件
    gcc memdev_ioctl_app.c -o memdev_ioctl_app
    在这里插入图片描述

  • 执行可执行文件
    sudo ./memdev_ioctl_app
    在这里插入图片描述

执行成功!!!!!!!!!

ioctl源码资源!!!!!!!

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/unistd.h>
 

#define BASEMINOR 0
#define COUNT     3
#define NAME      "memdev_ioctl"
#define MEMDEV_SIZE		4096

#define MEMDEV_IOC_MAGIC 'j'			//定义幻数
#define MEMDEV_IOCPRTDATA _IO(MEMDEV_IOC_MAGIC,1)			//控制驱动打印数据
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC,3,int)			//set数据到驱动中
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC,2,int)			//从驱动中读数据
#define MEMDEV_IOCMAXNR 3

dev_t devno;

struct memdev{
	char *data;
	unsigned long size;
};

struct cdev *cdevp = NULL;
struct memdev *memdevp = NULL;

int mem_open (struct inode *inode,struct file *filp){
	//printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
	struct memdev *mdev = NULL;
	
	int num = MINOR(inode->i_rdev);
	if(num >= COUNT){
		return -ENODEV;
	}
	mdev = &memdevp[num];          
	filp->private_data = mdev;
	
	return 0;
}
int mem_release (struct inode *inode,struct file *filp){
	printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
	return 0;
}

static long mem_ioctl(struct file *filp,unsigned int cmd,unsigned long arg){
	
	long ret = 0;
	int err = 0;
	int ioarg = 0;
	
	//第一步验证命令cmd参数的有效性
	if(_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC){
	return -EINVAL;}
	if(_IOC_NR(cmd) > MEMDEV_IOCMAXNR){
	return -EINVAL;}
	
	//检测参数空间是否可以正确访问
	//ioctl读取数据,需要向用户空间写入数据
	if(_IOC_DIR(cmd) & _IOC_READ){
		err = !access_ok(VERIFY_WRITE,(void *)arg,_IOC_SIZE(cmd));
	}
	//ioctl设置数据,需要向用户空间读取数据
	else if(_IOC_DIR(cmd) & _IOC_WRITE){
		err = !access_ok(VERIFY_READ,(void *)arg,_IOC_SIZE(cmd));
	}
	if(err){
		return -EFAULT;
	}
	
	switch(cmd){
		case MEMDEV_IOCPRTDATA:
			printk("CMD MEMDEV_IOCPRTDATA DONE!!!!!\n");
			break;
		case MEMDEV_IOCSETDATA:
			ret = __get_user(ioarg,(int *)arg);
			printk("CMD MEMDEV_IOCGETDATA IS %d!!!!!\n",ioarg);
			break;
		case MEMDEV_IOCGETDATA:
			ioarg = 1101;
			ret = __put_user(ioarg,(int *)arg);
			break;
		default:
			return -EINVAL;
	}
	return ret;
}

static const struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = mem_open,
	.release = mem_release,
	.unlocked_ioctl = mem_ioctl
};

static int __init mem_init(void)
{
	int ret;
	int i;
/***********************************************************************/
	ret = alloc_chrdev_region(&devno,BASEMINOR,COUNT,NAME);
	if(ret < 0){
		printk(KERN_ERR "alloc_chrdev_region failed...\n");
		goto err1;
	}
	//设备号申请成功打印出来
	printk(KERN_INFO "major = %d \n",MAJOR(devno));
/***********************************************************************/
	cdevp = cdev_alloc();
	if(NULL == cdevp){
		printk(KERN_ERR "cdev_alloc failed...\n");
		ret = -ENOMEM;
		goto err2;
		//cdev结构体申请失败需要将上一步申请到的设备号资源释放。
	}
/***********************************************************************/	
	cdev_init(cdevp,&fops);
/***********************************************************************/	
	ret = cdev_add(cdevp,devno,COUNT);
	if(ret < 0){
		printk(KERN_ERR "cdev_add failed...\n");
		goto err2;
	}
/***********************************************************************/
	//为memdev设备结构体分配内存
	memdevp = kmalloc(COUNT * sizeof(struct memdev),GFP_KERNEL);
	if(NULL == memdevp){
			printk(KERN_ERR "memdevp kmalloc failed...\n");
			ret = -ENOMEM;
			goto err2;
		}
	memset(memdevp,0,sizeof(struct memdev));

	//上一步申请的是一片空间,这一片空间里是COUNT个设备的总空间,需要为每一个设备分配空间
	for(i = 0;i<COUNT;i++){
		memdevp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
		memdevp[i].size = MEMDEV_SIZE;
		memset(memdevp[i].data,0,MEMDEV_SIZE);
	}

/***********************************************************************/
	printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
	return 0;
err2:
	unregister_chrdev_region(devno,COUNT);			//释放申请到的设备号资源
err1:
	return ret;
}

static void __exit mem_exit(void)
{
	cdev_del(cdevp);
	unregister_chrdev_region(devno,COUNT);			//释放申请到的设备号资源
	printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
}

module_init(mem_init);
module_exit(mem_exit);
MODULE_LICENSE("GPL");

ioctl_app源码资源!!!!!!!

#include <stdio.h>
#include <string.h>
#include <linux/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>

int main()
{
	int fd = 0;
	char buf[4096];
	int cmd;
	int arg = 0;
	
	fd = open("/dev/memdev_ioctl0",O_RDWR);
	if(fd < 0){
		printf("open memdev0 failed!!!\n");
		return -1;
	}
	
	//调用MEMDEV_IOCPRTDATA
	printf("调用MEMDEV_IOCPRTDATA   ing!!!!\n");
	cmd = _IO('j',1);
	if(ioctl(fd,cmd,&arg) < 0){
		printf("调用MEMDEV_IOCPRTDATA    failed!!!!\n");
		return -1;
	}
	
	//调用MEMDEV_IOCSETDATA
	printf("调用MEMDEV_IOCSETDATA   ing!!!!\n");
	cmd = _IOW('j',3,int);
	arg = 2007;
	if(ioctl(fd,cmd,&arg) < 0){
		printf("调用MEMDEV_IOCSETDATA    failed!!!!\n");
		return -1;
	}
	
	//调用MEMDEV_IOCGETDATA
	printf("调用MEMDEV_IOCGETDATA   ing!!!!\n");
	cmd = _IOR('j',2,int);
	if(ioctl(fd,cmd,&arg) < 0){
		printf("调用MEMDEV_IOCGETDATA    failed!!!!\n");
		return -1;
	}
	printf("调用MEMDEV_IOCGETDATA  is %d!!!!\n",arg);
	
	close(fd);
	
	return 0;
}

Makefile源码资源!!!!!!!


KERNDIR = /lib/modules/4.15.0-107-generic/build
PWD = $(shell pwd)

obj-m:=memdev_ioctl.o

all:
	make -C $(KERNDIR) M=$(PWD) modules
	
clean:
	make -C $(KERNDIR) M=$(PWD) clean

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值