当前位置: 首页 > news >正文

imx6ull-驱动开发篇30——Linux 非阻塞IO实验

目录

实验程序编写

noblockio.c

noblockioApp.c

Makefile 文件

运行测试


在之前的文章里:Linux阻塞IO 实验,我们已经在驱动中添加了阻塞 IO 的代码。

本文里,我们继续完善驱动,加入非阻塞 IO 驱动代码。

实验程序编写

noblockio.c

代码如下:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define IMX6UIRQ_CNT		1			/* 设备号个数 	*/
#define IMX6UIRQ_NAME		"noblockio"	/* 名字 		*/
#define KEY0VALUE			0X01		/* KEY0按键值 	*/
#define INVAKEY				0XFF		/* 无效的按键值 */
#define KEY_NUM				1			/* 按键数量 	*//* 中断IO描述结构体 */
struct irq_keydesc {int gpio;								/* gpio */int irqnum;								/* 中断号     */unsigned char value;					/* 按键对应的键值 */char name[10];							/* 名字 */irqreturn_t (*handler)(int, void *);	/* 中断服务函数 */
};/* imx6uirq设备结构体 */
struct imx6uirq_dev{dev_t devid;			/* 设备号 	 */	struct cdev cdev;		/* cdev 	*/                 struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */int major;				/* 主设备号	  */int minor;				/* 次设备号   */struct device_node	*nd; /* 设备节点 */	atomic_t keyvalue;		/* 有效的按键键值 */atomic_t releasekey;	/* 标记是否完成一次完成的按键,包括按下和释放 */struct timer_list timer;/* 定义一个定时器*/struct irq_keydesc irqkeydesc[KEY_NUM];	/* 按键init述数组 */unsigned char curkeynum;				/* 当前init按键号 */wait_queue_head_t r_wait;	/* 读等待队列头 */
};struct imx6uirq_dev imx6uirq;	/* irq设备 *//* @description		: 中断服务函数,开启定时器		*				  	  定时器用于按键消抖。* @param - irq 	: 中断号 * @param - dev_id	: 设备结构。* @return 			: 中断执行结果*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{struct imx6uirq_dev *dev = (struct imx6uirq_dev*)dev_id;dev->curkeynum = 0;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));	/* 10ms定时 */return IRQ_RETVAL(IRQ_HANDLED);
}/* @description	: 定时器服务函数,用于按键消抖,定时器到了以后*				  再次读取按键值,如果按键还是处于按下状态就表示按键有效。* @param - arg	: 设备结构变量* @return 		: 无*/
void timer_function(unsigned long arg)
{unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;num = dev->curkeynum;keydesc = &dev->irqkeydesc[num];value = gpio_get_value(keydesc->gpio); 	/* 读取IO值 */if(value == 0){ 						/* 按下按键 */atomic_set(&dev->keyvalue, keydesc->value);}else{ 									/* 按键松开 */atomic_set(&dev->keyvalue, 0x80 | keydesc->value);atomic_set(&dev->releasekey, 1);	/* 标记松开按键,即完成一次完整的按键过程 */}               /* 唤醒进程 */if(atomic_read(&dev->releasekey)) {	/* 完成一次按键过程 *//* wake_up(&dev->r_wait); */wake_up_interruptible(&dev->r_wait);}
}/** @description	: 按键IO初始化* @param 		: 无* @return 		: 无*/
static int keyio_init(void)
{unsigned char i = 0;char name[10];int ret = 0;imx6uirq.nd = of_find_node_by_path("/key");if (imx6uirq.nd== NULL){printk("key node not find!\r\n");return -EINVAL;} /* 提取GPIO */for (i = 0; i < KEY_NUM; i++) {imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd ,"key-gpio", i);if (imx6uirq.irqkeydesc[i].gpio < 0) {printk("can't get key%d\r\n", i);}}/* 初始化key所使用的IO,并且设置成中断模式 */for (i = 0; i < KEY_NUM; i++) {memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(name));	/* 缓冲区清零 */sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);		/* 组合名字 */gpio_request(imx6uirq.irqkeydesc[i].gpio, name);gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);	imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i);
#if 0imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endif}/* 申请中断 */imx6uirq.irqkeydesc[0].handler = key0_handler;imx6uirq.irqkeydesc[0].value = KEY0VALUE;for (i = 0; i < KEY_NUM; i++) {ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, imx6uirq.irqkeydesc[i].name, &imx6uirq);if(ret < 0){printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);return -EFAULT;}}/* 创建定时器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_function;/* 初始化等待队列头 */init_waitqueue_head(&imx6uirq.r_wait);return 0;
}/** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int imx6uirq_open(struct inode *inode, struct file *filp)
{filp->private_data = &imx6uirq;	/* 设置私有数据 */return 0;
}/** @description     : 从设备读取数据 * @param - filp    : 要打开的设备文件(文件描述符)* @param - buf     : 返回给用户空间的数据缓冲区* @param - cnt     : 要读取的数据长度* @param - offt    : 相对于文件首地址的偏移* @return          : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;if (filp->f_flags & O_NONBLOCK)	{ /* 非阻塞访问 */if(atomic_read(&dev->releasekey) == 0)	/* 没有按键按下,返回-EAGAIN */return -EAGAIN;} else {							/* 阻塞访问 *//* 加入等待队列,等待被唤醒,也就是有按键按下 */ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey)); if (ret) {goto wait_error;}}keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if (releasekey) { /* 有按键按下 */	if (keyvalue & 0x80) {keyvalue &= ~0x80;ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));} else {goto data_error;}atomic_set(&dev->releasekey, 0);/* 按下标志清零 */} else {goto data_error;}return 0;wait_error:return ret;
data_error:return -EINVAL;
}/** @description     : poll函数,用于处理非阻塞访问* @param - filp    : 要打开的设备文件(文件描述符)* @param - wait    : 等待列表(poll_table)* @return          : 设备或者资源状态,*/
unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct *wait)
{unsigned int mask = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;poll_wait(filp, &dev->r_wait, wait);	/* 将等待队列头添加到poll_table中 */if(atomic_read(&dev->releasekey)) {		/* 按键按下 */mask = POLLIN | POLLRDNORM;			/* 返回PLLIN */}return mask;
}/* 设备操作函数 */
static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,.poll = imx6uirq_poll,
};/** @description	: 驱动入口函数* @param 		: 无* @return 		: 无*/
static int __init imx6uirq_init(void)
{/* 1、构建设备号 */if (imx6uirq.major) {imx6uirq.devid = MKDEV(imx6uirq.major, 0);register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);} else {alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}/* 2、注册字符设备 */cdev_init(&imx6uirq.cdev, &imx6uirq_fops);cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);/* 3、创建类 */imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.class)) {	return PTR_ERR(imx6uirq.class);}/* 4、创建设备 */imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.device)) {return PTR_ERR(imx6uirq.device);}/* 5、始化按键 */atomic_set(&imx6uirq.keyvalue, INVAKEY);atomic_set(&imx6uirq.releasekey, 0);keyio_init();return 0;
}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static void __exit imx6uirq_exit(void)
{unsigned i = 0;/* 删除定时器 */del_timer_sync(&imx6uirq.timer);	/* 删除定时器 *//* 释放中断 */	for (i = 0; i < KEY_NUM; i++) {free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);gpio_free(imx6uirq.irqkeydesc[i].gpio);}cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);device_destroy(imx6uirq.class, imx6uirq.devid);class_destroy(imx6uirq.class);
}	module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");

