目录
简介
用户空间与内核的交互方式,使用copy_from_user(), copy_to_user().
除了这两种交互方式,内核还提供了其他高级的方式,对于写驱动来说很重要。有proc、sysfs、debugfs、netlink、ioctl。
本文学习ioctl
一、交互方法笔记与总结
procfs | sysfs | debugfs | netlink | ioctl | |
容易开发 | 容易开发与使用 | 相对容易学习与使用 | 非常容易学习与使用 | 困难,必须用户空间和内核空间同步编程 | 相对困难,必须写用户空间程序 |
适合场景 | 仅仅内核,旧内核使用,避免驱动使用 | 设备驱动 | 为生产与调试目的的驱动接口 | 多种接口(设备、驱动、网络、udev系统) | 设备驱动 |
接口可见性 | 可见;用户权限控制访问 | 可见;用户权限控制访问 | 可见;用户权限控制访问 | 在文件系统中隐藏,避免污染内核空间 | 在文件系统中隐藏,避免污染内核空间 |
支持度 | 已经废弃使用 | 与用户空间的“正式”方法 | 在内核中大量使用 | 支持很好 | 支持很好 |
是否用于debug | 是 | 不是 | 对于调试非常有用 | 不是 | 是(通过隐藏的命令) |
二、ioctl
input-output control
经常用在驱动程序中,与open、read、write一起使用。
API
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
读-写-读写 函数
_IO(type,nr)//没有参数
_IOR(type,nr,datatype)//读
_IOW(type,nr,datatype)//写
_IOWR(type,nr,datatype)//读/写
三、实战
1、头文件
公共头文件
/* our dummy ioctl (IOC) RESET command */
#define IOCTL_LLKD_IOCRESET _IO(IOCTL_LLKD_MAGIC, 0)
/* our dummy ioctl (IOC) Query POWER command */
#define IOCTL_LLKD_IOCQPOWER _IOR(IOCTL_LLKD_MAGIC, 1, int)
/* our dummy ioctl (IOC) Set POWER command */
#define IOCTL_LLKD_IOCSPOWER _IOW(IOCTL_LLKD_MAGIC, 2, int)
2、应用程序
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "ioctl_llkd.h"
//使用说明
int main(int argc, char **argv)
{
int fd, power;
//打开文件
if ((fd = open(argv[1], O_RDWR, 0)) == -1) {
perror("open");
exit(EXIT_FAILURE);
}
printf("device opened: fd=%d\n", fd);
//重置设备
if (ioctl(fd, IOCTL_LLKD_IOCRESET, 0) == -1) {
close(fd);
exit(EXIT_FAILURE);
}
printf("%s: device reset.\n", argv[0]);
// 2. 查询
if (ioctl(fd, IOCTL_LLKD_IOCQPOWER, &power) == -1) {
close(fd);
exit(EXIT_FAILURE);
}
printf("%s: power=%d\n", argv[0], power);
// 3. 翻转power值
if (0 == power) {
printf("%s: Device OFF, powering it On now ...\n", argv[0]);
//设置命令,fd 文件描述符;命令 IOCTL_LLKD_IOCSPOWER ,1 值
if (ioctl(fd, IOCTL_LLKD_IOCSPOWER, 1) == -1) {
close(fd);
exit(EXIT_FAILURE);
}
printf("%s: power is ON now.\n", argv[0]);
} else if (1 == power) {
printf("%s: Device ON, powering it OFF in 3s ...\n", argv[0]);
sleep(3); /* yes, careful here of sleep & signals! */
//设置命令,fd 文件描述符;命令 IOCTL_LLKD_IOCSPOWER ,0 值
if (ioctl(fd, IOCTL_LLKD_IOCSPOWER, 0) == -1) {
close(fd);
exit(EXIT_FAILURE);
}
printf("%s: power OFF ok, exiting..\n", argv[0]);
}
close(fd);
exit(EXIT_SUCCESS);
}
3、内核程序
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/version.h>
#include <linux/uaccess.h>
#include "ioctl_llkd.h"
#define OURMODNAME "ioctl_llkd_kdrv"
MODULE_AUTHOR("wy");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_VERSION("0.1");
static int ioctl_intf_major,power = 1;
static long ioctl_intf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int retval = 0;
pr_debug("In ioctl method, cmd=%d\n", _IOC_NR(cmd));
switch (cmd) {
case IOCTL_LLKD_IOCRESET:
pr_debug("reset\n");
break;
case IOCTL_LLKD_IOCQPOWER: /*获取参数 */
//将内核的值power的值,赋值给arg
//retval是返回值
retval = __put_user(power, (int __user *)arg);
break;
case IOCTL_LLKD_IOCSPOWER: /*设置参数*/
power = arg;//用户空间命令行传递的参数arg 赋值给power
pr_debug("In ioctl cmd option: IOCTL_LLKD_IOCSPOWER\n"
"power=%d now.\n", power);
break;
default:
return -ENOTTY;
}
return retval;
}
static const struct file_operations ioctl_intf_fops = {
.llseek = no_llseek,
.unlocked_ioctl = ioctl_intf_ioctl,
};
static int ioctl_intf_open(struct inode *inode, struct file *filp)
{
pr_debug("Device node with minor # %d being used\n", iminor(inode));
switch (iminor(inode)) {
case 0:
filp->f_op = &ioctl_intf_fops;
break;
default:
return -ENXIO;
}
if (filp->f_op && filp->f_op->open)
return filp->f_op->open(inode, filp);
return 0;
}
static struct file_operations ioctl_intf_open_fops = {
.open = ioctl_intf_open,
};
static int __init ioctl_llkd_kdrv_init(void)
{
int result;
result = register_chrdev(ioctl_intf_major, OURMODNAME, &ioctl_intf_open_fops);
if (result < 0) {
pr_info("register_chrdev() failed trying to get ioctl_intf_major=%d\n", ioctl_intf_major);
return result;
}
if (ioctl_intf_major == 0)
ioctl_intf_major = result;
pr_debug("registered:: ioctl_intf_major=%d\n", ioctl_intf_major);
return 0;
}
static void ioctl_llkd_kdrv_cleanup(void)
{
unregister_chrdev(ioctl_intf_major, OURMODNAME);
pr_info("removed\n");
}
module_init(ioctl_llkd_kdrv_init);
module_exit(ioctl_llkd_kdrv_cleanup);
4、 程序输出
# ./ioctl_llkd_userspace /dev/ioctl_intf
device opened: fd=3
./ioctl_llkd_userspace: device reset.
./ioctl_llkd_userspace: power=0
./ioctl_llkd_userspace: Device OFF, powering it On now ...
./ioctl_llkd_userspace: power is ON now.
# ./ioctl_llkd_userspace /dev/ioctl_intf
device opened: fd=3
./ioctl_llkd_userspace: device reset.
./ioctl_llkd_userspace: power=1
./ioctl_llkd_userspace: Device ON, powering it OFF in 3s ...
./ioctl_llkd_userspace: power OFF ok, exiting..