Loading...
/*
* linux/drivers/input/keyboard/android_keypad.c
*
* Modified from driver for the pxa27x matrix keyboard controller.
* Modified from linux/drivers/input/keyboard/pxa27x_keypad.c
*
* Modified:   April 6, 2009
* Author:     Mask
*
* Created:   Feb 22, 2007
* Author:     Rodolfo Giometti
*
* Based on a previous implementations by Kevin O'Connor
*  and Alex Osborne  and
* on some suggestions by Nicolas Pitre .
*
* 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/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/delay.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>

#include <asm/arch/hardware.h>
#include <asm/arch/android_keypad.h>

#include <linux/kthread.h>
#include <linux/freezer.h>

#define MAX_MATRIX_KEY_NUM (4*4)
#define LOG_LEVEL KERN_ALERT

struct android_keypad {
      struct android_keypad_platform_data *pdata;// maybe take off
      struct input_dev *input_dev;
      unsigned long jiffy;
      unsigned long jiffy_diff;
      unsigned long jiffy_start_polling;
      struct task_struct *polling_thread;
      wait_queue_head_t keypad_wq;
      struct mutex mutex;
      spinlock_t spinlock;
      unsigned long idle_period;
      int polling;
};

static DEFINE_MUTEX(mutex_io_reg2);
void io_reg2_write(unsigned short val)
{
      mutex_lock(&mutex_io_reg2);
      IO_REG2 = val;
      mutex_unlock(&mutex_io_reg2);
}

static DEFINE_MUTEX(mutex_io_reg1);
unsigned short io_reg1_read(void)
{
      unsigned short val;
      mutex_lock(&mutex_io_reg1);
      val = IO_REG1;
      mutex_unlock(&mutex_io_reg1);
      return val;
}
/*  1(KEY_BACK) 2(KEY_UP) 3(KEY_H) A(KEY_MENU)
 *  4(KEY_LEFT) 5(KEY_RESERVED) 6(KEY_RIGHT) B(KEY_HOME)
 *  7(KEY_T) 8(KEY_DOWN) 9(KEY_P) C(KEY_BACKSPACE)
 *  *(KEY_LEFTSHIFT)0(KEY_DOT) #(KEY_SLASH) D(KEY_SPACE)
*/
struct android_keymap {
      unsigned int hw_key;
      unsigned int keycode;
      unsigned int shift_keycode;
};
#define KEY(r3,r2,r1,r0, c3,c2,c1,c0) \
( (r3<<7)|(r2<<6)|(r1<<5)|(r0<<4)|(c3<<3)|(c2<<2)|(c1<<1)|c0 )
static struct android_keymap keymap[] = {
{KEY(1,1,1,0, 1,1,1,0),      KEY_1,      KEY_BACK},
{KEY(1,1,1,0, 1,1,0,1),      KEY_2,      KEY_UP},
{KEY(1,1,1,0, 1,0,1,1),      KEY_3,      KEY_H},
{KEY(1,1,1,0, 0,1,1,1),      KEY_A,      KEY_MENU},
{KEY(1,1,0,1, 1,1,1,0),      KEY_4,      KEY_LEFT},
{KEY(1,1,0,1, 1,1,0,1),      KEY_5,      KEY_RESERVED},
{KEY(1,1,0,1, 1,0,1,1),      KEY_6,      KEY_RIGHT},
{KEY(1,1,0,1, 0,1,1,1),      KEY_B,      KEY_HOME},
{KEY(1,0,1,1, 1,1,1,0),      KEY_7,      KEY_T},
{KEY(1,0,1,1, 1,1,0,1),      KEY_8,      KEY_DOWN},
{KEY(1,0,1,1, 1,0,1,1),      KEY_9,      KEY_P},
{KEY(1,0,1,1, 0,1,1,1),      KEY_C,      KEY_BACKSPACE},
{KEY(0,1,1,1, 1,1,1,0),      KEY_LEFTSHIFT, KEY_LEFTSHIFT},
{KEY(0,1,1,1, 1,1,0,1),      KEY_0,      KEY_DOT},
{KEY(0,1,1,1, 1,0,1,1),      KEY_0,      KEY_SLASH},
{KEY(0,1,1,1, 0,1,1,1),      KEY_D,      KEY_SPACE},
{0,KEY_UNKNOWN, KEY_UNKNOWN },
};
static void android_keypad_setkeycode(struct android_keypad *keypad)
{
      struct input_dev *input_dev = keypad->input_dev;
      unsigned int i;
      for(i = 0; keymap[i].keycode != KEY_UNKNOWN; ++i)
      {
            set_bit(keymap[i].keycode,input_dev->keybit);
            set_bit(keymap[i].shift_keycode,input_dev->keybit);
      }
}

