📄 s3c2410_scankey.c
字号:
/*
* s3c2410_scankey.c
*
* genric routine for S3C2410-base machine's button
*
* Author: Ligang Wang <wangzitan@163.com>
* Date : 2006/05/3
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
* 2006-05-03 Initial code by nandy
*/
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/hardware.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
/*
* define some macro for debug
*/
#define TEST_DEBUG
#ifdef TEST_DEBUG
#define DEBUG(str,args...) printk ("device_test:"str,##args)
#else
#define DEBUG(str,args...)
#endif
/*
* scankey device data struct
*/
struct s3c2410_key_device
{
struct fasync_struct *async_queue; /* Asynchronous notification */
wait_queue_head_t waitq; /* Wait queue for reading */
struct semaphore lock; /* Mutex for reading */
unsigned int usage_count; /* Increment on each open */
unsigned scankey;
spinlock_t irq_lock;
int keypressed;
};
//static int key_pressed = 0;
static char skey_major = 0;
struct s3c2410_key_device s3c2410_key;
/*
* declear opration funcation for VFS
*/
static int scankey_open (struct inode *inode, struct file *filp);
static int scankey_release (struct inode *inode, struct file *filp);
static ssize_t scankey_read (struct file *filp, char *buf, size_t count,loff_t *f_pos);
static ssize_t scankey_write (struct file *filp, const char *buf, size_t count,loff_t *f_pos);
/*
* declear some funcation for this module
*/
static int setup_scankey(void);
static void clear_line(void);
static void set_line(int line);
/*
* Create a file operations struct
*/
static struct file_operations s3c2410_scankey_fops =
{
owner: THIS_MODULE,
open: scankey_open,
read: scankey_read,
write: scankey_write,
release: scankey_release,
};
/*
* routine open function for VFS
*/
static int scankey_open (struct inode *inode, struct file *filp)
{
struct s3c2410_key_device *dev ;
filp->private_data = &s3c2410_key;
dev = &s3c2410_key;
dev->usage_count++; /* We're the first open - clear the buffer */
dev->keypressed = 0;
MOD_INC_USE_COUNT;
return 0;
}
/*
* routine close/release function for VFS
*/
static int scankey_release (struct inode *inode, struct file *filp)
{
struct s3c2410_key_device *dev = (struct s3c2410_key_device *)filp->private_data;
dev->usage_count--;
filp->f_op->fasync( -1, filp, 0 ); /* Remove ourselves from the async list */
MOD_DEC_USE_COUNT;
return 0;
}
/*
* routine write function for VFS
*/
static ssize_t scankey_write (struct file *filp, const char *buf, size_t count,loff_t *f_pos)
{
return 0;
}
static ssize_t scankey_read (struct file *filp, char *buf, size_t count,loff_t *f_pos)
{
struct s3c2410_key_device *dev = (struct s3c2410_key_device *)filp->private_data;
if (count < sizeof(unsigned char))
return -EINVAL;
if(dev->keypressed == 0)
return 0;
if (copy_to_user(buf, &(dev->scankey), sizeof(unsigned char)))
return -EFAULT;
dev->keypressed = 0;
return sizeof(unsigned char);
}
/*
* routine setup scankey
*/
static int setup_scankey(void)
{
/* setup GPE13,GPE11,GPG6,GPG2 for line scan
* Output Disable pullup reg
*/
set_gpio_ctrl(GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_E13);
set_gpio_ctrl(GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_E11);
set_gpio_ctrl(GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_G6);
set_gpio_ctrl(GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_G2);
/* setup GPF0,GPF2,GPG3,GPG1 for row scan
* interrupt, enable pullup reg
*/
set_gpio_ctrl(GPIO_MODE_ALT0 | GPIO_PULLUP_EN | GPIO_F0);
set_gpio_ctrl(GPIO_MODE_ALT0 | GPIO_PULLUP_EN | GPIO_F2);
set_gpio_ctrl(GPIO_MODE_ALT0 | GPIO_PULLUP_EN | GPIO_G3);
set_gpio_ctrl(GPIO_MODE_ALT0 | GPIO_PULLUP_EN | GPIO_G11);
/*
* setup ext interrupt EXT_FALLING_EDGE
*/
set_external_irq(IRQ_EINT0, EXT_LOWLEVEL, GPIO_PULLUP_EN);
set_external_irq(IRQ_EINT2, EXT_LOWLEVEL, GPIO_PULLUP_EN);
set_external_irq(IRQ_EINT11, EXT_LOWLEVEL, GPIO_PULLUP_EN);
set_external_irq(IRQ_EINT19, EXT_LOWLEVEL, GPIO_PULLUP_EN);
/*
* Now we has finished setup scankey,so we should enable interrupt
*/
enable_irq(IRQ_EINT0);
enable_irq(IRQ_EINT2);
enable_irq(IRQ_EINT11);
enable_irq(IRQ_EINT19);
/*
* claer all line for line scan
*/
clear_line();
/* everything has been down*/
return 0;
}
/*
* routine clear line for line scan
*/
static void clear_line(void)
{
write_gpio_bit(GPIO_E13,0);
write_gpio_bit(GPIO_E11,0);
write_gpio_bit(GPIO_G6,0);
write_gpio_bit(GPIO_G2,0);
}
/*
* routine set line for line scan
*/
static void set_line(int line)
{
clear_line();
switch (line)
{
case 0:
write_gpio_bit(GPIO_E11,1);
break;
case 1:
write_gpio_bit(GPIO_G6,1);
break;
case 2:
write_gpio_bit(GPIO_E13,1);
break;
case 3:
write_gpio_bit(GPIO_G2,1);
break;
}
}
static void test_scankey_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
int i;
struct s3c2410_key_device *dev ;
int row = -1;
int line = -1;
dev = &s3c2410_key;
spin_lock_irq(&(dev->irq_lock));
mdelay(80);
switch(irq)
{
case IRQ_EINT0:
if(read_gpio_bit(GPIO_F0) == 0)
row = 0;
break;
case IRQ_EINT2:
if(read_gpio_bit(GPIO_F2) == 0)
row = 1;
break;
case IRQ_EINT11:
if(read_gpio_bit(GPIO_G3) == 0)
row = 2;
break;
case IRQ_EINT19:
if(read_gpio_bit(GPIO_G11) == 0)
row = 3;
break;
}
if(row == -1)
goto done;
for(i=0; i<4; i++)
{
set_line(i);
if( read_gpio_bit(GPIO_F0) && read_gpio_bit(GPIO_F2)
&& read_gpio_bit(GPIO_G3) && read_gpio_bit(GPIO_G11))
{
line = i;
break;
}
}
if(line != -1)
{
dev->scankey = row * 4 + line + 1;
dev->keypressed = 1;
mdelay(30);
}
done:
clear_line();
spin_unlock_irq(&(dev->irq_lock));
return;
}
/*
* routine module init
*/
int __init scankey_init(void)
{
int result,i;
int ext_irq_id[4] = {IRQ_EINT11,IRQ_EINT19,IRQ_EINT2,IRQ_EINT0};
struct s3c2410_key_device *dev;
dev = &s3c2410_key;
DEBUG("[INFO: start init scankey ]\n");
result = register_chrdev(0,"s3c2410_scankey", &s3c2410_scankey_fops);
if(result < 0)
{
printk("[FAILED: Cannot register s3c2410_scankey!] \n");
return result;
}
else
{
skey_major = result;
printk("[INFO:s3c2410_scankey major is %d\n",skey_major);
}
setup_scankey();
for(i=0; i<4; i++)
{
if((result = request_irq(ext_irq_id[i], test_scankey_interrupt,
SA_INTERRUPT, "s3c2410_scankey", NULL)) != 0)
{
printk("[FAILED: result of irq is :%d\n]",result);
printk(KERN_INFO "[FAILED:Cannot register scankey scankey_interrupt!],%d\n",ext_irq_id[i]);
return -EBUSY;
}
}
init_waitqueue_head(&dev->waitq); //当有几个程序同时使用键盘,避免冲突
init_MUTEX(&dev->lock);
dev->async_queue = NULL;
DEBUG("[INFO:s3c44b0 scankey driver installed. ]\n");
return 0;
}
/*
* routine module clean
*/
void __exit scankey_cleanup(void)
{
int i;
int ext_irq_id[4] = {IRQ_EINT11,IRQ_EINT19,IRQ_EINT2,IRQ_EINT0};
DEBUG("[INFO: clean scankey. ]\n");
for(i=0; i<4; i++)
{
free_irq(ext_irq_id[i], NULL);
}
unregister_chrdev(skey_major,"s3c2410_scankey");
return ;
}
module_init(scankey_init);
module_exit(scankey_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ligang Wang wangzitan@163.com");
MODULE_DESCRIPTION("scankey for S3C2410");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -