您的位置:首页 > 运维架构 > Linux

【Tiny6410 And Linux】—(2.3)—使用工作队列处理按键中断——代码

2012-05-14 23:07 656 查看
做了做用工作队列处理按键中断的实验,对中断开始明白~~

呵呵~~其实今天就是加了个全局变量(虽然这在驱动程序中不是很合适吧),还有就是加了个消抖(就是通过延时等待而已)!

1、驱动程序

①、plat_btn_dev.c

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/device.h>

#define DEVICE_NAME     "tiny6410_buttons"

/* 平台资源的定义 */
static struct resource tiny6410_buttons_resource[] = {
[0] = {
.start = IRQ_EINT(0),
.end   = IRQ_EINT(0),
.flags = IORESOURCE_IRQ,
},
[1] = {
.start = IRQ_EINT(1),
.end   = IRQ_EINT(1),
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_EINT(2),
.end   = IRQ_EINT(2),
.flags = IORESOURCE_IRQ,
},
[3] = {
.start = IRQ_EINT(3),
.end   = IRQ_EINT(3),
.flags = IORESOURCE_IRQ,
},
[4] = {
.start = IRQ_EINT(4),
.end   = IRQ_EINT(4),
.flags = IORESOURCE_IRQ,
},
[5] = {
.start = IRQ_EINT(5),
.end   = IRQ_EINT(5),
.flags = IORESOURCE_IRQ,
},
[6] = {
.start = IRQ_EINT(19),
.end   = IRQ_EINT(19),
.flags = IORESOURCE_IRQ,
},
[7] = {
.start = IRQ_EINT(20),
.end   = IRQ_EINT(20),
.flags = IORESOURCE_IRQ,
}                                       /* 这里不需要加逗号 */
};

static struct platform_device *tiny6410_buttons_dev;

static int __init platform_init(void)
{
printk("[Call platform_init!]\n");
/* 分配一个 platform_device 结构 */
tiny6410_buttons_dev = platform_device_alloc(DEVICE_NAME, -1);

/* 为平台设备添加平台设备资源 */
platform_device_add_resources(tiny6410_buttons_dev,
tiny6410_buttons_resource, 8);

/* 注册平台设备 */
platform_device_add(tiny6410_buttons_dev);

return 0;
}

static void __exit platform_exit(void)
{
printk("[Call platform_exit!]\n");

platform_device_unregister(tiny6410_buttons_dev);
}

module_init(platform_init);
module_exit(platform_exit);

MODULE_AUTHOR("_Justin");
MODULE_LICENSE("GPL");


②、plat_but_drv.c

#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/unistd.h>
#include <linux/device.h>
#include <linux/workqueue.h>

#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>

#define DRIVER_NAME     "tiny6410_buttons"
#define DEVICE_NAME     "tiny6410_buttons"

/* 注意:
* 在一个较规范的驱动程序中,通常不会将设备资源在平台驱动程序中
* 以全局变量的形式给出(这里是为了简化操作,将部分设备资源以全局变量形式
* 给出),一个更好的办法是,将这些资源都添加到对应的平台设备中,以平台
* 设备资源或者平台设备似有数据的形式管理。
*/
/* 记录按键中断号 */
typedef struct
{
int irq;                        /* 中断号,初始化为 0,将在
* probe 获取资源 */
int num;                        /* 对应的按键编号 */
char *name;                     /* 中断所属的名字 */
} button_irq_t;

/* 按键数据结构 */
typedef struct
{
wait_queue_head_t bwq;		/* 按键等待队列 */
struct delayed_work dwork;	/* 用于按键中断下半部分的延迟工作 */
button_irq_t *birqs;		/* 指向记录按键中断资源数组 */
int nirqs;			/* 设备支持的按键中断数 */
int val;			/* 记录键值 */
volatile int cnum;		/* 记录产生中断的按键编号 */
volatile int press;		/* 等待队列条件 */
} buttons_data_t;

button_irq_t  button_irqs[] =
{
{0, 0, "KEY0"},
{0, 1, "KEY1"},
{0, 2, "KEY2"},
{0, 3, "KEY3"},
{0, 4, "KEY4"},
{0, 5, "KEY5"},
{0, 6, "KEY6"},
{0, 7, "KEY7"},
};

/* 按键驱动数据全局变量 g_bd */
static buttons_data_t g_bd =
{
.birqs	= button_irqs,
.nirqs	= sizeof(button_irqs) / sizeof(button_irqs[0]),
.press	= 0,
};

/*
* buttons_work_func 函数
* 延迟工作的回调函数则负责完成中断处理程序中未完成的工作
*/
static void buttons_work_func(struct work_struct *w)
{
int down;
unsigned tmp;
int num = g_bd.cnum;

switch(num)
{
case 0: case 1: case 2: case 3: case 4: case 5:
tmp = readl(S3C64XX_GPNDAT);
down = !(tmp & (1 << num));
break;

case 6: case 7:
tmp = readl(S3C64XX_GPLDAT);
down = !(tmp & (1 << (num + 5)));
break;
default:
down = 0;
}

if(down == !(g_bd.val & (1 << num)))
{
g_bd.val = down ? g_bd.val | (1<<num) : g_bd.val & ~(1 << num);

g_bd.press = 1;
wake_up_interruptible(&g_bd.bwq);
}

}

/*
* buttons_interrupt
* 使用一个整形变量 key_value 的 0~7 位来记录键值,0~7
* 位分别对应 KEY0~KEY7 的抬起或者按下情况( 1 表示按下)。
*/
static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
/* 定义一个指针,指向所对应的中断号 */
button_irq_t *birq = (button_irq_t *)dev_id;

g_bd.cnum = birq->num;