static unsigned int lookup_keycode(unsigned int hw_key)
{
      unsigned int i;
      static bool shift_key = false; // maybe change to static
      for(i = 0; keymap[i].keycode != KEY_UNKNOWN; ++i)
      {
            if(keymap[i].hw_key == hw_key)
                  break;
      }
      if(keymap[i].keycode == KEY_LEFTSHIFT)
            shift_key = !shift_key;
      if(shift_key)
            return keymap[i].shift_keycode;
      return keymap[i].keycode;
}

#define MAX_IDLE_MSEC (1*700) // 0.7 sec
#define MIN_IDLE_MSEC (1*100) // 0.1 sec
#define IDLE_STEPS 3
static unsigned int min_idle_msec = MIN_IDLE_MSEC;
static unsigned int max_idle_msec = MAX_IDLE_MSEC;
static unsigned int idle_steps = IDLE_STEPS;
#define SCAN(r3,r2,r1,r0) ( (r3<<3)|(r2<<2)|(r1<<1)|r0 )
unsigned short scan_key[] = {
SCAN(1,1,1,0),
SCAN(1,1,0,1),
SCAN(1,0,1,1),
SCAN(0,1,1,1),
};
static int android_keypad_thread(void * data)
{
      struct android_keypad *keypad = data;
      struct sched_param param = { .sched_priority = 1 };
      unsigned short scan;
      unsigned int keycode;
      int i;
      keypad->idle_period = min_idle_msec;
      sched_setscheduler(current, SCHED_FIFO, &param);
      current->flags |= PF_NOFREEZE;
      do {
            i = 0;
            do
            {
                  scan = scan_key[i];
                  io_reg2_write(scan); // maybe change to 0xe because bit 4~7 don't care
                  keycode = lookup_keycode( (scan << 4) | ((io_reg1_read() & 0x0f00) >>
8));
                  if(keycode != KEY_UNKNOWN)
                        break;
                  ++i;
            } while(i < ARRAY_SIZE(scan_key));
            if(keycode == KEY_UNKNOWN) // no input --- need to modify
            {
                  if(keypad->idle_period < max_idle_msec)
                        keypad->idle_period += (max_idle_msec -
min_idle_msec)/idle_steps;
                  if(keypad->idle_period > max_idle_msec)
                        keypad->idle_period = max_idle_msec;
      }
      else
      {
                  // calculate jiffies to cancel fast continuing key
                  keypad->jiffy_diff = jiffies - keypad->jiffy;
                  keypad->jiffy = jiffies;
                  input_report_key(keypad->input_dev,keycode,1);
                  input_sync(keypad->input_dev);
                  input_report_key(keypad->input_dev,keycode,0);
                  input_sync(keypad->input_dev);
                  keypad->idle_period = min_idle_msec;
                  printk(LOG_LEVEL "%s: jiffy_diff = %lu\n",
__func__,keypad->jiffy_diff);
                  printk(LOG_LEVEL "%s: keycode = %d\n",__func__,keycode);
            }
            set_task_state(current, TASK_INTERRUPTIBLE);
            if (!kthread_should_stop())
                  schedule_timeout(msecs_to_jiffies(keypad->idle_period));
            set_task_state(current, TASK_RUNNING);
      } while(!kthread_should_stop());
      return 0;
}

static int android_keypad_open(struct input_dev *dev)
{
      struct android_keypad *keypad = input_get_drvdata(dev); // remember check
"private" in _probe function
      int err;

      keypad->polling_thread =
kthread_run(android_keypad_thread,keypad,"kandroid_keypadd");
      if(IS_ERR(keypad->polling_thread))
      {
            err = PTR_ERR(keypad->polling_thread);
            printk(LOG_LEVEL "%s: create kthread ERROR: %d\n",__func__,err);
            return err;
      }
      return 0;
}

static void android_keypad_close(struct input_dev *dev)
{
      struct android_keypad *keypad = input_get_drvdata(dev);
      kthread_stop(keypad->polling_thread);
}

#ifdef CONFIG_PM
static int android_keypad_suspend(struct platform_device *pdev, pm_message_t state)
{
      struct android_keypad *keypad = platform_get_drvdata(pdev);

      mutex_lock(&keypad->mutex);
      keypad->idle_period = max_idle_msec;
      mutex_unlock(&keypad->mutex);

      return 0;
}

static int android_keypad_resume(struct platform_device *pdev)
{
      struct android_keypad *keypad = platform_get_drvdata(pdev);

      mutex_lock(&keypad->mutex);
      keypad->idle_period = min_idle_msec;
      mutex_unlock(&keypad->mutex);

      return 0;
}
#else
#define android_keypad_suspend NULL
#define android_keypad_resume NULL
#endif
static int __devinit android_keypad_probe(struct platform_device *pdev)
{
      struct android_keypad *keypad;
      struct input_dev *input_dev;
      int error;

      keypad = kzalloc(sizeof(struct android_keypad), GFP_KERNEL);
      if (keypad == NULL) {
      dev_err(&pdev->dev, "failed to allocate driver data\n");
      return -ENOMEM;
      }

      mutex_init(&keypad->mutex);
      spin_lock_init(&keypad->spinlock);

      /* Create and register the input driver. */
      input_dev = input_allocate_device();
      if (!input_dev) {
      dev_err(&pdev->dev, "failed to allocate input device\n");
      error = -ENOMEM;
      goto failed_free;
      }

      input_dev->name = pdev->name;
      input_dev->id.bustype = BUS_HOST;
      input_dev->open = android_keypad_open;
      input_dev->close = android_keypad_close;
      input_dev->dev.parent = &pdev->dev;

      keypad->input_dev = input_dev;
      input_set_drvdata(input_dev, keypad);

      set_bit(EV_KEY,input_dev->evbit);
      set_bit(EV_REL,input_dev->evbit);

      android_keypad_setkeycode(keypad);
      platform_set_drvdata(pdev, keypad);

      /* Register the input device */
      error = input_register_device(input_dev);
      if (error) {
            dev_err(&pdev->dev, "failed to register input device\n");
            goto failed_free_dev;
      }

      return 0;
failed_free_dev:
      platform_set_drvdata(pdev, NULL);
      input_free_device(input_dev);
failed_free:
      kfree(keypad);
      return error;
}

static int __devexit android_keypad_remove(struct platform_device *pdev)
{
      struct android_keypad *keypad = platform_get_drvdata(pdev);

      input_unregister_device(keypad->input_dev);
      input_free_device(keypad->input_dev);

      platform_set_drvdata(pdev, NULL);
      kfree(keypad);
      return 0;
}

static struct platform_driver android_keypad_driver = {
      .probe      = android_keypad_probe,
      .remove      = __devexit_p(android_keypad_remove),
      .suspend      = android_keypad_suspend,
      .resume      = android_keypad_resume,
      .driver      = {
            .name      = "android-keypad",
      },
};

static int __init android_keypad_init(void)
{
      return platform_driver_register(&android_keypad_driver);
}

static void __exit android_keypad_exit(void)
{
      platform_driver_unregister(&android_keypad_driver);
}

module_init(android_keypad_init);
module_exit(android_keypad_exit);

MODULE_DESCRIPTION("Android Keypad Controller Driver");
MODULE_LICENSE("GPL");
Loading...