/*
* 文件名 : asyncnoti.c
* 作者 : glen
* 描述 : asyncnoti驱动文件
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IRQ_CNT 1 /* 设备号个数 */
#define IRQ_NAME "asyncnoti" /* 名字 */
#define KEY0_PRESS 0x01 /* KEY0按键按下 */
#define KEY0_RELEASE 0x81 /* KEY0按键释放 */
#define KEY_NUM 1 /* 按键数量 */
/* 中断IO描述结构体
*/
struct irq_key {
int gpio; /* gpio */
unsigned int irq_num; /* 中断号 */
unsigned char val; /* 按键对应的键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int, void *);/* 中断服务函数 */
};
/* asyncnoti设备结构体
*/
struct irq_dev {
dev_t devid; /* 设备ID */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
atomic_t key_val; /* 有效按键值 */
atomic_t release_key; /* 标记是否一次完成的按键 */
struct timer_list timer; /* 定义一个定时器 */
struct irq_key key[KEY_NUM];/* 按键描述数组 */
unsigned char cur_key; /* 当前按键 */
wait_queue_head_t wait_h; /* 读等待队列头 */
struct fasync_struct *async_queue; /* 异步相关结构体 */
};
struct irq_dev irqdev; /* asyncnoti设备 */
/**
* \brief 中断服务函数,开启定时器,延时10ms用于按键消抖
* \param irq 中断号
* dev_id 设备结构
* \retval 中断执行结果
*/
static irqreturn_t key0_handler(int asyncnoti, void *dev_id)
{
unsigned char num;
struct irq_dev *dev = (struct irq_dev *)dev_id;
dev->cur_key = 0;
dev->timer.data = (volatile long)dev_id;
num = dev->cur_key;
if (gpio_get_value(dev->key[num].gpio) == 0) {
atomic_set(&dev->key_val, KEY0_PRESS); /* 按键按下 */
printk("key %d falling edge!\r\n", num);
} else {
atomic_set(&dev->key_val, KEY0_RELEASE); /* 按键松开 */
printk("key %d rising edge!\r\n", num);
}
atomic_set(&dev->release_key, 0); /* 无按键 */
/* 启动定时器 */
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
return IRQ_RETVAL(IRQ_HANDLED);
}
/**
* \brief 定时器服务函数, 用于按键消抖, 定时器到了以后再次读取按键值, 如果
* 按键还是处理于按下状态就表示按有效。
* \param arg 设备结构变量
* \retval 无
*/
void timer_function(unsigned long arg)
{
unsigned char val;
unsigned char num;
struct irq_key *pkey;
struct irq_dev *dev = (struct irq_dev *)arg;
num = dev->cur_key;
pkey = &dev->key[num];
val = gpio_get_value(pkey->gpio); /* 读取IO值 */
if (val == 0) {
if (atomic_read(&dev->key_val) == KEY0_PRESS) {
atomic_set(&dev->key_val, KEY0_PRESS); /* 按键按下 */
atomic_set(&dev->release_key, 1);
printk("key %d low level!\r\n", num);
} else {
atomic_set(&dev->key_val, 0);
}
} else {
if (atomic_read(&dev->key_val) == KEY0_RELEASE) {
atomic_set(&dev->key_val, KEY0_RELEASE); /* 按键松开 */
atomic_set(&dev->release_key, 1);
printk("key %d high level!\r\n", num);
} else {
atomic_set(&dev->key_val, 0);
}
}
if (atomic_read(&dev->release_key)) { /* 一次完整的按键过程 */
if (dev->async_queue) {
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
}
}
/**
* \brief 按键IO初始化
* \param 无
* \retval 无
*/
static int key_io_init(void)
{
unsigned char i = 0;
char name[10];
int ret = 0;
irqdev.nd = of_find_node_by_path("/key");
if (irqdev.nd == NULL) {
printk("Key node is not found!\r\n");
return -EINVAL;
}
for (i = 0; i < KEY_NUM; i++) {
/* 提取GPIO */
irqdev.key[i].gpio = of_get_named_gpio(irqdev.nd, "key-gpio", i);
if (irqdev.key[i].gpio < 0) {
printk("Can't get key %d\r\n", i);
}
}
for (i=0; i<KEY_NUM; i++) {
/* 初始化key所使用的IO,并设置成中断模式 */
memset(irqdev.key[i].name, 0, sizeof(name));
sprintf(irqdev.key[i].name, "KEY%d", i);
gpio_request(irqdev.key[i].gpio, name);
gpio_direction_input(irqdev.key[i].gpio);
irqdev.key[i].irq_num = irq_of_parse_and_map(irqdev.nd, i);
printk("key%d:gpio=%d, irq_num=%d\r\n", i, irqdev.key[i].gpio, irqdev.key[i].irq_num);
}
/* 申请中断 */
irqdev.key[0].handler = key0_handler;
irqdev.key[0].val = KEY0_RELEASE;
for (i=0; i<KEY_NUM; i++) {
ret = request_irq(irqdev.key[i].irq_num, irqdev.key[i].handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
irqdev.key[i].name, &irqdev);
if (ret < 0) {
printk("irq %d request failed!\r\n", irqdev.key[i].irq_num);
return -EFAULT;
}
}
/* 创建定时器 */
init_timer(&irqdev.timer);
irqdev.timer.function = timer_function;
/* 初始化等待队列头 */
init_waitqueue_head(&irqdev.wait_h);
return 0;
}
/**
* \brief 打开设备
* \param inode 传给驱动的inode
* filp 设备文件
* \retval 0 成功 其它 失败
*/
static int irqdev_open(struct inode *inode, struct file *filp)
{
/* 设置私有数据 */
filp->private_data = &irqdev;
return 0;
}
/**
* \brief 从设备读取数据
* \param filp 要打开的设备文件(文件描述符)
* buf 返回给用户空间的数据缓冲区
* cnt 要读取的数据长度
* offt 相对于文件首地址的偏移
* \retval 读取的字节数, 如果为负值, 表示读取失败
*/
static ssize_t irqdev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
unsigned char key_val = 0;
unsigned char release_key = 0;
struct irq_dev *dev = (struct irq_dev *)filp->private_data;
if (filp->f_flags & O_NONBLOCK) { /* 非阻塞访问 */
if (atomic_read(&dev->release_key) == 0) { /* 按键无效 */
return -EAGAIN;
}
} else {
/* 加入等等队列,等待有按键时被唤醒 */
ret = wait_event_interruptible(dev->wait_h, atomic_read(&dev->release_key));
if (ret) {
goto wait_error;
}
}
key_val = atomic_read(&dev->key_val);
release_key = atomic_read(&dev->release_key);
if (release_key) {
if (key_val == KEY0_PRESS || key_val == KEY0_RELEASE) {
ret = copy_to_user(buf, &key_val, sizeof(key_val));
printk("Key val translation complete!\r\n");
}
atomic_set(&dev->release_key, 0); /* 按下标志清零 */
} else {
goto data_error;
}
return 0;
wait_error:
return ret;
data_error:
return -EINVAL;
}
/**
* \brief poll函数,用于处理非阻塞访问
* \param filp 要打开的设备文件(文件描述符)
* wait 等待列表(poll_table)
* \retval 设备或者资源状态
*/
unsigned int irqdev_poll(struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask = 0;
struct irq_dev *dev = (struct irq_dev *)filp->private_data;
poll_wait(filp, &dev->wait_h, wait);
if (atomic_read(&dev->release_key)) { /* 按键按下 */
mask = POLLIN | POLLRDNORM; /* 返回PLLIN */
}
return mask;
}
/**
* \brief fasync函数,用于处理异步通知
* \param fd 文件描述符
* filp 要打开的劥文件(文件描述符)
* on 模式
* \retval 负数表示函数执行失败
*/
static int irqdev_fasync(int fd, struct file *filp, int on)
{
struct irq_dev *dev = (struct irq_dev *)filp->private_data;
return fasync_helper(fd, filp, on, &dev->async_queue);
}
/**
* \brief release函数,应用程序调用close关闭驱动文件的时候会执行
* \param inode inode节点
* filp 要打开的设备文件(文件描述符)
* \retval 负数表示函数执行失败
*/
static int irqdev_release(struct inode *inode, struct file *filp)
{
return irqdev_fasync(-1, filp, 0);
}
/* 设备操作函数
*/
static struct file_operations irqdev_fops = {
.owner = THIS_MODULE,
.open = irqdev_open,
.read = irqdev_read,
.poll = irqdev_poll,
.fasync = irqdev_fasync,
.release = irqdev_release,
};
/**
* \brief 驱动入口函数
* \param 无
* \retval 无
*/
static int __init irqdev_init(void)
{
/* 创建设备号 */
if (irqdev.major) {
/* 主备号已分配 */
/* 根据设备号向内核获取设备ID */
irqdev.devid = MKDEV(irqdev.major, 0);
/* 向内核注册设备 */
register_chrdev_region(irqdev.devid, IRQ_CNT, IRQ_NAME);
} else {
/* 向内核获取设备ID,主设备号,名称 */
alloc_chrdev_region(&irqdev.devid, 0, IRQ_CNT, IRQ_NAME);
irqdev.major = MAJOR(irqdev.devid);
irqdev.minor = MINOR(irqdev.devid);
}
/* 注册字符设备 */
/* 初始化字符设备 */
cdev_init(&irqdev.cdev, &irqdev_fops);
/* 创建字符设备 */
cdev_add(&irqdev.cdev, irqdev.devid, IRQ_CNT);
/* 创建类 */
irqdev.class = class_create(THIS_MODULE, IRQ_NAME);
if (IS_ERR(irqdev.class)) {
return PTR_ERR(irqdev.class);
}
/* 创建设备 */
irqdev.device = device_create(irqdev.class, NULL, irqdev.devid, NULL, IRQ_NAME);
if (IS_ERR(irqdev.device)) {
return PTR_ERR(irqdev.device);
}
/* 初始化按键 */
atomic_set(&irqdev.key_val, KEY0_RELEASE);
atomic_set(&irqdev.release_key, 0);
key_io_init();
return 0;
}
/**
* \brief 驱动入口函数
* \param 无
* \retval 无
*/
static void __exit irqdev_exit(void)
{
unsigned int i = 0;
/* 删除定时器 */
del_timer_sync(&irqdev.timer);
/* 释放中断 */
for (i=0; i<KEY_NUM; i++) {
free_irq(irqdev.key[i].irq_num, &irqdev);
}
cdev_del(&irqdev.cdev);
unregister_chrdev_region(irqdev.devid, IRQ_CNT);
device_destroy(irqdev.class, irqdev.devid);
class_destroy(irqdev.class);
}
/* 设备注册入口, 展开后
* static initcall_t \
* __initcall_irqdev_init6 \
* __used \
* __attribute__((__section__(".initcall6.init"))) \
* = irqdev_init;
*/
module_init(irqdev_init);
/* 设备注册出口, 展开后
* static exitcall_t \
* __exitcall_irqdev_exit \
* __exit_call \
* = irqdev_exit;
*/
module_exit(irqdev_exit);
/* 模块的许可证声明, 展开后
* static const char __UNIQUE_ID_license__COUNTER__[] \
* __used __attribute__((section(".modinfo"), unused, aligned(1))) \
* = "license=GPL";
*/
MODULE_LICENSE("GPL");
/* 模块的作者声明, 展开后
* static const char __UNIQUE_ID_author__COUNTER__[] \
* __used __attribute__((section(".modinfo"), unused, aligned(1))) \
* = "author=glen_cao"
*/
MODULE_AUTHOR("glen");
/*
* 文件名 : asyncnoti_test.c
* 作者 : glen
* 描述 : asyncnoti测试程序
*/
#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"
#include "signal.h"
static int fd = 0; /* 文件描述符 */
/**
* \brief SIGIO信号处理函数
* \param signum 信号值
* \retval 无
*/
static void sigio_signal_fun(int signum)
{
int err = 0;
unsigned int keyval = 0;
err = read(fd, &keyval, sizeof(keyval));
if (err < 0) {
printf("read key filure!\r\n");
} else {
printf("sigio signal! key value=%#x\r\n", keyval);
}
}
/**
* @brief : main函数
* @par : argc argv数组元素的个数
* argv 参数数组
* @retval : 0 成功 其它 失败
*/
int main(int argc, char *argv[])
{
int ret = 0;
int flags = 0;
char *filename;
if (argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开驱动文件 */
fd = open(filename, O_RDWR); /* 阻塞访问 */
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
/* 设置信号SIGIO的处理函数 */
signal(SIGIO, sigio_signal_fun);
fcntl(fd, F_SETOWN, getpid()); /* 将当前进程的进程号告诉内核 */
flags = fcntl(fd, F_GETFD); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 设置进程启用异步通知功能 */
while(1) {
sleep(2);
}
/* 关闭文件 */
ret = close(fd);
if (ret < 0) {
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
拷贝到NFS网络文件系统的/lib/modules/4.1.15目录下
glen@ubuntu:~/linux/imx6ull/linux/driver/14_asyncnoti$ sudo cp asyncnoti.ko asyncnoti_test ~/linux/nfs/rootfs/lib/modules/4.1.15 -f
/lib/modules/4.1.15 # insmod asyncnoti.ko
key0:gpio=18, irq_num=49
/lib/modules/4.1.15 # random: nonblocking pool is initialized
/lib/modules/4.1.15 # ./asyncnoti_test /dev/asyncnoti &
/lib/modules/4.1.15 # key 0 falling edge!
key 0 low level!
Key val translation complete!
sigio signal! key value=0x1
key 0 rising edge!
key 0 high level!
Key val translation complete!
sigio signal! key value=0x81
key 0 falling edge!
key 0 low level!
Key val translation complete!
sigio signal! key value=0x1
key 0 rising edge!
key 0 falling edge!
key 0 low level!
Key val translation complete!
sigio signal! key value=0x1
key 0 rising edge!
key 0 high level!
Key val translation complete!
sigio signal! key value=0x81
key 0 falling edge!
key 0 low level!
Key val translation complete!
sigio signal! key value=0x1
key 0 rising edge!
key 0 high level!
Key val translation complete!
sigio signal! key value=0x81