⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 matrix_button.c

📁 我自己编的基于arm9的矩阵键盘驱动
💻 C
字号:
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <asm/system.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/regs-gpio.h> 
#include <asm-arm/arch-s3c2410/irqs.h>
#include <asm-arm/arch-s3c2410/regs-irq.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/sched.h> 
#include <linux/delay.h> 
#include <linux/poll.h> 
#include <linux/spinlock.h> 
#include <linux/irq.h>
#include <asm/hardware.h>
#include <linux/interrupt.h>
#include <asm/signal.h>

#define S3C2410_GPG11 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 11) // S3C2410_GPG11 undefined in regs-gpio.h


#define DEVICE_NAME "matrix_button" 
#define BUTTON_MAJOR 232 

unsigned int s3c2410_gpio_getpin(unsigned int pin)
{
	 unsigned long base = S3C2410_GPIO_BASE(pin);
	 unsigned long offs = S3C2410_GPIO_OFFSET(pin); 
         return __raw_readl(base + 0x04) & (1<< offs);
}

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
	unsigned long base = S3C2410_GPIO_BASE(pin);
	unsigned long mask;
	unsigned long con;
	unsigned long flags;
	if(pin<S3C2410_GPIO_BANKB)
		mask=1<<S3C2410_GPIO_OFFSET(pin);
	else
		mask =3<< (S3C2410_GPIO_OFFSET(pin)*2);
	local_irq_save(flags);

	con = __raw_readl(base + 0x00);

	con &= ~mask;
	con |= function;

	__raw_writel(con, base + 0x00);

	local_irq_restore(flags);
}

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
	unsigned long base = S3C2410_GPIO_BASE(pin);
	unsigned long offs = S3C2410_GPIO_OFFSET(pin);
	unsigned long flags;
	unsigned long dat;

	local_irq_save(flags);

	dat = __raw_readl(base + 0x04);
	dat &= ~(1 << offs);
	dat |= to << offs;
	__raw_writel(dat, base + 0x04);

	local_irq_restore(flags);
}

#define EXTINT_OFF (IRQ_EINT4 - 4)

// clear SRCPND&INTPND
void s3c_irq_ack(unsigned int irqno) 
{
	unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
	__raw_writel(bitval, S3C2410_SRCPND);
	__raw_writel(bitval, S3C2410_INTPND);
	return;
}
void s3c_irqext_ack(unsigned int irqno)
{
	unsigned long req;
	unsigned long bit;
	bit = 1UL << (irqno - EXTINT_OFF);
	__raw_writel(bit, S3C2410_EINTPEND);
	req = __raw_readl(S3C2410_EINTPEND);
	if (irqno <= IRQ_EINT7 )
	{
		if ((req & 0xf0) == 0)
		s3c_irq_ack(IRQ_EINT4t7);
	}
	else
	{
		if ((req >> 8) == 0)
		s3c_irq_ack(IRQ_EINT8t23);
	}
	return;
}


// set external interrupt mode, trigger type and gpio registers
int realarm_interrupt_init(unsigned int irq, unsigned int type)
{
	unsigned long gpcon_reg;
	unsigned long gpcon_offset;
	unsigned long extint_reg;
	unsigned long extint_offset;
	unsigned long newvalue = 0;
	unsigned long value;
	if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
	{
		gpcon_reg = S3C2410_GPFCON;
		extint_reg = S3C2410_EXTINT0;
		gpcon_offset = (irq - IRQ_EINT0) * 2;
		extint_offset = (irq - IRQ_EINT0) * 4;
	}
	else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7))
	{
		gpcon_reg = S3C2410_GPFCON;
		extint_reg = S3C2410_EXTINT0;
		gpcon_offset = (irq - EXTINT_OFF) * 2;
		extint_offset = (irq - EXTINT_OFF) * 4;
	}
	else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15))
	{
		gpcon_reg = S3C2410_GPGCON;
		extint_reg = S3C2410_EXTINT1;
		gpcon_offset = (irq - IRQ_EINT8) * 2;
		extint_offset = (irq - IRQ_EINT8) * 4;
	}
	else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23))
	{
		gpcon_reg = S3C2410_GPGCON;
		extint_reg = S3C2410_EXTINT2;
		gpcon_offset = (irq - IRQ_EINT8) * 2;
		extint_offset = (irq - IRQ_EINT16) * 4;
	} else
	{
		return -1;
	}
	/* Set the GPIO to external interrupt mode */
	value = __raw_readl(gpcon_reg);
	value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
	__raw_writel(value, gpcon_reg);
	/* Set the external interrupt to pointed trigger type */
	switch (type)
	{
	case IRQT_NOEDGE:
		printk(KERN_WARNING "No edge setting!\n");
		break;
	case IRQT_RISING:
		newvalue = S3C2410_EXTINT_RISEEDGE;
		break;
	case IRQT_FALLING:
		newvalue = S3C2410_EXTINT_FALLEDGE;
		break;
	case IRQT_BOTHEDGE:
		newvalue = S3C2410_EXTINT_BOTHEDGE;
		break;
	case IRQT_LOW:
		newvalue = S3C2410_EXTINT_LOWLEV;
		break;
	case IRQT_HIGH:
		newvalue = S3C2410_EXTINT_HILEV;
		break;
	default:
		printk(KERN_ERR "No such irq type %d", type);
		return -1;
	}
	value = __raw_readl(extint_reg);
	value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
	__raw_writel(value, extint_reg);
	return 0;
}



