📄 key_driver.c
字号:
/*
* Copyright (C) 2006 Shenzhen Genvict Technologies Co., Ltd.
* Author: Zhong Yong <zelog@163.com>
* Date : 2006/05/22
* vision: 0.03
*
* 8*3 keyboard driver, 74hc164 based.
*
*
*
* 20080401 port to kernel 2.6 and add resume/suspend support
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.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 <linux/delay.h>
#include <linux/time.h>
#include <asm/hardware.h>
//rwen
#include <linux/interrupt.h>
#include <asm/arch/hardware.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-irq.h>
#include <asm/io.h>
#include <linux/compiler.h>
#include <asm/plat-s3c24xx/irq.h>
#include <linux/timer.h>
#include <linux/device.h>
#include <linux/platform_device.h>
//#define DEBUG
#define DEVICE_NAME "RSU_key"
#define RSU_KEY_MAJOR 251
//#define KEY_TEST
#define UCHAR unsigned char
#define KEY_DISABLE 1
#define KEY_ENABLE 0
//74HC164 micro define
#define I_DSB S3C2410_GPH9
#define I_DSB_OUTP S3C2410_GPH9_OUTP
#define I_CP S3C2410_GPH10
#define I_CP_OUTP S3C2410_GPH10_OUTP
//for 8*3 keys
#define ROW_NUM 8
#define COL_NUM ( (sizeof irq_col_tab) / sizeof (irq_col_tab[0]) )
//#define ClearPending(x) {SRCPND = (1 << (x)); INTPND = (1 << (x));}
#define ClearPending(x)
volatile int slowExit;
#define BIT_ALLMSK (0xffffffff)
#define FCLK 202800000
#define HCLK (202800000/2)
#define PCLK (202800000/4)
#define UCLK PCLK
unsigned int saveREFRESH, saveINTMSK;
static int irq_col_tab[] = { IRQ_EINT1, IRQ_EINT2, IRQ_EINT11 };
static unsigned gpio_col_tab[] = { S3C2410_GPF1, S3C2410_GPF2, S3C2410_GPG3 };
static unsigned gpio_col_mode[] = {S3C2410_GPF1_INP, S3C2410_GPF2_INP, S3C2410_GPG3_INP };
static DECLARE_WAIT_QUEUE_HEAD(buttons_wait);
static int waitting_down_tab[] = {1, 1, 1};
static spinlock_t lk;
static int key_value = 0;
#define MAX_KEY_LIST 8192
static unsigned char key_list[MAX_KEY_LIST];
static int key_count = 0;
#define USE_LOCK
#ifdef USE_LOCK
#define lock() spin_lock(lk)
#define unlock() spin_unlock(lk)
#else
#define lock()
#define unlock()
#endif
static struct class *RSU_keys_class;
/*
void Test_SlowMode(void)
{
int i;
slowExit=0;
saveINTMSK = INTMSK;
INTMSK=~(IRQ_EINT1 | IRQ_EINT2 | IRQ_EINT11);
LCDCON1 = LCDCON1 & 0x3fffe; // ENVID Off
//CLKSLOW=7|(1<<4)|(1<<5)|(1<<7); //FCLK=FIN/1,SLOW mode,MPLL=off,UPLL=off
CLKSLOW=0|(1<<4)|(1<<5)|(1<<7); //FCLK=FIN/1,SLOW mode,MPLL=off,UPLL=off
saveREFRESH=REFRESH;
REFRESH=(1<<23)|(unsigned int)(2048+1-12*15.6); //Trp=2clk,Trc=4clk
//The other memory control register should be optimized for SLOW mode.
while(!slowExit)
{
mdelay(1);
}
INTMSK=BIT_ALLMSK;
CLKSLOW=0|(1<<4)|(0<<5);//PLL on,MPLL=on
for(i=0;i<2048;i++); //S/W MPLL lock-time
CLKSLOW=0|(0<<4)|(0<<5);//NORMAL mode,PLL on,MPLL=on
INTMSK = saveINTMSK;
LCDCON1 |= 1; // ENVID=ON
}
*/
static released=1;
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
int i;
int col_no = -1, row_no = -1;
unsigned long flags;
int r;
/*
lock();
local_irq_save(flags);
*/
disable_irq(irq);
#ifdef DEBUG
printk("got irq = %d \n", irq);
#endif
for (i = 0; i < COL_NUM; i++)
{
if (irq_col_tab[i] == irq)
{
col_no = i;
break;
}
}
if (col_no < 0)
{
goto EXIT;
}
udelay(300);
if (waitting_down_tab[col_no])
{
released = 0;
s3c2410_gpio_setpin(I_DSB, 1);
s3c2410_gpio_setpin(I_CP, 1);
s3c2410_gpio_setpin(I_CP, 0);
r = s3c2410_gpio_getpin(gpio_col_tab[col_no]);
if(r > 0)
{
row_no = 0;
udelay(300);
//the follow codes is fore checking wether the button is released.
s3c2410_gpio_setpin(I_DSB, 0);
s3c2410_gpio_setpin(I_CP, 1);
s3c2410_gpio_setpin(I_CP, 0);
// printk("<0>" "before: the value r = %d\n", r);
r = s3c2410_gpio_getpin(gpio_col_tab[col_no]);
// printk("<0>" "after: the value r = %d\n", r);
if (r > 0) // the wrong key value from the dither
goto EXIT;
}
else
{
s3c2410_gpio_setpin(I_DSB, 0);
for(i = 1; i < 8; i++)
{
s3c2410_gpio_setpin(I_CP, 1);
s3c2410_gpio_setpin(I_CP, 0);
r = s3c2410_gpio_getpin(gpio_col_tab[col_no]);
if(r > 0)
{
row_no = i;
break;
}
}
}
if (col_no < 0 || row_no < 0)
{
goto EXIT;
}
//need this?
// s3c2410_gpio_setpin(gpio_col_tab[col_no], 0);
slowExit = 1;
if(key_count < MAX_KEY_LIST)
{
key_value = row_no * 3 + col_no + 1;
key_list[key_count] = key_value;
#ifdef DEBUG
printk("<0>" "event key down, irq = %d, row = %d, col = %d, value = %d\n", irq, row_no, col_no, key_value);
#endif
key_count++;
}
}
else
{
released = 1;
if(key_count < MAX_KEY_LIST)
{
key_list[key_count] = key_value | 0x80;
#ifdef DEBUG
printk("event key up, irq = %d, row = %d, col = %d, value = %d\n", irq, row_no, col_no, key_value);
#endif
key_count++;
}
}
waitting_down_tab[col_no] = !waitting_down_tab[col_no];
set_irq_type(irq, waitting_down_tab[col_no]? IRQT_FALLING: IRQT_RISING);
// set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
wake_up_interruptible(&buttons_wait);
EXIT:
s3c2410_gpio_setpin(I_DSB, 0);
for(i = 0; i < 8; i++)
{
s3c2410_gpio_setpin(I_CP, 1);
s3c2410_gpio_setpin(I_CP, 0);
}
enable_irq(irq);
/*
local_irq_restore(flags);
unlock();
*/
return IRQ_HANDLED;
}
static void buttons_row_port_init(void)
{
//set gpio status
UCHAR i;
s3c2410_gpio_cfgpin(I_DSB, I_DSB_OUTP);
s3c2410_gpio_pullup(I_DSB, 1);
s3c2410_gpio_cfgpin(I_CP, I_CP_OUTP);
s3c2410_gpio_pullup(I_CP, 1);
s3c2410_gpio_setpin(I_DSB, 0);
for(i = 0; i < 8; i++)
{
s3c2410_gpio_setpin(I_CP, 1);
s3c2410_gpio_setpin(I_CP, 0);
}
for (i = 0; i < COL_NUM; i++)
{
s3c2410_gpio_cfgpin(gpio_col_tab[i], gpio_col_mode[i]);
s3c2410_gpio_pullup(gpio_col_tab[i], 1); //disable
}
//reset 74hc164 output
s3c2410_gpio_setpin(I_DSB, 0);
s3c2410_gpio_setpin(I_CP, 0);
}
static int RSU_key_read(struct file * file, char * buffer, size_t count, loff_t *ppos)
{
//static int key;
static int keybuffer[2];
int result;
int i;
if (count != sizeof keybuffer)
{
return -EINVAL;
}
lock();
if(key_count > 0)
{
keybuffer[0] = key_list[0] & 0x7F;
keybuffer[1] = key_list[0] < 0x80;
copy_to_user(buffer, &keybuffer, sizeof(keybuffer));
for(i = 0; i < key_count-1; i++)
{
key_list[i] = key_list[i+1];
}
key_count--;
result = sizeof(keybuffer);
}
else
{
result = -EAGAIN;
}
unlock();
return result;
}
static unsigned int RSU_key_poll(struct file *file, struct poll_table_struct *wait)
{
int r;
lock();
r = key_count > 0;
unlock();
if(r)
{
return 1;
}
else
{
poll_wait(file, &buttons_wait, wait);
return 0;
}
}
static int RSU_key_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
int i;
int irq;
// int fd;
switch(cmd)
{
case 0:
{
key_value = 0;
for (i = 0; i < COL_NUM; i++)
{
irq = irq_col_tab[i];
/// INTMSK &= ~(1 << irq); //unmask interrupts
// ClearPending(irq);
enable_irq(irq);
}
return 0;
}
case 1:
{
key_value = 0;
for (i = 0; i < COL_NUM; i++)
{
irq = irq_col_tab[i];
/// INTMSK |= (1 << irq); //mask interrupts
// ClearPending(irq);
disable_irq(irq);
}
return 0;
}
case 2:
{
// Test_SlowMode();
return 0;
}
default:
return -EINVAL;
}
}
static int request_irqs(void)
{
int i;
int irq;
int ret;
for (i = 0; i < COL_NUM; i++) {
irq = irq_col_tab[i];
set_irq_type(irq, IRQT_FALLING);
// set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
ret = request_irq(irq, &buttons_irq, 0, DEVICE_NAME, &buttons_irq);
if (ret)
{
unregister_chrdev(RSU_KEY_MAJOR, DEVICE_NAME);
printk(DEVICE_NAME " can't request irq %d\n", irq);
return ret;
} else {
#ifdef DEBUG
printk("%dth: request irq %d ok\n",i, irq);
#endif
}
}
}
static void free_irqs(void)
{
int i;
for (i = 0; i < COL_NUM; i++)
{
int irq = irq_col_tab[i];
free_irq(irq, buttons_irq);
}
}
static struct file_operations RSU_key_fops =
{
.owner = THIS_MODULE,
.ioctl = RSU_key_ioctl,
.poll = RSU_key_poll,
.read = RSU_key_read,
};
static int RSU_key_probe(struct platform_device *pdev)
{
int ret;
int i;
int err;
printk("rsukeys init ......\n");
ret = register_chrdev(RSU_KEY_MAJOR, DEVICE_NAME, &RSU_key_fops);
if (ret < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
buttons_row_port_init();
ret = request_irqs();
// setup_timer(&isr_timer, timer_handler, 0);
key_value = 0;
// these need any more?
for (i = 0; i < COL_NUM; i++)
{
int irq;
irq = irq_col_tab[i];
//NO need this anymore
#ifndef DEBUG
// disable_irq(irq);
#endif
}
RSU_keys_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(RSU_keys_class)) {
err = PTR_ERR(RSU_keys_class);
goto ERR;
}
class_device_create(RSU_keys_class, NULL, MKDEV(RSU_KEY_MAJOR, 0), NULL, DEVICE_NAME);
#ifdef DEBUG
printk("init finished.\n");
#endif
ERR:
return 0;
}
static int RSU_key_remove(struct platform_device *pdev)
{
class_device_destroy(RSU_keys_class, MKDEV(RSU_KEY_MAJOR, 0));
class_destroy(RSU_keys_class);
free_irqs();
unregister_chrdev(RSU_KEY_MAJOR, DEVICE_NAME);
return 0;
}
static struct platform_driver rsukeys_driver = {
.driver = {
.name = "rsu-keys",
.owner = THIS_MODULE,
},
.probe = RSU_key_probe,
.remove = RSU_key_remove,
// .suspend = RSU_key_suspend,
// .resume = RSU_key_resume,
};
static int __init rsukeys_init(void)
{
return platform_driver_register(&rsukeys_driver); /* search board and register */
}
static void __exit
rsukeys_cleanup(void)
{
platform_driver_unregister(&rsukeys_driver);
}
module_init(rsukeys_init);
module_exit(rsukeys_cleanup);
MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -