Johannes 《Linux内核模块与设备驱动开发:编写Linux驱动程序》 (12)(13)IOCTL
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
然后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 内核里面字符设备的各种常用概念。。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)