/*****************************************************************************/
/* Copyright (c) 2008 NXP B.V.                                               */
/*                                                                           */
/* This program is free software; you can redistribute it and/or modify      */
/* it under the terms of the GNU General Public License as published by      */
/* the Free Software Foundation, using version 2 of the License.             */
/*                                                                           */
/* This program is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
/* GNU General Public License for more details.                              */
/*                                                                           */
/* You should have received a copy of the GNU General Public License         */
/* along with this program; if not, write to the Free Software               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307       */
/* USA.                                                                      */
/*                                                                           */
/*****************************************************************************/
/**
   @file   phStbKey.c
   @brief  Frontpanel keyboard device driver.

   @b  Component:  phStbKey.

   Set your editor for 4 space indentation.
*/
/**
Rev Date       Author      Comments
--------------------------------------------------------------------------------
  1 20061123   neill       Original
  2 20061205   neill       Add device to input class
  3 20070111   neill       Standardise front panel and remote control key handling
  4 20070112   steel       Update for 2.6.19 kernel
  5 20070118   neill       Change class device name
  6 20070118   neill       Unify GPL header
  7 20080205   batelaan    Update file copyright/license text
  8 20080319   neill       Reduce frequency of key scanning
  9 20080829   batelaan    Print error on input_register_device failure
  9 20080920   legendrd    Change key mapping for STB use-case
  9.1 20081001 legendrd    Backport modification of Mike Neill
 10 20080925   neill       Add support for software keyboard using misc device
 11 20080930   neill       Add module parameter for release key sending
 12 20081202   laird       Merged from 'phStbKey.c-11:csrc:SOUsb225#1' and 'phStbKey.c-CFR_STB#10:csrc:SOUsb225#1' by laird.
 13 20090121   batelaan    Update author/license.
*/

#define __PHSTBKEY_C__

#include <asm/io.h>
#include <asm/mach-pnx833x/irq.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/major.h>

#include <tmosal.h>
#include "tmdlPio.h"

#include <linux/miscdevice.h>
#include "phStbKey.h"

/******************************************************************************
* LOCAL MACROS                                                                *
*******************************************************************************/

/**** Module Setup ****/
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION(PHSTBKEY_MODULE_DESCRIPTION);
MODULE_AUTHOR("NXP B.V.");

//  ----------------------- PHSTBKEY MODULE Debug Level   ------------------------
static int debug = 0;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-1)");