关键代码分析如下:

修改设备文件名字为“noblockio”,当驱动程序加载成功以后就会在根文件系统中出现一个名为“/dev/noblockio”的文件。

#define IMX6UIRQ_NAME "noblockio" /* 名字 */

imx6uirq_read函数里,判断是否为非阻塞式读取访问,如果是的话就判断按键是否有效,也就是判断一下有没有按键按下,如果没有的话就返回-EAGAIN。

	if (filp->f_flags & O_NONBLOCK)	{ /* 非阻塞访问 */if(atomic_read(&dev->releasekey) == 0)	/* 没有按键按下,返回-EAGAIN */return -EAGAIN;}

 imx6uirq_poll 函数,就是 file_operations 驱动操作集中的 poll 函数,当应用程序调用 select 或者 poll 函数的时候 imx6uirq_poll 函数就会执行。

调用 poll_wait 函数将等待队列头添加到 poll_table 中,然后判断按键是否有效,如果按键有效的话就向应用程序返回 POLLIN 这个事件,表示有数据可以读取。

unsigned int imx6uirq_poll(struct file *filp, struct poll_table_struct *wait)
{unsigned int mask = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;poll_wait(filp, &dev->r_wait, wait);	/* 将等待队列头添加到poll_table中 */if(atomic_read(&dev->releasekey)) {		/* 按键按下 */mask = POLLIN | POLLRDNORM;			/* 返回PLLIN */}return mask;
}