static struct key_info { 
int irq_no; 
unsigned int gpio_port; 
int function_inp;
int function_eintn; 
}key_info_tab[4]={
		{ IRQ_EINT0, S3C2410_GPF0,S3C2410_GPF0_INP,S3C2410_GPF0_EINT0}, 
		{ IRQ_EINT2, S3C2410_GPF2,S3C2410_GPF2_INP,S3C2410_GPF2_EINT2}, 
		{ IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_INP,S3C2410_GPG3_EINT11 }, 
		{ IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_INP,S3C2410_GPG11_EINT19}};

struct matrix_button
{
	int key_value;
	int ready;
	int open_count;
	wait_queue_head_t buttons_wait;
	struct cdev cdev;
	struct semaphore sem;
};

static struct matrix_button* matrix_button_dev;

static struct output_pin
{
	unsigned int gpio_pin;
	int function_outp;
}output_pin_tab[4]={
	{S3C2410_GPE11,S3C2410_GPE11_OUTP},
	{S3C2410_GPG6,S3C2410_GPG6_OUTP},
	{S3C2410_GPE13,S3C2410_GPE13_OUTP},
	{S3C2410_GPG2,S3C2410_GPG2_OUTP}
};

static int  key_scan(void)
{

	int i,j,k,ret=0;
	for(i=0;i<4;i++)
	{
				
			for(j=0;j<4;j++)
			{
				if(i==j)s3c2410_gpio_setpin(output_pin_tab[j].gpio_pin,0);
				 else s3c2410_gpio_setpin(output_pin_tab[j].gpio_pin,1);
			}
			for(k=0;k<4;k++)
			{
				if(s3c2410_gpio_getpin(key_info_tab[k].gpio_port)==0)
				{
					ret=i*10+k+1;
					return ret;
				}
			}
	}	
	return ret;
}

static void port_init(void)
{
	int i;
	for(i=0;i<4;i++)
	{
		s3c2410_gpio_cfgpin(output_pin_tab[i].gpio_pin,output_pin_tab[i].function_outp);
		s3c2410_gpio_setpin(output_pin_tab[i].gpio_pin,0);
		s3c2410_gpio_cfgpin(key_info_tab[i].gpio_port,key_info_tab[i].function_eintn);
		realarm_interrupt_init(key_info_tab[i].irq_no,IRQT_FALLING);
	}
}

static irqreturn_t matrix_button_irq(int irq, void *dev_id, struct pt_regs *reg) 
{ 
	struct matrix_button *dev=dev_id;
	struct key_info *k; 
	int i; 
	int found = 0;
	int flags;
	down(&dev->sem); 
	for (i = 0; i < sizeof(key_info_tab)/ sizeof(key_info_tab[0]); i++)
	{ 
			k = key_info_tab + i; 
			if (k->irq_no == irq)
			{ 
					found = 1; 
					break; 
			} 
	} 
	if (!found) 
	{ 
		printk(KERN_NOTICE "bad irq %d in button\n", irq); 
		return IRQ_HANDLED; 
	} 
	save_flags(flags); // save CPSR
	cli(); 	// Close interrupt 
	if(k->irq_no<IRQ_EINT3)
		s3c_irq_ack(k->irq_no);
	else
		s3c_irqext_ack(k->irq_no);
	for(i=0;i<sizeof(key_info_tab)/sizeof(key_info_tab[0]);i++)
	{
		k=key_info_tab+i;
		s3c2410_gpio_cfgpin(k->gpio_port,k->function_inp);
	}
	mdelay(100);
	dev->key_value=key_scan();
	if(dev->key_value==0)
	{
		up(&dev->sem);
		port_init();
		restore_flags(flags);
		return IRQ_HANDLED; 
	}
	dev->ready=1;
	up(&dev->sem);
	wake_up(&dev->buttons_wait);
	port_init();
	restore_flags(flags); 
	return IRQ_HANDLED; 
}


