驱动源码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#include <mach/gpio-bank-k.h>
#define DEVICE_NAME "buttons"
int m=0,n=0;
int j=0;
struct button_irq_desc {
int irq;
int number;
char *name;
};
static struct button_irq_desc button_irqs [] = {
{IRQ_EINT(0), 0, "KEY0"},//101,IRQ_EINT(0)根据宏定义计算
};
static volatile char key_values [] = {'0'};
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
int down;//表示按键按下之前的状态
int number;
unsigned tmp;
udelay(0);
number = button_irqs->number;
switch(number)
{
case 0:
tmp = readl(S3C64XX_GPNDAT);
down = !(tmp & (1<<number));//按键按下,down的值变为1
break;
default:
down = 0;break;
}
printk("m=%d\n",m);
m++;
if (down == 1) {
key_values[number]++ ;//按键按下key值加1
ev_press = 1;
wake_up_interruptible(&button_waitq);/*唤醒指定的注册在等待队列上的进程。该函数不能直接的立即唤醒进程,而是由调度程序转换上下文,调整为可运行状态*/
j++;
printk("j=%d\n",j);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static int s3c64xx_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_FALLING,
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;
printk("n=%d",n);
n++;
return 0;
}
static int s3c64xx_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;
}
static int s3c64xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long err;
if (!ev_press) {
//printk("?????????????\n");打印
if (filp->f_flags & O_NONBLOCK)
{
//printk("###########\n");始终不打印
return -EAGAIN;
}
else
wait_event_interruptible(button_waitq, ev_press);//按键不按下,时间休眠,直到ev_press=1;
//printk("@@@@@@@@@@@@\n");打印
}
//printk("&&&&&&&&&&&&&&&&&&&\n");打印
ev_press = 0;
err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count));
return err ? -EFAULT : min(sizeof(key_values), count);
}
static unsigned int s3c64xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = s3c64xx_buttons_open,
.release = s3c64xx_buttons_close,
.read = s3c64xx_buttons_read,
.poll = s3c64xx_buttons_poll,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
unsigned tmp;
tmp = readl(S3C64XX_GPNCON);
tmp = (tmp & ~(0x03U))|(0x1U);
writel(tmp, S3C64XX_GPNCON);
ret = misc_register(&misc);
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define DEVICE_NAME "/dev/buttons"
int main(int argc, char* argv[])
{
int fd;
fd = open(DEVICE_NAME, O_RDWR);
if(fd < 0)
{
printf("Open %s failed!\n", DEVICE_NAME);
exit(-1);
}
unsigned char key_values[1];
for( ; ; )
{
read(fd, key_values, sizeof(key_values));//这个read()函数式内核中断中的驱动函数
printf("key(1~8) value: %d \n", \
key_values[0]);
}
close(fd);
return 0;
}
中断的控制寄存器在源码里已经设置 在Irq_eint.c文件中设置int request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long frags, const char *device, void *dev_id); 5个参数的含义如下: 第一个参数irq:申请的硬件中断号; 第二个参数handler:是一个函数指针,向系统登记的中断处理函数,是一个回调函数,当中断发生时,系统调用这个函数,传入的参数包括中断设备 id,寄存器值。 第三个参数flags:指定了快速中断或中断共享等中断处理属性。 第四个参数devices:指定设备驱动程序的名称。 第五个参数dev_id:传入中断处理程序的参数,可以为NULL,在注册共享中断时,此参数不能为NULL,作为共享中断时的中断区别参数。 返回值: 函数运行正常时返回 0 ,否则返回对应错误的负值。
/* arch/arm/plat-s3c64xx/irq-eint.c * * Copyright 2008 Openmoko, Inc. * Copyright 2008 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> * http://armlinux.simtec.co.uk/ * * S3C64XX - Interrupt handling for IRQ_EINT(x) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/sysdev.h> #include <linux/gpio.h> #include <linux/irq.h> #include <linux/io.h> #include <asm/hardware/vic.h> #include <plat/regs-irqtype.h> #include <mach/regs-gpio.h> #include <plat/gpio-cfg.h> #include <mach/map.h> #include <plat/cpu.h> #include <plat/pm.h> #define eint_offset(irq) ((irq) - IRQ_EINT(0)) #define eint_irq_to_bit(irq) ((u32)(1 << eint_offset(irq))) static inline void s3c_irq_eint_mask(struct irq_data *data) { u32 mask; mask = __raw_readl(S3C64XX_EINT0MASK); mask |= (u32)data->chip_data; __raw_writel(mask, S3C64XX_EINT0MASK); } static void s3c_irq_eint_unmask(struct irq_data *data) { u32 mask; mask = __raw_readl(S3C64XX_EINT0MASK); mask &= ~((u32)data->chip_data); __raw_writel(mask, S3C64XX_EINT0MASK); } static inline void s3c_irq_eint_ack(struct irq_data *data) { __raw_writel((u32)data->chip_data, S3C64XX_EINT0PEND); } static void s3c_irq_eint_maskack(struct irq_data *data) { /* compiler should in-line these */ s3c_irq_eint_mask(data); s3c_irq_eint_ack(data); } static int s3c_irq_eint_set_type(struct irq_data *data, unsigned int type) { int offs = eint_offset(data->irq); int pin, pin_val; int shift; u32 ctrl, mask; u32 newvalue = 0; void __iomem *reg; if (offs > 27) return -EINVAL; if (offs <= 15) reg = S3C64XX_EINT0CON0; else reg = S3C64XX_EINT0CON1; switch (type) { case IRQ_TYPE_NONE: printk(KERN_WARNING "No edge setting!\n"); break; case IRQ_TYPE_EDGE_RISING: newvalue = S3C2410_EXTINT_RISEEDGE; break; case IRQ_TYPE_EDGE_FALLING: newvalue = S3C2410_EXTINT_FALLEDGE; break; case IRQ_TYPE_EDGE_BOTH: newvalue = S3C2410_EXTINT_BOTHEDGE; break; case IRQ_TYPE_LEVEL_LOW: newvalue = S3C2410_EXTINT_LOWLEV; break; case IRQ_TYPE_LEVEL_HIGH: newvalue = S3C2410_EXTINT_HILEV; break; default: printk(KERN_ERR "No such irq type %d", type); return -1; } if (offs <= 15) shift = (offs / 2) * 4; else shift = ((offs - 16) / 2) * 4; mask = 0x7 << shift; ctrl = __raw_readl(reg); ctrl &= ~mask; ctrl |= newvalue << shift; __raw_writel(ctrl, reg); /* set the GPIO pin appropriately */ if (offs < 16) { pin = S3C64XX_GPN(offs); pin_val = S3C_GPIO_SFN(2); } else if (offs < 23) { pin = S3C64XX_GPL(offs + 8 - 16); pin_val = S3C_GPIO_SFN(3); } else { pin = S3C64XX_GPM(offs - 23); pin_val = S3C_GPIO_SFN(3); } s3c_gpio_cfgpin(pin, pin_val); return 0; } static struct irq_chip s3c_irq_eint = { .name = "s3c-eint", .irq_mask = s3c_irq_eint_mask, .irq_unmask = s3c_irq_eint_unmask, .irq_mask_ack = s3c_irq_eint_maskack, .irq_ack = s3c_irq_eint_ack, .irq_set_type = s3c_irq_eint_set_type, .irq_set_wake = s3c_irqext_wake, }; /* s3c_irq_demux_eint * * This function demuxes the IRQ from the group0 external interrupts, * from IRQ_EINT(0) to IRQ_EINT(27). It is designed to be inlined into * the specific handlers s3c_irq_demux_eintX_Y. */ static inline void s3c_irq_demux_eint(unsigned int start, unsigned int end) { u32 status = __raw_readl(S3C64XX_EINT0PEND); u32 mask = __raw_readl(S3C64XX_EINT0MASK); unsigned int irq; status &= ~mask; status >>= start; status &= (1 << (end - start + 1)) - 1; for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) { if (status & 1) generic_handle_irq(irq); status >>= 1; } } static void s3c_irq_demux_eint0_3(unsigned int irq, struct irq_desc *desc) { s3c_irq_demux_eint(0, 3); } static void s3c_irq_demux_eint4_11(unsigned int irq, struct irq_desc *desc) { s3c_irq_demux_eint(4, 11); } static void s3c_irq_demux_eint12_19(unsigned int irq, struct irq_desc *desc) { s3c_irq_demux_eint(12, 19); } static void s3c_irq_demux_eint20_27(unsigned int irq, struct irq_desc *desc) { s3c_irq_demux_eint(20, 27); } static int __init s3c64xx_init_irq_eint(void) { int irq; for (irq = IRQ_EINT(0); irq <= IRQ_EINT(27); irq++) { set_irq_chip(irq, &s3c_irq_eint); set_irq_chip_data(irq, (void *)eint_irq_to_bit(irq)); set_irq_handler(irq, handle_level_irq); set_irq_flags(irq, IRQF_VALID); } set_irq_chained_handler(IRQ_EINT0_3, s3c_irq_demux_eint0_3); set_irq_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11); set_irq_chained_handler(IRQ_EINT12_19, s3c_irq_demux_eint12_19); set_irq_chained_handler(IRQ_EINT20_27, s3c_irq_demux_eint20_27); return 0; } arch_initcall(s3c64xx_init_irq_eint);