设置 file_operations , poll 成员变量为 imx6uirq_poll。

static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,.poll = imx6uirq_poll,
};

noblockioApp.c

测试app的代码如下:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"/** @description		: main主程序* @param - argc 	: argv数组元素个数* @param - argv 	: 具体参数* @return 			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd;int ret = 0;char *filename;struct pollfd fds;fd_set readfds;struct timeval timeout;unsigned char data;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR | O_NONBLOCK);	/* 非阻塞访问 */if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}#if 0/* 构造结构体 */fds.fd = fd;fds.events = POLLIN;while (1) {ret = poll(&fds, 1, 500);if (ret) {	/* 数据有效 */ret = read(fd, &data, sizeof(data));if(ret < 0) {/* 读取错误 */} else {if(data)printf("key value = %d \r\n", data);} 	} else if (ret == 0) { 	/* 超时 *//* 用户自定义超时处理 */} else if (ret < 0) {	/* 错误 *//* 用户自定义错误处理 */}}
#endifwhile (1) {	FD_ZERO(&readfds);FD_SET(fd, &readfds);/* 构造超时时间 */timeout.tv_sec = 0;timeout.tv_usec = 500000; /* 500ms */ret = select(fd + 1, &readfds, NULL, NULL, &timeout);switch (ret) {case 0: 	/* 超时 *//* 用户自定义超时处理 */break;case -1:	/* 错误 *//* 用户自定义错误处理 */break;default:  /* 可以读取数据 */if(FD_ISSET(fd, &readfds)) {ret = read(fd, &data, sizeof(data));if (ret < 0) {/* 读取错误 */} else {if (data)printf("key value=%d\r\n", data);}}break;}	}close(fd);return ret;
}

关键代码分析如下:

这段代码使用 poll 函数,来实现非阻塞访问,在 while 循环中使用 poll 函数不断的轮询,检查驱动程序是否有数据可以读取,如果可以读取的话就调用 read 函数读取按键数据。

#if 0/* 构造结构体 */fds.fd = fd;fds.events = POLLIN;while (1) {ret = poll(&fds, 1, 500);if (ret) {	/* 数据有效 */ret = read(fd, &data, sizeof(data));if(ret < 0) {/* 读取错误 */} else {if(data)printf("key value = %d \r\n", data);} 	} else if (ret == 0) { 	/* 超时 *//* 用户自定义超时处理 */} else if (ret < 0) {	/* 错误 *//* 用户自定义错误处理 */}}
#endif

这段代码使用 select 函数,来实现非阻塞访问。

