nhmk/示例/3-ioctl/ioctl.c

213 lines
7.0 KiB
C

/*
* ioctl.c
*/
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/version.h>
// 定义用于 ioctl 操作的数据结构
struct ioctl_arg {
unsigned int val;
};
// 定义 ioctl 操作的宏
/* Documentation/userspace-api/ioctl/ioctl-number.rst */
#define IOC_MAGIC '\x66' // 自定义的魔术数字,用于标识 ioctl 操作的类别
// 定义不同的 ioctl 操作
#define IOCTL_VALSET _IOW(IOC_MAGIC, 0, struct ioctl_arg) // 写操作,设置一个值
#define IOCTL_VALGET _IOR(IOC_MAGIC, 1, struct ioctl_arg) // 读操作,获取一个值
#define IOCTL_VALGET_NUM _IOR(IOC_MAGIC, 2, int) // 读操作,获取一个整数
#define IOCTL_VALSET_NUM _IOW(IOC_MAGIC, 3, int) // 写操作,设置一个整数
#define IOCTL_VAL_MAXNR 3 // 最大的 ioctl 命令号
#define DRIVER_NAME "ioctltest" // 驱动名称
// 定义一些全局变量
static unsigned int test_ioctl_major = 0; // 主设备号
static unsigned int num_of_dev = 1; // 设备数量
static struct cdev test_ioctl_cdev; // 字符设备结构体
static int ioctl_num = 0; // 用于存储整数值的全局变量
// 定义用于保存设备数据的结构体
struct test_ioctl_data {
unsigned char val; // 存储一个值
rwlock_t lock; // 读写锁,用于保护数据的并发访问
};
// 处理 ioctl 操作的函数
static long test_ioctl_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct test_ioctl_data *ioctl_data = filp->private_data; // 从文件私有数据中获取设备数据
int retval = 0; // 操作结果
unsigned char val;
struct ioctl_arg data;
memset(&data, 0, sizeof(data)); // 清空 data 结构体
switch (cmd) {
case IOCTL_VALSET:
// 处理 IOCTL_VALSET 命令,将用户空间的数据复制到内核空间
if (copy_from_user(&data, (int __user *)arg, sizeof(data))) {
retval = -EFAULT;
goto done;
}
pr_alert("IOCTL 设置值:%x .\n", data.val); // 打印设置的值
write_lock(&ioctl_data->lock); // 获取写锁
ioctl_data->val = data.val; // 更新设备数据
write_unlock(&ioctl_data->lock); // 释放写锁
break;
case IOCTL_VALGET:
// 处理 IOCTL_VALGET 命令,将内核空间的数据复制到用户空间
read_lock(&ioctl_data->lock); // 获取读锁
val = ioctl_data->val; // 读取设备数据
read_unlock(&ioctl_data->lock); // 释放读锁
data.val = val;
if (copy_to_user((int __user *)arg, &data, sizeof(data))) {
retval = -EFAULT; // 复制失败
goto done;
}
break;
case IOCTL_VALGET_NUM:
// 处理 IOCTL_VALGET_NUM 命令,将整数值返回给用户空间
retval = __put_user(ioctl_num, (int __user *)arg);
break;
case IOCTL_VALSET_NUM:
// 处理 IOCTL_VALSET_NUM 命令,从用户空间获取整数值
ioctl_num = arg;
break;
default:
retval = -ENOTTY; // 命令不被支持
}
done:
return retval; // 返回操作结果
}
// 处理文件读取操作的函数
static ssize_t test_ioctl_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
struct test_ioctl_data *ioctl_data = filp->private_data; // 从文件私有数据中获取设备数据
unsigned char val;
int retval;
int i = 0;
read_lock(&ioctl_data->lock); // 获取读锁
val = ioctl_data->val; // 读取设备数据
read_unlock(&ioctl_data->lock); // 释放读锁
// 将数据从内核空间复制到用户空间
for (; i < count; i++) {
if (copy_to_user(&buf[i], &val, 1)) {
retval = -EFAULT; // 复制失败
goto out;
}
}
retval = count; // 成功读取的字节数
out:
return retval; // 返回读取结果
}
// 处理文件关闭操作的函数
static int test_ioctl_close(struct inode *inode, struct file *filp)
{
pr_alert("%s 呼叫\n", __func__); // 打印函数名
if (filp->private_data) {
kfree(filp->private_data); // 释放内存
filp->private_data = NULL;
}
return 0; // 成功关闭文件
}
// 处理文件打开操作的函数
static int test_ioctl_open(struct inode *inode, struct file *filp)
{
struct test_ioctl_data *ioctl_data;
pr_alert("%s 呼叫\n", __func__); // 打印函数名
ioctl_data = kmalloc(sizeof(struct test_ioctl_data), GFP_KERNEL); // 分配内存
if (ioctl_data == NULL)
return -ENOMEM; // 内存分配失败
rwlock_init(&ioctl_data->lock); // 初始化读写锁
ioctl_data->val = 0xFF; // 初始化设备数据
filp->private_data = ioctl_data; // 将设备数据指针保存到文件私有数据中
return 0;
}
// 文件操作结构体,定义了设备文件的操作函数
static struct file_operations fops = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
.owner = THIS_MODULE, // 设置模块所有者
#endif
.open = test_ioctl_open, // 打开文件操作函数
.release = test_ioctl_close, // 关闭文件操作函数
.read = test_ioctl_read, // 读取文件操作函数
.unlocked_ioctl = test_ioctl_ioctl, // ioctl 操作函数
};
// 模块初始化函数
static int __init ioctl_init(void)
{
dev_t dev;
int alloc_ret = -1;
int cdev_ret = -1;
// 分配字符设备号
alloc_ret = alloc_chrdev_region(&dev, 0, num_of_dev, DRIVER_NAME);
if (alloc_ret)
goto error; // 分配失败,跳转到错误处理
test_ioctl_major = MAJOR(dev); // 获取主设备号
cdev_init(&test_ioctl_cdev, &fops); // 初始化字符设备结构体
cdev_ret = cdev_add(&test_ioctl_cdev, dev, num_of_dev); // 添加字符设备
if (cdev_ret)
goto error; // 添加失败,跳转到错误处理
pr_alert("%s 驱动(主编号: %d) 已安装\n", DRIVER_NAME,
test_ioctl_major); // 打印安装成功信息
return 0;
error:
if (cdev_ret == 0)
cdev_del(&test_ioctl_cdev); // 释放字符设备
if (alloc_ret == 0)
unregister_chrdev_region(dev, num_of_dev); // 释放设备号
return -1;
}
// 模块卸载函数
static void __exit ioctl_exit(void)
{
dev_t dev = MKDEV(test_ioctl_major, 0); // 创建 dev_t 变量
cdev_del(&test_ioctl_cdev); // 删除字符设备
unregister_chrdev_region(dev, num_of_dev); // 释放字符设备号
pr_alert("%s 模块卸载 \n", DRIVER_NAME); // 打印卸载信息
}
module_init(ioctl_init);
module_exit(ioctl_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("这是 ioctl 测试模块");