kobox : key_wq.c -v1 如何使用工作队列 workqueue
kobox: key_wq.c - v1
说明:
TQ2440主要驱动因素,四个按键驱动的处理
key_wq.c和key.c类别似,与key.c之间的差异的主要驱动力:
key.c使用计时器,在中断发生100ms调用定时器处理函数来防止按键抖动
key_wq.c使用工作队列。在内核调度共享工作队列,在工作队列中延时100ms然后推断按键状态来防止按键抖动
问题:
仅仅有内核共享工作队列,且不延时的情况下。程序运行才正常:
/* 使用内核共享队列,马上调度。延时放到中断函数中 */
schedule_work(&gpio_key_work[key]);//运行正常
使用其它三种情况。程序都会崩掉:
/* 使用内核共享队列,延时调度 */
// schedule_delayed_work(&gpio_key_work[key], KEY_TIMER_DELAY2);//会OOPS
/* 使用单独队列。延时调度 */
// queue_delayed_work(&key_wq[key], &gpio_key_work[key], KEY_TIMER_DELAY2);//相同崩掉!
/* 使用内核共享队列。马上调度,延时放到中断函数中 */
// queue_work(&key_wq[key], &gpio_key_work[key]);//相同崩掉!
眼下还不清楚原因
源代码例如以下:
- #include "key.h"
- #define S3C_ADDR_BASE 0xF6000000
- #define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
- #define S3C2410_PA_UART (0x50000000)
- #define S3C2410_PA_GPIO (0x56000000)
- #define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */
- #define S3C24XX_PA_UART S3C2410_PA_UART
- #define S3C24XX_VA_UART S3C_VA_UART
- #define S3C24XX_PA_GPIO S3C2410_PA_GPIO
- #define S3C24XX_VA_GPIO ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
- #define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO)
- #define S3C2410_GPBCON S3C2410_GPIOREG(0x10)
- #define S3C2410_GPBDAT S3C2410_GPIOREG(0x14)
- #define S3C2410_GPBUP S3C2410_GPIOREG(0x18)
- #define S3C2410_GPFCON S3C2410_GPIOREG(0x50)
- #define S3C2410_GPFDAT S3C2410_GPIOREG(0x54)
- #define S3C2410_GPFUP S3C2410_GPIOREG(0x58)
- #define S3C2410_EXTINT0 S3C2410_GPIOREG(0x88)
- #define S3C2410_EXTINT1 S3C2410_GPIOREG(0x8C)
- #define S3C2410_EXTINT2 S3C2410_GPIOREG(0x90)
- #define S3C2410_CPUIRQ_OFFSET (16)
- #define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)
- /* main cpu interrupts */
- #define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */
- #define IRQ_EINT1 S3C2410_IRQ(1) /* 17 */
- #define IRQ_EINT2 S3C2410_IRQ(2) /* 18 */
- #define IRQ_EINT4t7 S3C2410_IRQ(4) /* 20 */
- #define IRQ_EINT4 S3C2410_IRQ(36) /* 52 */
- #define IRQF_DISABLED 0x00000020
- #define IRQF_SHARED 0x00000080
- #define IRQF_PROBE_SHARED 0x00000100
- #define __IRQF_TIMER 0x00000200
- #define IRQF_PERCPU 0x00000400
- #define IRQF_NOBALANCING 0x00000800
- #define IRQF_IRQPOLL 0x00001000
- #define IRQF_ONESHOT 0x00002000
- #define IRQF_NO_SUSPEND 0x00004000
- #define IRQF_FORCE_RESUME 0x00008000
- #define IRQF_NO_THREAD 0x00010000
- #define IRQF_EARLY_RESUME 0x00020000
- typedef struct gpioRes
- {
- int irqNum; /* 中断号 */
- unsigned int ctrlReg; /* 控制寄存器,用于设置复用为GPIO */
- unsigned int ctrlBit; /* 控制寄存器的哪一位,用于复用为GPIO */
- unsigned int trigReg; /* 中断方式寄存器。设置中断的触发方式 */
- unsigned int trigBit; /* 中断方式寄存器哪一位,设置中断的触发方式 */
- unsigned int irqFlag; /* 共享还是不共享,注冊中断的flag */
- char irqName[32]; /* 中断名称 */
- unsigned int gpfPin; /* GPF的第几个pin */
- char Reserved[10]; /* 保留 */
- }gpioRes;
- #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
- unsigned int pressCnt[4] = {0, 0, 0, 0};
- /* 定义一个work_queue数组 */
- struct work_struct gpio_key_work[4];
- static struct workqueue_struct *key_wq[4] = {NULL, NULL, NULL, NULL};
- static void gpio_key_wq0_handler(struct work_struct *work);
- static void gpio_key_wq1_handler(struct work_struct *work);
- static void gpio_key_wq2_handler(struct work_struct *work);
- static void gpio_key_wq3_handler(struct work_struct *work);
- /* 定义一个函数指针数组,分别处理上面四个work_queue */
- int (*gpio_key_wq_handler[4])(struct work_struct *work) =
- {
- gpio_key_wq0_handler,
- gpio_key_wq1_handler,
- gpio_key_wq2_handler,
- gpio_key_wq3_handler,
- };
- static int kobox_key_open(struct inode *inode, struct file *file)
- {
- return 0;
- }
- static int kobox_key_release(struct inode *inode, struct file *file)
- {
- return 0;
- }
- static long kobox_key_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
- {
- return 0;
- }
- static int kobox_key_read(struct file *file, char __user *buff, size_t count, loff_t *pos)
- {
- printk("Enter [%s][%d]\n", __FUNCTION__,__LINE__);
- copy_to_user(buff, &pressCnt[0], sizeof(pressCnt));
- return 0;
- }
- /*
- GPF相关寄存器:
- GPFCON 0x56000050 R/W Configures the pins of port F 0x0
- GPFDAT 0x56000054 R/W The data register for port F Undef.
- GPFUP 0x56000058 R/W Pull-up disable register for port F 0x000
- K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input 01 = Output 10 = EINT[1] 11 = Reserved
- K2: GPF4 -EINT4: GPF4 [9:8] 00 = Input 01 = Output 10 = EINT[4] 11 = Reserved
- K3: GPF2 -EINT2: GPF2 [5:4] 00 = Input 01 = Output 10 = EINT2] 11 = Reserved
- K4: GPF0 -EINT0: GPF0 [1:0] 00 = Input 01 = Output 10 = EINT[0] 11 = Reserved
- */
- gpioRes key_gpio_res[4] =
- {
- {IRQ_EINT1, S3C2410_GPFCON, 2, S3C2410_EXTINT0, 5, NULL, "key1", 1}, /* key1 */
- {IRQ_EINT4, S3C2410_GPFCON, 8, S3C2410_EXTINT0, 17, IRQF_SHARED, "key2", 4}, /* key2 */
- {IRQ_EINT2, S3C2410_GPFCON, 4, S3C2410_EXTINT0, 9, NULL, "key3", 2}, /* key3 */
- {IRQ_EINT0, S3C2410_GPFCON, 0, S3C2410_EXTINT0, 1, NULL, "key4", 0}, /* key4 */
- };
- #define KEY_TIMER_DELAY1 (HZ/50) //按键按下去抖延时20毫秒
- #define KEY_TIMER_DELAY2 (HZ/10) //按键抬起去抖延时100毫秒
- #define KEY_COUNT 4
- static void set_gpio_as_eint(void)
- {
- int i;
- unsigned uiVal = 0;
- for(i=0; i< ARRAY_SIZE(key_gpio_res); i++)
- {
- uiVal = readl(key_gpio_res[i].ctrlReg);
- uiVal &= ~(0x01 << key_gpio_res[i].ctrlBit);
- uiVal |= (0x01 << (key_gpio_res[i].ctrlBit + 1));
- writel(uiVal, key_gpio_res[i].ctrlReg);
- }
- return;
- }
- static void set_gpio_as_gpio(void)
- {
- int i;
- unsigned uiVal = 0;
- for(i=0; i< ARRAY_SIZE(key_gpio_res); i++)
- {
- uiVal = readl(key_gpio_res[i].ctrlReg);
- uiVal &= ~(0x01 << key_gpio_res[i].ctrlBit);
- uiVal &= ~(0x01 << (key_gpio_res[i].ctrlBit + 1));
- writel(uiVal, key_gpio_res[i].ctrlReg);
- }
- return;
- }
- static irqreturn_t kobox_gpio_irq_handle(int irq, void *dev_id)
- {
- int key;
- // disable_irq_nosync(irq);
- printk("irq = %d\n", irq);
- if(dev_id)
- printk("dev_id:%s\n", dev_id);
- switch(irq)
- {
- case IRQ_EINT1:
- key = 0;
- break;
- case IRQ_EINT4:
- key = 1;
- break;
- case IRQ_EINT2:
- key = 2;
- break;
- case IRQ_EINT0:
- key = 3;
- break;
- default:
- printk("invalid irq:%d\n", irq);
- return IRQ_HANDLED;
- }
- /* 去抖:延时100ms后。在buttons_timer中读取按键状态,假设还是按下的。就说明是被正常按下的
- 使用timer是一种方式,后面再採用工作队列、tasklet中的方式来处理 */
- /* 使用内核共享队列,延时调度 */
- // schedule_delayed_work(&gpio_key_work[key], KEY_TIMER_DELAY2);//会OOPS
- /* 使用内核共享队列,马上调度,延时放到中断函数中 */
- schedule_work(&gpio_key_work[key]);//运行正常
- /* 使用单独队列。延时调度 */
- // queue_delayed_work(&key_wq[key], &gpio_key_work[key], KEY_TIMER_DELAY2);//相同崩掉!
- /* 使用内核共享队列,马上调度,延时放到中断函数中 */
- // queue_work(&key_wq[key], &gpio_key_work[key]);//相同崩掉!
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- /*
- GPF相关寄存器:
- GPFCON 0x56000050 R/W Configures the pins of port F 0x0
- GPFDAT 0x56000054 R/W The data register for port F Undef.
- GPFUP 0x56000058 R/W Pull-up disable register for port F 0x000
- K1: GPF1 -EINT1: GPF1 [3:2] 00 = Input 01 = Output 10 = EINT[1] 11 = Reserved
- K2: GPF4 -EINT4: GPF4 [9:8] 00 = Input 01 = Output 10 = EINT[4] 11 = Reserved
- K3: GPF2 -EINT2: GPF2 [5:4] 00 = Input 01 = Output 10 = EINT2] 11 = Reserved
- K4: GPF0 -EINT0: GPF0 [1:0] 00 = Input 01 = Output 10 = EINT[0] 11 = Reserved
- */
- /* 该函数返回0表示按键被按下,返回非0表示没有再被按下,觉得这是电平毛刺导致的。是噪声信号
- 所以。该函数返回0,表示有按键被按下,返回非0表示是抖动 */
- static int get_gpio_portf_value(unsigned int pin)
- {
- int ret;
- unsigned int uiVal = 0;
- printk("I AM @ [%s][%d], pin:%d\n", __FUNCTION__,__LINE__, pin);
- uiVal = readl(S3C2410_GPFDAT);
- ret = (0x1 << pin) & uiVal;
- printk("I AM @ [%s][%d], ret:%d\n", __FUNCTION__,__LINE__, ret);
- return ret;
- }
- static void gpio_key_wq0_handler(struct work_struct *work)
- {
- int ret;
- unsigned int pin;
- /* 中断后100ms才会导致,运行该函数 */
- printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__);
- msleep(100);
- pin = key_gpio_res[0].gpfPin;
- /* 将引脚由EINTX设置会GPIO */
- set_gpio_as_gpio();
- /* 读取相应引脚GPIO的值。返回0表示按键真正被按下,返回1表示抖动 */
- ret = get_gpio_portf_value(pin);
- if(0 == ret)
- {
- pressCnt[0]++;
- printk("key0 pressed: pressCnt[0]:%d\n", pressCnt[0]);
- }
- /* 将引脚设置回EINTX */
- set_gpio_as_eint();
- return;
- }
- static void gpio_key_wq1_handler(struct work_struct *work)
- {
- int ret;
- unsigned int pin;
- /* 中断后100ms才会导致。运行该函数 */
- printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__);
- msleep(100);
- pin = key_gpio_res[1].gpfPin;
- /* 将引脚由EINTX设置会GPIO */
- set_gpio_as_gpio();
- /* 读取相应引脚GPIO的值,返回0表示按键真正被按下,返回1表示抖动 */
- ret = get_gpio_portf_value(pin);
- if(0 == ret)
- {
- pressCnt[1]++;
- printk("key1 pressed: pressCnt[1]:%d\n", pressCnt[1]);
- }
- /* 将引脚设置回EINTX */
- set_gpio_as_eint();
- return;
- }
- static void gpio_key_wq2_handler(struct work_struct *work)
- {
- int ret;
- unsigned int pin;
- /* 中断后100ms才会导致,运行该函数 */
- printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__);
- msleep(100);
- pin = key_gpio_res[2].gpfPin;
- /* 将引脚由EINTX设置会GPIO */
- set_gpio_as_gpio();
- /* 读取相应引脚GPIO的值,返回0表示按键真正被按下。返回1表示抖动 */
- ret = get_gpio_portf_value(pin);
- if(0 == ret)
- {
- pressCnt[2]++;
- printk("key2 pressed: pressCnt[2]:%d\n", pressCnt[2]);
- }
- /* 将引脚设置回EINTX */
- set_gpio_as_eint();
- return;
- }
- static void gpio_key_wq3_handler(struct work_struct *work)
- {
- int ret;
- unsigned int pin;
- /* 中断后100ms才会导致,运行该函数 */
- printk("i am at [%s][%d]\n", __FUNCTION__, __LINE__);
- msleep(100);
- pin = key_gpio_res[3].gpfPin;
- /* 将引脚由EINTX设置会GPIO */
- set_gpio_as_gpio();
- /* 读取相应引脚GPIO的值。返回0表示按键真正被按下。返回1表示抖动 */
- ret = get_gpio_portf_value(pin);
- if(0 == ret)
- {
- pressCnt[3]++;
- printk("key3 pressed: pressCnt[3]:%d\n", pressCnt[3]);
- }
- /* 将引脚设置回EINTX */
- set_gpio_as_eint();
- return;
- }
- static int request_irq_for_gpio(void)
- {
- int i;
- int ret;
- unsigned uiVal;
- int nouse;
- for(i=0; i<ARRAY_SIZE(key_gpio_res);i++)
- {
- /* 设置中断触发方式:下降沿有效,触发中断。以便依据GPIO的值来推断是否仍在按下 */
- uiVal = readl(key_gpio_res[i].trigReg);
- uiVal |= (0x1 << (key_gpio_res[i].trigBit));
- uiVal &= ~(0x1 << (key_gpio_res[i].trigBit + 1));
- writel(uiVal, key_gpio_res[i].trigReg);
- /* 注冊中断 */
- ret = request_irq(key_gpio_res[i].irqNum,
- kobox_gpio_irq_handle,
- key_gpio_res[i].irqFlag,
- key_gpio_res[i].irqName,
- (void *)key_gpio_res[i].irqName);
- if(ret)
- printk("[func:%s][line:%d] request_irq failed, ret:%d!\n", __FUNCTION__,__LINE__,ret);
- else
- printk("[func:%s][line:%d] request_irq ok, irq:%d!\n", __FUNCTION__,__LINE__, key_gpio_res[i].irqNum);
- key_wq[i] = create_workqueue(key_gpio_res[i].irqName);
- if (!key_wq[i] ) {
- printk("create_workqueue key_wq[%d] failed!\n", i);
- }
- /* 初始化工作队列。用于响应中断后半部,中断响应后100ms调度以便去抖动 */
- INIT_WORK(&gpio_key_work[i], gpio_key_wq_handler[i]);
- }
- return 0;
- }
- struct file_operations kobox_key_operations = {
- .owner = THIS_MODULE,
- .open = kobox_key_open,
- .read = kobox_key_read,
- .release = kobox_key_release,
- .unlocked_ioctl = kobox_key_ioctl,
- };
- //GPB0
- int major;
- int minor;
- struct cdev cdev;
- struct class *kobox_key_class;
- struct device *pstdev = NULL;
- #define GPIO_KEY_NAME "kobox_key"
- int __init key_drv_init(void)
- {
- int error;
- dev_t dev;
- printk("#####enter key_drv_init!\n");
- major = register_chrdev(0, GPIO_KEY_NAME, &kobox_key_operations);
- if (major < 0)
- {
- printk(" can't register major number\n");
- return major;
- }
- /* create class */
- kobox_key_class = class_create(THIS_MODULE, GPIO_KEY_NAME);
- if(IS_ERR(kobox_key_class))
- {
- printk("class_create failed!\n");
- goto fail;
- }
- /* create /dev/kobox_gpio */
- pstdev = device_create(kobox_key_class, NULL, MKDEV(major, 0), NULL, GPIO_KEY_NAME);
- if(!pstdev)
- {
- printk("device_create failed!\n");
- goto fail1;
- }
- /* set gpf0/1/2/4 as extern interrupt pins */
- set_gpio_as_eint();
- request_irq_for_gpio();
- printk("#####key_drv_init ok!\n");
- return 0;
- fail1:
- class_destroy(kobox_key_class);
- fail:
- unregister_chrdev(major, GPIO_KEY_NAME);
- return -1;
- }
- void __exit key_drv_exit(void)
- {
- printk("exit gpio drv!\n");
- device_destroy(kobox_key_class, MKDEV(major, 0));
- class_destroy(kobox_key_class);
- unregister_chrdev(major, GPIO_KEY_NAME);
- return;
- }
- module_init(key_drv_init);
- module_exit(key_drv_exit);
- MODULE_LICENSE("GPL");
kobox : key_wq.c -v1 如何使用工作队列 workqueue的更多相关文章
- 工作队列(workqueue) create_workqueue/schedule_work/queue_work
--- 项目需要,在驱动模块里用内核计时器timer_list实现了一个状态机.郁闷的是,运行时总报错"Scheduling while atomic",网上搜了一下:" ...
- linux工作队列 - workqueue总览【转】
转自:https://blog.csdn.net/cc289123557/article/details/52551176 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载 ...
- kobox: key_proc.c -v1 怎样使用proc文件系统调试驱动
使用proc文件系统能够非常方便调试驱动.查看驱动中的一些数据 平台:TQ2440 系统版本号: root@ubuntu:/mnt/shared/kobox# uname -a Linux ubunt ...
- 工作队列workqueue应用
工作队列是另一种将工作推后执行的形式,它可以把工作交给一个内核线程去执行,这个下半部是在进程上下文中执行的,因此,它可以重新调度还有睡眠. 区分使用软中断/tasklet还是工作队列比较简单,如果推后 ...
- java线程池-工作队列workQueue
线程池之工作队列 ArrayBlockingQueue 采用数组来实现,并采用可重入锁ReentrantLock来做并发控制,无论是添加还是读取,都先要获得锁才能进行操作 可看出进行读写操作都使用了R ...
- [内核]Linux workqueue
转自:http://blog.chinaunix.net/uid-24148050-id-296982.html 一.workqueue简介workqueue与tasklet类似,都是允许内核代码请求 ...
- jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)
jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...
- rabbitmq 重复ACK导致消息丢失
rabbitmq 重复确认导致消息丢失 背景 rabbitmq 在应用场景中,大多采用工作队列 work-queue的模式. 在一个常见的工作队列模式中,消费者 worker 将不断的轮询从队列中拉取 ...
- 异步框架asyn4j的原理
启动时调用init方法 public void init(){ if (!run){ run = true; //工作队列 workQueue = newPriorityBlockingQueue(m ...
随机推荐
- 关于wxFileSystemWatcher输出文件名的解决方法
本文针对的wxWidgets版本: 2.9.4, 2.9.5,其他版本未作测试. 如果要使用 wxFileSystemWatcher 并且让其产生的wxFileSystemWatcherEvent 事 ...
- 详解js和jquery里的this关键字
详解js和jquery里的this关键字 js中的this 我们要记住:this永远指向函数运行时所在的对象!而不是函数被创建时所在的对象.this对象是在运行时基于函数的执行环境绑定的,在全局环境中 ...
- 二维码闪电登录流程详解,附demo(1/2)
二维码,最早发明于日本,它是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设 ...
- webstorm与phpstorm主题配置
原创. 更换webstorm的主题的,照着网上的教程试了好多次都发现不行,而且我之前有个同学也是这样的问题,找不到相关的colors文件夹,所以在网上教程的基础上对于更改主题做了细微的修改. 1.下载 ...
- Axure基础系列教程
Axure rp 6.5的软件安装.汉化与注册 认识Axure的软件界面 生成网页原型的三种方法 如何关闭IE浏览器在生成原型时候的安全警告 在chrome中使用axure生成原型的问题 站点地图 ...
- Boost::asio io_service 实现分析
io_service的作用 io_servie 实现了一个任务队列,这里的任务就是void(void)的函数.Io_servie最常用的两个接口是post和run,post向任务队列中投递任务,run ...
- nohup命令与&区别,jobs,fg,bg,Ctrl-Z、Ctrl-C、Ctrl-D
&方式: Unix/Linux下一般想让某个程序在后台运行,很多都是使用 & 在程序结尾来让程序自动运行.比如我们要运行mysql在后台: /usr/local/my ...
- 基于Hadoop2.0、YARN技术的大数据高阶应用实战(Hadoop2.0\YARN\Ma
Hadoop的前景 随着云计算.大数据迅速发展,亟需用hadoop解决大数据量高并发访问的瓶颈.谷歌.淘宝.百度.京东等底层都应用hadoop.越来越多的企 业急需引入hadoop技术人才.由于掌握H ...
- SQL server语句练习
相关表: <span style="white-space:pre">create table DEPT ( <span style="white-sp ...
- 通过OpenSSL解码X509证书文件
在Windows平台下.假设要解析一个X509证书文件,最直接的办法是使用微软的CryptoAPI. 可是在非Windows平台下,就仅仅能使用强大的开源跨平台库OpenSSL了.一个X509证书通过 ...