static int request_irqs(struct matrix_button *dev) 
{ 
	struct key_info *k; 
	int i; 
	for (i = 0; i < sizeof(key_info_tab) / sizeof(key_info_tab[0]); i++)
	{ 
		k = key_info_tab + i;
		if (request_irq(k->irq_no, &matrix_button_irq, SA_INTERRUPT, DEVICE_NAME, dev)) return -1;  
	} 
	return 0; 
} 

static void free_irqs(struct matrix_button *dev) 
{ 
	struct key_info *k; 
	int i; 
	for (i = 0; i < sizeof(key_info_tab) / sizeof(key_info_tab[0]); i++)
	{ 
		k = key_info_tab + i; 
		free_irq(k->irq_no,dev); 
	} 
} 

static int matrix_button_read(struct file * filp, char __user * buffer, size_t count, loff_t *f_pos) 
{ 
	struct matrix_button *dev=filp->private_data; 
	down(&dev->sem); 
	if(!dev->ready)
	   return -EAGAIN;
	copy_to_user(buffer, &dev->key_value, sizeof(int)); 
	dev->ready = 0; 
	up(&dev->sem);
	return count; 
} 

static unsigned int matrix_button_poll( struct file *filp, struct poll_table_struct *wait) 
{ 
	struct matrix_button *dev=filp->private_data; 
	poll_wait(filp, &dev->buttons_wait, wait); 
	if (dev->ready) 
		return POLLIN;
	else 
		return 0;
} 


static int matrix_button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 
{ 
	switch(cmd) { 
	default: 
	return -EINVAL; 
	} 
} 

static int matrix_button_open(struct inode *inode,struct file *filp)
{
	int ret;
	struct matrix_button *dev;
	dev=container_of(inode->i_cdev,struct matrix_button,cdev);
	down(&dev->sem);
	filp->private_data=dev;
	if(dev->open_count==0)
	{
		ret = request_irqs(dev); 
		if (ret) 
		{
		cdev_del(&matrix_button_dev->cdev);
		unregister_chrdev_region(MKDEV(BUTTON_MAJOR,0),1);
		kfree(matrix_button_dev);
		printk(DEVICE_NAME " can't request irqs\n"); 
		return ret; 
		}
	}
	dev->open_count++;
	up(&dev->sem);
	return 0;
}

static int matrix_button_release(struct inode *inode,struct file *filp)
{
	struct matrix_button *dev=filp->private_data;
	down(&dev->sem);
	if((dev->open_count--)==1)
		free_irqs(dev);
	up(&dev->sem);
	return 0;
}

static struct file_operations matrix_button_fops = { 
owner: THIS_MODULE, 
ioctl: matrix_button_ioctl, 
poll: matrix_button_poll, 
read: matrix_button_read, 
open: matrix_button_open,
release:matrix_button_release
}; 

static int __init matrix_button_init(void) 
{ 
	int ret; 
	dev_t dev=MKDEV(BUTTON_MAJOR,0);
	ret=register_chrdev_region(dev,1,"matrix_button");
	if(ret<0)
		return ret;
	matrix_button_dev=kmalloc(sizeof(struct matrix_button),GFP_KERNEL);
	memset(matrix_button_dev,0,sizeof(struct matrix_button));
	matrix_button_dev->ready=0;
	matrix_button_dev->key_value=0;
	matrix_button_dev->open_count=0;
	init_waitqueue_head(&matrix_button_dev->buttons_wait);
	init_MUTEX(&matrix_button_dev->sem);	
	cdev_init(&matrix_button_dev->cdev,&matrix_button_fops);
	matrix_button_dev->cdev.owner=THIS_MODULE;
	matrix_button_dev->cdev.ops=&matrix_button_fops;
	port_init();
	ret=cdev_add(&matrix_button_dev->cdev,dev,1);
	if(ret)
		printk(KERN_NOTICE "Error %d",ret); 
	
	return 0; 
} 

static void __exit matrix_button_exit(void) 
{ 
	cdev_del(&matrix_button_dev->cdev);
	unregister_chrdev_region(MKDEV(BUTTON_MAJOR,0),1);
	kfree(matrix_button_dev);
	free_irqs(matrix_button_dev);  
} 

module_init(matrix_button_init); 
module_exit(matrix_button_exit); 
MODULE_LICENSE("GPL"); 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -