是做网站好还是做游戏好/网络黄页平台网址有哪些
本文主要参考韦东山老师视频及文档
-
Linux中断处理流程
-
中断系统中相关数据结构
每一个irq_desc数组项中都有一个函数:handle_irq,还有一个action链表。要理解它们,需要先看中断结构图:
外部设备1、外部设备n共享一个GPIO中断B,多个GPIO中断汇聚到GIC(通用中断控制器)的A号中断,GIC再去中断CPU。那么软件处理时就是反过来,先读取GIC获得中断号A,再细分出GPIO中断B,最后判断是哪一个外部芯片发生了中断。
所以,中断的处理函数来源有三:
① GIC的处理函数:
假设
irq_desc[A].handle_irq
是XXX_gpio_irq_handler
,这个函数需要读取芯片的GPIO控制器,细分发生的是哪一个GPIO中断(假设是B),再去调用irq_desc[B]. handle_irq
。注意:
irq_desc[A].handle_irq
细分出中断后B,调用对应的irq_desc[B].handle_irq
。显然中断A是CPU接收到的顶层中断,GIC中断CPU时,CPU读取GIC状态得到中断A。
② 模块的中断处理函数:
比如对于GPIO模块向GIC发出的中断B,它的处理函数是
irq_desc[B].handle_irq
。BSP开发人员会设置对应的处理函数,一般是
handle_level_irq
或handle_edge_irq
,是用来处理电平触发的中断、边沿触发的中断。注意:导致GPIO中断B发生的原因很多,可能是外部设备1,可能是外部设备n,可能只是某一个设备,也可能是多个设备。所以
irq_desc[B].handle_irq
会调用某个链表里的函数,这些函数由外部设备提供。这些函数自行判断该中断是否自己产生,若是则处理。③ 外部设备提供的处理函数:
这里说的“外部设备”可能是芯片,也可能总是简单的按键。它们的处理函数由自己驱动程序提供,这是最熟悉这个设备的“人”:它知道如何判断设备是否发生了中断,如何处理中断。
对于共享中断,比如GPIO中断B,它的中断来源可能有多个,每个中断源对应一个中断处理函数。所以
irq_desc[B]
中应该有一个链表,存放着多个中断源的处理函数。一旦程序确定发生了GPIO中断B,那么就会从链表里把那些函数取出来一一执行。这个链表就是action链表。-
struct irq_desc
结构体/*** struct irq_desc - interrupt descriptor* @irq_data: per irq and chip data passed down to chip functions* @preflow_handler: handler called before the flow handler (currently used by sparc)* @action: the irq action chain* @name: flow handler name for /proc/interrupts output*/ struct irq_desc {struct irq_data irq_data;irq_flow_handler_t handle_irq;struct irqaction *action; /* IRQ action list */int threads_handled_last;const char *name;...... };
-
struct irqaction
结构体struct irqaction {irq_handler_t handler;void *dev_id;void __percpu *percpu_dev_id;struct irqaction *next;irq_handler_t thread_fn;struct task_struct *thread;unsigned int irq;unsigned int flags;unsigned long thread_flags;unsigned long thread_mask;const char *name;struct proc_dir_entry *dir; };
当调用
request_irq
、request_threaded_irq
注册中断处理函数时,内核就会构造一个irqaction
结构体。在里面保存name
、dev_id
等,最重要的是handler
、thread_fn
、thread
。
handler
是中断处理的上半部函数,用来处理紧急的事情。
thread_fn
对应一个内核线程thread
,当handler
执行完毕,Linux内核会唤醒对应的内核线程。在内核线程里,会调用thread_fn
函数。 可以提供
handler
而不提供thread_fn
,就退化为一般的request_irq
函数。 可以不提供
handler
只提供thread_fn
,完全由内核线程来处理中断。 也可以既提供
handler
也提供thread_fn
,这就是中断上半部、下半部。在
reqeust_irq
时可以传入dev_id
,其作用有以下两点:① 中断处理函数执行时,可以使用
dev_id
② 卸载中断时要传入
dev_id
,这样才能在action
链表中根据dev_id
找到对应项所以在共享中断中必须提供
dev_id
,非共享中断可以不提供。 -
struct irq_data
结构体struct irq_data {u32 mask;unsigned int irq;unsigned long hwirq;unsigned int node;unsigned int state_use_accessors;struct irq_chip *chip;struct irq_domain *domain; #ifdef CONFIG_IRQ_DOMAIN_HIERARCHYstruct irq_data *parent_data; #endifvoid *handler_data;void *chip_data;struct msi_desc *msi_desc;cpumask_var_t affinity; };
它就是个中转站,里面有
irq_chip
指针irq_domain
指针,都是指向别的结构体。
irq
、hwirq
,irq
是软件中断号,hwirq
是硬件中断号。上面的例子,在GPIO中断B是软件中断号,可以找到irq_desc[B]
这个数组项;GPIO里的第x号中断,这就是hwirq
。 谁来建立
irq
、hwirq
之间的联系呢?由irq_domain
来建立。irq_domain
会把本地的hwirq
映射为全局的irq
,什么意思?比如GPIO控制器里有第1号中断,UART模块里也有第1号中断,这两个“第1号中断”是不一样的,它们属于不同的“域”──irq_domain。 -
struct irq_chip
结构体struct irq_chip {void (*irq_enable)(struct irq_data *data);void (*irq_disable)(struct irq_data *data);void (*irq_ack)(struct irq_data *data);void (*irq_mask)(struct irq_data *data);void (*irq_mask_ack)(struct irq_data *data);...... };
这个结构体跟“chip”即芯片相关,里面各成员的作用在头文件中也列得很清楚,摘录部分如下:
* @irq_startup: start up the interrupt (defaults to ->enable if NULL) * @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL) * @irq_enable: enable the interrupt (defaults to chip->unmask if NULL) * @irq_disable: disable the interrupt * @irq_ack: start of a new interrupt * @irq_mask: mask an interrupt source * @irq_mask_ack: ack and mask an interrupt source * @irq_unmask: unmask an interrupt source * @irq_eoi: end of interrupt
在
request_irq
后,并不需要手动去使能中断,原因就是系统调用对应的irq_chip
里的函数已使能了中断。 我们提供的中断处理函数中,也不需要执行主芯片相关的清中断操作,也是系统帮我们调用
irq_chip
中的相关函数。 但是对于外部设备相关的清中断操作,还是需要我们自己做的。
就像上面图里的“外部设备1“、“外部设备n”,外设备千变万化,内核里可没有对应的清除中断操作。
-
struct irq_domain
结构体struct irq_domain {const struct irq_domain_ops *ops;irq_hw_number_t hwirq_max;unsigned int revmap_size;unsigned int linear_revmap[]; };
在驱动中会使用
request_irq(irq, handler)
这样的函数来注册中断,irq
是什么?它是软件中断号,它可以从设备树中interrupt-parent = <&www>
、interrupts = <n xxx>
转换得来。 谁把
hwirq
转换为irq
?由www
的相关数据结构,就是www
对应的irq_domain
结构体。irq_domain
结构体中有一个irq_domain_ops
结构体,里面有各种操作函数,主要是:①
xlate
用来解析设备树的中断属性,提取出hwirq
、type
等信息。②
map
把hwirq
转换为irq
。 -
struct irq_domain_ops
结构体struct irq_domain_ops {int (*match)(struct irq_domain *d, struct device_node *node);int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);void (*unmap)(struct irq_domain *d, unsigned int virq);int (*xlate)(struct irq_domain *d, struct device_node *node,const u32 *intspec, unsigned int intsize,unsigned long *out_hwirq, unsigned int *out_type);...... };
-
-
中断的线程化处理
中断线程化主要是考虑到实时性的要求。如果实时任务不能抢占ISR,那么其执行的延迟(
latency
)时间将不确定,这不符合实时性的要求。如果内核在编译的时候配置了CONFIG_PREEMPT
选项,那么将支持线程抢占,借此就可以实现中断线程化,线程化的中断处理部分被称为threaded interrupt handler
。
__do_softirq()
执行次数/时间超过限制后触发ksoftirqd
只是中断线程化的一种情况,更通用的是使用request_threaded_irq()
,只要没有在flag参数中指定"IRQF_NO_THREAD",那么就将按中断线程化的方式处理。 当使用ps命令查看的时候,格式为"irq/%d-%s"就是这些
threaded interrupt handler
,其中"d"为irq号,"s"为名称:/drv_module # ps -ef | grep "\[irq"46 0 0:00 [irq/40-edt-ft54]47 0 0:00 [irq/225-mmc0]48 0 0:00 [irq/50-2190000.]49 0 0:00 [irq/226-mmc1]59 0 0:00 [irq/205-imx_the]83 0 0:00 grep \[irq
这些线程是在
__setup_irq()
中创建的:__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) {.../** Create a handler thread when a thread function is supplied* and the interrupt does not nest into another interrupt* thread.*/if (new->thread_fn && !nested) {struct task_struct *t;static const struct sched_param param = {.sched_priority = MAX_USER_RT_PRIO/2,};t = kthread_create(irq_thread, new, "irq/%d-%s", irq,new->name);...sched_setscheduler_nocheck(t, SCHED_FIFO, ¶m);/** We keep the reference to the task struct even if* the thread dies to avoid that the interrupt code* references an already freed task_struct.*/get_task_struct(t);new->thread = t;/** Tell the thread to set its affinity. This is* important for shared interrupt handlers as we do* not invoke setup_affinity() for the secondary* handlers as everything is already set up. Even for* interrupts marked with IRQF_NO_BALANCE this is* correct as we want the thread to move to the cpu(s)* on which the requesting code placed the interrupt.*/set_bit(IRQTF_AFFINITY, &new->thread_flags);}... }
线程的入口函数存放在
new->thread_fn
中,而这个thread_fn
就是request_threaded_irq()
相较于传统的request_irq()
新增的那个参数。 实际上,
threaded irq
,为每一个中断创建一个内核线程;多个中断的内核线程可以分配到多个CPU
上执行,这提高了效率。 -
以按键进行编程实践
5.1 按键设备驱动文件
在button_drv.c
文件中,-
加载模块时,
btn_hw_drv_probe
函数中调用request_threaded_irq
将按键工作中断上半部处理函数gpio_btn_isr
、button_threaded_isr_fun
赋给struct irq_desc
数组的action
指针; -
gpio_btn_isr
按键中断服务程序(中断上半部)处理完毕,返回IRQ_WAKE_THREAD
,用以后续调用button_threaded_isr_fun
处理比较耗时的事情; -
button_threaded_isr_fun
为内核线程所调用,进一步执行。
-
/*** 文件 : button_drv.c* 作者 : glen * 描述 : button driver文件*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/workqueue.h>#define KEY_BUF_LEN 16
#define NEXT_POS(x) ((x+1) % KEY_BUF_LEN)struct gbtn_irq {int gpio;struct gpio_desc *gpiod;int flag;int irq;int idx;char kval[KEY_BUF_LEN];int r, w;struct fasync_struct *fp;struct timer_list key_timer;struct tasklet_struct tasklet;struct work_struct wq;
};struct button_drv {struct class *class;struct gbtn_irq *gbtn_irq;char *name;int count;int major;
};static struct button_drv *btn_drv;static int is_key_buf_empty(void *arg)
{struct gbtn_irq *p = (struct gbtn_irq *)arg;return (p->r == p->w);
}static int is_key_buf_full(void *arg)
{struct gbtn_irq *p = (struct gbtn_irq *)arg;return (p->r == NEXT_POS(p->w));
}static void put_key(char key, void *arg)
{struct gbtn_irq *p = (struct gbtn_irq *)arg;if (!is_key_buf_full(arg)) {p->kval[p->w] = key;p->w = NEXT_POS(p->w);}
}static char get_key(void *arg)
{char key = 'N';struct gbtn_irq *p = (struct gbtn_irq *)arg;if (!is_key_buf_full(arg)) {key = p->kval[p->r];p->r = NEXT_POS(p->r);}return key;
}/* 等待队列头的静态初始化 */
static DECLARE_WAIT_QUEUE_HEAD(gpio_button_wait);static void key_timer_expire(unsigned long data)
{int val;char key;struct gbtn_irq *ops = (struct gbtn_irq *)data;/* 读取按键的值 */val = gpiod_get_value(ops->gpiod);printk("button%d %d %d\n", ops->idx, ops->gpio, val);key = (ops->gpio << 4) | val;put_key(key, ops);/* 唤醒等待队列 */wake_up_interruptible(&gpio_button_wait);kill_fasync(&ops->fp, SIGIO, POLL_IN);/* enable btn*/enable_irq(ops->irq);
}/* 实现file_operations结构体成员 read 函数 */
ssize_t button_drv_read (struct file *filp, char __user *buf, size_t size, loff_t *pos)
{int minor = iminor(filp->f_inode);struct gbtn_irq *ops = (struct gbtn_irq *)filp->private_data;char kval;size = (size >= 1) ? 1 : 0;if (ops == NULL) {printk("Please register button instance %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);return -EIO;}if (is_key_buf_empty(ops) && (filp->f_flags & O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_button_wait, !is_key_buf_empty(ops));kval = get_key(ops);if (copy_to_user(buf, &kval, size))return -EFAULT;printk("Read button%d value successfully:", minor);return size;
}/* 实现file_operations结构体成员 open 函数 */
int button_drv_open(struct inode *nd, struct file *filp)
{int ret;int minor = iminor(nd);struct gbtn_irq *ops; if (btn_drv == NULL) {printk("Please register button instance %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);return -EIO;}ops = &btn_drv->gbtn_irq[minor];ret = gpiod_direction_input(ops->gpiod);if (ret) printk("Set the button pin as input error %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);else printk("Set the button%d pin as input successfully!\n", minor);filp->private_data = ops;return 0;
}/* 实现file_operations结构体成员 release 函数 */
int button_drv_release (struct inode *nd, struct file *filp)
{struct gbtn_irq *ops = (struct gbtn_irq *)filp->private_data;if (ops == NULL) {printk("Please register button instance %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);return -EIO;}filp->private_data = NULL;return 0;
}/* 实现file_operations结构体成员 poll 函数 */
unsigned int button_drv_poll (struct file *filp, struct poll_table_struct * wait)
{int minor = iminor(filp->f_inode);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);poll_wait(filp, &gpio_button_wait, wait);return (is_key_buf_empty(&btn_drv->gbtn_irq[minor]) ? 0 : POLLIN | POLLRDNORM);
}static int button_drv_fasync(int fd, struct file *filp, int on)
{struct gbtn_irq *ops = (struct gbtn_irq *)filp->private_data;if (fasync_helper(fd, filp, on, &ops->fp) >= 0)return 0;elsereturn -EIO;
}/*** 1. 构造file_operations结构体 */
static struct file_operations button_drv_ops = {.owner = THIS_MODULE,.read = button_drv_read,.open = button_drv_open,.release = button_drv_release,.poll = button_drv_poll,.fasync = button_drv_fasync,
};/* 中断服务函数 */
static irqreturn_t gpio_btn_isr (int irq, void *dev_id)
{struct gbtn_irq *ops = dev_id;// printk("gpio_btn_isr key %d irq happened\n", ops->gpio);tasklet_schedule(&ops->tasklet);mod_timer(&ops->key_timer, jiffies + HZ / 50);schedule_work(&ops->wq);disable_irq_nosync(irq);return IRQ_WAKE_THREAD;
}/* tasklet action function */
static void button_tasklet_func (unsigned long data)
{int val;struct gbtn_irq *ops = (struct gbtn_irq *)data;/* 读取按键的值 */val = gpiod_get_value(ops->gpiod);printk("button_tasklet_func key%d %d %d\n", ops->idx, ops->gpio, val);}static void button_work_func (struct work_struct *work)
{int val;struct gbtn_irq *ops = container_of(work, struct gbtn_irq, wq);/* 读取按键的值 */val = gpiod_get_value(ops->gpiod);printk("button_work_func: the process is %s pid %d\n", current->comm, current->pid);printk("button_work_func key%d %d %d\n", ops->idx, ops->gpio, val);
}static irqreturn_t button_threaded_isr_fun (int irq, void *dev)
{int val;struct gbtn_irq *ops = dev;/* 读取按键的值 */val = gpiod_get_value(ops->gpiod);printk("button_threaded_isr_fun: the process is %s pid %d\n", current->comm, current->pid);printk("button_threaded_isr_fun key%d %d %d\n", ops->idx, ops->gpio, val);return IRQ_HANDLED;
}/* platform_driver结构体的 probe成员函数实现 */
int btn_hw_drv_probe (struct platform_device *pdev)
{int i;int ret;int count;// enum of_gpio_flags flag;struct device_node *node = pdev->dev.of_node;/* 从设备节点获取gpio数量 */count = of_gpio_count(node);if (!count) {printk("%s %s line %d, there isn't any gpio available!\n", __FILE__, __FUNCTION__, __LINE__);return -EIO;}btn_drv = kzalloc(sizeof(struct button_drv), GFP_KERNEL);if (btn_drv == NULL) return -ENOMEM;btn_drv->gbtn_irq = kzalloc(sizeof(struct gbtn_irq) * count, GFP_KERNEL);if (btn_drv->gbtn_irq == NULL)return -ENOMEM;for (i = 0; i < count; i++) {btn_drv->gbtn_irq[i].gpiod = gpiod_get_index_optional(&pdev->dev, NULL, i, GPIOD_ASIS);if (btn_drv->gbtn_irq[i].gpiod == NULL) {printk("%s %s line %d, gpiod_get_index_optional failed!\n", __FILE__, __FUNCTION__, __LINE__);return -EIO;}btn_drv->gbtn_irq[i].irq = gpiod_to_irq(btn_drv->gbtn_irq[i].gpiod);btn_drv->gbtn_irq[i].gpio = desc_to_gpio(btn_drv->gbtn_irq[i].gpiod);setup_timer(&btn_drv->gbtn_irq[i].key_timer, key_timer_expire, &btn_drv->gbtn_irq[i]);btn_drv->gbtn_irq[i].key_timer.expires = ~0;add_timer(&btn_drv->gbtn_irq[i].key_timer);tasklet_init(&btn_drv->gbtn_irq[i].tasklet, button_tasklet_func, &btn_drv->gbtn_irq[i]);INIT_WORK(&btn_drv->gbtn_irq[i].wq, button_work_func);btn_drv->gbtn_irq[i].idx = i;}for (i = 0; i < count; i++) /* 申请irq中断, 将中断服务程序注册到上半部 */// ret = request_irq(btn_drv->gbtn_irq[i].irq, gpio_btn_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, // "gpio_btn", &btn_drv->gbtn_irq[i]);request_threaded_irq(btn_drv->gbtn_irq[i].irq, gpio_btn_isr, button_threaded_isr_fun, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "gpio_btn", &btn_drv->gbtn_irq[i]);/* 注册file_operationss结构体对象 -- button_drv_ops */btn_drv->major = register_chrdev(btn_drv->major, "gbtn", &button_drv_ops);btn_drv->class = class_create(THIS_MODULE, "gbtn");if (IS_ERR(btn_drv->class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(btn_drv->major, "gbtn");return PTR_ERR(btn_drv->class);}for (i = 0; i < count; i++)device_create(btn_drv->class, NULL, MKDEV(btn_drv->major, i), NULL, "gbtn%d", i);btn_drv->count = count;return 0;
}/* platform_driver结构体的 remove成员函数实现 */
int btn_hw_drv_remove(struct platform_device *pdev)
{int i;struct device_node *node = pdev->dev.of_node;int count = of_gpio_count(node);for (i = 0; i < count; i++) {device_destroy(btn_drv->class, MKDEV(btn_drv->major, i));free_irq(btn_drv->gbtn_irq[i].irq, &btn_drv->gbtn_irq[i]);del_timer(&btn_drv->gbtn_irq[i].key_timer);tasklet_kill(&btn_drv->gbtn_irq[i].tasklet);}class_destroy(btn_drv->class);unregister_chrdev(btn_drv->major, "gbtn");kfree(btn_drv);return 0;
}/* 构造用于配置的设备属性 */
static const struct of_device_id gbtns_id[] = {{.compatible = "glen,gbtn"},{ },
};/* 构造(初始化)file_operations结构体 */
static struct platform_driver btn_hw_drv = {.driver = {.name = "gbtn",.of_match_table = gbtns_id,},.probe = btn_hw_drv_probe,.remove = btn_hw_drv_remove,
};/* 初始化 */
static int __init button_drv_init(void)
{int ret;ret = platform_driver_register(&btn_hw_drv);if (ret)pr_err("Unable to initialize button driver\n");elsepr_info("The button driver is registered.\n");return 0;
}
module_init(button_drv_init);static void __exit button_drv_exit(void)
{platform_driver_unregister(&btn_hw_drv);printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}
module_exit(button_drv_exit);/* insert author information for module */
MODULE_AUTHOR("glen");
/* insert license for module */
MODULE_LICENSE("GPL");
probe函数采用了先获取按键节点数量,然后分别读取gpio描述符并通过其获取为gpio和irq号,并申请注册中断服务程序。
3.2 设备树文件(不作更改)
pinctrl_btn0:btn0 {fsl,pins = <MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080 /* KEY0 */ >;};pinctrl_btn1:btn1 {fsl,pins = <MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xF080 /* KEY1 此按键不存在 */>;};/* 在根节点下添加基于pinctrl的gbtns设备节点 */gbtns {compatible = "glen,gbtn";#address-cells = <1>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_btn0 &pinctrl_btn1>;gpio-controller;#gpio-cells = <2>;gpios = <&gpio1 18 GPIO_ACTIVE_LOW /* button0 */&gpio1 3 GPIO_ACTIVE_LOW>; /* button1 */};
-
取消了gpios前缀“xxx-",相应地,在驱动程序用gpiod_get_index_optional函数获取gpio描述符时,第2个形参 ”const char *con_id“ 传递NULL即可;
-
将pinctrl-0、gpios属性值由 “<>,<>;” 改为 “<>;",效果是一样的
3.3 应用程序
应用程序文件button_drv_test.c提供:
- 定义sig_fun信号处理函数并注册信号,以后APP收到SIGIO信号时,这个函数会被自动调用;
- fcntl(fd, F_SETOWN, getpid()); 把APP的PID(进程ID)告诉驱动程序,这个调用不涉及驱动程序,在内核的文件系统层次记录PID;
- oflags = fcntl(fd, F_GETFL); 读取驱动程序文件oflags
- fcntl(fd, F_SETFL, oflags | FASYNC); 设置oflags里面的FASYNC位为1:当FASYNC位发生变化时,会导致驱动程序的fasync被调用
/** 文件名 : button_drv_test.c* 作者 : glen* 描述 : button_drv应用程序*/#include "stdio.h"#include "sys/types.h"#include "sys/stat.h"#include "stdlib.h"#include "string.h"#include "poll.h"#include <signal.h>#include <unistd.h>#include <fcntl.h>int fd;static void sig_fun(int sig){char kval;read(fd, &kval, 1);printf("The glen button value is: %d!\n", kval);}/*** @brief : main函数* @par : argc argv数组元素的个数* argv 参数数组* @retval : 0 成功 其它 失败*/int main(int argc, char *argv[]){int ret;int oflags;char *filename;if (argc != 2) {printf("Error Usage!\r\n");return -1;}signal(SIGIO, sig_fun);filename = argv[1];/* 打开驱动文件 */fd = open(filename, O_RDWR);if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}fcntl(fd, F_SETOWN, getpid());oflags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, oflags | FASYNC);while (1) {sleep(2);printf("Read the glen button in sleepping!\n");}/* 关闭文件 */ret = close(fd);if (ret < 0) {printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;}
3.4 在alientek_linux_alpha开发板实测验证如下
/drv_module # insmod button_drv.ko
The button driver is registered.
/drv_module # ./btn_drv_test /dev/gbtn0
/drv_module # button_tasklet_func key0 18 1
button_threaded_isr_fun: the process is irq/49-gpio_btn pid 83
button_threaded_isr_fun key0 18 1
button_work_func: the process is kworker/0:2 pid 45
button_work_func key0 18 1
button0 18 1
button_tasklet_func key0 18 0
button_threaded_isr_fun: the process is irq/49-gpio_btn pid 83
button_threaded_isr_fun key0 18 0
button_work_func: the process is kworker/0:2 pid 45
button_work_func key0 18 0
button0 18 0
button_tasklet_func key0 18 1
button_threaded_isr_fun: the process is irq/49-gpio_btn pid 83
button_threaded_isr_fun key0 18 1
button_work_func: the process is kworker/0:2 pid 45
button_work_func key0 18 1
button0 18 1
button_tasklet_func key0 18 0
button_threaded_isr_fun: the process is irq/49-gpio_btn pid 83
button_threaded_isr_fun key0 18 0
button_work_func: the process is kworker/0:2 pid 45
button_work_func key0 18 0
button0 18 0