一、阻塞实验
1.驱动程序
blockio.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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.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/of_irq.h>
/**
* file name:blockio
* date: 2021-08-14 09:37
* version:1.0
* author:luatao
* describe:blockio device drive
*/
#define GPIOKEY_CNT 1 /* 设备号个数 */
#define GPIOKEY_NAME "blockio" /* 设备名*/
/* 定义按键值 */
#define KEY0VALUE 0x01 /* 按键值 */
#define INVAKEY 0x00 /* 无效的按键值 */
#define KEY_NUM 1 /* 按键数量 */
/* 中断IO描述结构体 */
struct irq_keydesc {
int gpio; /* gpio */
int irqnum; /* 中断号*/
unsigned char value; /* 按键对应的键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int , void *); /* 中断服务函数 */
};
/* 设备结构体 自定义 */
struct gpiokey_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]; /* 按键描述数组 */
unsigned char curkeynum; /* 当前的按键号 */
wait_queue_head_t r_wait; /* 读等待队列头 */
};
/* 定义一个设备结构体 */
struct gpiokey_dev gpiokey; /* key 设备 */
/* 中断服务函数 开启定时器 延时 10ms 定时器用于按键消抖*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{
struct gpiokey_dev *dev = (struct gpiokey_dev *)dev_id;
dev->curkeynum = 0; // 当前按键号
dev->timer.data = (volatile long )dev_id; // 传递数据
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10 ms定时 */
return IRQ_RETVAL(IRQ_HANDLED);
}
/* 定时器服务函数 用于按键*/
void timer_function(unsigned long arg)
{
unsigned char value; // 按键值
unsigned char num; // 当前按键号
struct irq_keydesc *keydesc; /* 中断描述 */
struct gpiokey_dev *dev = (struct gpiokey_dev *)arg;
num = dev->curkeynum; // 当前按键号
keydesc = &dev->irqkeydesc[num]; // 当前按键号的中断描述
value = gpio_get_value(keydesc->gpio); // 读取按键值
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_interruptible(&dev->r_wait);
}
}
/* 打开设备 */
static int key_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpiokey; /* 设置私有数据 */
printk("key open!\r\n");
return 0;
}
/* 从设备读取数据 */
static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
unsigned char keyvalue = 0, releasekey = 0; // 按键值和按键释放标志
struct gpiokey_dev *dev = filp->private_data;
/* 定义一个等待队列 */
DECLARE_WAITQUEUE(wait, current);
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{
printk("data error!\r\n");
return -EINVAL;; // 数据错误
}
atomic_set(&dev->releasekey, 0); /* 按下标志清零 */
}else{ // 没有按键按下
add_wait_queue(&dev->r_wait, &wait); /* 将等待队列项添加到等待队列头 */
__set_current_state(TASK_INTERRUPTIBLE); /* 设置任务状态 可以被信号打断*/
schedule(); /* 进行一次任务切换 */
/* 执行到这 进程会休眠 当进程被唤醒 从下面开始执行 */
if(signal_pending(current)){ /* 判断是否为信号源引起的唤醒 */
ret = -ERESTARTSYS;
goto wait_error;
}
/* 执行下面 不是信号唤醒的 也就是按键唤醒的 */
goto wait_error;
}
//printk("key read !\r\n");
return 0;
wait_error:
__set_current_state(TASK_RUNNING); /* 设置当前任务为运行状态 */
remove_wait_queue(&dev->r_wait, &wait); /* 将对应的队列项从等待队列头删除*/
return 0;
}
/* 往设备写数据 */
static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
printk("key write !\r\n");
return 0;
}
/* 释放设备 */
static int key_release(struct inode *inode, struct file *filp)
{
//printk("key release!\r\n");
return 0;
}
/* 设备操作函数结构体 */
static struct file_operations gpiokey_fops = {
.owner = THIS_MODULE,
.open = key_open,
.read = key_read,
.write = key_write,
.release = key_release,
};
/* 驱动入口函数 */
static int __init mkey_init(void)
{
int ret,i = 0; // 返回值
/* 初始化原子变量 */
atomic_set(&gpiokey.keyvalue, INVAKEY); // 原子变量初始值为无效的按键值
atomic_set(&gpiokey.releasekey, 0); // 原子变量初始值为无效的按键值
/* 初始化等待队列头 */
init_waitqueue_head(&gpiokey.r_wait); //
/* 获取设备数中的属性数据 */
/* 1. 获取设备节点 /key*/
gpiokey.nd = of_find_node_by_path("/key"); // 通过绝对路径查找设备节点
if(gpiokey.nd == NULL){
printk("key node no find!\r\n");
return -EINVAL; /* 无效参数 不知道这个返回值是啥意思,我觉得返回一个负数就可以,这个值是23,不知道有没有处理*/
}
/* 2. 获取设备树中的gpio属性 得到key所使用的gpio编号 */
/* 可能有多个按键 这里统一处理 兼容性更好 */
for(i = 0; i< KEY_NUM; i++){
gpiokey.irqkeydesc[i].gpio = of_get_named_gpio(gpiokey.nd, "key-gpio", i);
if(gpiokey.irqkeydesc[i].gpio < 0 ){
printk("can't get key-gpio%d\r\n",i);
return -EINVAL; /* 无效参数 不知道这个返回值是啥意思,我觉得返回一个负数就可以,这个值是23,不知道有没有处理*/
}
printk("key-gpio%d num = %d \r\n", i,gpiokey.irqkeydesc[i].gpio); // 打印获取的key-gpio属性值
}
/* 初始化key所使用的IO 并且设置为中断模式 */
for(i = 0; i< KEY_NUM; i++) {
memset(gpiokey.irqkeydesc[i].name, 0, sizeof(gpiokey.irqkeydesc[i].name)); // 缓冲区清零
sprintf(gpiokey.irqkeydesc[i].name, "key%d", i); // 组合名字
gpio_request(gpiokey.irqkeydesc[i].gpio, gpiokey.irqkeydesc[i].name); // 申请IO
gpio_direction_input(gpiokey.irqkeydesc[i].gpio); // 设置为输入模式
gpiokey.irqkeydesc[i].irqnum = irq_of_parse_and_map(gpiokey.nd, i); // 获取中断号
#if 0
imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endif
/* 打印按键信息 */
printk("key%d:gpio=%d, irqnum=%d\r\n", i, gpiokey.irqkeydesc[i].gpio, gpiokey.irqkeydesc[i].irqnum);
}
/* 申请中断 */
gpiokey.irqkeydesc[0].handler = key0_handler; // 中断处理函数
gpiokey.irqkeydesc[0].value = KEY0VALUE; // 按键值
for(i = 0 ; i < KEY_NUM; i++){
ret = request_irq(gpiokey.irqkeydesc[i].irqnum, gpiokey.irqkeydesc[i].handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, gpiokey.irqkeydesc[i].name, &gpiokey);
if(ret < 0){
printk("irq %d request failed!\r\n", gpiokey.irqkeydesc[i].irqnum); // 中断号申请失败
return -EFAULT;
}
}
/* 创建定时器 */
init_timer(&gpiokey.timer);
gpiokey.timer.function = timer_function; // 定时器中断函数
/* 注册字符设备驱动 */
/* 1. 创建设备号 */
if(gpiokey.major){ // 定义了设备号
gpiokey.devid = MKDEV(gpiokey.major, 0 ); // 根据主设备号和次设备号合成设备号
register_chrdev_region(gpiokey.devid, GPIOKEY_CNT, GPIOKEY_NAME); // 注册设备号
}else{ // 没有定义设备号 动态生成
alloc_chrdev_region(&gpiokey.devid,0,GPIOKEY_CNT, GPIOKEY_NAME ); // 申请设备号
gpiokey.major = MAJOR(gpiokey.devid); // 获取主设备号
gpiokey.minor = MINOR(gpiokey.devid); // 获取次设备号
}
printk("gpiokey major = %d,minor = %d\r\n",gpiokey.major, gpiokey.minor); // 打印主设备号和次设备号
/* 2. 初始化 cdev */
gpiokey.cdev.owner = THIS_MODULE;
cdev_init(&gpiokey.cdev, &gpiokey_fops); // 初始化cdev
/* 3. 添加cdev */
cdev_add(&gpiokey.cdev, gpiokey.devid, GPIOKEY_CNT ); // 向linux系统添加cdev
/* 自动创建设备节点文件 */
/* 4. 创建类 */
gpiokey.class = class_create(THIS_MODULE, GPIOKEY_NAME); // 创建类
if(IS_ERR(gpiokey.class)){
return PTR_ERR(gpiokey.class);
}
/* 创建设备 */
gpiokey.device = device_create(gpiokey.class, NULL, gpiokey.devid, NULL, GPIOKEY_NAME);
if(IS_ERR(gpiokey.device)){
return PTR_ERR(gpiokey.device);
}
return 0;
}
/* 驱动出口函数 */
static void __exit mkey_exit(void)
{
unsigned char i = 0;
/* 释放IO */
for(i = 0; i < KEY_NUM; i++){
gpio_free(gpiokey.irqkeydesc[i].gpio);
}
/* 删除定时器 */
del_timer_sync(&gpiokey.timer);
/* 释放中断 */
for(i = 0; i < KEY_NUM; i++){
free_irq(gpiokey.irqkeydesc[i].irqnum, &gpiokey);
}
/* 注销字符设备驱动 */
cdev_del(&gpiokey.cdev); /* 删除 cdev */
unregister_chrdev_region(gpiokey.devid, GPIOKEY_CNT ); /* 注销设备号 */
device_destroy(gpiokey.class, gpiokey.devid); /* 注销设备 */
class_destroy(gpiokey.class); /* 注销类 */
printk("key drive unregsister ok !\r\n");
}
/* 加载驱动入口和出口函数 */
module_init(mkey_init);
module_exit(mkey_exit);
/* LICENSE 和 AUTHOR 信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luatao");
2.应用程序
和irq实验一样。
3.测试
加载驱动:
运行:
查看进程:
杀死进程:
卸载驱动:
二、非阻塞实验
1.驱动程序
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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.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/of_irq.h>
#include <linux/poll.h>
/**
* file name:noblockio
* date: 2021-08-14 10:16
* version:1.0
* author:luatao
* describe:noblockio device drive
*/
#define GPIOKEY_CNT 1 /* 设备号个数 */
#define GPIOKEY_NAME "noblockio" /* 设备名*/
/* 定义按键值 */
#define KEY0VALUE 0x01 /* 按键值 */
#define INVAKEY 0x00 /* 无效的按键值 */
#define KEY_NUM 1 /* 按键数量 */
/* 中断IO描述结构体 */
struct irq_keydesc {
int gpio; /* gpio */
int irqnum; /* 中断号*/
unsigned char value; /* 按键对应的键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int , void *); /* 中断服务函数 */
};
/* 设备结构体 自定义 */
struct gpiokey_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]; /* 按键描述数组 */
unsigned char curkeynum; /* 当前的按键号 */
wait_queue_head_t r_wait; /* 读等待队列头 */
};
/* 定义一个设备结构体 */
struct gpiokey_dev gpiokey; /* key 设备 */
/* 中断服务函数 开启定时器 延时 10ms 定时器用于按键消抖*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{
struct gpiokey_dev *dev = (struct gpiokey_dev *)dev_id;
dev->curkeynum = 0; // 当前按键号
dev->timer.data = (volatile long )dev_id; // 传递数据
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10)); /* 10 ms定时 */
return IRQ_RETVAL(IRQ_HANDLED);
}
/* 定时器服务函数 用于按键*/
void timer_function(unsigned long arg)
{
unsigned char value; // 按键值
unsigned char num; // 当前按键号
struct irq_keydesc *keydesc; /* 中断描述 */
struct gpiokey_dev *dev = (struct gpiokey_dev *)arg;
num = dev->curkeynum; // 当前按键号
keydesc = &dev->irqkeydesc[num]; // 当前按键号的中断描述
value = gpio_get_value(keydesc->gpio); // 读取按键值
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_interruptible(&dev->r_wait);
}
}
/* 打开设备 */
static int key_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpiokey; /* 设置私有数据 */
printk("key open!\r\n");
return 0;
}
/* 从设备读取数据 */
static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
unsigned char keyvalue = 0, releasekey = 0; // 按键值和按键释放标志
struct gpiokey_dev *dev = filp->private_data;
keyvalue = atomic_read(&dev->keyvalue); // 读取按键值
releasekey = atomic_read(&dev->releasekey); // 读取按键释放值
/* 判断访问方式 */
if(filp->f_flags & O_NONBLOCK){ /* 以非阻塞访问的 */
if(releasekey == 0){ /* 没有按键按下 */
return -EAGAIN;
}
}else{ // 阻塞访问
/* 加入等待队列 等待被唤醒 也就是有按键按下 */
ret = wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));
if(ret){
goto wait_error;
}
}
/* 当上面的被唤醒后 才能执行下面的 */
if(releasekey){ // 有按键按下
if(keyvalue & 0x80){
keyvalue &= ~0x80;
ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue)); // 拷贝到用户空间
}else{
printk("data error!\r\n");
return -EINVAL;; // 数据错误
}
atomic_set(&dev->releasekey, 0); /* 按下标志清零 */
}else{ // 没有按键按下
return -EINVAL;
}
//printk("key read !\r\n");
return 0;
wait_error:
return ret;
}
/* 往设备写数据 */
static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
printk("key write !\r\n");
return 0;
}
/* 释放设备 */
static int key_release(struct inode *inode, struct file *filp)
{
//printk("key release!\r\n");
return 0;
}
/* poll 函数 用于处理非阻塞访问 */
unsigned int key_poll(struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask = 0;
struct gpiokey_dev *dev = filp->private_data;
poll_wait(filp, &dev->r_wait, wait); /* 将等待队列头添加到 poll_table中 */
if(atomic_read(&dev->releasekey)){ // 按键按下
mask = POLL_IN | POLLRDNORM; // 返回PLLIN
}
return mask;
}
/* 设备操作函数结构体 */
static struct file_operations gpiokey_fops = {
.owner = THIS_MODULE,
.open = key_open,
.read = key_read,
.write = key_write,
.poll = key_poll,
.release = key_release,
};
/* 驱动入口函数 */
static int __init mkey_init(void)
{
int ret,i = 0; // 返回值
/* 初始化原子变量 */
atomic_set(&gpiokey.keyvalue, INVAKEY); // 原子变量初始值为无效的按键值
atomic_set(&gpiokey.releasekey, 0); // 原子变量初始值为无效的按键值
/* 初始化等待队列头 */
init_waitqueue_head(&gpiokey.r_wait); //
/* 获取设备数中的属性数据 */
/* 1. 获取设备节点 /key*/
gpiokey.nd = of_find_node_by_path("/key"); // 通过绝对路径查找设备节点
if(gpiokey.nd == NULL){
printk("key node no find!\r\n");
return -EINVAL; /* 无效参数 不知道这个返回值是啥意思,我觉得返回一个负数就可以,这个值是23,不知道有没有处理*/
}
/* 2. 获取设备树中的gpio属性 得到key所使用的gpio编号 */
/* 可能有多个按键 这里统一处理 兼容性更好 */
for(i = 0; i< KEY_NUM; i++){
gpiokey.irqkeydesc[i].gpio = of_get_named_gpio(gpiokey.nd, "key-gpio", i);
if(gpiokey.irqkeydesc[i].gpio < 0 ){
printk("can't get key-gpio%d\r\n",i);
return -EINVAL; /* 无效参数 不知道这个返回值是啥意思,我觉得返回一个负数就可以,这个值是23,不知道有没有处理*/
}
printk("key-gpio%d num = %d \r\n", i,gpiokey.irqkeydesc[i].gpio); // 打印获取的key-gpio属性值
}
/* 初始化key所使用的IO 并且设置为中断模式 */
for(i = 0; i< KEY_NUM; i++) {
memset(gpiokey.irqkeydesc[i].name, 0, sizeof(gpiokey.irqkeydesc[i].name)); // 缓冲区清零
sprintf(gpiokey.irqkeydesc[i].name, "key%d", i); // 组合名字
gpio_request(gpiokey.irqkeydesc[i].gpio, gpiokey.irqkeydesc[i].name); // 申请IO
gpio_direction_input(gpiokey.irqkeydesc[i].gpio); // 设置为输入模式
gpiokey.irqkeydesc[i].irqnum = irq_of_parse_and_map(gpiokey.nd, i); // 获取中断号
#if 0
imx6uirq.irqkeydesc[i].irqnum = gpio_to_irq(imx6uirq.irqkeydesc[i].gpio);
#endif
/* 打印按键信息 */
printk("key%d:gpio=%d, irqnum=%d\r\n", i, gpiokey.irqkeydesc[i].gpio, gpiokey.irqkeydesc[i].irqnum);
}
/* 申请中断 */
gpiokey.irqkeydesc[0].handler = key0_handler; // 中断处理函数
gpiokey.irqkeydesc[0].value = KEY0VALUE; // 按键值
for(i = 0 ; i < KEY_NUM; i++){
ret = request_irq(gpiokey.irqkeydesc[i].irqnum, gpiokey.irqkeydesc[i].handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, gpiokey.irqkeydesc[i].name, &gpiokey);
if(ret < 0){
printk("irq %d request failed!\r\n", gpiokey.irqkeydesc[i].irqnum); // 中断号申请失败
return -EFAULT;
}
}
/* 创建定时器 */
init_timer(&gpiokey.timer);
gpiokey.timer.function = timer_function; // 定时器中断函数
/* 注册字符设备驱动 */
/* 1. 创建设备号 */
if(gpiokey.major){ // 定义了设备号
gpiokey.devid = MKDEV(gpiokey.major, 0 ); // 根据主设备号和次设备号合成设备号
register_chrdev_region(gpiokey.devid, GPIOKEY_CNT, GPIOKEY_NAME); // 注册设备号
}else{ // 没有定义设备号 动态生成
alloc_chrdev_region(&gpiokey.devid,0,GPIOKEY_CNT, GPIOKEY_NAME ); // 申请设备号
gpiokey.major = MAJOR(gpiokey.devid); // 获取主设备号
gpiokey.minor = MINOR(gpiokey.devid); // 获取次设备号
}
printk("gpiokey major = %d,minor = %d\r\n",gpiokey.major, gpiokey.minor); // 打印主设备号和次设备号
/* 2. 初始化 cdev */
gpiokey.cdev.owner = THIS_MODULE;
cdev_init(&gpiokey.cdev, &gpiokey_fops); // 初始化cdev
/* 3. 添加cdev */
cdev_add(&gpiokey.cdev, gpiokey.devid, GPIOKEY_CNT ); // 向linux系统添加cdev
/* 自动创建设备节点文件 */
/* 4. 创建类 */
gpiokey.class = class_create(THIS_MODULE, GPIOKEY_NAME); // 创建类
if(IS_ERR(gpiokey.class)){
return PTR_ERR(gpiokey.class);
}
/* 创建设备 */
gpiokey.device = device_create(gpiokey.class, NULL, gpiokey.devid, NULL, GPIOKEY_NAME);
if(IS_ERR(gpiokey.device)){
return PTR_ERR(gpiokey.device);
}
return 0;
}
/* 驱动出口函数 */
static void __exit mkey_exit(void)
{
unsigned char i = 0;
/* 释放IO */
for(i = 0; i < KEY_NUM; i++){
gpio_free(gpiokey.irqkeydesc[i].gpio);
}
/* 删除定时器 */
del_timer_sync(&gpiokey.timer);
/* 释放中断 */
for(i = 0; i < KEY_NUM; i++){
free_irq(gpiokey.irqkeydesc[i].irqnum, &gpiokey);
}
/* 注销字符设备驱动 */
cdev_del(&gpiokey.cdev); /* 删除 cdev */
unregister_chrdev_region(gpiokey.devid, GPIOKEY_CNT ); /* 注销设备号 */
device_destroy(gpiokey.class, gpiokey.devid); /* 注销设备 */
class_destroy(gpiokey.class); /* 注销类 */
printk("key drive unregsister ok !\r\n");
}
/* 加载驱动入口和出口函数 */
module_init(mkey_init);
module_exit(mkey_exit);
/* LICENSE 和 AUTHOR 信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("luatao");
2.应用程序
noblockApp.c
#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>
/**
* file name:noblockApp
* date: 2021-08-14 10:35
* version:1.0
* author:luatao
* describe:非阻塞测试APP
* 执行命令:./noblockApp 读取按键值
*/
/* 主程序 */
int main(int argc, char *argv[])
{
char *filename; // 可执行文件名
int fd,ret; // fd: 文件句柄 ret:函数操作返回值
unsigned char keyvalue; // 按键值
struct pollfd fds; // 结构体
/* 先判断输入的参数 */
if(argc != 2){ // 本身文件名带1个 执行文件1个
printf("parameter error!\r\n");
return -1;
}
/* 分析参数 ,提取有用的信息 */
filename = argv[1]; // 可执行文件名
/* 打开key文件 */
fd = open(filename, O_RDWR | O_NONBLOCK); // 可读可写 非阻塞访问
if(fd < 0){
printf("can't open file:%s\r\n",filename);
return -1;
}
/* 构造结构体 */
fds.fd = fd;
fds.events = POLLIN; // 请求的事件 有数据可以读取
/* 循环读取按键值 */
while(1){
ret = poll(&fds, 1, 500); /* 要检测的文件描述符数量为1 超时时间500ms*/
if(ret){ // 数据有效 0超时 -1 发生错误
ret = read(fd, &keyvalue, sizeof(keyvalue));
if(ret >= 0 ){ /* 数据有效 */
if(keyvalue) /* 读取到数据 */
printf("key0 press, value = %#X\r\n", keyvalue); // 打印出按键值
}else{ // 数据无效
}
}else if(ret == 0){ // 超时
// 自行处理
}else { // 错误
//自行处理
}
}
/* 关闭文件 */
ret = close(fd);
if(ret < 0){
printf("can't close file %s \r\n", filename);
return -1;
}
return 0;
}
3.测试
加载驱动:
运行:
查看进程:
杀死进程:
卸载驱动: