一步一步学习 Linux 驱动之设备控制接口函数(ioctl 函数)
1、 什么是ioctl
ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。大部分驱动需要 -- 除了读写设备的能力 -- 通过设备驱动进行各种硬件控制的能力. 大部分设备可进行超出简单的数据传输之外的操作; 用户空间必须常常能够请求, 例如, 设备锁上它的门,弹出它的介质, 报告错误信息, 改变波特率, 或者自我销毁. 这些操作常常通过 ioctl 方法来支持, 它通过相同名子的系统调用来实现.
在用户空间, ioctl 系统调用有下面的原型:
int ioctl(int fd, unsigned long cmd, ...);
ioctl 驱动方法有和用户空间版本不同的原型:
int (*ioctl) (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg);
inode 是对应应用程序传递的文件描述符 fd 的值;
filp 指针传递给 open 方法的相同参数;
cmd 参数从用户那里不改变地传下来, 并且可选的参数 ;
arg 参数以一个 unsigned long的形式传递, 不管它是否由用户给定为一个整数或一个指针.如果调用程序不传递第 3 个参数, 被驱动操作收到的 arg 值是无定义的. 因为类型检查在这个额外参数上被关闭, 编译器不能警告你如果一个无效的参数被传递给 ioctl, 并且任何关联的错误将难以查找.
ioctl 命令数字应当在这个系统是唯一的, 为了阻止向错误的设备发出正确的命令而引起的错误. 为帮助程序员创建唯一的 ioctl 命令代码, 这些编码已被划分为几个位段.
2、ioctl的命令号
定义 ioctl 命令号的正确方法使用 4 个位段, 它们有下列的含义. 这个列表中介绍的新符号定义在 <linux/ioctl.h>.
type
魔数. 只是选择一个数(在参考了 ioctl-number.txt之后)并且使用它在整个驱动中. 这个成员是 8 位宽(_IOC_TYPEBITS).
number
序(顺序)号. 它是 8 位(_IOC_NRBITS)宽.
direction
数据传送的方向,如果这个特殊的命令涉及数据传送. 可能的值是 _IOC_NONE(没有数据传输), _IOC_READ, _IOC_WRITE, 和 _IOC_READ|_IOC_WRITE (数据在2个方向被传送). 数据传送是从应用程序的观点来看待的; _IOC_READ 意思是从设备读, 因此设备必须写到用户空间. 注意这个成员是一个位掩码, 因此 _IOC_READ 和 _IOC_WRITE 可使用一个逻辑 AND 操作来抽取.
size
涉及到的用户数据的大小. 这个成员的宽度是依赖体系的, 但是常常是 13 或者 14 位. 你可为你的特定体系在宏 _IOC_SIZEBITS 中找到它的值. 你使用这个 size 成员不是强制的 -内核不检查它 -- 但是它是一个好主意. 正确使用这个成员可帮助检测用户空间程序的错误并使你实现向后兼容, 如果你曾需要改变相关数据项的大小. 如果你需要更大的数据结构, 但是, 你可忽略这个 size 成员. 我们很快见到如何使用这个成员.
定义宏来帮助建立命令号, 如下:
_IO(type,nr)(给没有参数的命令),
_IOR(type, nre, datatype)(给从驱动中读数据的),
_IOW(type,nr,datatype)(给写数据),
_IOWR(type,nr,datatype)(给双向传送).
type 和 number 成员作为参数被传递, 并且 size 成员通过应用 sizeof 到 datatype 参数而得到.
驱动中来解码这个号: _IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr), 和 _IOC_SIZE(nr).
3、测试程序
新建一个Ioctl_test.h的头文件
#ifndef _IOCTLTESH_H_
#define _IOCTLTESH_H_
#define TEST_MAGIC 'x'
#define TEST_MAX_NR 3
#define TEST_READ _IO(TEST_MAGIC,1)//自定义的修改内核内存数据命令
#define TEST_WRITE _IO(TEST_MAGIC,2)//自定义的修改内核内存数据命令
struct ioctl_data //用于测试的数据结构体
{
unsigned int size;
char buf[100];
};
#endif //end _IOCTLTESH_H_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/device.h>
#include "Ioctl_test.h"
static struct class *Myioctl_class; //自动注册驱动主设备
static struct device *Myioctl_dev;
static int Myioctl_open(struct inode *inode, struct file *file)
{
printk("My device is open!\n");
return 0;
}
static int Myioctl_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct ioctl_data val;
char str[100];
if(_IOC_TYPE(cmd)!=TEST_MAGIC)
return -EINVAL;
if(_IOC_NR(cmd)>TEST_MAX_NR)
return -EINVAL;
val.size = 100;
strcpy(val.buf,"Hello,world");
switch(cmd){
case TEST_READ:
if (copy_to_user((struct ioctl_data *)arg, &val, sizeof(val)) != 0) {
return - EFAULT;
}
break;
case TEST_WRITE:
if(copy_from_user(&str, (char *)arg, sizeof(str)) != 0) {
return - EFAULT;
}
printk("%s\n",str);
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations Myioctl_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = Myioctl_open,
.ioctl = Myioctl_ioctl,
};
static int major; //全局变量
static int Myioctl_init(void)
{
major = register_chrdev(0, "Myioctl", &Myioctl_fops); // 注册, 告诉内核
Myioctl_class = class_create(THIS_MODULE, "Myioctl");
Myioctl_dev = device_create(Myioctl_class, NULL, MKDEV(major, 0), NULL, "Myioctl"); /* /dev/Myioctl */
return 0;
}
static void Myioctl_exit(void)
{
unregister_chrdev(major, "Myioctl"); // 卸载
printk("Myioctl has been unregistered!\n");
device_unregister(Myioctl_dev);
class_destroy(Myioctl_class);
}
module_init(Myioctl_init);
module_exit(Myioctl_exit);
MODULE_LICENSE("GPL");
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include "Ioctl_test.h"
int main()
{
int fd;
char buf[100]="Welcome to China";
struct ioctl_data val;
fd = open("/dev/Myioctl",O_RDONLY);
if(fd < 0)
{
printf("open /dev/Myioctl error\n");
return 0;
}
ioctl(fd,TEST_WRITE,&buf);//测试write
ioctl(fd,TEST_READ,&val);//测试read
printf("%d\n",val.size);//打印从内核空间传上来的数据
printf("%s\n",val.buf);
close(fd);
return 0;
}
更多推荐
所有评论(0)