static unsigned char kpdKeyMachineState;
static unsigned int kpdPressCount;
static unsigned int kpdRepeatCount;
static unsigned char kpdCurrButtonCode;
static unsigned char kpdCurrScanCode;
static unsigned char kpdKeyCount;
static void kpdKeyMachineInit( void )
{
unsigned int value;
/* reset machine state */
kpdKeyMachineState = KPD_START;
kpdKeyCount = 0;
value=readl(GPIO_DATA(6));
value&=0xf0;
writel(value, GPIO_DATA(6));
return;
}
static void keypad_timer_handler(unsigned long data)
{
unsigned char key;
key = keypad_scan_keyval();
switch( kpdKeyMachineState )
{
case KPD_START:
if( key == KPD_NO_KEY )
{
kpdKeyMachineInit();
break;
}
/* get current scan code and change state machine */
kpdCurrScanCode = key;
kpdKeyMachineState = KPD_CHECK_PRESS;
break;
case KPD_CHECK_PRESS:
if( key == kpdCurrScanCode )
{
/* ok make sure the key is pressed, and change scan code to button code*/
kpdCurrButtonCode = keypad_scan_keyval();
key = kpdCurrButtonCode | 0x80;
key_push((const char*)&key, 1);
// printk("%d down\n", kpdCurrButtonCode);
kpdKeyCount++;
kpdKeyMachineState = KPD_WAIT_RELEASE;
}
else /* key bounce,enable keypad interrupt */
{
kpdKeyMachineInit();
}
break;
case KPD_WAIT_RELEASE:
if( key == KPD_NO_KEY )
{
kpdKeyMachineState = KPD_CHECK_RELEASE;
}
else if( key == kpdCurrScanCode)
{
/* The key still pressed*/
kpdPressCount++;
/* if reached repeat count, produce a repeat key */
if( kpdPressCount == kpdRepeatCount )
{
kpdRepeatCount += 4;
if(kpdCurrButtonCode>=1 && kpdCurrButtonCode<=63)
{
key = kpdCurrButtonCode | 0xC0;
key_push((const char*)&key, 1);
// printk("%d repeat\n", kpdCurrButtonCode);
}
}
}
else
{
kpdKeyMachineInit();
}
break;
case KPD_CHECK_RELEASE:
if( key != KPD_NO_KEY )
{
kpdKeyMachineState = KPD_WAIT_RELEASE;
}
else
{
kpdPressCount = 0;
kpdRepeatCount = 15;
kpdKeyMachineInit();
key = kpdCurrButtonCode & ~0x80;
key_push((const char*)&key, 1);
kpdKeyCount--;
// printk("%d up\n", kpdCurrButtonCode);
}
break;
default:
break;
}//switch( kpdKeyMachineState )
key_timer.expires = jiffies + 10;
add_timer(&key_timer);
}
static int keypad_timer_init(void)
{
init_timer(&key_timer);
key_timer.function = keypad_timer_handler;
key_timer.expires = jiffies + 20;
return 0;
}
static void keypad_hardware_init(void)
{
unsigned int value;
value=readl(GPIO_DIR(5));
value&=0xf0;
writel(value, GPIO_DIR(5));
value=readl(GPIO_DIR(6));
value|=0x0f;
writel(value, GPIO_DIR(6));
value=readl(GPIO_DATA(6));
value&=0xf0;
writel(value, GPIO_DATA(6));
}
static void keypad_context_init(void)
{
init_MUTEX(&key_pipe->sem);
spin_lock_init (&key_pipe->lock);
key_pipe->keypad_fifo = kfifo_alloc(KEYPAD_BUFFER_SIZE, GFP_KERNEL, &key_pipe->lock);
init_waitqueue_head(&key_pipe->queue);
keypad_timer_init();
kpdKeyMachineInit();
}
static size_t key_push(const char *buf, size_t count)
{
ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
if (down_interruptible(&key_pipe->sem))
return -ERESTARTSYS;
if ( count>KEYPAD_BUFFER_SIZE ) count = KEYPAD_BUFFER_SIZE;
count = kfifo_put(key_pipe->keypad_fifo, buf, count);
retval = count;
up(&key_pipe->sem);
/* finally, awake any reader */
wake_up_interruptible(&key_pipe->queue); /* blocked in read() and select() */
return retval ;
}
static int keypad_open(struct inode *inode, struct file *filp)
{
struct key_pipe_t *dev; /* device information */
dev = container_of(inode->i_cdev, struct key_pipe_t, cdev);
filp->private_data = dev; /* for other methods */
//printk("%s: keypad_open\n", current->comm);
key_timer.expires = jiffies + 20;
add_timer(&key_timer);
return nonseekable_open(inode, filp); /* success */
}
static int keypad_release(struct inode *inode, struct file *filp)
{
//printk("%s: keypad_release\n", current->comm);
del_timer(&key_timer);
return 0;
}
/*
* Data management: read and write
*/
static ssize_t keypad_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct key_pipe_t *dev = filp->private_data;
ssize_t retval = 0;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
while (!kfifo_len(dev->keypad_fifo)) { /* nothing to read */
up(&dev->sem); /* release the lock */
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
printk("\"%s\" reading: going to sleep\n", current->comm);
if (wait_event_interruptible(dev->queue, kfifo_len(dev->keypad_fifo)))
return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
/* otherwise loop, but first reacquire the lock */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
if (count > kfifo_len(dev->keypad_fifo))
count = kfifo_len(dev->keypad_fifo);
count = kfifo_get(dev->keypad_fifo, keypad_buffer, count);
if (copy_to_user(buf, keypad_buffer, count)) {
retval = -EFAULT;
goto out;
}
retval = count;
out:
up(&dev->sem);
return retval;
}
static unsigned int keypad_poll(struct file *filp, poll_table *wait)
{
struct key_pipe_t *dev = filp->private_data;
unsigned int mask=0;
poll_wait(filp, &dev->queue, wait);
if(kfifo_len(dev->keypad_fifo))
mask |= POLLIN | POLLRDNORM; /* readable */
return mask;
}
/*
* The file operations for the keypad device
*/
struct file_operations keypad_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = keypad_read,
.poll = keypad_poll,
.open = keypad_open,
.release = keypad_release,
};
/*
* Set up the char_dev structure for this device.
*/
static void keypad_setup_cdev(struct key_pipe_t *dev)
{
int err, devno = MKDEV(keypad_major, keypad_minor );
cdev_init(&dev->cdev, &keypad_dev_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding keypad", err);
}
int __init keypad_init(void)
{
int result;
dev_t dev = 0;
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (keypad_major) {
dev = MKDEV(keypad_major, keypad_minor);
result = register_chrdev_region(dev, 1, "keypad");
} else {
result = alloc_chrdev_region(&dev, keypad_minor, 1,
"keypad");
keypad_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "keypad: can't get major %d\n", keypad_major);
return result;
}
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
gpio_base[5] = ioremap(0x2002c000, SZ_4K);
if(gpio_base[5]==NULL){
printk("<1> remap 2002c000 error\n");
return -ENODEV;
}
gpio_base[6] = ioremap(0x2002d000, SZ_4K);
if(gpio_base[6]==NULL){
printk("<1> remap 2002d000 error\n");
iounmap(gpio_base[5]);
return -ENODEV;
}
key_pipe = kmalloc(sizeof(struct key_pipe_t), GFP_KERNEL);
if (!key_pipe) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(key_pipe, 0, sizeof(struct key_pipe_t));
keypad_buffer = kmalloc(KEYPAD_BUFFER_SIZE, GFP_KERNEL);
if (!keypad_buffer) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
keypad_hardware_init();
keypad_context_init();
keypad_setup_cdev(key_pipe);
return 0;
fail:
kfree(key_pipe);
if (keypad_buffer) kfree(keypad_buffer);
return result;
}
void __exit keypad_cleanup(void)
{
dev_t devno = MKDEV(keypad_major, keypad_minor);
/* Get rid of our char dev entries */
if (key_pipe) {
if (key_pipe->keypad_fifo) {
kfifo_free(key_pipe->keypad_fifo);
}
cdev_del(&key_pipe->cdev);
kfree(key_pipe);
}
/* cleanup_module is never called if registering failed */
del_timer(&timer);
if (keypad_buffer) kfree(keypad_buffer);
iounmap(gpio_base[5]);
iounmap(gpio_base[6]);
unregister_chrdev_region(devno, 1);
}
module_init(keypad_init);
module_exit(keypad_cleanup);
MODULE_AUTHOR("Li Huiwu <>");
MODULE_DESCRIPTION("KeyPad Driver for SAT");
MODULE_LICENSE("GPL");