赞
踩
本文主要参考韦东山老师视频及文档
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_HIERARCHY struct irq_data *parent_data; #endif void *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; else return -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"); else pr_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提供:
/* * 文件名 : 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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。