while (1) {	FD_ZERO(&readfds);FD_SET(fd, &readfds);/* 构造超时时间 */timeout.tv_sec = 0;timeout.tv_usec = 500000; /* 500ms */ret = select(fd + 1, &readfds, NULL, NULL, &timeout);switch (ret) {case 0: 	/* 超时 *//* 用户自定义超时处理 */break;case -1:	/* 错误 *//* 用户自定义错误处理 */break;default:  /* 可以读取数据 */if(FD_ISSET(fd, &readfds)) {ret = read(fd, &data, sizeof(data));if (ret < 0) {/* 读取错误 */} else {if (data)printf("key value=%d\r\n", data);}}break;}	}

Makefile 文件

makefile文件只需要修改 obj-m 变量的值,改为noblockio.o。

内容如下:

KERNELDIR := /home/huax/linux/linux_test/linux-imx-rel_imx_4.1.15_2.1.0_gaCURRENT_PATH := $(shell pwd)
obj-m := noblockio.obuild: kernel_modules
kernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

运行测试

make -j32 //编译makefile文件
arm-linux-gnueabihf-gcc noblockioApp.c -o noblockioApp  //编译测试app

编译成功以后,就会生成一个名为“noblockio.ko”的驱动模块文件,和noblcokioApp 这个应用程序。

将编译出来 noblockio.ko 和 noblockioApp 这 两 个 文 件 拷 贝 到rootfs/lib/modules/4.1.15 目录中,重启开发板。

进入到目录 lib/modules/4.1.15 中,输入如下命令加载 blockio.ko 驱动模块:

depmod //第一次加载驱动的时候需要运行此命令
modprobe noblockio.ko //加载驱动

驱动加载成功以后,使用如下命令打开 noblockioApp 这个测试 APP,并且以后台模式运行:

./noblockioApp /dev/noblockio &

按下开发板上的 KEY0 按键,结果如图:

当按下 KEY0 按键以后, noblockioApp 这个测试 APP 就会打印出按键值。

输入“top”命令,查看 noblockioAPP 这个应用 APP 的 CPU 使用率,如图:

采用非阻塞方式读处理以后, noblockioApp 的 CPU 占用率也低至0.0%。

http://www.lryc.cn/news/627023.html

相关文章:

  • redis---常用数据类型及内部编码
  • 设计具有功能安全和网络安全能力的新型半导体芯片
  • 攻克PostgreSQL专家认证
  • Unicode 字符串转 UTF-8 编码算法剖析
  • JVM面试精选 20 题(终)
  • SQL count(*)与 sum 区别
  • 第三阶段数据-4:SqlHelper类,数据库删除,DataTable创建
  • STM32F4 内存管理介绍及应用
  • 建模工具Sparx EA的多视图协作教程
  • PyTorch - Developer Notes
  • 吴恩达 Machine Learning(Class 3)
  • 国产化PDF处理控件Spire.PDF教程:如何使用 Python 添加水印到 PDF
  • Linux命令大全-ps命令
  • Linux系统之部署nullboard任务管理工具
  • 基于springboot中学信息技术课程教学网站
  • 栈上创建和堆上创建区别
  • Nginx 的完整配置文件结构、配置语法以及模块详解
  • 设计模式1-单例模式
  • 继续记事本项目
  • 盲盒商城h5源码搭建可二开幸运盲盒回收转增定制开发教程
  • Hyperledger Fabric官方中文教程-改进笔记(十三)-使用测试网络创建通道
  • Google Chrome 扩展不受信任 - 不受支持的清单版本 解决方案
  • 整体设计 之定稿 “凝聚式中心点”原型 --整除:智能合约和DBMS的在表层挂接 能/所 依据的深层套接
  • AR 虚实叠加技术在工业设备运维中的实现流程方案
  • 云原生环境下的ITSM新趋势:从传统运维到智能化服务管理
  • MySQL 50 道经典练习题及答案
  • YOLOv8n-pose 模型使用
  • 学习中需不需要划线、做笔记
  • LLM常见名词记录
  • 【0基础PS】图片格式