//  ----------------------- PHSTBKEY Kernel Debug --------------------------------
#ifdef __PHSTBKEY_C__
#undef  dprintk
#define dprintk(num, format, args...)                                           \
    do {                                                                        \
        if (debug >= num) printk(format, ##args);                               \
    } while (0)
#endif

/******************************************************************************
* LOCAL TYPEDEFS                                                              *
*******************************************************************************/

/******************************************************************************
* STATIC FUNCTION PROTOTYPES                                                  *
*******************************************************************************/

/******************************************************************************
* EXPORTED DATA                                                               *
*******************************************************************************/

/******************************************************************************
* LOCAL DATA                                                                  *
*******************************************************************************/

static unsigned int keyList[] = { 
    KEY_KP0,
    KEY_KP1, 
    KEY_KP2, 
    KEY_KP3, 
    KEY_KP4, 
    KEY_KP5, 
    KEY_KP6, 
    KEY_KP7, 
    KEY_KP8,
    KEY_KP9,
};

#define NUM_KEYS sizeof(keyList)/sizeof(unsigned int)
#define PSU_KEY  (9)

#define PHSTBKEY_EVENT_ID 1

#define PHSTBKEY_TASK_DELAY ((HZ) / 100)
#define PHSTBKEY_TIMEOUT 35

#define PSU_PIN   (41)
#define PSU_MASK  ((tmdlPioLines_t)1<<PSU_PIN)

#define COL0_PIN  (15)
#define COL1_PIN  (17)
#define COL2_PIN  (19)
#define COL0_MASK ((tmdlPioLines_t)1<<COL0_PIN)
#define COL1_MASK ((tmdlPioLines_t)1<<COL1_PIN)
#define COL2_MASK ((tmdlPioLines_t)1<<COL2_PIN)

#define ROW0_PIN  (38)
#define ROW1_PIN  (18)
#define ROW2_PIN  (40)
#define ROW0_MASK ((tmdlPioLines_t)1<<ROW0_PIN)
#define ROW1_MASK ((tmdlPioLines_t)1<<ROW1_PIN)
#define ROW2_MASK ((tmdlPioLines_t)1<<ROW2_PIN)

#define BUFFER_SIZE (16)

static tmInstance_t         phStbKey_pioInstance = (tmInstance_t)Null;
static const tmdlPioLines_t columnInput[3] = { COL0_MASK, COL1_MASK, COL2_MASK };
static unsigned int         cycleValue = 0;
static int                  phStbKeyInitialised = 0;
static struct               timer_list timer;
static struct input_dev     *phStbKey_input_dev;

static int                  key_sendrelease = 0;

/******************************************************************************
* FUNCTION IMPLEMENTATION                                                     *
*******************************************************************************/

/*=============================================================================
| File/Device functions                                                       |
==============================================================================*/

/*=============================================================================
| Interrupt Handler                                                           |
==============================================================================*/

/*=============================================================================
| Module functions                                                            |
==============================================================================*/

static void setupOutput(int which)
{
    tmErrorCode_t error;

    error = tmdlPioWrite(phStbKey_pioInstance, (tmdlPioLines_t)(COL0_MASK|COL1_MASK|COL2_MASK), columnInput[which]);
    if (error != TM_OK) 
    {
        dprintk(1, "Error returned from tmdlPioWrite %x\n", error);
    }
}

static int checkOutput(int columnEnable)
{
    tmErrorCode_t error;
    tmdlPioLines_t bitPattern;

    error = tmdlPioRead(phStbKey_pioInstance, (tmdlPioLines_t)(ROW0_MASK|ROW1_MASK|ROW2_MASK), &bitPattern);
    if (error == TM_OK) 
    {
        if (bitPattern & ROW0_MASK)
        {
            return (columnEnable*3);
        }
        if (bitPattern & ROW1_MASK)
        {
            return (columnEnable*3)+1;
        }
        if (bitPattern & ROW2_MASK)
        {
            return (columnEnable*3)+2;
        }
    }
    else
    {
        dprintk(1, "Error returned from tmdlPioRead %x\n", error);
    }
    return -1;
}

static int checkPSU(void)
{
    tmErrorCode_t error;
    tmdlPioLines_t bitPattern;
    error = tmdlPioRead(phStbKey_pioInstance, (tmdlPioLines_t)(PSU_MASK), &bitPattern);
    if (error == TM_OK) 
    {
        if ((bitPattern & PSU_MASK)==0)
        {
            return PSU_KEY;
        }
    }
    else
    {
        dprintk(1, "Error returned from tmdlPioRead %x\n", error);
    }
    return -1;
}

static void phStbKey_timer (unsigned long data)
{
   static int columnEnable = 0;
   static int timeout = 0;
   static int lastKey = -1;
   static int savedKey = -1;
   static bool pressed = false;
   unsigned long newTimeout = jiffies + PHSTBKEY_TASK_DELAY;
   int thisKey;

   if (timeout)
   {
       timeout--;
   }

   thisKey = checkOutput(columnEnable);
   if (thisKey < 0)
   {
       thisKey = checkPSU();
   }

   if (key_sendrelease)
   {
       if (timeout == 0) 
       {
           if (thisKey >= 0) 
           {
               input_report_key( phStbKey_input_dev, keyList[thisKey], 1 );
               input_report_key( phStbKey_input_dev, keyList[thisKey], 0 );
               timeout = PHSTBKEY_TIMEOUT;
           }
       }
   }
   else
   {
       if (timeout == 0)
       {
           /* Check if a key has already been pressed */
           if (pressed)
           {
               /* A different key OR no key at all was pressed */
               if (((thisKey >= 0) && (thisKey != lastKey))||
                   (savedKey == -1))
               {
                   /* Key released */
                   input_report_key( phStbKey_input_dev, keyList[lastKey], 0 );
                   lastKey = -1;
                   pressed = false;
               }
               else
               {
                   /* Repeated key press - dealt with by auto-repeat */
                   timeout = PHSTBKEY_TIMEOUT;
               }
           }

           if ((thisKey >= 0) && (thisKey != lastKey))
           {
               /* New (initial) key press */
               input_report_key( phStbKey_input_dev, keyList[thisKey], 1 );
               timeout = PHSTBKEY_TIMEOUT;
               lastKey = thisKey;
               pressed = true;
           }
           else
           {
               /* No key pressed - Do nothing more ! */
           }

           savedKey = -1;
       }
       else
       {
           /* Avoid key bounce - by waiting before checking keys */
           if (timeout < (PHSTBKEY_TIMEOUT / 2))
           {
               if (thisKey >= 0)
               {
                   /* Check to see if the same key is still being pressed */
                   if (thisKey == lastKey)
                   {
                       savedKey = thisKey;
                   }
                   else
                   {
                       /* A different key is being pressed */
                       if (pressed)
                       {
                           /* Release previously pressed key */
                           input_report_key( phStbKey_input_dev, keyList[lastKey], 0 );
                       }

                       /* New (initial) key press */
                       input_report_key( phStbKey_input_dev, keyList[thisKey], 1 );
                       lastKey = thisKey;
                       savedKey = -1;
                       timeout = PHSTBKEY_TIMEOUT;
                       pressed = true;
                   }
               }
           }
       }
   }

   cycleValue++;
   if ((cycleValue%4)==0) 
   {
       columnEnable++;
       if (columnEnable==3) 
       {
           columnEnable=0;
       }
       setupOutput(columnEnable);
   }

   mod_timer(&timer, newTimeout);
}


static int phStbKey_open( struct input_dev * pDev )
{
    tmdlPioInstanceSetup_t pioSetup;
    tmdlPioIcfgCmd_t       pioCfgCmd;
    tmdlPioSetModeMask_t   pioCfgData;
    tmErrorCode_t error;

    if (phStbKeyInitialised)
    {
        return -1;
    }

    error = tmdlPioOpen( &phStbKey_pioInstance );
    if ( error == TM_OK )
    {
        pioSetup.lineSelect = (COL0_MASK|COL1_MASK|COL2_MASK) | (ROW0_MASK|ROW1_MASK|ROW2_MASK) | PSU_MASK;
        
        error = tmdlPioInstanceSetup( phStbKey_pioInstance, &pioSetup );
        if ( error == TM_OK )
        {
            pioCfgCmd = tmdlPioCmdSetMode;
            pioCfgData.lineSelInput  = (ROW0_MASK|ROW1_MASK|ROW2_MASK|PSU_MASK);
            pioCfgData.lineSelOutput = (COL0_MASK|COL1_MASK|COL2_MASK);
            pioCfgData.lineSelPeriph = 0;
            
            error = tmdlPioInstanceConfig( phStbKey_pioInstance, pioCfgCmd, (pVoid)&pioCfgData  );
            if (error != TM_OK) 
            {
                dprintk(1, "Error returned from tmdlPioInstanceConfig %x\n", error);
            }
        }
        else
        {
            dprintk(1, "Error returned from tmdlPioInstanceSetup %x\n", error);
        }
    }
    else
    {
        dprintk(1, "Error returned from tmdlPioOpen %x\n", error);
    }

    setupOutput(0);

    /* create timer task to modify GPIO outputs */
    init_timer(&timer);
    timer.expires = jiffies + PHSTBKEY_TASK_DELAY;
    timer.data = 0;
    timer.function = phStbKey_timer;
    add_timer(&timer);

    phStbKeyInitialised = 1;
    return 0;
}

static void phStbKey_close( struct input_dev * pDev )
{
    if (phStbKeyInitialised)
    {
        del_timer(&timer);
        tmdlPioClose(phStbKey_pioInstance);
        phStbKeyInitialised = 0;
    }

}

/* *********************************************************** */
/* *****                                                 ***** */
/* ***** Software keyboard implementation                ***** */
/* *****                                                 ***** */
/* *********************************************************** */

static char keycodes[512] ;

static void set_keycodes(int in, int out)
{
  keycodes[in]=out ;
  set_bit (out, phStbKey_input_dev->keybit);
}

static void init_swkeyboard_keycodes(void)
{
  memset (keycodes,0, 512);

  /* letters */
  set_keycodes('a',KEY_A );
  set_keycodes('b',KEY_B );
  set_keycodes('c',KEY_C );
  set_keycodes('d',KEY_D );
  set_keycodes('e',KEY_E );
  set_keycodes('f',KEY_F );
  set_keycodes('g',KEY_G );
  set_keycodes('h',KEY_H );
  set_keycodes('i',KEY_I );
  set_keycodes('j',KEY_J );
  set_keycodes('k',KEY_K );
  set_keycodes('l',KEY_L );
  set_keycodes('o',KEY_O );
  set_keycodes('m',KEY_M );
  set_keycodes('n',KEY_N );
  set_keycodes('p',KEY_P );
  set_keycodes('q',KEY_Q );
  set_keycodes('r',KEY_R );
  set_keycodes('s',KEY_S );
  set_keycodes('t',KEY_T );
  set_keycodes('u',KEY_U );
  set_keycodes('w',KEY_W );
  set_keycodes('v',KEY_V );
  set_keycodes('x',KEY_X );
  set_keycodes('y',KEY_Y );
  set_keycodes('z',KEY_Z );

  /* digits */
  set_keycodes('0',KEY_0 );
  set_keycodes('1',KEY_1 );
  set_keycodes('2',KEY_2 );
  set_keycodes('3',KEY_3 );
  set_keycodes('4',KEY_4 );
  set_keycodes('5',KEY_5 );
  set_keycodes('6',KEY_6 );
  set_keycodes('7',KEY_7 );
  set_keycodes('8',KEY_8 );
  set_keycodes('9',KEY_9 );

  /* specials ..... */
  set_keycodes(':', KEY_SEMICOLON);
  set_keycodes(';', KEY_SEMICOLON);
  set_keycodes(' ', KEY_SPACE);
  set_keycodes('-', KEY_KPMINUS);
  set_keycodes('+', KEY_KPPLUS);
  set_keycodes('=', KEY_EQUAL);
  set_keycodes(',', KEY_COMMA);
  set_keycodes('.', KEY_DOT);
  set_keycodes('\'', KEY_APOSTROPHE);
  set_keycodes('/', KEY_SLASH);
  set_keycodes('`', KEY_GRAVE);
  
  /* just set them....*/
  set_bit (KEY_SEMICOLON,  phStbKey_input_dev->keybit);
  set_bit (KEY_SPACE,      phStbKey_input_dev->keybit);
  set_bit (KEY_LEFTBRACE,  phStbKey_input_dev->keybit);
  set_bit (KEY_RIGHTBRACE, phStbKey_input_dev->keybit);
  set_bit (KEY_BACKSPACE,  phStbKey_input_dev->keybit);

  set_bit (KEY_ENTER,  phStbKey_input_dev->keybit);
  set_bit (KEY_ESC,  phStbKey_input_dev->keybit);
  set_bit (KEY_TAB,  phStbKey_input_dev->keybit);

  set_bit (KEY_LEFTSHIFT, phStbKey_input_dev->keybit);
  set_bit (KEY_RIGHTSHIFT, phStbKey_input_dev->keybit);
  set_bit (KEY_LEFTCTRL, phStbKey_input_dev->keybit);
  set_bit (KEY_RIGHTCTRL, phStbKey_input_dev->keybit);
  set_bit (KEY_LEFTALT, phStbKey_input_dev->keybit);

  set_bit (KEY_CAPSLOCK, phStbKey_input_dev->keybit);
  set_bit (KEY_APOSTROPHE, phStbKey_input_dev->keybit);
  set_bit (KEY_GRAVE, phStbKey_input_dev->keybit);
  set_bit (KEY_SLASH, phStbKey_input_dev->keybit);

  set_bit (KEY_F1 , phStbKey_input_dev->keybit);
  set_bit (KEY_F2 , phStbKey_input_dev->keybit);
  set_bit (KEY_F3 , phStbKey_input_dev->keybit);
  set_bit (KEY_F4 , phStbKey_input_dev->keybit);
  set_bit (KEY_F5 , phStbKey_input_dev->keybit);
  set_bit (KEY_F6 , phStbKey_input_dev->keybit);
  set_bit (KEY_F7 , phStbKey_input_dev->keybit);
  set_bit (KEY_F8 , phStbKey_input_dev->keybit);
  set_bit (KEY_F9 , phStbKey_input_dev->keybit);
  set_bit (KEY_F10 , phStbKey_input_dev->keybit);
  set_bit (KEY_F11 , phStbKey_input_dev->keybit);
  set_bit (KEY_F12 , phStbKey_input_dev->keybit);
  set_bit (KEY_F13 , phStbKey_input_dev->keybit);
  set_bit (KEY_F14 , phStbKey_input_dev->keybit);
  set_bit (KEY_F15 , phStbKey_input_dev->keybit);
  set_bit (KEY_F16 , phStbKey_input_dev->keybit);
  set_bit (KEY_F17 , phStbKey_input_dev->keybit);
  set_bit (KEY_F18 , phStbKey_input_dev->keybit);
  set_bit (KEY_F19 , phStbKey_input_dev->keybit);
  set_bit (KEY_F20 , phStbKey_input_dev->keybit);

  set_bit ( KEY_LEFT,  phStbKey_input_dev->keybit);
  set_bit ( KEY_RIGHT, phStbKey_input_dev->keybit);
  set_bit ( KEY_UP,	   phStbKey_input_dev->keybit);
  set_bit ( KEY_DOWN,  phStbKey_input_dev->keybit);

  set_bit ( KEY_BACKSLASH, phStbKey_input_dev->keybit);
}

static void press_rel (int key)
{
    input_report_key(phStbKey_input_dev, key, 1); /* keypress */
    input_report_key(phStbKey_input_dev, key, 0); /* release */
}

static void press (int key)
{
    input_report_key(phStbKey_input_dev, key, 1); /* keypress */
}

static void release (int key)
{
    input_report_key(phStbKey_input_dev, key, 0); /* release */
}

static void fake_esc (char *str)
{
    if (str[0]=='F')
    {
        int key;
        if (strcmp(str,"F1")==0)
            key=KEY_F1;
        if (strcmp(str,"F2")==0)
            key=KEY_F2;
        if (strcmp(str,"F3")==0)
            key=KEY_F3;
        if (strcmp(str,"F4")==0)
            key=KEY_F4;
        if (strcmp(str,"F5")==0)
            key=KEY_F5;
        if (strcmp(str,"F6")==0)
            key=KEY_F6;
        if (strcmp(str,"F7")==0)
            key=KEY_F7;
        if (strcmp(str,"F8")==0)
            key=KEY_F8;
        if (strcmp(str,"F9")==0)
            key=KEY_F9;
        else if (strcmp(str,"F10")==0)
            key=KEY_F10;
        else if (strcmp(str,"F11")==0)
            key=KEY_F11;
        else if (strcmp(str,"F12")==0)
            key=KEY_F12;
        else if (strcmp(str,"F13")==0)
            key=KEY_F13;
        else if (strcmp(str,"F14")==0)
            key=KEY_F14;
        else if (strcmp(str,"F15")==0)
            key=KEY_F15;
        else if (strcmp(str,"F16")==0)
            key=KEY_F16;
        else if (strcmp(str,"F17")==0)
            key=KEY_F17;
        else if (strcmp(str,"F18")==0)
            key=KEY_F18;
        else if (strcmp(str,"F19")==0)
            key=KEY_F19;
        else if (strcmp(str,"F20")==0)
            key=KEY_F20;
        else 
            key=0;

        if (key!=0)
        {
            press_rel (key);
        }
    }
    else if (strcmp (str, "BACK_SPACE")==0)
    {
        press_rel(KEY_BACKSPACE); 
    }
    else if (strcmp (str, "ENTER")==0)
    {
        press_rel(KEY_ENTER); 
    }
    else if (strcmp (str, "TAB")==0)
    {
        press_rel(KEY_TAB); 
    }
    else if (strcmp (str, "KEY_LEFT")==0)
    {
        press_rel(KEY_LEFT); 
    }
    else if (strcmp (str, "KEY_RIGHT")==0)
    {
        press_rel(KEY_RIGHT); 
    }
    else if (strcmp (str, "KEY_DOWN")==0)
    {
        press_rel(KEY_DOWN); 
    }
    else if (strcmp (str, "KEY_UP")==0)
    {
        press_rel(KEY_UP); 
    }
    else if (strcmp (str, "LEFT_BRACE")==0)
    {
        press_rel(KEY_LEFTBRACE); 
    }
    else if (strcmp (str, "RIGHT_BRACE")==0)
    {
        press_rel(KEY_RIGHTBRACE); 
    }
    else if (strcmp (str, "BACK_SLASH")==0)
    {
        press_rel(KEY_BACKSLASH); 
    }
    else if (strcmp (str, "CONTROL_DOWN")==0)
    {
        press(KEY_LEFTCTRL); 
    }
    else if (strcmp (str, "CONTROL_UP")==0)
    {
        press_rel(KEY_LEFTCTRL); 
    }
    else if (strcmp (str, "CONTROL")==0)
    {
        press_rel(KEY_LEFTCTRL); 
    }
    else if (strcmp (str, "SHIFT_DOWN")==0)
    {
        press(KEY_LEFTSHIFT); 
    }
    else if (strcmp (str, "SHIFT_UP")==0)
    {
        press_rel(KEY_LEFTSHIFT); 
    }
    else if (strcmp (str, "SHIFT")==0)
    {
        press_rel(KEY_LEFTSHIFT); 
    }
    else
    {
    }
}

static void fake_key(char c)
{
    bool do_shift=false;
    if (( c>='A' ) && ( c<='Z'))
    {
        do_shift=true;
        c=c-'A'+'a';
    }
    
    if (do_shift) 
    {
        press(KEY_LEFTSHIFT);
    }

    press_rel(keycodes[(uint8_t)c]);
    
    if (do_shift) 
    {
        release(KEY_LEFTSHIFT);
    }
}

static ssize_t phStbSwKeyboard_write(struct file *filp, 
                                     const char *buf, 
                                     size_t count,
                                     loff_t *offp)
{
    static char localbuf[BUFFER_SIZE];
    static char escapebuf[BUFFER_SIZE];
    int i;
    int idx=0;
    bool found=false;
    
    if (count > BUFFER_SIZE) count=BUFFER_SIZE;
    copy_from_user(localbuf, buf, count);

    /* scan written data */
    for (i=0; i<count; i++)
    {
        if (localbuf[i])
        {
            if ( localbuf[i] == '<')
            {
                /* Start of escape sequence found */
                found=true;
            } 	
            else 
            if ( localbuf[i] == '>')
            {
                /* End of escape sequence found */
                found=false;
                escapebuf[idx]='\0';
                escapebuf[idx++]='\0';
                fake_esc (escapebuf);
                idx=0;
            }
            else
            {
                if (found)
                {
                    /* Add the next escape sequence character */
                    escapebuf[idx++]=localbuf[i];
                }
                else
                {
                    /* Send the key through directly */
                    fake_key(localbuf[i]);
                }
            }
        }
    }
    escapebuf[0]='\0';
    localbuf[0]='\0';
    return count;
}

struct file_operations phStbSwKeyboard_file_operations = {
	write:    phStbSwKeyboard_write,
};

struct miscdevice phStbSwKeyboard_misc = {
    minor:      MISC_DYNAMIC_MINOR,
    name:       "phStbSwKeyboard",
    fops:       &phStbSwKeyboard_file_operations,
};

/** Module initialisation. */
static int __init phStbKey_Init( void )
{
    int error;
    unsigned int index;

    // intialize structure
    phStbKey_input_dev = input_allocate_device();

    phStbKey_input_dev->name = "STB225 Front Panel Keyboard";
    phStbKey_input_dev->phys = "";

    phStbKey_input_dev->id.bustype = BUS_HOST;
    phStbKey_input_dev->id.vendor  = 0x0001;
    phStbKey_input_dev->id.product = 0x0001;
    phStbKey_input_dev->id.version = 0x0001;

    set_bit(EV_KEY, phStbKey_input_dev->evbit);
    set_bit(EV_REP, phStbKey_input_dev->evbit);

    // set keys
    for(index=0; index<NUM_KEYS; index++)
    {
       set_bit( keyList[index], phStbKey_input_dev->keybit );
    }
    init_swkeyboard_keycodes();

    // Set functions
    phStbKey_input_dev->open  = phStbKey_open;
    phStbKey_input_dev->close = phStbKey_close;

    error = input_register_device( phStbKey_input_dev );
    if (error) {
      printk (KERN_ERR "Could not register device %s (%s-%s)\n", PHSTBKEY_MODULE_DESCRIPTION, __DATE__, __TIME__);
      input_free_device(phStbKey_input_dev);
      return error;
    }
    
    /* Set up the repeat delay to be slightly longer than the scan loop */
    phStbKey_input_dev->rep[REP_DELAY]  = (PHSTBKEY_TIMEOUT + 5) * 10;
    /* Set up the repeat period to be the same as the scan loop */
    phStbKey_input_dev->rep[REP_PERIOD] = PHSTBKEY_TIMEOUT * 10;


    /* Set up the repeat delay to be slightly longer than the scan loop */
    phStbKey_input_dev->rep[REP_DELAY]  = (PHSTBKEY_TIMEOUT + 5) * 10;
    /* Set up the repeat period to be the same as the scan loop */
    phStbKey_input_dev->rep[REP_PERIOD] = PHSTBKEY_TIMEOUT * 10;

    class_device_create(&input_class, NULL,
                         MKDEV(INPUT_MAJOR, PHSTBKEY_EVENT_ID),
                         phStbKey_input_dev->cdev.dev,
                         "keyboard");

    error = misc_register(&phStbSwKeyboard_misc);
    if (error)
    {
        /* return if failure ... */
        printk(KERN_INFO "Failed to register the phStbSwKeyboard as a misc device\n");
        return error;
    }

    printk ("%s (%s-%s)\n", PHSTBKEY_MODULE_DESCRIPTION, __DATE__, __TIME__);
    return 0;
}
module_init(phStbKey_Init);

/** Module deinitialisation */
static void __exit phStbKey_Exit( void )
{	
    class_device_destroy(&input_class,MKDEV(INPUT_MAJOR, PHSTBKEY_EVENT_ID));
    input_unregister_device( phStbKey_input_dev );
    misc_deregister(&phStbSwKeyboard_misc);
}
module_exit(phStbKey_Exit);

module_param(key_sendrelease, int, 0644);
MODULE_PARM_DESC(key_sendrelease, "Send key release after each key press [default: 0]");

