ioctl,字面意思是输入输出控制,lesson12 里面对这个玩意的作用说了个清除,超越常规的r/w/open/close 操作之外,设备文件有时候需要更多的参数来配置控制。比如典型的触摸屏,需要知道触摸屏大小,像素等。lesson12/13 讲的就是这ioctl.

lesson12 复制的是lesson8/9的代码,给了cdev_init(&my_cdev,&fops) 中fops 添加

static struct file_operations fops = {
    .unlocked_ioctl = my_ioctl,
};

查询linux 内核源码,file_operation 里面有unlocked_ioctl 和 compate_ioctl 两种。关于这个unlocked_ioctl 以及它和早期的linux 内核中的ioctl() 见其他大佬的说明。按照下文,ioctl 已经废弃,从古早内核移植的需要改名为unlocked_ioctl 和 compate_ioctl

内核新的ioctl方式--unlocked_ioctl和compat_ioctl(解决error:unknown field 'ioctl' specified in initializer)_vgdyvcommonioctl:unknow-CSDN博客

然后my_ioctl()添加了打印一句话做提示。lesson12只是给了这个ioctl调用接口,实际上没有用它做什么。

lesson12的代码忽略,看看lesson13代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#include "my_ioctl.h"

static dev_t dev_nr;
static struct cdev my_cdev;
static struct class *my_class;

static int answer = 42;

static long int my_ioctl(struct file *f, unsigned int cmd, unsigned long args)
{
	int status;
	struct mystruct s;
	pr_info("hello_cdev - ioctl called with cmd: 0x%x and args: %p\n", cmd, (void *) args);

	switch (cmd) {
		case WR_VAL:
			status = copy_from_user(&answer, (int *) args, sizeof(int));
			if (status) {
				pr_err("hello_cdev - Error on WR_VAL\n");
				return status;
			}
			break;
		case RD_VAL:
			status = copy_to_user((int *) args, &answer, sizeof(int));
			if (status) {
				pr_err("hello_cdev - Error on RD_VAL\n");
				return status;
			}
			break;
		case GREET:
			status = copy_from_user(&s, (struct mystruct *) args, sizeof(struct mystruct));
			if (status) {
				pr_err("hello_cdev - Error on GREET\n");
				return status;
			}

			for (int i=0; i<s.repeat; i++)
				pr_info("hello_cdev - Hello, %s\n", s.name);
			break;

		default:
			return -EOPNOTSUPP;
	}
	return 0;
}

static struct file_operations fops = {
	.unlocked_ioctl = my_ioctl,
};

static int __init my_init(void)
{
	int status;
#ifdef STATIC_DEVNR
	dev_nr = STATIC_DEVNR;
	status = register_chrdev_region(dev_nr, MINORMASK + 1, "hello_cdev");
#else
	status = alloc_chrdev_region(&dev_nr, 0, MINORMASK + 1, "hello_cdev");
#endif
	if (status) {
		pr_err("hello_cdev - Error reserving the region of device numbers\n");
		return status;
	}

	cdev_init(&my_cdev, &fops);
	my_cdev.owner = THIS_MODULE;

	status = cdev_add(&my_cdev, dev_nr, MINORMASK + 1);
	if (status) {
		pr_err("hello_cdev - Error adding cdev\n");
		goto free_devnr;
	}

	pr_info("hello_cdev - Registered a character device for Major %d starting with Minor %d\n", MAJOR(dev_nr), MINOR(dev_nr));

	my_class = class_create("my_class");
	if (!my_class) {
		pr_err("hello_cdev - Could not create class my_class\n");
		status = ENOMEM;
		goto delete_cdev;
	}

	if (!device_create(my_class, NULL, dev_nr, NULL, "hello%d", 0)) {
		pr_err("hello_cdev - Could not create device hello0\n");
		status = ENOMEM;
		goto delete_class;
	}

	pr_info("hello_cdev - Created device under /sys/class/my_class/hello0\n");

	return 0;

delete_class:
	class_unregister(my_class);
	class_destroy(my_class);
delete_cdev:
	cdev_del(&my_cdev);
free_devnr:
	unregister_chrdev_region(dev_nr, MINORMASK + 1);
	return status;
}

static void __exit my_exit(void)
{
	device_destroy(my_class, dev_nr);
	class_unregister(my_class);
	class_destroy(my_class);
	cdev_del(&my_cdev);
	unregister_chrdev_region(dev_nr, MINORMASK + 1);
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johannes 4Linux");
MODULE_DESCRIPTION("A sample driver for manually registering a character device");

应用层代码:

头文件my_ioctl.h

#ifndef MY_IOCTL_H_
#define MY_IOCTL_H_

struct mystruct {
	int repeat;
	char name[64];
};

#define WR_VAL _IOW('a', 'b', int *)
#define RD_VAL _IOR('a', 'c', int *)
#define GREET _IOW('a', 'd', struct mystruct *)

#endif

应用层测试程序test.c

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

#include "my_ioctl.h"

int main(int argc, char **argv)
{
	int answer, status;
	struct mystruct s;
	int fd = open("/dev/hello0", O_RDWR);

	if (fd < 0) {
		perror("open");
		return fd;
	}

	switch (argc) {
		case 1:
			status = ioctl(fd, RD_VAL, &answer);
			if (status) 
				perror("ioctl");
			printf("the answer is %d\n", answer);
			break;
		case 2:
			answer = atoi(argv[1]);
			status = ioctl(fd, WR_VAL, &answer);
			if (status) 
				perror("ioctl");
			break;
		case 3:
			s.repeat = atoi(argv[1]);
			strcpy(s.name, argv[2]);
			status = ioctl(fd, GREET, &s);
			if (status) 
				perror("ioctl");
			break;
		default:
			status = ioctl(fd, 123, NULL);
			if (status) 
				perror("ioctl");
			break;
	}

	close(fd);
	return 0;
}

看视频lesson13 即可。

https://www.bilibili.com/video/BV18SE26cEjn?vd_source=92b7efa4daa5bb4fdcdc9f2db97c307b&p=13

总体看都是比较浅显的说明了linux 内核里面字符设备的各种常用概念。。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