系列文章目录
第一章 Linux 中内核与驱动程序
第二章 Linux 设备驱动编写 (misc)
第三章 Linux 设备驱动编写及设备节点自动生成 (cdev)
第四章 Linux 平台总线platform与设备树
第五章 Linux 设备树中pinctrl与gpio(lichee nano pi)
第六章 Linux ioctl接口应用
前言
前面提到了,利用file_operations结构体中的write\read函数,在其中利用copy_to_user/copy_from_user进行内核和用户的通信。但在一些场合下,例如:点灯中,需要在用户程序中调用write(1)函数,通过file_operations调用xx_write函数,在里面通过copy_from_user读取1,再进行判断,到最后点灯。这是比较繁琐的,那么就需要一种更简单的方式—ioctrl
。
一、ioctrl是什么?
ioctl接口
改成了unlocked_ioctl接口,只是名字改了,但是功能和对应的系统调用均没有发生变化。
那么unlocked_ioctl和read/write函数有什么相同点和不同点?
相同点:都可以往内核写数据。
不同点: read函数只能完成读的功能,write只能完成写的功能。读取大数据的时候效率高。ioctl 既可以读也可以写。读取大数据的时候效率不高。
二、使用步骤
1.ioctl讲解
unlocked_ioctl接口命令规则,实际上传递的是一个cmd参数,其有32位长:
- 第一个分区:0-7,命令的编号,范围是0-255。
- 第二个分区:8-15,命令的幻数。
- 第三个分区:16-29表示传递的数据大小。
- 第四个分区:30-31 代表读写的方向。
00:表示用户程序和驱动程序没有数据传递
10:表示用户程序从驱动里面读数
01:表示用户程序向驱动里面写数据
11:先写数据到驱动里面然后在从驱动里面把数据读出来。
第一个分区和第二个分区主要作用是用来区分命令的。
头文件<sys/ioctl.h>
还定义了有关于cmd参数的宏:合成宏与分解宏
合成宏:
- _lO(type,nr):用来定义没有数据传递的命令
- _IOR(type,nr,size) :用来定义从驱动中读取数据的命令
- _IOW(type,nr,size):用来定义向驱动写入数据的命令
- _IOWR(type,nr, size):用来定义数据交换类型的命令,先写入数据,再读取数据这类命令。
- 参数:
type:表示命令组成的幻(魔)数,也就是8~15位
nr:表示命令组成的编号,也就是0~7位
size:表示命令组成的参数传递大小,注意这里不是传递数字,而是数据类型,如要传递4字节,就可以写成int。
其中,读写方向的信息存放在宏的名字里了
分解宏
- _IOC_DIR(nr)]分解命令的方向,也就是上面说30~31位的值
- _IOC_TYPE(nr)分解命令的魔数,也就是上面说8~15位的值
- _IOC NR(nr)分解命令的编号,也就是上面说0~7位
- _IOC_SIZE(nr) 分解命令的复制数据大小,也就是上面说的16~29位参数说明:
nr :要分解的命令,例如,整个_lO(type,nr)
//测试程序如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define CMD_TEST0 _IO('a',0)
#define CMD_TEST1 _IO('b',1)
#define cMD_TEST2 _IOW('c',2,int)
#define CMD_TEST3 _IOR('d',3,int)
int main(int argc ,char *argv[]){
printf("30-31 is %d\n",_IOC_DIR(CMD_TEST0));
printf("30-31 is %d\n",_IOC_DIR(CMD_TEST3));
printf("7-15 is %c\n",_IOC_TYPE(CMD_TEST0));
printf("7-15 is %c\n",_IOC_TYPE(CMD_TEST1));
return 0;
}
2.ioctl驱动编写与测试
写在前面:注意:
1、在测试程序中和驱动程序中都要定义
命令例如:#define CMD_TEST0 _IO(‘a’,0)
2、向内核传数据时,函数ioctl(fd,CMD,1),代表传1
到xx_ioctl函数中的形参value
。
3、在从内核读数据时,ioctl(fd,CMD——TEST4,&value(自己定义的变量)
),传的是&value,故copy_to_user需要强制转换。
4、_IOWR一般不常用。
在这里利用前面讲到的misc设备的代码来改写具有ioctl接口
的驱动,并测试。
同样在file_operations结构体
中添加unlocked_ioctl
项,并编写对应的xx_ioctl。如下:
long misc_ioctl (struct file *file, unsigned int cmd, unsigned long value)
{
switch(cmd){
case CMD_TEST0:
printk("LED ON!!n");
break;
case CMD_TEST1:
printk("LED OFF!!n");
break;
}
return 0;
}
struct file_operations misc_fops={
.owner =THIS_MODULE,
.open =misc_open,
.release =misc_release,
.read =misc_read,
.write = misc_write,
.unlocked_ioctl=misc_ioctl,
};
在misc_ioctl
其中打印输出LED ON!!
和LED OFF!!
模拟灯的闪烁。当在测试程序中调用ioctl函数
时会调用编写的该xx_ioctl函数(本文中是misc_ioctl )。然后编写测试程序如下:通过不断地传cmd参数就可以改变switch中的条件进而点灯。
测试程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define CMD_TEST0 _IO('a',0)//这里用的是_IO没有传输数据!!!!!!!!!!!!!!!!!!
#define CMD_TEST1 _IO('a',1)
int main(int argc ,char *argv[]){
int fd;
fd = open("/dev/my_misc_ioctl",O_RDWR);
if(fd < 0)
{
printf("open error\n");
return fd;
}
while(1){
ioctl(fd,CMD_TEST0);
sleep(2);
ioctl(fd,CMD_TEST1);
sleep(2);
}
return 0;
}
在Ubuntu上模拟灯闪烁的程序,输出如下。
完整的驱动程序;
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/miscdevice.h> /* misc... */
#include <linux/fs.h> /* everything... */
#include <linux/types.h> /* size_t */
#include <linux/uaccess.h>
#include <linux/io.h>
#define CMD_TEST0 _IO('a',0)
#define CMD_TEST1 _IO('a',1)
int misc_open(struct inode *inode,struct file *file)
{
printk( "open my_misc_dev\n");
return 0;
}
int misc_release(struct inode *inode,struct file *file)
{
printk( "release my_misc_dev\n");
return 0;
}
ssize_t misc_read(struct file *file,char __user *ubuf,size_t size,loff_t *loff_t)
{
char kbuf[64] = "xieshangle";
if(copy_to_user(ubuf,kbuf,strlen(kbuf))!= 0)
{
printk( "copy_to_user error\n");
return -1;
}
printk( "misc_read\n");
return 0;
}
ssize_t misc_write(struct file *file,const char __user *ubuf,size_t size,loff_t *loff_t)
{
char kbuf[64] = {0};
if(copy_from_user(kbuf,ubuf,size)!= 0)
{
printk( "copy_from_user error\n");
return -1;
}
return 0;
}
long misc_ioctl (struct file *file, unsigned int cmd, unsigned long value)
{
switch(cmd){
case CMD_TEST0:
printk("LED ON!!n");
break;
case CMD_TEST1:
printk("LED OFF!!n");
break;
}
return 0;
}
struct file_operations misc_fops={
.owner =THIS_MODULE,
.open =misc_open,
.release =misc_release,
.read =misc_read,
.write = misc_write,
.unlocked_ioctl=misc_ioctl,
};
struct miscdevice misc_dev ={
.minor =MISC_DYNAMIC_MINOR,
.name ="my_misc_ioctl",
.fops =&misc_fops,
};
static int misc_init(void)
{
int ret;
ret =misc_register(&misc_dev);
if(ret < 0){
printk( "misc register is failed\n");
return -1;
}
printk( "misc register is succeed\n");
return 0;
}
static void misc_exit(void)
{
misc_deregister(&misc_dev);
printk(KERN_ALERT "Goodbye,misc\n");
}
/* register the init and exit routine of the module */
module_init( misc_init );
module_exit( misc_exit );
MODULE_LICENSE("GPL");