/* 延迟 20ms,用作按键消抖 */
schedule_delayed_work(&g_bd.dwork,HZ/50);

return IRQ_RETVAL(IRQ_HANDLED);
}

/*
* tiny6410_buttons_open
*/
static int tiny6410_buttons_open(struct inode *inode,struct file *file)
{
int i;
int err = 0;

for(i = 0;i < sizeof(button_irqs) / sizeof(button_irqs[0]);i ++ )
{
if (button_irqs[i].irq < 0)
continue;

/* 申请中断 */
err = request_irq(button_irqs[i].irq,buttons_interrupt,
IRQ_TYPE_EDGE_BOTH,
button_irqs[i].name,
(void *)&button_irqs[i]);
if (err)
break;
}

if(err)
{
i--;
for(;i >= 0;i -- )
{
if(button_irqs[i].irq < 0)
continue;

disable_irq(button_irqs[i].irq);
free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}

return -EBUSY;
}

//	ev_press = 1;

return 0;
}

/*
* tiny6410_buttons_close
*/
static int tiny6410_buttons_close(struct inode *inode,struct file *file)
{
int i;

for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i ++ )
{
if(button_irqs[i].irq < 0)
continue;

free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}

return 0;
}

/*
* tiny6410_buttons_read
*/
static int tiny6410_buttons_read(struct file *filp,char __user *buff,
size_t count,loff_t *offp)
{
unsigned long err;

if(!g_bd.press)
{
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
else
wait_event_interruptible(g_bd.bwq,g_bd.press);
}

g_bd.press = 0;

err = copy_to_user((void *)buff,(const void *)(&g_bd.val),
min(sizeof(&g_bd.val),count));

return err ? -EFAULT : min(sizeof(&g_bd.val),count);
}

/*
* poll 实现函数
*/
static unsigned int tiny6410_buttons_poll(struct file *file,
struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file,&g_bd.bwq,wait);

if(g_bd.press)
mask |= POLLIN | POLLRDNORM;

return mask;
}

/*
* file_operation
*/
static struct file_operations dev_fops =
{
.owner	= THIS_MODULE,
.open   = tiny6410_buttons_open,
.release = tiny6410_buttons_close,
.read   = tiny6410_buttons_read,
.poll   = tiny6410_buttons_poll,
};

/*
* 混合设备结构体
*/
static struct miscdevice tiny6410_buttons_misc =
{
.minor = MISC_DYNAMIC_MINOR,
.name  = DEVICE_NAME,
.fops  = &dev_fops,
};

/*
* probe 实现函数
*/
static int tiny6410_buttons_probe(struct platform_device *pdev)
{
int ret;
int i;
static struct resource   *buttons_irq;

printk("[call %s]\n", __func__);

/* 初始化等待队列 */
init_waitqueue_head(&g_bd.bwq);

/* 初始化 delay_work 结构,来使用延迟调度工作 */
INIT_DELAYED_WORK(&g_bd.dwork,buttons_work_func);

/* 获取设备资源 */
for(i = 0;i < 8;i ++ )
{
buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,i);
button_irqs[i].irq = buttons_irq->start;
}

ret = misc_register(&tiny6410_buttons_misc);

return 0;
}

/*
* remove 实现函数
*/
static int tiny6410_buttons_remove(struct platform_device *dev)
{
misc_deregister(&tiny6410_buttons_misc);
return 0;
}

/* 平台设备驱动结构 */
static struct platform_driver tiny6410_buttons_driver = {
.probe  = tiny6410_buttons_probe,
.remove = tiny6410_buttons_remove,
.driver =
{
.owner  = THIS_MODULE,
.name   = DRIVER_NAME,
},
};

static int __init buttons_init(void)
{
printk("[Call buttons_init!]\n");

/* 注册驱动 */
platform_driver_register(&tiny6410_buttons_driver);
return 0;
}

static void __exit buttons_exit(void)
{
printk("[Call buttons_exit!]\n");

platform_driver_unregister(&tiny6410_buttons_driver);
}

module_init(buttons_init);
module_exit(buttons_exit);

MODULE_AUTHOR("_Justin");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Tiny6410 Buttons Driver");


③、Makefile

ifneq ($(KERNELRELEASE),)
obj-m := plat_btn_dev.o plat_btn_drv.o

else
KDIR := /home/_Jana/linux-2.6.38

all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-

clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

endif


2、测试程序

①、app_btn.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <time.h>

#define DEVICE_NAME     "/dev/tiny6410_buttons"

static int key_value = 0;

int main(void)
{
int fd, ret;
fd_set rfds;
int last_kval = key_value;

if(-1 == (fd = open(DEVICE_NAME,O_RDONLY)))
{
printf("open %s error\n",DEVICE_NAME);
_exit(EXIT_FAILURE);
}

/* 先清空集合 */
FD_ZERO(&rfds);

/* 设置要监控的文件描述符 */
FD_SET(fd,&rfds);

printf("Test for tiny6410_buttons: ...\n");

while(1)
{
if(-1 == (ret = select(fd + 1,&rfds,NULL,NULL,NULL)))
{
printf("select error\n");
_exit(EXIT_FAILURE);
}

if(FD_ISSET(fd,&rfds))
{
read(fd, &key_value, sizeof(key_value));

int i;
for(i = 0;i < 8;i ++ )
{
if((key_value & (1 << i)) != (last_kval & (1 << i)))
{
printf("KEY%d: %s (key_value=0x%x)\n", i+1,
(key_value& (1<<i))? "DOWN": "UP", key_value);
}
}
last_kval = key_value;
}
}
_exit(EXIT_SUCCESS);
}


3、测试结果

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: