kobox : key_wq.c -v1

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");
时间: 2024-10-11 13:37:41

kobox : key_wq.c -v1的相关文章

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然后推断按键状态来防止按键抖动 问题: 仅仅有内核共享工作队列,且不延时的情况下.程序运行才正常: /* 使用内核共享队列,马上调度.延时放到中断函数中 */ schedul

kobox : key_wq.c -v2

将kobox : key_wq.c -v1中的四个工作队列处理函数改成一个工作队列处理函数 因为多个工作队列可以使用同一个工作队列处理函数 #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 S

kobox : key_waitqueue.c -v1 怎样内核线程,怎样使用等待队列

平台:TQ2440 按键驱动 (1)在init中创建一个内核线程作为等待队列的处理函数,该内核线程是一个while(1)死循环,一直检测等待队列的触发条件 /* create a kernel thread */ kthread_run(key_wait_queue_handler, "thread_key_waitqueue", "[key_wait_queue]"); static int key_wait_queue_handler(void *name) {

kobox : key_waitqueue.c -v1 如何内核线程,如何使用等待队列

平台:TQ2440 按键驱动 (1)在init中创建一个内核线程作为等待队列的处理函数,该内核线程是一个while(1)死循环,一直检測等待队列的触发条件 DECLARE_WAIT_QUEUE_HEAD(key_driver_wq); /* create a kernel thread */ kthread_run(key_wait_queue_handler, "thread_key_waitqueue", "[key_wait_queue]"); static

深度学习面试题20:GoogLeNet(Inception V1)

目录 简介 网络结构 对应代码 网络说明 参考资料 简介 2014年,GoogLeNet和VGG是当年ImageNet挑战赛(ILSVRC14)的双雄,GoogLeNet获得了第一名.VGG获得了第二名,这两类模型结构的共同特点是层次更深了.VGG继承了LeNet以及AlexNet的一些框架结构,而GoogLeNet则做了更加大胆的网络结构尝试,虽然深度只有22层,但大小却比AlexNet和VGG小很多,GoogleNet参数为500万个,AlexNet参数个数是GoogleNet的12倍,VG

Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. Memc

heartbeat v1 实现 MariaDB数据库的高可用

MariaDB数据库服务的高可用: 使用 heartbeat v1 版实现两个节点的 MariaDB数据库服务的高可用. 节点: node1        192.168.60.33 node2        192.168.60.88 MySQL数据库的数据文件使用nfs共享文件系统解决 nfs Server       192.168.60.22 架构如下图: 一.heartbeat 节点之间通信要求的设置: (1).解决节点的主机名解析 因为 heartbeat 的节点间通讯基于名称.基于

Python自动化 【第十一篇】:Python进阶-RabbitMQ队列/Memcached/Redis

 本节内容: RabbitMQ队列 Memcached Redis 1.  RabbitMQ 安装 http://www.rabbitmq.com/install-standalone-mac.html 安装python rabbitMQ module pip install pika or easy_install pika or 源码 https://pypi.python.org/pypi/pika 实现最简单的队列 send 端 received 端 1.1 Work Queues 在这

2016/01/14开始学习git:标签管理:创建标签

标签也是版本库的一个快照指向某个commit的指针(分支可以移动,标签不能移动) 切换到需要打标签的分支上git tag <name>就可以打一个新标签: $ git tag v1.0 git tag查看所有标签: $ git tagv1.0 打之前提交的版本的commit需要当时的commit ID$ git tag v0.9 93ddf60 查看tag$ git tagv0.9v1.0 标签不是按时间顺序列出,而是按字母排序的.可以用git show <tagname>查看标签