ioctl是linux中一种除read和write之外的数据传递机制
驱动层IOCTL:
int (*ioctl) (struct inode *inode, struct file *fp, unsigned int request, unsigned long args);
以上函数参数的含义如下。
在2.6.36以后ioctl函数已经不存在了,用unlocked_ioctl和compat_ioctl两个函数代替。参数去除了原来ioctl中的struct
inode参数,返回值也发生了改变。
新的代码
#include
long (*unlocked_ioctl) (struct file * fp, unsigned int request, unsigned long args);
long (*compat_ioctl) (struct file * fp, unsigned int request, unsigned long args);
应用层IOCTL:
#include
int ioctl(int fd, unsigned long request, ...);
注意
驱动代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BASEMINOR 0
#define COUNT 3
#define NAME "ioctl_test"
#define MEMDEV_IOC_MAGIC 'k' //定义幻数
#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 cdev *cdevp = NULL;
static long test_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((void *)arg,_IOC_SIZE(cmd));
}
//ioctl设置数据,需要向用户空间读取数据
else if(_IOC_DIR(cmd) & _IOC_WRITE){
err = !access_ok((void *)arg,_IOC_SIZE(cmd));
}
if(err){
return -EFAULT;
}
switch(cmd){
case MEMDEV_IOCPRTDATA:
printk("---------打印函数执行成功---------\n");
break;
case MEMDEV_IOCSETDATA:
ret = __get_user(ioarg,(int *)arg);
printk("从用户空间拿到的值:%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,
.unlocked_ioctl = test_ioctl
};
static int __init ioctl_init(void)
{
int ret;
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;
}
printk(KERN_INFO "---init over :%s---%s---%d---\n",__FILE__,__func__,__LINE__);
return 0;
err2:
unregister_chrdev_region(devno,COUNT); //释放申请到的设备号资源
err1:
return ret;
}
static void __exit ioctl_exit(void)
{
cdev_del(cdevp);
unregister_chrdev_region(devno,COUNT); //释放申请到的设备号资源
printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
}
module_init(ioctl_init);
module_exit(ioctl_exit);
MODULE_LICENSE("GPL");
编译驱动的Makefile:
KERNDIR = /lib/modules/`uname -r`/build
PWD = $(shell pwd)
obj-m:=char_ioctl_driver.o
all:
make -C $(KERNDIR) M=$(PWD) modules
clean:
make -C $(KERNDIR) M=$(PWD) clean
应用层代码:
#include
#include
#include
#include
#include
#include
int main()
{
int fd = 0;
int cmd;
int arg = 0;
fd = open("/dev/ioctl_test",O_RDWR);
if(fd < 0){
printf("open memdev0 failed!!!\n");
return -1;
}
cmd = _IO('k',1);//利用kernel api构造cmd
if(ioctl(fd,cmd,&arg) < 0){
printf("-------打印命令传输失败--------\n");
return -1;
}
cmd = _IOW('k',3,int);//利用kernel api构造cmd
arg = 2007;
if(ioctl(fd,cmd,&arg) < 0){
printf("-----------应用层数据copy到驱动失败--------\n");
return -1;
}
cmd = _IOR('k',2,int);//利用kernel api构造cmd
if(ioctl(fd,cmd,&arg) < 0){
printf("-------驱动数据copy到应用层数据失败---------\n");
return -1;
}
printf("从内核获取到的数据值:%d\n",arg);
close(fd);
return 0;
}
编译与验证流程:
驱动make直接编译:
ubuntu:~/Desktop/huangrui/project_1/char_ioctl$ make
make -C /lib/modules/`uname -r`/build M=/home/xj/Desktop/huangrui/project_1/char_ioctl modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-149-generic'
CC [M] /home/Desktop/huangrui/project_1/char_ioctl/char_ioctl_driver.o
Building modules, stage 2.
MODPOST 1 modules
CC [M] /home/Desktop/huangrui/project_1/char_ioctl/char_ioctl_driver.mod.o
LD [M] /home/Desktop/huangrui/project_1/char_ioctl/char_ioctl_driver.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-149-generic'
文件罗列:
char_ioctl$ ls
char_ioctl_driver.c char_ioctl_driver.mod char_ioctl_driver.mod.o char_ioctl_user Makefile Module.symvers
char_ioctl_driver.ko char_ioctl_driver.mod.c char_ioctl_driver.o char_ioctl_user.c modules.order
驱动加载:
sudo insmod char_ioctl_driver.ko
驱动模块查看:
project_1/char_ioctl$ lsmod
Module Size Used by
char_ioctl_driver 16384 0
btrfs 1249280 0
xor 24576 1 btrfs
查看驱动程序生成的设备及其主设备号:(可以作为mknod的参数)
$ cat /proc/devices
Character devices:
240 ioctl_test
创建设备节点:(cd到/dev下)
/dev$ sudo mknod ioctl_test c 240 0
查看设备节点情况:
/dev$ ls
ioctl_test
应用层代码编译:
$ gcc char_ioctl_user.c -o char_ioctl_user
执行结果:
$ sudo ./char_ioctl_user
从内核获取到的数据值:1101
驱动打印:
$ dmesg
[1192849.439778] major = 240
[1192849.439781] ---init over :/home/xj/Desktop/huangrui/project_1/char_ioctl/char_ioctl_driver.c---ioctl_init---97---
[1193159.265924] ---------打印函数执行成功---------
[1193159.265925] 从用户空间拿到的值:2007
验证成功!!!
这个就代表正在定义新的cmd