/*
   (c) Copyright 2001-2009  The world wide DirectFB Open Source Community (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              Claudio Ciccani <klan@users.sf.net>.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#include <config.h>


#include <linux/version.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
typedef unsigned long kernel_ulong_t;
#define BITS_PER_LONG    (sizeof(long)*8)
#endif

#include <linux/input.h>
#include <poll.h>

#ifndef KEY_OK
/* Linux kernel 2.5.42+ defines additional keys in linux/input.h */
#include "input_fake.h"
#endif

#ifndef EV_CNT
#define EV_CNT (EV_MAX+1)
#define KEY_CNT (KEY_MAX+1)
#define REL_CNT (REL_MAX+1)
#define ABS_CNT (ABS_MAX+1)
#define LED_CNT (LED_MAX+1)
#endif

/* compat defines for older kernel like 2.4.x */
#ifndef EV_SYN
#define EV_SYN			0x00
#define SYN_REPORT              0
#define SYN_CONFIG              1
#define ABS_TOOL_WIDTH		0x1c
#define BTN_TOOL_DOUBLETAP	0x14d
#define BTN_TOOL_TRIPLETAP	0x14e
#endif

#ifndef EVIOCGLED
#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len)
#endif

#ifndef EVIOCGRAB
#define EVIOCGRAB _IOW('E', 0x90, int)
#endif

#define DEV_LOGIC_MOUSE "/dev/input/logic_mouse_xxxxxx"
#define DEV_LOGIC_KEYBOARD "/dev/input/logic_keyboard_xxxxxx"
#define DEV_LOGIC_JOYSTICK "/dev/input/logic_joystick_xxxxxx"

#define DEVICE_NAME_MAX_LENGTH  50


#if 0
#define DBG_INFO printf
#else
#define DBG_INFO(msg ...)               do {} while(0)
#endif

#if 0
#define THREAD_DBG_INFO printf
#else
#define THREAD_DBG_INFO(msg ...)               do {} while(0)
#endif


#if 0
#define DBG_FORCE_PRINT printf
#else
#define DBG_FORCE_PRINT(msg ...)               do {} while(0)
#endif

#include <linux/keyboard.h>


#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#include <dfb_types.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/kd.h>
#include <stdlib.h>

#define DFB_INPUTDRIVER_HAS_AXIS_INFO

#include <directfb.h>
#include <directfb_keyboard.h>

#include <core/coredefs.h>
#include <core/coretypes.h>
#include <core/input.h>
#include <core/system.h>

#include <direct/debug.h>
#include <direct/mem.h>
#include <direct/messages.h>
#include <direct/thread.h>
#include <direct/util.h>

#include <misc/conf.h>
#include <misc/util.h>

#ifdef LINUX_INPUT_USE_FBDEV
#include <fbdev/fbdev.h>
#endif

#include <core/input_driver.h>


DFB_INPUT_DRIVER( mstar_linux_input )
 void AddLinuxInputModule()
 {
 }
D_DEBUG_DOMAIN( Debug_LinuxInput, "Input/Linux", "Linux input driver" );

#ifndef BITS_PER_LONG
#define BITS_PER_LONG        (sizeof(long) * 8)
#endif
#define NBITS(x)             ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x)               ((x)%BITS_PER_LONG)
#define BIT(x)               (1UL<<OFF(x))
#define LONG(x)              ((x)/BITS_PER_LONG)
#undef test_bit
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)

/* compat for 2.4.x kernel - just a compile fix */
#ifndef HAVE_INPUT_ABSINFO
struct input_absinfo {
        s32 value;
        s32 minimum;
        s32 maximum;
        s32 fuzz;
        s32 flat;
};
#endif


/*
 * declaration of private data
 */
typedef struct {
     CoreInputDevice         *device;
     DirectThread            *thread;

     int                      fd;
     int                      quitpipe[2];

     bool                     has_leds;
     unsigned long            led_state[NBITS(LED_CNT)];
     DFBInputDeviceLockState  locks;

     int                      vt_fd;

     int                      dx;
     int                      dy;

     bool                     touchpad;
     char*                pDeviceName;
     int                       sensitive_numerator;
     int                       sensitive_denominator;
     int                       rel_axis_x_motion_reminder;
     int                       rel_axis_y_motion_reminder;
} LinuxInputData;


#define MAX_LINUX_INPUT_DEVICES 16

static int num_devices = 0;
static char *device_names[MAX_LINUX_INPUT_DEVICES];


static const
int basic_keycodes [] = {
     DIKI_UNKNOWN, DIKI_ESCAPE, DIKI_1, DIKI_2, DIKI_3, DIKI_4, DIKI_5,
     DIKI_6, DIKI_7, DIKI_8, DIKI_9, DIKI_0, DIKI_MINUS_SIGN,
     DIKI_EQUALS_SIGN, DIKI_BACKSPACE,

     DIKI_TAB, DIKI_Q, DIKI_W, DIKI_E, DIKI_R, DIKI_T, DIKI_Y, DIKI_U,
     DIKI_I, DIKI_O, DIKI_P, DIKI_BRACKET_LEFT, DIKI_BRACKET_RIGHT,
     DIKI_ENTER,

     DIKI_CONTROL_L, DIKI_A, DIKI_S, DIKI_D, DIKI_F, DIKI_G, DIKI_H, DIKI_J,
     DIKI_K, DIKI_L, DIKI_SEMICOLON, DIKI_QUOTE_RIGHT, DIKI_QUOTE_LEFT,

     DIKI_SHIFT_L, DIKI_BACKSLASH, DIKI_Z, DIKI_X, DIKI_C, DIKI_V, DIKI_B,
     DIKI_N, DIKI_M, DIKI_COMMA, DIKI_PERIOD, DIKI_SLASH, DIKI_SHIFT_R,
     DIKI_KP_MULT, DIKI_ALT_L, DIKI_SPACE, DIKI_CAPS_LOCK,

     DIKI_F1, DIKI_F2, DIKI_F3, DIKI_F4, DIKI_F5, DIKI_F6, DIKI_F7, DIKI_F8,
     DIKI_F9, DIKI_F10, DIKI_NUM_LOCK, DIKI_SCROLL_LOCK,

     DIKI_KP_7, DIKI_KP_8, DIKI_KP_9, DIKI_KP_MINUS,
     DIKI_KP_4, DIKI_KP_5, DIKI_KP_6, DIKI_KP_PLUS,
     DIKI_KP_1, DIKI_KP_2, DIKI_KP_3, DIKI_KP_0, DIKI_KP_DECIMAL,

     /*KEY_103RD,*/ DIKI_BACKSLASH,
     /*KEY_F13,*/ DFB_FUNCTION_KEY(13),
     /*KEY_102ND*/ DIKI_LESS_SIGN,

     DIKI_F11, DIKI_F12, DFB_FUNCTION_KEY(14), DFB_FUNCTION_KEY(15),
     DFB_FUNCTION_KEY(16), DFB_FUNCTION_KEY(17), DFB_FUNCTION_KEY(18),
     DFB_FUNCTION_KEY(19), DFB_FUNCTION_KEY(20),

     DIKI_KP_ENTER, DIKI_CONTROL_R, DIKI_KP_DIV, DIKI_PRINT, DIKS_ALTGR,

     /*KEY_LINEFEED*/ DIKI_UNKNOWN,

     DIKI_HOME, DIKI_UP, DIKI_PAGE_UP, DIKI_LEFT, DIKI_RIGHT,
     DIKI_END, DIKI_DOWN, DIKI_PAGE_DOWN, DIKI_INSERT, DIKI_DELETE,

     /*KEY_MACRO,*/ DIKI_UNKNOWN,

     DIKS_MUTE, DIKS_VOLUME_DOWN, DIKS_VOLUME_UP, DIKS_POWER, DIKI_KP_EQUAL,

     /*KEY_KPPLUSMINUS,*/ DIKI_UNKNOWN,

     DIKS_PAUSE, DFB_FUNCTION_KEY(21), DFB_FUNCTION_KEY(22),
     DFB_FUNCTION_KEY(23), DFB_FUNCTION_KEY(24),

     DIKI_KP_SEPARATOR, DIKI_META_L, DIKI_META_R, DIKI_SUPER_L,

     DIKS_STOP,

     /*DIKS_AGAIN, DIKS_PROPS, DIKS_UNDO, DIKS_FRONT, DIKS_COPY,
     DIKS_OPEN, DIKS_PASTE, DIKS_FIND, DIKS_CUT,*/
     DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN,
     DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN,

     DIKS_HELP, DIKS_MENU, DIKS_CALCULATOR, DIKS_SETUP,

     /*KEY_SLEEP, KEY_WAKEUP, KEY_FILE, KEY_SENDFILE, KEY_DELETEFILE,
     KEY_XFER,*/
     DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN,
     DIKI_UNKNOWN,

     /*KEY_PROG1, KEY_PROG2,*/
     DIKS_CUSTOM1, DIKS_CUSTOM2,

     DIKS_INTERNET,

     /*KEY_MSDOS, KEY_COFFEE, KEY_DIRECTION, KEY_CYCLEWINDOWS,*/
     DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN,

     DIKS_MAIL,

     /*KEY_BOOKMARKS, KEY_COMPUTER, */
     DIKI_UNKNOWN, DIKI_UNKNOWN,

     DIKS_BACK, DIKS_FORWARD,

     /*KEY_CLOSECD, KEY_EJECTCD, KEY_EJECTCLOSECD,*/
     DIKS_EJECT, DIKS_EJECT, DIKS_EJECT,

     DIKS_NEXT, DIKS_PLAYPAUSE, DIKS_PREVIOUS, DIKS_STOP, DIKS_RECORD,
     DIKS_REWIND, DIKS_PHONE,

     /*KEY_ISO,*/ DIKI_UNKNOWN,
     /*KEY_CONFIG,*/ DIKS_SETUP,
     /*KEY_HOMEPAGE, KEY_REFRESH,*/ DIKI_UNKNOWN, DIKS_SHUFFLE,

     DIKS_EXIT, /*KEY_MOVE,*/ DIKI_UNKNOWN, DIKS_EDITOR,

     /*KEY_SCROLLUP,*/ DIKS_PAGE_UP,
     /*KEY_SCROLLDOWN,*/ DIKS_PAGE_DOWN,
     /*KEY_KPLEFTPAREN,*/ DIKI_UNKNOWN,
     /*KEY_KPRIGHTPAREN,*/ DIKI_UNKNOWN,

     /* unused codes 181-182: */
     DIKI_UNKNOWN, DIKI_UNKNOWN,

     DFB_FUNCTION_KEY(13), DFB_FUNCTION_KEY(14), DFB_FUNCTION_KEY(15),
     DFB_FUNCTION_KEY(16), DFB_FUNCTION_KEY(17), DFB_FUNCTION_KEY(18),
     DFB_FUNCTION_KEY(19), DFB_FUNCTION_KEY(20), DFB_FUNCTION_KEY(21),
     DFB_FUNCTION_KEY(22), DFB_FUNCTION_KEY(23), DFB_FUNCTION_KEY(24),

     /* unused codes 195-199: */
     DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN, DIKI_UNKNOWN,

     /* KEY_PLAYCD, KEY_PAUSECD */
     DIKS_PLAY, DIKS_PAUSE,

     /*KEY_PROG3, KEY_PROG4,*/
     DIKS_CUSTOM3, DIKS_CUSTOM4,

     DIKI_UNKNOWN,

     /*KEY_SUSPEND, KEY_CLOSE*/
     DIKI_UNKNOWN, DIKI_UNKNOWN,

     /* KEY_PLAY */
     DIKS_PLAY,

     /* KEY_FASTFORWARD */
     DIKS_FASTFORWARD,

     /* KEY_BASSBOOST */
     DIKI_UNKNOWN,

     /* KEY_PRINT */
     DIKS_PRINT,

     /* KEY_HP             */  DIKI_UNKNOWN,
     /* KEY_CAMERA         */  DIKI_UNKNOWN,
     /* KEY_SOUND          */  DIKS_AUDIO,
     /* KEY_QUESTION       */  DIKS_HELP,
     /* KEY_EMAIL          */  DIKS_MAIL,
     /* KEY_CHAT           */  DIKI_UNKNOWN,
     /* KEY_SEARCH         */  DIKI_UNKNOWN,
     /* KEY_CONNECT        */  DIKI_UNKNOWN,
     /* KEY_FINANCE        */  DIKI_UNKNOWN,
     /* KEY_SPORT          */  DIKI_UNKNOWN,
     /* KEY_SHOP           */  DIKI_UNKNOWN,
     /* KEY_ALTERASE       */  DIKI_UNKNOWN,
     /* KEY_CANCEL         */  DIKS_CANCEL,
     /* KEY_BRIGHTNESSDOWN */  DIKI_UNKNOWN,
     /* KEY_BRIGHTNESSUP   */  DIKI_UNKNOWN,
     /* KEY_MEDIA          */  DIKS_MIDEA,
};

static const
int ext_keycodes [] = {
     DIKS_OK, DIKS_SELECT, DIKS_GOTO, DIKS_CLEAR, DIKS_POWER2, DIKS_OPTION,
     DIKS_INFO, DIKS_TIME, DIKS_VENDOR, DIKS_ARCHIVE, DIKS_PROGRAM,
     DIKS_CHANNEL, DIKS_FAVORITES, DIKS_EPG, DIKS_PVR, DIKS_MHP,
     DIKS_LANGUAGE, DIKS_TITLE, DIKS_SUBTITLE, DIKS_ANGLE, DIKS_ZOOM,
     DIKS_MODE, DIKS_KEYBOARD, DIKS_SCREEN, DIKS_PC, DIKS_TV, DIKS_TV2,
     DIKS_VCR, DIKS_VCR2, DIKS_SAT, DIKS_SAT2, DIKS_CD, DIKS_TAPE,
     DIKS_RADIO, DIKS_TUNER, DIKS_PLAYER, DIKS_TEXT, DIKS_DVD, DIKS_AUX,
     DIKS_MP3, DIKS_AUDIO, DIKS_VIDEO, DIKS_DIRECTORY, DIKS_LIST, DIKS_MEMO,
     DIKS_CALENDAR, DIKS_RED, DIKS_GREEN, DIKS_YELLOW, DIKS_BLUE,
     DIKS_CHANNEL_UP, DIKS_CHANNEL_DOWN, DIKS_FIRST, DIKS_LAST, DIKS_AB,
     DIKS_NEXT, DIKS_RESTART, DIKS_SLOW, DIKS_SHUFFLE, DIKS_FASTFORWARD,
     DIKS_PREVIOUS, DIKS_NEXT, DIKS_DIGITS, DIKS_TEEN, DIKS_TWEN, DIKS_BREAK,
     DIKS_ZOOMIN,DIKS_ZOOMOUT
};

/*
 * Touchpads related stuff
 */
enum {
     TOUCHPAD_FSM_START,
     TOUCHPAD_FSM_MAIN,
     TOUCHPAD_FSM_DRAG_START,
     TOUCHPAD_FSM_DRAG_MAIN,
};
struct touchpad_axis {
     int old, min, max;
};
struct touchpad_fsm_state {
     int fsm_state;
     struct touchpad_axis x;
     struct touchpad_axis y;
     struct timeval timeout;
};


typedef struct {
     DirectLink               link;

     int                      magic;

     DirectModuleEntry       *module;

     const InputDriverFuncs  *funcs;

     InputDriverInfo          info;

     int                      nr_devices;
} InputDriver;

typedef struct {
     int                          min_keycode;
     int                          max_keycode;
     int                          num_entries;
     DFBInputDeviceKeymapEntry   *entries;
} InputDeviceKeymap;

typedef struct {
     int                          magic;

     DFBInputDeviceID             id;            /* unique device id */

     int                          num;

     InputDeviceInfo              device_info;

     InputDeviceKeymap            keymap;

     DFBInputDeviceModifierMask   modifiers_l;
     DFBInputDeviceModifierMask   modifiers_r;
     DFBInputDeviceLockState      locks;
     DFBInputDeviceButtonMask     buttons;

     DFBInputDeviceKeyIdentifier  last_key;      /* last key pressed */
     DFBInputDeviceKeySymbol      last_symbol;   /* last symbol pressed */
     bool                         first_press;   /* first press of key */

     FusionReactor               *reactor;       /* event dispatcher */
     FusionSkirmish               lock;

     FusionCall                   call;          /* driver call via master */

     unsigned int                 axis_num;
     DFBInputDeviceAxisInfo      *axis_info;
} InputDeviceShared;

struct __DFB_CoreInputDevice {
     DirectLink          link;

     int                 magic;

     InputDeviceShared  *shared;

     InputDriver        *driver;
     void               *driver_data;

     CoreDFB            *core;
};

/**********************************************************************************************************************/

typedef struct {
     int                 magic;

     int                 num;
     InputDeviceShared  *devices[MAX_INPUTDEVICES];
} DFBInputCoreShared;

struct __DFB_DFBInputCore {
     int                 magic;

     CoreDFB            *core;

     DFBInputCoreShared *shared;

     DirectLink         *drivers;
     DirectLink         *devices;
};

typedef struct S_LOGIC_MOUSE {
     int mousePipe[2];
      int mousefd[MAX_LINUX_INPUT_DEVICES];
      char device_name[MAX_LINUX_INPUT_DEVICES][DEVICE_NAME_MAX_LENGTH];
      bool isPhysicalAttached[MAX_LINUX_INPUT_DEVICES];
}LOGIC_MOUSE_T;

typedef struct S_LOGIC_KEYBOARD {
     int keyboardPipe[2];
      int keyboardfd[MAX_LINUX_INPUT_DEVICES];
      char device_name[MAX_LINUX_INPUT_DEVICES][DEVICE_NAME_MAX_LENGTH];

       int keyboardfd2[MAX_LINUX_INPUT_DEVICES];
      char device_name2[MAX_LINUX_INPUT_DEVICES][DEVICE_NAME_MAX_LENGTH];

      bool isPhysicalAttached[MAX_LINUX_INPUT_DEVICES];
}LOGIC_KEYBOARD_T;


typedef struct S_LOGIC_JOYSTICK {
    int joystickPipe[2];
    int joystickfd[MAX_LINUX_INPUT_DEVICES];
    char device_name[MAX_LINUX_INPUT_DEVICES][DEVICE_NAME_MAX_LENGTH];

    int joystickfd2[MAX_LINUX_INPUT_DEVICES];
    char device_name2[MAX_LINUX_INPUT_DEVICES][DEVICE_NAME_MAX_LENGTH];

    bool isPhysicalAttached[MAX_LINUX_INPUT_DEVICES];
}LOGIC_JOYSTICK_T;

static LOGIC_MOUSE_T g_logic_mouse;
static LOGIC_KEYBOARD_T g_logic_keyboard;
static LOGIC_JOYSTICK_T g_logic_joystick;
static bool isHotplugExit = false;

static void
touchpad_fsm_init( struct touchpad_fsm_state *state );
static int
touchpad_fsm( struct touchpad_fsm_state *state,
              const struct input_event  *levt,
              DFBInputEvent             *devt );

static bool
timeout_passed( const struct timeval *timeout, const struct timeval *current );
static bool
timeout_is_set( const struct timeval *timeout );
static void
timeout_sub( struct timeval *timeout, const struct timeval *sub );
static int
linux_start_hotplug_polling( void *arg);

static int debugtime =0;
static int debugtime2 =0;

u32 linux_GetSystemTime(void)
{
    struct timespec         ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return ts.tv_sec* 1000+ ts.tv_nsec/1000000;
}
u32 linux_DiffTimeFromNow(u32  ti)
{
    return linux_GetSystemTime()-ti;
}

/*
 * Translates a Linux input keycode into a DirectFB keycode.
 */
static int
translate_key( unsigned short code )
{

     if (code < D_ARRAY_SIZE( basic_keycodes ))
          return basic_keycodes[code];

     if (code >= KEY_OK)
          if (code - KEY_OK < D_ARRAY_SIZE( ext_keycodes ))
               return ext_keycodes[code-KEY_OK];
     return DIKI_UNKNOWN;
}

static DFBInputDeviceKeySymbol
keyboard_get_symbol( int                             code,
                     unsigned short                  value,
                     DFBInputDeviceKeymapSymbolIndex level )
{
     unsigned char type  = KTYP(value);
     unsigned char index = KVAL(value);
     int           base  = (level == DIKSI_BASE);

     switch (type) {
          case KT_FN:
               if (index < 20)
                    return DFB_FUNCTION_KEY( index + 1 );
               break;
          case KT_LETTER:
          case KT_LATIN:
               switch (index) {
                    case 0x1c:
                         return DIKS_PRINT;
                    case 0x7f:
                         return DIKS_BACKSPACE;
                    case 0xa4:
                         return 0x20ac; /* euro currency sign */
                    default:
                         return index;
               }
               break;
          case KT_DEAD:
               switch (value) {
                    case K_DGRAVE:
                         return DIKS_DEAD_GRAVE;

                    case K_DACUTE:
                         return DIKS_DEAD_ACUTE;

                    case K_DCIRCM:
                         return DIKS_DEAD_CIRCUMFLEX;

                    case K_DTILDE:
                         return DIKS_DEAD_TILDE;

                    case K_DDIERE:
                         return DIKS_DEAD_DIAERESIS;

                    case K_DCEDIL:
                         return DIKS_DEAD_CEDILLA;

                    default:
                         break;
               }
               break;
          case KT_PAD:
               if (index <= 9 && level != DIKSI_BASE)
                    return DIKS_0 + index;
               break;
          case 0xe: /* special IPAQ H3600 case - AH */
               switch (index) {
                    case 0x20:     return DIKS_CALENDAR;
                    case 0x1a:     return DIKS_BACK;
                    case 0x1c:     return DIKS_MEMO;
                    case 0x21:     return DIKS_POWER;
               }
               break;
          case 0xd: /* another special IPAQ H3600 case - AH */
               switch (index) {
                    case 0x2:     return DIKS_DIRECTORY;
                    case 0x1:     return DIKS_MAIL;  /* Q on older iPaqs */
               }
               break;
     }

     switch (value) {
          case K_LEFT:    return DIKS_CURSOR_LEFT;
          case K_RIGHT:   return DIKS_CURSOR_RIGHT;
          case K_UP:      return DIKS_CURSOR_UP;
          case K_DOWN:    return DIKS_CURSOR_DOWN;
          case K_ENTER:   return DIKS_ENTER;
          case K_CTRL:    return DIKS_CONTROL;
          case K_SHIFT:   return DIKS_SHIFT;
          case K_ALT:     return DIKS_ALT;
          case K_ALTGR:   return DIKS_ALTGR;
          case K_INSERT:  return DIKS_INSERT;
          case K_REMOVE:  return DIKS_DELETE;
          case K_FIND:    return DIKS_HOME;
          case K_SELECT:  return DIKS_END;
          case K_PGUP:    return DIKS_PAGE_UP;
          case K_PGDN:    return DIKS_PAGE_DOWN;
          case K_NUM:     return DIKS_NUM_LOCK;
          case K_HOLD:    return DIKS_SCROLL_LOCK;
          case K_PAUSE:   return DIKS_PAUSE;
          case K_BREAK:   return DIKS_BREAK;
          case K_CAPS:    return DIKS_CAPS_LOCK;

          case K_P0:      return DIKS_INSERT;
          case K_P1:      return DIKS_END;
          case K_P2:      return DIKS_CURSOR_DOWN;
          case K_P3:      return DIKS_PAGE_DOWN;
          case K_P4:      return DIKS_CURSOR_LEFT;
          case K_P5:      return DIKS_BEGIN;
          case K_P6:      return DIKS_CURSOR_RIGHT;
          case K_P7:      return DIKS_HOME;
          case K_P8:      return DIKS_CURSOR_UP;
          case K_P9:      return DIKS_PAGE_UP;
          case K_PPLUS:   return DIKS_PLUS_SIGN;
          case K_PMINUS:  return DIKS_MINUS_SIGN;
          case K_PSTAR:   return DIKS_ASTERISK;
          case K_PSLASH:  return DIKS_SLASH;
          case K_PENTER:  return DIKS_ENTER;
          case K_PCOMMA:  return base ? DIKS_DELETE : DIKS_COMMA;
          case K_PDOT:    return base ? DIKS_DELETE : DIKS_PERIOD;
          case K_PPARENL: return DIKS_PARENTHESIS_LEFT;
          case K_PPARENR: return DIKS_PARENTHESIS_RIGHT;
     }

     /* special keys not in the map, hack? */
     if (code == 99)
          return DIKS_PRINT;

     if (code == 124)         /* keypad equal key */
          return DIKS_EQUALS_SIGN;

     if (code == 125)         /* left windows key */
          return DIKS_META;

     if (code == 126)         /* right windows key */
          return DIKS_META;

     if (code == 127)         /* context menu key */
          return DIKS_SUPER;

     return DIKS_NULL;
}

static DFBInputDeviceKeyIdentifier
keyboard_get_identifier( int code, unsigned short value )
{
     unsigned char type  = KTYP(value);
     unsigned char index = KVAL(value);

     if (type == KT_PAD) {
          if (index <= 9)
               return DIKI_KP_0 + index;

          switch (value) {
               case K_PSLASH: return DIKI_KP_DIV;
               case K_PSTAR:  return DIKI_KP_MULT;
               case K_PMINUS: return DIKI_KP_MINUS;
               case K_PPLUS:  return DIKI_KP_PLUS;
               case K_PENTER: return DIKI_KP_ENTER;
               case K_PCOMMA:
               case K_PDOT:   return DIKI_KP_DECIMAL;
          }
     }

     /* Looks like a hack, but don't know a better way yet. */
     switch (code) {
          case 12: return DIKI_MINUS_SIGN;
          case 13: return DIKI_EQUALS_SIGN;
          case 26: return DIKI_BRACKET_LEFT;
          case 27: return DIKI_BRACKET_RIGHT;
          case 39: return DIKI_SEMICOLON;
          case 40: return DIKI_QUOTE_RIGHT;
          case 41: return DIKI_QUOTE_LEFT;
          case 43: return DIKI_BACKSLASH;
          case 51: return DIKI_COMMA;
          case 52: return DIKI_PERIOD;
          case 53: return DIKI_SLASH;
          case 54: return DIKI_SHIFT_R;
          case 97: return DIKI_CONTROL_R;
          case 100: return DIKI_ALT_R;
          default:
               ;
     }

     /* special keys not in the map, hack? */
     if (code == 124)         /* keypad equal key */
          return DIKI_KP_EQUAL;

     if (code == 125)         /* left windows key */
          return DIKI_META_L;

     if (code == 126)         /* right windows key */
          return DIKI_META_R;

     if (code == 127)         /* context menu key */
          return DIKI_SUPER_R;

     return DIKI_UNKNOWN;
}

#include "defkeymap.hxx"
static unsigned short
keyboard_read_value( const LinuxInputData *data,
                     unsigned char table, unsigned char index )
{

     if( data->vt_fd >= 0)
    {
            struct kbentry entry;

            entry.kb_table = table;
            entry.kb_index = index;
            entry.kb_value = 0;
            if (ioctl( data->vt_fd, KDGKBENT, &entry )) {
                          D_PERROR("DirectFB/keyboard: KDGKBENT (table: %d, index: %d) "
                                     "failed!\n", table, index);
                       return 0;
            }

            return entry.kb_value;
     }
     else
     {
              u_short *key_map, val;
              //read kernel default keymap
     		key_map = key_maps[table];
     		if (key_map) {
     		      val = U(key_map[index]);
		//    if ( KTYP(val) >= NR_TYPES)
		//	val = K_HOLE;
     		} else
     		      val = (index ? K_HOLE : K_NOSUCHMAP);
		return val;
     }
}

/*
 * Translates key and button events.
 */
static bool
key_event( const struct input_event *levt,
           DFBInputEvent            *devt )
{
     int code = levt->code;


     /* map touchscreen and smartpad events to button mouse */
     if (code == BTN_TOUCH || code == BTN_TOOL_FINGER)
          code = BTN_MOUSE;

     if ((code >= BTN_MOUSE && code < BTN_JOYSTICK) || code == BTN_TOUCH) {
          /* ignore repeat events for buttons */
          if (levt->value == 2)
               return false;

          devt->type   = levt->value ? DIET_BUTTONPRESS : DIET_BUTTONRELEASE;
          /* don't set DIEF_BUTTONS, it will be set by the input core */
          devt->button = DIBI_FIRST + code - BTN_MOUSE;
     }
     else if((code >= BTN_JOYSTICK)&&(code < BTN_GAMEPAD)) {
                  /* ignore repeat events for buttons */
          if (levt->value == 2)
               return false;

          devt->type   = levt->value ? DIET_BUTTONPRESS : DIET_BUTTONRELEASE;
          /* don't set DIEF_BUTTONS, it will be set by the input core */
          devt->button = DIBI_FIRST + code - BTN_JOYSTICK;
     }
     else if((code >= BTN_GAMEPAD)&&(code < BTN_DIGI)) {
                  /* ignore repeat events for buttons */
          if (levt->value == 2)
               return false;

          devt->type   = levt->value ? DIET_BUTTONPRESS : DIET_BUTTONRELEASE;
          /* don't set DIEF_BUTTONS, it will be set by the input core */
          devt->button = DIBI_FIRST + code - BTN_GAMEPAD;
     }
     else {
          int key = translate_key( code );

          if (key == DIKI_UNKNOWN)
               return false;

          devt->type = levt->value ? DIET_KEYPRESS : DIET_KEYRELEASE;

          if (DFB_KEY_TYPE(key) == DIKT_IDENTIFIER) {
               devt->key_id = key;
               devt->flags |= DIEF_KEYID;
          }
          else {
               devt->key_symbol = key;
               devt->flags |= DIEF_KEYSYMBOL;
               if (DFB_KEY_TYPE(key) == DIKT_SPECIAL)
                    devt->flags &= ~DIEF_KEYSYMBOL;
          }

          devt->flags |= DIEF_KEYCODE;
          devt->key_code = code;
     }

     if (levt->value == 2)
          devt->flags |= DIEF_REPEAT;

     return true;
}

/*
 * Translates relative axis events.
 */
static bool
rel_event( const struct input_event *levt,
           DFBInputEvent            *devt )
{
     switch (levt->code) {
          case REL_X:
               devt->axis = DIAI_X;
               devt->axisrel = levt->value;
               break;

          case REL_Y:
               devt->axis = DIAI_Y;
               devt->axisrel = levt->value;
               break;

          case REL_Z:
          case REL_WHEEL:
               devt->axis = DIAI_Z;
               devt->axisrel = -levt->value;
               break;

          default:
               if (levt->code > REL_MAX || levt->code > DIAI_LAST)
                    return false;
               devt->axis = levt->code;
               devt->axisrel = levt->value;
     }

     devt->type    = DIET_AXISMOTION;
     devt->flags  |= DIEF_AXISREL;

     return true;
}

/*
 * Translates absolute axis events.
 */
static bool
abs_event( const struct input_event *levt,
           DFBInputEvent            *devt )
{
     switch (levt->code) {
          case ABS_X:
               devt->axis = DIAI_X;
               break;

          case ABS_Y:
               devt->axis = DIAI_Y;
               break;

          case ABS_Z:
          case ABS_WHEEL:
               devt->axis = DIAI_Z;
               break;

          default:
               if (levt->code >= ABS_PRESSURE || levt->code > DIAI_LAST)
                    return false;
               devt->axis = levt->code;
     }

     devt->type    = DIET_AXISMOTION;
     devt->flags  |= DIEF_AXISABS;
     devt->axisabs = levt->value;

     return true;
}

/*
 * Translates a Linux input event into a DirectFB input event.
 */
static bool
translate_event( const struct input_event *levt,
                 DFBInputEvent            *devt )
{
     devt->flags     = DIEF_TIMESTAMP;
     devt->timestamp = levt->time;


     switch (levt->type) {
          case EV_KEY:
               return key_event( levt, devt );

          case EV_REL:
               return rel_event( levt, devt );

          case EV_ABS:
               return abs_event( levt, devt );

          default:
               ;
     }

     return false;
}

static void
set_led( const LinuxInputData *data, int led, int state )
{
     struct input_event levt;

     levt.type = EV_LED;
     levt.code = led;
     levt.value = !!state;

     write( data->fd, &levt, sizeof(levt) );
}

static void
flush_xy( LinuxInputData *data, bool last )
{
     DFBInputEvent evt = { .type = DIET_UNKNOWN };

     if (data->dx) {
          evt.type    = DIET_AXISMOTION;
          evt.flags   = DIEF_AXISREL;
          evt.axis    = DIAI_X;
          evt.axisrel = data->dx;

          /* Signal immediately following event. */
          if (!last || data->dy)
               evt.flags |= DIEF_FOLLOW;

          dfb_input_dispatch( data->device, &evt );

          data->dx = 0;
     }

     if (data->dy) {
          evt.type    = DIET_AXISMOTION;
          evt.flags   = DIEF_AXISREL;
          evt.axis    = DIAI_Y;
          evt.axisrel = data->dy;

          /* Signal immediately following event. */
          if (!last)
               evt.flags |= DIEF_FOLLOW;

          dfb_input_dispatch( data->device, &evt );

          data->dy = 0;
     }
}

/*
 * Input thread reading from device.
 * Generates events on incoming data.
 */
static void*
linux_input_EventThread( DirectThread *thread, void *driver_data )
{
    DBG_INFO("[directFB:input]-------->start linux_input_EventThread-\n");
     LinuxInputData    *data = (LinuxInputData*) driver_data;
     int                readlen, status;
     unsigned int       i;
     int                fdmax;
     struct input_event levt[64];
     fd_set             set;
     struct touchpad_fsm_state fsm_state;

     D_DEBUG_AT( Debug_LinuxInput, "%s()\n", __FUNCTION__ );

     fdmax = (data->fd > data->quitpipe[0]) ? data->fd : data->quitpipe[0];
      DBG_INFO("[directFB:input]--------> linux_input_EventThread-- data->quitpipe[0]=%d,data->fd=%d,  fdmax=%d\n", data->quitpipe[0],data->fd,  fdmax);
     /* Query min/max coordinates. */
     if (data->touchpad) {
          struct input_absinfo absinfo;

          touchpad_fsm_init( &fsm_state );

          ioctl( data->fd, EVIOCGABS(ABS_X), &absinfo );
          fsm_state.x.min = absinfo.minimum;
          fsm_state.x.max = absinfo.maximum;

          ioctl( data->fd, EVIOCGABS(ABS_Y), &absinfo );
          fsm_state.y.min = absinfo.minimum;
          fsm_state.y.max = absinfo.maximum;
     }
     DBG_INFO("--- linux_input_EventThread------data->fd:%d,  data->pDeviceName:%s\n", data->fd, data->pDeviceName);

     while (1) {
          #if 0
          DBG_INFO("-- linux_input_EventThread    polling....\n");
          #endif
          DFBInputEvent devt = { .type = DIET_UNKNOWN };

          FD_ZERO( &set );
          FD_SET( data->fd, &set );
          FD_SET( data->quitpipe[0], &set );

          if (data->touchpad && timeout_is_set( &fsm_state.timeout )) {
               struct timeval time;
               gettimeofday( &time, NULL );

               if (!timeout_passed( &fsm_state.timeout, &time )) {
                    struct timeval timeout = fsm_state.timeout;
                    timeout_sub( &timeout, &time );
                    status = select( fdmax + 1, &set, NULL, NULL, &timeout );
               } else {
                    status = 0;
               }
          }
          else {
               struct timeval timeout={1,0};
               status = select( fdmax + 1, &set, NULL, NULL, &timeout );
          }

          if (status < 0 && errno != EINTR)
          {
               printf(" linux_input_EventThread  break line:%d\n", __LINE__);
               break;
          }

          /*
          if (FD_ISSET( data->quitpipe[0], &set ))
          {
              printf(" linux_input_EventThread  break line:%d\n", __LINE__);
               break;
          }
          */

          direct_thread_testcancel( thread );

          if (status < 0)
               continue;

          /* timeout? */
          if (status == 0) {
               if (data->touchpad && touchpad_fsm( &fsm_state, NULL, &devt ) > 0)
                    dfb_input_dispatch( data->device, &devt );

               continue;
          }
          DBG_INPUT_MSG("-----------------[%s]-----------------------before read\n", __FUNCTION__);
          readlen = read( data->fd, levt, sizeof(levt) );
          DBG_INPUT_MSG("-----------------[%s]-----------------------readlen:%d\n", __FUNCTION__, readlen);
          if (readlen < 0 && errno != EINTR)
          {
               printf(" linux_input_EventThread  break line:%d\n", __LINE__);
               break;
          }

          direct_thread_testcancel( thread );
          if (readlen <= 0)
               continue;
          for (i=0; i<readlen / sizeof(levt[0]); i++) {
               DFBInputEvent temp = { .type = DIET_UNKNOWN };
               if (data->touchpad) {
                    status = touchpad_fsm( &fsm_state, &levt[i], &temp );
                    if (status < 0) {
                         /* Not handled. Try the direct approach. */
                         if (!translate_event( &levt[i], &temp ))
                              continue;
                    }
                    else if (status == 0) {
                         /* Handled but no further processing is necessary. */
                         continue;
                    }
               }
               else {

                    if (!translate_event( &levt[i], &temp ))
                         continue;
               }
               /* Flush previous event with DIEF_FOLLOW? */
               if (devt.type != DIET_UNKNOWN) {
                    flush_xy( data, false );

                    /* Signal immediately following event. */
                    devt.flags |= DIEF_FOLLOW;
                    dfb_input_dispatch( data->device, &devt );

                    if (data->has_leds && (devt.locks != data->locks)) {
                         set_led( data, LED_SCROLLL, devt.locks & DILS_SCROLL );
                         set_led( data, LED_NUML, devt.locks & DILS_NUM );
                         set_led( data, LED_CAPSL, devt.locks & DILS_CAPS );
                         data->locks = devt.locks;
                    }
                    devt.type  = DIET_UNKNOWN;
                    devt.flags = DIEF_NONE;
               }
               devt = temp;
               if(D_FLAGS_IS_SET( devt.flags, DIEF_AXISREL ) && devt.type == DIET_AXISMOTION)
               {
                  switch (devt.axis)
                  {
                         case DIAI_X:
                               data->rel_axis_x_motion_reminder  += devt.axisrel*data->sensitive_numerator;
                              devt.axisrel = data->rel_axis_x_motion_reminder/data->sensitive_denominator;
                              data->rel_axis_x_motion_reminder %=data->sensitive_denominator;
                              break;

                         case DIAI_Y:
                              data->rel_axis_y_motion_reminder  += devt.axisrel*data->sensitive_numerator;
                              devt.axisrel = data->rel_axis_y_motion_reminder/data->sensitive_denominator;
                              data->rel_axis_y_motion_reminder %=data->sensitive_denominator;
                              break;
                          default:
                             break;
                   }
                }



               if (D_FLAGS_IS_SET( devt.flags, DIEF_AXISREL ) && devt.type == DIET_AXISMOTION &&
                   dfb_config->mouse_motion_compression)
               {
                    switch (devt.axis) {
                         case DIAI_X:
                              data->dx += devt.axisrel;
                              continue;

                         case DIAI_Y:
                              data->dy += devt.axisrel;
                              continue;

                         default:
                              break;
                    }
               }
               /* Event is dispatched in next round of loop. */
          }
          /* Flush last event without DIEF_FOLLOW. */
          if (devt.type != DIET_UNKNOWN) {
               flush_xy( data, false );
               dfb_input_dispatch( data->device, &devt );

               if (data->has_leds && (devt.locks != data->locks)) {
                    set_led( data, LED_SCROLLL, devt.locks & DILS_SCROLL );
                    set_led( data, LED_NUML, devt.locks & DILS_NUM );
                    set_led( data, LED_CAPSL, devt.locks & DILS_CAPS );
                    data->locks = devt.locks;
               }
          }
          else
               flush_xy( data, true );
     }

     if (status <= 0)
          D_PERROR ("linux_input thread died\n");

     return NULL;
}

/*
 * Fill device information.
 * Queries the input device and tries to classify it.
 */
static void
get_device_info( int              fd,
                 InputDeviceInfo *info,
                 bool            *touchpad )
{
     unsigned int  num_keys     = 0;
     unsigned int  num_ext_keys = 0;
     unsigned int  num_buttons  = 0;
     unsigned int  num_rels     = 0;
     unsigned int  num_abs      = 0;

     unsigned long evbit[NBITS(EV_CNT)];
     unsigned long keybit[NBITS(KEY_CNT)];
     unsigned long relbit[NBITS(REL_CNT)];
     unsigned long absbit[NBITS(ABS_CNT)];

     /* get device name */
     ioctl( fd, EVIOCGNAME(DFB_INPUT_DEVICE_DESC_NAME_LENGTH - 1), info->desc.name );

     /* set device vendor */
     snprintf( info->desc.vendor,
               DFB_INPUT_DEVICE_DESC_VENDOR_LENGTH, "Linux" );

     /* get event type bits */
     ioctl( fd, EVIOCGBIT(0, sizeof(evbit)), evbit );

     if (test_bit( EV_KEY, evbit )) {
          int i;

          /* get keyboard bits */
          ioctl( fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit );

	  /**  count typical keyboard keys only */
          for (i=KEY_Q; i<=KEY_M; i++)
               if (test_bit( i, keybit ))
                    num_keys++;

          for (i=KEY_OK; i<KEY_CNT; i++)
               if (test_bit( i, keybit ))
                    num_ext_keys++;

/*
          for (i=BTN_MOUSE; i<BTN_JOYSTICK; i++)
               if (test_bit( i, keybit ))
                    num_buttons++;
*/
          for (i=BTN_MOUSE; i<BTN_GAMEPAD; i++)
               if (test_bit( i, keybit ))
                    num_buttons++;

          for (i=BTN_GAMEPAD; i<BTN_DIGI; i++)
               if (test_bit( i, keybit ))
                    num_buttons++;
        
     }

     if (test_bit( EV_REL, evbit )) {
          int i;

          /* get bits for relative axes */
          ioctl( fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit );

          for (i=0; i<REL_CNT; i++)
               if (test_bit( i, relbit ))
                    num_rels++;
     }

     if (test_bit( EV_ABS, evbit )) {
          int i;

          /* get bits for absolute axes */
          ioctl( fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit );

          for (i=0; i<ABS_PRESSURE; i++)
               if (test_bit( i, absbit ))
                    num_abs++;
     }

     /* Touchpad? */
     /* FIXME: can we rely on BTN_TOUCH? xorg synaptics driver doesn't use it. */
     if (test_bit( EV_KEY, evbit ) &&
         test_bit( BTN_TOUCH, keybit ) &&
         test_bit( BTN_TOOL_FINGER, keybit) &&
         test_bit( EV_ABS, evbit ) &&
         test_bit( ABS_X, absbit ) &&
         test_bit( ABS_Y, absbit ) &&
         test_bit( ABS_PRESSURE, absbit ))
          *touchpad = true;
     else
          *touchpad = false;


     /* Mouse, Touchscreen or Smartpad ? */
     if ((test_bit( EV_KEY, evbit ) &&
          (test_bit( BTN_TOUCH, keybit ) || test_bit( BTN_TOOL_FINGER, keybit ))) ||
          ((num_rels >= 2 && num_buttons)  ||  (num_abs == 2 && (num_buttons == 1))))
          info->desc.type |= DIDTF_MOUSE;
     else if (num_abs && num_buttons) /* Or a Joystick? */
          info->desc.type |= DIDTF_JOYSTICK;

     /* A Keyboard, do we have at least some letters? */
     if (num_keys > 20) {
          info->desc.type |= DIDTF_KEYBOARD;
          info->desc.caps |= DICAPS_KEYS;

          info->desc.min_keycode = 0;
          info->desc.max_keycode = 127;
     }

     /* A Remote Control? */
     if (num_ext_keys) {
          info->desc.type |= DIDTF_REMOTE;
          info->desc.caps |= DICAPS_KEYS;
     }

     /* Buttons */
     if (num_buttons) {
          info->desc.caps       |= DICAPS_BUTTONS;
          info->desc.max_button  = DIBI_FIRST + num_buttons - 1;
     }

     /* Axes */
     if (num_rels || num_abs) {
          info->desc.caps       |= DICAPS_AXES;
          info->desc.max_axis    = DIAI_FIRST + MAX(num_rels, num_abs) - 1;
     }

     /* Decide which primary input device to be. */
     if (info->desc.type & DIDTF_KEYBOARD)
          info->prefered_id = DIDID_KEYBOARD;
     else if (info->desc.type & DIDTF_REMOTE)
          info->prefered_id = DIDID_REMOTE;
     else if (info->desc.type & DIDTF_JOYSTICK)
          info->prefered_id = DIDID_JOYSTICK;
     else if (info->desc.type & DIDTF_MOUSE)
          info->prefered_id = DIDID_MOUSE;
     else
          info->prefered_id = DIDID_ANY;
}

static bool
check_device( const char *device )
{
     int  fd;
           #if 0
            if(linux_DiffTimeFromNow(debugtime) > 2000)
            {
                printf("hotplug polling  check  device......\n");
                 debugtime = linux_GetSystemTime();
            }
            #endif

     /* Check if we are able to open the device */
     fd = open( device, O_RDWR );
     if (fd < 0) {
          return false;
     }
     else {
        //printf("open device :%s  success\n",device);
          InputDeviceInfo info;
          bool touchpad;

          /* try to grab the device */
          if (dfb_config->linux_input_grab) {
               /* 2.4 kernels don't have EVIOCGRAB so ignore EINVAL */
               if (ioctl( fd, EVIOCGRAB, 1 ) && errno != EINVAL) {
                    close( fd );
                    return false;
               }
          }

          memset( &info, 0, sizeof(InputDeviceInfo) );

          get_device_info( fd, &info, &touchpad );


          if (dfb_config->linux_input_grab)
               ioctl( fd, EVIOCGRAB, 0 );
          close( fd );

          if (!dfb_config->linux_input_ir_only ||
              (info.desc.type & DIDTF_REMOTE))
               return true;
     }

     return false;
}

/* exported symbols */

/*
 * Return the number of available devices.
 * Called once during initialization of DirectFB.
 */
static int
driver_get_available( void )
{
     int   i;
     char *tsdev;

#ifdef LINUX_INPUT_USE_FBDEV
     if (dfb_system_type() != CORE_FBDEV)
          return 0;
#endif
     int j = 0;
     for(j = 0; j < MAX_LINUX_INPUT_DEVICES; j++)
     {
        device_names[j] = NULL;
     }

     //the device_names[0] represents logic mouse
     memset(&g_logic_mouse, 0, sizeof(LOGIC_MOUSE_T));
     device_names[0] = D_STRDUP(DEV_LOGIC_MOUSE);
     num_devices++;
     pipe(g_logic_mouse.mousePipe);
      for(j = 0; j < MAX_LINUX_INPUT_DEVICES; j++)
      {
           g_logic_mouse.mousefd[j] = -1;
            g_logic_mouse.isPhysicalAttached[j] = false;
             memset(&g_logic_mouse.device_name[j], 0, DEVICE_NAME_MAX_LENGTH);
      }



     //the device_names[1] represents logic keyboard
     device_names[1] = D_STRDUP(DEV_LOGIC_KEYBOARD);
     num_devices++;
     pipe(g_logic_keyboard.keyboardPipe);
     for(j = 0; j < MAX_LINUX_INPUT_DEVICES; j++)
     {
         g_logic_keyboard.keyboardfd[j] = -1;
         memset(&g_logic_keyboard.device_name[j], 0, DEVICE_NAME_MAX_LENGTH);
         g_logic_keyboard.isPhysicalAttached[j] = false;
         g_logic_keyboard.keyboardfd2[j] = -1;
         memset(&g_logic_keyboard.device_name2[j], 0, DEVICE_NAME_MAX_LENGTH);
     }

    //the device_names[2] represents logic joystick
    device_names[2] = D_STRDUP(DEV_LOGIC_JOYSTICK);
    num_devices++;
    pipe(g_logic_joystick.joystickPipe);
    for(j = 0; j < MAX_LINUX_INPUT_DEVICES; j++)
    {
        g_logic_joystick.joystickfd[j] = -1;
        memset(&g_logic_joystick.device_name[j], 0, DEVICE_NAME_MAX_LENGTH);
        g_logic_joystick.isPhysicalAttached[j] = false;
        g_logic_joystick.joystickfd2[j] = -1;
        memset(&g_logic_joystick.device_name2[j], 0, DEVICE_NAME_MAX_LENGTH);
    }

#if 0
     /* Use the devices specified in the configuration. */
     if (fusion_vector_has_elements( &dfb_config->linux_input_devices )) {
          const char *device;

          fusion_vector_foreach (device, i, dfb_config->linux_input_devices) {
               if (num_devices >= MAX_LINUX_INPUT_DEVICES)
                    break;

               if (check_device( device ))
                    device_names[num_devices++] = D_STRDUP( device );
          }

          return num_devices;
     }

     /* Check for tslib device being used. */
     tsdev = getenv( "TSLIB_TSDEVICE" );

     /* No devices specified. Try to guess some. */
     for (i=0; i<MAX_LINUX_INPUT_DEVICES; i++) {
          char buf[32];

          snprintf( buf, 32, "/dev/input/event%d", i );

          /* Let tslib driver handle its device. */
          if (tsdev && !strcmp( tsdev, buf ))
               continue;

          if (check_device( buf ))
               device_names[num_devices++] = D_STRDUP( buf );
     }
#endif
     linux_start_hotplug_polling(NULL);
     return num_devices;
}

/*
 * Fill out general information about this driver.
 * Called once during initialization of DirectFB.
 */
static void
driver_get_info( InputDriverInfo *info )
{
     /* fill driver info structure */
     snprintf ( info->name,
                DFB_INPUT_DRIVER_INFO_NAME_LENGTH, "Linux Input Driver" );
     snprintf ( info->vendor,
                DFB_INPUT_DRIVER_INFO_VENDOR_LENGTH, "directfb.org" );

     info->version.major = 0;
     info->version.minor = 1;
}

static void linux_detach_physicalDevice(int*  pdevicefd, char * device_name)
{
    if(*pdevicefd == -1)
        return;
      close(*pdevicefd);
      int i =0;
      int j = 0;
      for(i =0; i < MAX_LINUX_INPUT_DEVICES; i++)
      {
            if(device_names[i] == NULL)
            {
                continue;
            }
            if(strcmp(device_names[i], device_name) == 0)
            {
                THREAD_DBG_INFO("[directFB:input]-------->to remove device : %s\n", device_name);
                D_FREE(device_names[i]);
                device_names[i] = NULL;
                num_devices--;
            }
      }
      *pdevicefd= -1;
}
static bool linux_detect_device_connection(int  devicefd)
{
        if(devicefd == -1)
            return false;
          struct pollfd  pfd;
          memset(&pfd,0,sizeof(pfd));

          pfd.fd = devicefd;
          pfd.events = POLLIN;
          int nready = poll(&pfd, 1, 1);    // for hotplug issue     int poll(struct pollfd fds[], nfds_t nfds, int timeout)
          if((nready == 1) && pfd.revents == (POLLERR | POLLHUP))
          {
                  return false;
          }
          else
          {
                  return true;
          }
}
static void linux_hotplug_transport(int*  pdevicefd,char *device_name,  int outputPipe)
{
        if(*pdevicefd == -1)
        {
              return;
        }
        fd_set set;
        struct timeval timeout={0,5};
        struct input_event levt[64];
        FD_ZERO(&set);
        FD_SET(*pdevicefd, &set);

        int status = select( *pdevicefd + 1, &set, NULL, NULL, &timeout );
        if(status <= 0 ||  !FD_ISSET(*pdevicefd, &set))
        {
            return;
        }
        THREAD_DBG_INFO("======================linux_hotplug_transport read..      device :%s================\n", device_name);
        int readlen = read(*pdevicefd, levt, sizeof(levt) );
        THREAD_DBG_INFO("======================linux_hotplug_transport write readlen:%d================\n", readlen);
        if(readlen > 0)
        {
             THREAD_DBG_INFO("======================linux_hotplug_transport write...================\n");
              write(outputPipe, levt, readlen);
              THREAD_DBG_INFO("======================linux_hotplug_transport write end..================\n");
        }
        else
        {
            THREAD_DBG_INFO("\n======================linux_hotplug_transport can not read data from device %d,  may be disconnected ,close it\n", *pdevicefd);
            linux_detach_physicalDevice(pdevicefd, device_name);
        }

}
static bool linux_attache_physicalDevice(DFBInputCoreShared   *core_input, char * dev,  int *deviceType, int *devicePosition)
{
    THREAD_DBG_INFO("[directFB:input]-------->Enter  %s   line:%d    dev:%s\n", __FUNCTION__, __LINE__, dev);
    int fd = open( dev, O_RDWR );
    if (fd < 0) {
        D_PERROR( "DirectFB/linux_input: could not open device" );
        THREAD_DBG_INFO("[directFB:input]-------->%s   open: %s  fail,   line:%d\n", __FUNCTION__, dev, __LINE__);
        return false;
    }
    THREAD_DBG_INFO("[directFB:input]-------->%s   open: %s  success,  line:%d\n", __FUNCTION__, dev,  __LINE__);
    InputDeviceInfo device_info;
    memset( &device_info, 0, sizeof(InputDeviceInfo) );
    bool  istouchpad = false;
    get_device_info( fd, &device_info, &istouchpad);

    THREAD_DBG_INFO("[directFB:input]-------->dev:%s   device_info.desc.type:%x\n", dev,
        (device_info.desc.type&DIDTF_MOUSE) ? "DIDTF_MOUSE" : 
            (device_info.desc.type&DIDTF_KEYBOARD) ? "DIDTF_KEYBOARD" :
                (device_info.desc.type&DIDTF_JOYSTICK) ? "DIDTF_JOYSTICK" : "Unknown");
    if(device_info.desc.type & DIDTF_MOUSE)
    {
        int k = 0;
        for(k = 0; k < MAX_LINUX_INPUT_DEVICES; k++)
        {
            if(g_logic_mouse.mousefd[k] ==  -1 )
            {
                break;
            }
        }
        D_ASSERT(k<MAX_LINUX_INPUT_DEVICES);  //Coverity-03132013
        g_logic_mouse.mousefd[k] = fd;
        strncpy(&g_logic_mouse.device_name[k], dev, DEVICE_NAME_MAX_LENGTH-1);
        g_logic_mouse.isPhysicalAttached[k] = true;
        *deviceType = (int)DIDTF_MOUSE;
        *devicePosition = k;
        THREAD_DBG_INFO("[directFB:input]-------->device_names[0] :%s     strlen(device_names[0]):%d       %s     line:%d\n",device_names[0] ,strlen(device_names[0]),   __FUNCTION__, __LINE__);
        THREAD_DBG_INFO("[directFB:input]-------->g_logic_mouse.mousefd[%d]:%d,    g_logic_mouse.device_name[%d]:%s          %s     line:%d\n", k, g_logic_mouse.mousefd[k], k, g_logic_mouse.device_name[k],   __FUNCTION__, __LINE__);

        return true;
    }
    else if(device_info.desc.type & DIDTF_KEYBOARD)
    {
        int kindex = 0;
        for(kindex= 0; kindex < MAX_LINUX_INPUT_DEVICES; kindex++)
        {
            if(g_logic_keyboard.keyboardfd[kindex] ==  -1 )
            {
                break;
            }
        }
        D_ASSERT(kindex<MAX_LINUX_INPUT_DEVICES);
        g_logic_keyboard.keyboardfd[kindex] = fd;
        memset(&g_logic_keyboard.device_name[kindex], 0, DEVICE_NAME_MAX_LENGTH);
        strncpy(&g_logic_keyboard.device_name[kindex], dev, DEVICE_NAME_MAX_LENGTH-1);
        g_logic_keyboard.isPhysicalAttached[kindex] = true;
        *deviceType = (int)DIDTF_KEYBOARD;
        *devicePosition = kindex;

        THREAD_DBG_INFO("[directFB:input]-------->device_names[0] :%s     strlen(device_names[0]):%d       %s     line:%d\n",device_names[0] ,strlen(device_names[0]),   __FUNCTION__, __LINE__);
        THREAD_DBG_INFO("[directFB:input]-------->g_logic_keyboard.keyboardfd[%d]:%d,    g_logic_keyboard.device_name[%d]:%s          %s     line:%d\n", kindex, g_logic_keyboard.keyboardfd[kindex], kindex, g_logic_keyboard.device_name[kindex],   __FUNCTION__, __LINE__);

        return true;
    }
    else if(device_info.desc.type & DIDTF_JOYSTICK)
    {
        int kindex = 0;
        for(kindex= 0; kindex < MAX_LINUX_INPUT_DEVICES; kindex++)
        {
            if(g_logic_joystick.joystickfd[kindex] ==  -1 )
            {
                break;
            }
        }
        D_ASSERT(kindex<MAX_LINUX_INPUT_DEVICES);
        g_logic_joystick.joystickfd[kindex] = fd;
        memset(&g_logic_joystick.device_name[kindex], 0, DEVICE_NAME_MAX_LENGTH);
        strncpy(&g_logic_joystick.device_name[kindex], dev, DEVICE_NAME_MAX_LENGTH-1);
        g_logic_joystick.isPhysicalAttached[kindex] = true;
        *deviceType = (int)DIDTF_JOYSTICK;
        *devicePosition = kindex;

        THREAD_DBG_INFO("[directFB:input]-------->device_names[2] :%s     strlen(device_names[2]):%d       %s     line:%d\n",device_names[2] ,strlen(device_names[2]),   __FUNCTION__, __LINE__);
        THREAD_DBG_INFO("[directFB:input]-------->g_logic_joystick.joystickfd[%d]:%d,    g_logic_joystick.device_name[%d]:%s          %s     line:%d\n", kindex, g_logic_joystick.joystickfd[kindex], kindex, g_logic_joystick.device_name[kindex],   __FUNCTION__, __LINE__);

        return true;
    }
    else
    {
        THREAD_DBG_INFO("[directFB:input]-------->Not supported  hotplug device:  %s   \n", dev);
        close(fd);
    }
    THREAD_DBG_INFO("[directFB:input]-------->end of  %s   line:%d\n", __FUNCTION__, __LINE__);
    return false;
}


driver_hotplug_polling2( void *arg)
{
    static u32 lasttime = 0;
    DFBInputCoreShared *core_input = (DFBInputCoreShared *)arg;
    #if 0
    if(linux_DiffTimeFromNow(debugtime) > 2000)
    {
        printf("hotplug polling ......\n");
         debugtime = linux_GetSystemTime();
    }
    #endif

    //Moving input data from the physical devices to logical device
    int k=0;
    for(k = 0; k < MAX_LINUX_INPUT_DEVICES; k++)
    {
            if(g_logic_mouse.isPhysicalAttached[k])
            {
                #if 0
                if(linux_DiffTimeFromNow(debugtime2) > 2000)
                {
                    THREAD_DBG_INFO("hotplug polling   g_logic_mouse.isPhysicalAttached[%d]:%d,  g_logic_mouse.device_name[%d]:%s, g_logic_mouse.mousefd[%d]:%d......\n", k, g_logic_mouse.isPhysicalAttached[k] , k ,  g_logic_mouse.device_name[k], k, g_logic_mouse.mousefd[k]);
                    THREAD_DBG_INFO("linux_detect_device_connection(g_logic_mouse.mousefd[%d]):%d\n", k,linux_detect_device_connection(g_logic_mouse.mousefd[k]));
                     debugtime2 = linux_GetSystemTime();
                }
                #endif
                if(linux_detect_device_connection(g_logic_mouse.mousefd[k]))
                {
                    linux_hotplug_transport(&g_logic_mouse.mousefd[k], g_logic_mouse.device_name[k],  g_logic_mouse.mousePipe[1]);
                }
                else
                {
                    THREAD_DBG_INFO("----------should remove device \n");
                    if(g_logic_mouse.mousefd[k] > 0)
                    {
                        DBG_FORCE_PRINT("[directFB:input]-------->Detect  mouse device:%s  disconnected(%d)\n", g_logic_mouse.device_name[k], __LINE__);
                        linux_detach_physicalDevice(&g_logic_mouse.mousefd[k], g_logic_mouse.device_name[k]);
                        g_logic_mouse.mousefd[k]= -1;
                    }
                     g_logic_mouse.isPhysicalAttached[k] = false;
                }
            }
    }

    for(k = 0; k < MAX_LINUX_INPUT_DEVICES; k++)
    {
         if(g_logic_keyboard.isPhysicalAttached[k])
         {
             if(linux_detect_device_connection(g_logic_keyboard.keyboardfd[k]))
             {
                   linux_hotplug_transport(&g_logic_keyboard.keyboardfd[k], g_logic_keyboard.device_name[k],  g_logic_keyboard.keyboardPipe[1]);
             }
             else
             {
                  if(g_logic_keyboard.keyboardfd[k] > 0 )
                  {
                       DBG_FORCE_PRINT("[directFB:input]------>Detect  keyboard device:%s  disconnected(%d)\n", g_logic_keyboard.device_name[k], __LINE__);
                        linux_detach_physicalDevice(&g_logic_keyboard.keyboardfd[k],  g_logic_keyboard.device_name[k]);
                        g_logic_keyboard.keyboardfd[k] = -1;
                  }
                  if(g_logic_keyboard.keyboardfd2[k] > 0 )
                  {
                       DBG_FORCE_PRINT("[directFB:input]------>Detect  keyboard device:%s  disconnected(%d)\n", g_logic_keyboard.device_name2[k], __LINE__);
                        linux_detach_physicalDevice(&g_logic_keyboard.keyboardfd2[k],  g_logic_keyboard.device_name2[k]);
                        g_logic_keyboard.keyboardfd2[k] = -1;
                  }
                  g_logic_keyboard.isPhysicalAttached[k] = false;
             }
         }
    }

    for(k = 0; k < MAX_LINUX_INPUT_DEVICES; k++)
    {
        if(g_logic_joystick.isPhysicalAttached[k])
        {
            if(linux_detect_device_connection(g_logic_joystick.joystickfd[k]))
            {
                linux_hotplug_transport(&g_logic_joystick.joystickfd[k], g_logic_joystick.device_name[k],  g_logic_joystick.joystickPipe[1]);
            }
            else
            {
                if(g_logic_joystick.joystickfd[k] > 0 )
                {
                    DBG_FORCE_PRINT("[directFB:input]------>Detect  joystick device:%s  disconnected(%d)\n", g_logic_joystick.device_name[k], __LINE__);
                    linux_detach_physicalDevice(&g_logic_joystick.joystickfd[k],  g_logic_joystick.device_name[k]);
                    g_logic_joystick.joystickfd[k] = -1;
                }
                if(g_logic_joystick.joystickfd2[k] > 0 )
                {
                    DBG_FORCE_PRINT("[directFB:input]------>Detect  joystick device:%s  disconnected(%d)\n", g_logic_joystick.device_name2[k], __LINE__);
                    linux_detach_physicalDevice(&g_logic_joystick.joystickfd2[k],  g_logic_joystick.device_name2[k]);
                    g_logic_joystick.joystickfd2[k] = -1;
                }
                g_logic_joystick.isPhysicalAttached[k] = false;
            }
        }
    }

    bool shouldSleep = true;
    for(k = 0; k < MAX_LINUX_INPUT_DEVICES; k++)
    {
         if(g_logic_mouse.isPhysicalAttached[k] || g_logic_keyboard.isPhysicalAttached[k] || g_logic_joystick.isPhysicalAttached[k])
         {
              shouldSleep = false;
              break;
         }
    }
    if(shouldSleep)
   {
        usleep(1000*1000);
    }

    if(linux_DiffTimeFromNow(lasttime) < 1000)
        return -1;
    lasttime = linux_GetSystemTime();

     int   i;
     char *tsdev = NULL;
     bool opened = false;
     static u32 mouseLastAttachedTime = 0;
     static u32 keyboardLastAttachedTime = 0;
    static u32 joystickLastAttachedTime = 0;

    tsdev = getenv( "TSLIB_TSDEVICE" );

     /* No devices specified. Try to guess some. */
     for (i=0; i<MAX_LINUX_INPUT_DEVICES; i++)
    {
          char buf[32];

          snprintf( buf, 32, "/dev/input/event%d", i );

          /* Let tslib driver handle its device. */
          if (tsdev && !strcmp( tsdev, buf ))
               continue;
          opened = false;
          int j = 0;
          for(j = 0; j < MAX_LINUX_INPUT_DEVICES; j++)
          {
               if(device_names[j]  != NULL)
               {
                   if(strcmp(device_names[j], buf) == 0)//opened device
                   {
                        opened = true;
                   }
               }
          }
          if(opened)
          {
              continue;
          }
           #if 0
            if(linux_DiffTimeFromNow(debugtime) > 2000)
            {
                printf("hotplug polling  check  device......\n");
                 debugtime = linux_GetSystemTime();
            }
            #endif

          if (check_device( buf ))
          {
               //detect new device
               THREAD_DBG_INFO("[directFB:input]-----Detect new device:%s\n", buf);
               int j =0;
               for(j = 0; j < MAX_LINUX_INPUT_DEVICES; j++)
               {
                    if(device_names[j] == NULL)
                    {
                        char * toAttacheDev= D_STRDUP( buf );
                        static int lastAttachedDevicetype = -1;
                        static int lastAttachedDeviceposition = -1;

                        int devicetype = -1;
                        int deviceposition = -1;
                       bool isAttachedSuccess = linux_attache_physicalDevice(core_input,  toAttacheDev, &devicetype, &deviceposition);
                       if(isAttachedSuccess)
                       {
                             device_names[j] = toAttacheDev;
                             if(devicetype == (int)DIDTF_MOUSE)
                             {
                                 mouseLastAttachedTime = linux_GetSystemTime();
                                 lastAttachedDevicetype = devicetype;
                                 lastAttachedDeviceposition = deviceposition;
                             }
                             if(devicetype == (int)DIDTF_KEYBOARD)
                             {
                                 keyboardLastAttachedTime = linux_GetSystemTime();
                                 lastAttachedDevicetype = devicetype;
                                 lastAttachedDeviceposition = deviceposition;
                             }
                             if(devicetype == (int)DIDTF_JOYSTICK)
                             {
                                 joystickLastAttachedTime = linux_GetSystemTime();
                                 lastAttachedDevicetype = devicetype;
                                 lastAttachedDeviceposition = deviceposition;
                             }
                             DBG_FORCE_PRINT("[directFB:input]-----Attache Physical device:%s  SUCCESS!\n", toAttacheDev);
                             return  j;
                       }
                       else
                       {
                              DBG_FORCE_PRINT("[directFB:input]-----Attache Physical device:%s  FAILED!  %d\n", toAttacheDev, linux_DiffTimeFromNow(keyboardLastAttachedTime));
                              /*
                              if(linux_DiffTimeFromNow(lastAttachedTime) < 1500)  --> This is a patch for the following reasons:
                              There will be two devices file can be used to open the keyboard, while the second device file can not be read correctly,
                              therefore, the time interval is used to identify the device file is from the same usb keyboard,
                              so pull out the keyboard which can be found when a physical device  be removed
                              */
                              if(linux_DiffTimeFromNow(keyboardLastAttachedTime) < 1500)
                              {
                                   THREAD_DBG_INFO("[directFB:input]-----keyboard hotplug Try temporary solution...\n");
                                    int fd = open( toAttacheDev, O_RDWR );
                                    if(fd > 0)
                                    {
                                        THREAD_DBG_INFO("[directFB:input]-----keyboard hotplug Try temporary solution success! %d\n", lastAttachedDeviceposition);
                                        if(lastAttachedDeviceposition > -1 && lastAttachedDeviceposition < MAX_LINUX_INPUT_DEVICES)
                                        {
                                             g_logic_keyboard.keyboardfd2[lastAttachedDeviceposition] = fd;
                                             memset(&g_logic_keyboard.device_name2[lastAttachedDeviceposition], 0, DEVICE_NAME_MAX_LENGTH);
                                             strncpy(&g_logic_keyboard.device_name2[lastAttachedDeviceposition], toAttacheDev, DEVICE_NAME_MAX_LENGTH-1);
                                             device_names[j] = toAttacheDev;
                                        }
                                        else
                                        {
                                             DBG_FORCE_PRINT("[directFB:input]---keyboard hotplug Try temporary solution fail! %d\n", lastAttachedDeviceposition );
                                        }
                                    }
                                    else
                                    {
                                        D_FREE(toAttacheDev);
                                    }
                              }
                              else
                              {
                                    D_FREE(toAttacheDev);
                              }
                              break;
                             //return -1;
                       }

                    }

               }
               THREAD_DBG_INFO("[directFB:input]-----No  device slot for use!!!!\n");

          }

     }

     return -1;
}

static int
linux_input_hotplug_thread( void *arg)
{
    while(!isHotplugExit)
    {
        driver_hotplug_polling2(arg);
        usleep(50*1000);
    }
    printf("[directfb/input]---------------->hotplug thread  exit\n");
    return 1;
}
static int
linux_start_hotplug_polling( void *arg)
{
    printf("[directfb/input]---------------->start thread hotplug polling..\n");
    direct_thread_create( DTT_DEFAULT, linux_input_hotplug_thread, arg, "Linux hotplug" );
    printf("[directfb/input]----------------->start thread hotplug polling end..\n");
    return 1;
}

static DFBResult  linux_open_logic_mouse(CoreInputDevice  *device, unsigned int   number,InputDeviceInfo  *info, void  **driver_data)
{
    LinuxInputData* data = D_CALLOC( 1, sizeof(LinuxInputData) );
    data->sensitive_numerator = 1;
    data->sensitive_denominator = 1;
    data->rel_axis_x_motion_reminder = 0;
    data->rel_axis_y_motion_reminder = 0;
    if (!data) {
        return D_OOM();
    }
    data->fd     = g_logic_mouse.mousePipe[0];
    data->device = device;
    data->touchpad = 0;
    data->vt_fd    = -1;
    data->pDeviceName = device_names[number];

    info->desc.min_keycode = -1;
    info->desc.max_keycode = -1;
    info->desc.type |= DIDTF_MOUSE;
    info->desc.caps       |= DICAPS_BUTTONS;
    info->desc.caps       |= DICAPS_AXES;
    info->prefered_id = DIDID_MOUSE;
    strncpy(info->desc.name, device_names[number], DFB_INPUT_DEVICE_DESC_NAME_LENGTH-1);

    /* start input thread */
    DBG_INFO("========================start input thread  for logic mouse============================\n");
    data->thread = direct_thread_create( DTT_INPUT, linux_input_EventThread, data, "Linux Input" );
    /* set private data pointer */
    *driver_data = data;
    return DFB_OK;
}

static DFBResult  linux_open_logic_keyboard(CoreInputDevice  *device, unsigned int   number,InputDeviceInfo  *info, void  **driver_data)
{
    LinuxInputData* data = D_CALLOC( 1, sizeof(LinuxInputData) );
    data->sensitive_numerator = 1;
    data->sensitive_denominator = 1;
    data->rel_axis_x_motion_reminder = 0;
    data->rel_axis_y_motion_reminder = 0;
    if (!data) {
        return D_OOM();
    }
    data->fd     = g_logic_keyboard.keyboardPipe[0];
    data->device = device;
    data->touchpad = 0;
    data->vt_fd    = -1;
    data->pDeviceName = device_names[number];

    info->desc.type |= DIDTF_KEYBOARD;
    info->desc.caps |= DICAPS_KEYS;

    info->desc.min_keycode = 0;
    //info->desc.max_keycode = 127;
    info->desc.max_keycode = 0x250;

    info->desc.caps       |= DICAPS_BUTTONS;
    info->desc.caps       |= DICAPS_AXES;
    info->prefered_id = DIDID_KEYBOARD;
    strncpy(info->desc.name, device_names[number], DFB_INPUT_DEVICE_DESC_NAME_LENGTH-1);

    /* start input thread */
    DBG_INFO("========================start input thread  for logic keyboard============================\n");
    data->thread = direct_thread_create( DTT_INPUT, linux_input_EventThread, data, "Linux Input" );
    /* set private data pointer */
    *driver_data = data;
    return DFB_OK;
}

static DFBResult  linux_open_logic_joystick(CoreInputDevice  *device, unsigned int   number,InputDeviceInfo  *info, void  **driver_data)
{
    LinuxInputData* data = D_CALLOC( 1, sizeof(LinuxInputData) );
    data->sensitive_numerator = 1;
    data->sensitive_denominator = 1;
    data->rel_axis_x_motion_reminder = 0;
    data->rel_axis_y_motion_reminder = 0;
    if (!data) {
        return D_OOM();
    }
    data->fd     = g_logic_joystick.joystickPipe[0];
    data->device = device;
    data->touchpad = 0;
    data->vt_fd    = -1;
    data->pDeviceName = device_names[number];

    info->desc.type |= DIDTF_JOYSTICK;
    info->desc.caps |= DICAPS_BUTTONS;
    info->desc.caps |= DICAPS_AXES;
    info->prefered_id = DIDID_JOYSTICK;
    strncpy(info->desc.name, device_names[number], DFB_INPUT_DEVICE_DESC_NAME_LENGTH-1);

    /* start input thread */
    DBG_INFO("========================start input thread  for logic joystick============================\n");
    data->thread = direct_thread_create( DTT_INPUT, linux_input_EventThread, data, "Linux Input" );
    /* set private data pointer */
    *driver_data = data;
    return DFB_OK;
}

/*
 * Open the device, fill out information about it,
 * allocate and fill private data, start input thread.
 * Called during initialization, resuming or taking over mastership.
 */
static DFBResult
driver_open_device( CoreInputDevice  *device,
                    unsigned int      number,
                    InputDeviceInfo  *info,
                    void            **driver_data )
{
     int              fd, ret;
     bool             touchpad;
     unsigned long    ledbit[NBITS(LED_CNT)];
     LinuxInputData  *data;


      if(strcmp(device_names[number], DEV_LOGIC_MOUSE) == 0 && number == 0)
      {

            if(linux_open_logic_mouse(device, number, info, driver_data) == DFB_OK)
            {
                THREAD_DBG_INFO("=========================open logic mouse success============================\n");
                return DFB_OK;
            }
            else
            {
                return DFB_INIT;
            }
      }
      if(strcmp(device_names[number], DEV_LOGIC_KEYBOARD) == 0 && number == 1)
      {
            if(linux_open_logic_keyboard(device, number, info, driver_data) == DFB_OK)
            {
                THREAD_DBG_INFO("=========================open logic keyboard success============================\n");
                return DFB_OK;
            }
            else
            {
                return DFB_INIT;
            }
      }
    if(strcmp(device_names[number], DEV_LOGIC_JOYSTICK) == 0 && number == 2)
    {
        if(linux_open_logic_joystick(device, number, info, driver_data) == DFB_OK)
        {
            THREAD_DBG_INFO("=========================open logic joystick success============================\n");
            return DFB_OK;
        }
        else
        {
            return DFB_INIT;
        }
    }

     /* open device */
     fd = open( device_names[number], O_RDWR );
     if (fd < 0) {
          D_PERROR( "DirectFB/linux_input: could not open device" );
          return DFB_INIT;
     }

     /* grab device */
     if (dfb_config->linux_input_grab) {
          ret = ioctl( fd, EVIOCGRAB, 1 );
          /* 2.4 kernels don't have EVIOCGRAB so ignore EINVAL */
          if (ret && errno != EINVAL) {
               D_PERROR( "DirectFB/linux_input: could not grab device" );
               close( fd );
               return DFB_INIT;
          }
     }

     /* fill device info structure */
     get_device_info( fd, info, &touchpad );

     /* allocate and fill private data */
     data = D_CALLOC( 1, sizeof(LinuxInputData) );
     if (!data) {
          if (dfb_config->linux_input_grab)
               ioctl( fd, EVIOCGRAB, 0 );
          close( fd );
          return D_OOM();
     }

     data->fd     = fd;
     data->device = device;
     data->touchpad = touchpad;
     data->vt_fd    = -1;
     data->pDeviceName = device_names[number];
     data->sensitive_numerator = 1;
     data->sensitive_denominator = 1;
     data->rel_axis_x_motion_reminder = 0;
     data->rel_axis_y_motion_reminder = 0;

     if (info->desc.min_keycode >= 0 && info->desc.max_keycode >= info->desc.min_keycode) {
#ifdef LINUX_INPUT_USE_FBDEV
          FBDev *dfb_fbdev = dfb_system_data();

          if (dfb_fbdev->vt)
               data->vt_fd = dup( dfb_fbdev->vt->fd );
#endif
          if (data->vt_fd < 0)
               data->vt_fd = open( "/dev/tty0", O_RDWR | O_NOCTTY );

          if (data->vt_fd < 0)
               D_WARN( "no keymap support (requires /dev/tty0 - CONFIG_VT)" );
     }

     /* check if the device has LEDs */
     ret = ioctl( fd, EVIOCGBIT(EV_LED, sizeof(ledbit)), ledbit );
     if (ret < 0)
          D_PERROR( "DirectFB/linux_input: could not get LED bits" );
     else
          data->has_leds = test_bit( LED_SCROLLL, ledbit ) ||
                           test_bit( LED_NUML, ledbit ) ||
                           test_bit( LED_CAPSL, ledbit );

     if (data->has_leds) {
          /* get LED state */
          ret = ioctl( fd, EVIOCGLED(sizeof(data->led_state)), data->led_state );
          if (ret < 0) {
               D_PERROR( "DirectFB/linux_input: could not get LED state" );
               goto driver_open_device_error;
          }

          /* turn off LEDs */
          set_led( data, LED_SCROLLL, 0 );
          set_led( data, LED_NUML, 0 );
          set_led( data, LED_CAPSL, 0 );
     }

     /* open a pipe to awake the reader thread when we want to quit */
     ret = pipe( data->quitpipe );
     if (ret < 0) {
          D_PERROR( "DirectFB/linux_input: could not open quitpipe" );
          goto driver_open_device_error;
     }

     /* start input thread */
     data->thread = direct_thread_create( DTT_INPUT, linux_input_EventThread, data, "Linux Input" );

     /* set private data pointer */
     *driver_data = data;

     return DFB_OK;

driver_open_device_error:
     if (dfb_config->linux_input_grab)
          ioctl( fd, EVIOCGRAB, 0 );
     if (data->vt_fd >= 0)
          close( data->vt_fd );
     close( fd );
     D_FREE( data );

     return DFB_INIT;
}

/*
 * Obtain information about an axis (only absolute axis so far).
 */
static DFBResult
driver_get_axis_info( CoreInputDevice              *device,
                      void                         *driver_data,
                      DFBInputDeviceAxisIdentifier  axis,
                      DFBInputDeviceAxisInfo       *ret_info )
{
     LinuxInputData *data = (LinuxInputData*) driver_data;

     if (data->touchpad)
          return DFB_OK;

     if (axis <= ABS_PRESSURE && axis < DIAI_LAST) {
          unsigned long absbit[NBITS(ABS_CNT)];

          /* check if we have an absolute axes */
          ioctl( data->fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit );

          if (test_bit (axis, absbit)) {
               struct input_absinfo absinfo;

               if (ioctl( data->fd, EVIOCGABS(axis), &absinfo ) == 0 &&
                   (absinfo.minimum || absinfo.maximum)) {
                    ret_info->flags  |= DIAIF_ABS_MIN | DIAIF_ABS_MAX;
                    ret_info->abs_min = absinfo.minimum;
                    ret_info->abs_max = absinfo.maximum;
               }
          }
     }

     return DFB_OK;
}

static DFBResult
driver_device_ioctl( CoreInputDevice              *device,
                      void                         *driver_data,
                     InputDeviceIoctlData *param)
{
     LinuxInputData *data = (LinuxInputData*) driver_data;

     if(MDRV_DFB_IOC_MAGIC!= _IOC_TYPE(param->request))
         return DFB_INVARG;

     switch(param->request)
     {
       case DFB_DEV_IOC_SET_REL_AXIS_SENSE_NUMERATOR:
          if(((int)param->param[0])<=0)
              return DFB_INVARG;
          data->sensitive_numerator = ((int)param->param[0]);
          data->rel_axis_x_motion_reminder = data->rel_axis_y_motion_reminder = 0;
          break;
       case DFB_DEV_IOC_SET_REL_AXIS_SENSE_DENOMINATOR:
          if(((int)param->param[0])<=0)
              return DFB_INVARG;
          data->sensitive_denominator = ((int)param->param[0]);
          data->rel_axis_x_motion_reminder = data->rel_axis_y_motion_reminder = 0;
          break;
       case DFB_DEV_IOC_GET_PHY_KEYBOARD_CNT:
           if(g_logic_keyboard.isPhysicalAttached)
           {
                memset(param->param, 0, sizeof(param->param));
                param->param[0] = 1;
           }
            else
            {
                memset(param->param, 0, sizeof(param->param));
                param->param[0] = 0;
           }
            break;
        case DFB_DEV_IOC_GET_PHY_MOUSE_CNT:
        {
              bool mouseDeviceIsAttached = false;
                int k = 0;
               for(k = 0; k < MAX_LINUX_INPUT_DEVICES; k++)
               {
                    if(g_logic_mouse.isPhysicalAttached[k])
                    {
                        mouseDeviceIsAttached = true;
                    }
               }
               if(mouseDeviceIsAttached)
               {
                    memset(param->param, 0, sizeof(param->param));
                    param->param[0] = 1;
               }
                else
                {
                    memset(param->param, 0, sizeof(param->param));
                    param->param[0] = 0;
               }
          }
            break;
       default:
          return DFB_UNSUPPORTED;
     }

     return DFB_OK;
}

/*
 * Fetch one entry from the kernel keymap.
 */
static DFBResult
driver_get_keymap_entry( CoreInputDevice           *device,
                         void                      *driver_data,
                         DFBInputDeviceKeymapEntry *entry )
{
    // LinuxInputData             *data = (LinuxInputData*) driver_data;
     int                         code = entry->code;
     unsigned short              value;
     DFBInputDeviceKeyIdentifier identifier;

    // if (data->vt_fd < 0)
     //     return DFB_UNSUPPORTED;

     /* fetch the base level */
     value = keyboard_read_value( driver_data, K_NORMTAB, code );

     /* get the identifier for basic mapping */
     identifier = keyboard_get_identifier( code, value );

     /* is CapsLock effective? */
     if (KTYP(value) == KT_LETTER)
          entry->locks |= DILS_CAPS;

     /* is NumLock effective? */
     if (identifier >= DIKI_KP_DECIMAL && identifier <= DIKI_KP_9)
          entry->locks |= DILS_NUM;

     /* write identifier to entry */
     entry->identifier = identifier;

     /* write base level symbol to entry */
     entry->symbols[DIKSI_BASE] = keyboard_get_symbol( code, value, DIKSI_BASE );


     /* fetch the shifted base level */
     value = keyboard_read_value( driver_data, K_SHIFTTAB, entry->code );

     /* write shifted base level symbol to entry */
     entry->symbols[DIKSI_BASE_SHIFT] = keyboard_get_symbol( code, value,
                                                             DIKSI_BASE_SHIFT );


     /* fetch the alternative level */
     value = keyboard_read_value( driver_data, K_ALTTAB, entry->code );

     /* write alternative level symbol to entry */
     entry->symbols[DIKSI_ALT] = keyboard_get_symbol( code, value, DIKSI_ALT );


     /* fetch the shifted alternative level */
     value = keyboard_read_value( driver_data, K_ALTSHIFTTAB, entry->code );

     /* write shifted alternative level symbol to entry */
     entry->symbols[DIKSI_ALT_SHIFT] = keyboard_get_symbol( code, value,
                                                            DIKSI_ALT_SHIFT );

     return DFB_OK;
}

/*
 * End thread, close device and free private data.
 */
static void
driver_close_device( void *driver_data )
{
     LinuxInputData *data = (LinuxInputData*) driver_data;

     D_DEBUG_AT( Debug_LinuxInput, "%s()\n", __FUNCTION__ );
     printf("----->hotplug thread exit! %d\n", __LINE__);
     isHotplugExit = true;

     /* stop input thread */
     direct_thread_cancel( data->thread );
     (void)write( data->quitpipe[1], " ", 1 );
     direct_thread_join( data->thread );
     direct_thread_destroy( data->thread );
     close( data->quitpipe[0] );
     close( data->quitpipe[1] );

     if (data->has_leds) {
          /* restore LED state */
          set_led( data, LED_SCROLLL, test_bit( LED_SCROLLL, data->led_state ) );
          set_led( data, LED_NUML, test_bit( LED_NUML, data->led_state ) );
          set_led( data, LED_CAPSL, test_bit( LED_CAPSL, data->led_state ) );
     }

     /* release device */
     if (dfb_config->linux_input_grab)
          ioctl( data->fd, EVIOCGRAB, 0 );

  if (data->vt_fd >= 0)
          close( data->vt_fd );

     /* close file */
     close( data->fd );

     /* free private data */
     D_FREE( data );

     D_DEBUG_AT( Debug_LinuxInput, "%s() closed\n", __FUNCTION__ );
}

static bool
timeout_is_set( const struct timeval *timeout )
{
     return timeout->tv_sec || timeout->tv_usec;
}

static bool
timeout_passed( const struct timeval *timeout, const struct timeval *current )
{
     return !timeout_is_set( timeout ) ||
          current->tv_sec > timeout->tv_sec ||
          (current->tv_sec == timeout->tv_sec && current->tv_usec > timeout->tv_usec);
}

static void
timeout_clear( struct timeval *timeout )
{
     timeout->tv_sec  = 0;
     timeout->tv_usec = 0;
}

static void
timeout_add( struct timeval *timeout, const struct timeval *add )
{
     timeout->tv_sec += add->tv_sec;
     timeout->tv_usec += add->tv_usec;
     while (timeout->tv_usec >= 1000000) {
          timeout->tv_sec++;
          timeout->tv_usec -= 1000000;
     }
}

static void
timeout_sub( struct timeval *timeout, const struct timeval *sub )
{
     timeout->tv_sec -= sub->tv_sec;
     timeout->tv_usec -= sub->tv_usec;
     while (timeout->tv_usec < 0) {
          timeout->tv_sec--;
          timeout->tv_usec += 1000000;
     }
}

static void
touchpad_fsm_init( struct touchpad_fsm_state *state )
{
     state->x.old = -1;
     state->y.old = -1;
     state->fsm_state = TOUCHPAD_FSM_START;
     timeout_clear( &state->timeout );
}

static int
touchpad_normalize( const struct touchpad_axis *axis, int value )
{
     return ((value - axis->min) << 9) / (axis->max - axis->min);
}

static int
touchpad_translate( struct touchpad_fsm_state *state,
                    const struct input_event  *levt,
                    DFBInputEvent             *devt )
{
     struct touchpad_axis *axis = NULL;
     int abs, rel;

     devt->flags     = DIEF_TIMESTAMP | DIEF_AXISREL;
     devt->timestamp = levt->time;
     devt->type      = DIET_AXISMOTION;

     switch (levt->code) {
     case ABS_X:
          axis       = &state->x;
          devt->axis = DIAI_X;
          break;
     case ABS_Y:
          axis       = &state->y;
          devt->axis = DIAI_Y;
          break;
     default:
          return 0;
     }

     abs = touchpad_normalize( axis, levt->value );
     if (axis->old == -1)
          axis->old = abs;
     rel = abs - axis->old;

#define ACCEL_THRESHOLD 25
#define ACCEL_NUM       3
#define ACCEL_DENOM     1

     if (rel > ACCEL_THRESHOLD)
          rel += (rel - ACCEL_THRESHOLD) * ACCEL_NUM / ACCEL_DENOM;
     else if (rel < -ACCEL_THRESHOLD)
          rel += (rel + ACCEL_THRESHOLD) * ACCEL_NUM / ACCEL_DENOM;

     axis->old     = abs;
     devt->axisrel = rel;

     return 1;
}

static bool
touchpad_finger_landing( const struct input_event *levt )
{
     return levt->type == EV_KEY && levt->code == BTN_TOUCH && levt->value == 1;
}

static bool
touchpad_finger_leaving( const struct input_event *levt )
{
     return levt->type == EV_KEY && levt->code == BTN_TOUCH && levt->value == 0;
}

static bool
touchpad_finger_moving( const struct input_event *levt )
{
     return levt->type == EV_ABS && (levt->code == ABS_X || levt->code == ABS_Y);
}

/*
 * This FSM takes into accout finger landing on touchpad and leaving and
 * translates absolute DFBInputEvent into a relative one
 */
static int
touchpad_fsm( struct touchpad_fsm_state *state,
              const struct input_event  *levt,
              DFBInputEvent             *devt )
{
     struct timeval timeout = { 0, 125000 };

     /* select() timeout? */
     if (!levt) {
          /* Check if button release is due. */
          if (state->fsm_state == TOUCHPAD_FSM_DRAG_START) {
               devt->flags     = DIEF_TIMESTAMP;
               devt->timestamp = state->timeout; /* timeout of current time? */
               devt->type      = DIET_BUTTONRELEASE;
               devt->button    = DIBI_FIRST;

               touchpad_fsm_init( state );
               return 1;
          }

          /* Already passed, clear it so select() won't return until there is something to do. */
          timeout_clear( &state->timeout );
          return 0;
     }

     /* More or less ignore these events for now */
     if ((levt->type == EV_SYN && levt->code == SYN_REPORT) ||
         (levt->type == EV_ABS && levt->code == ABS_PRESSURE) ||
         (levt->type == EV_ABS && levt->code == ABS_TOOL_WIDTH) ||
         (levt->type == EV_KEY && levt->code == BTN_TOOL_FINGER) ||
         (levt->type == EV_KEY && levt->code == BTN_TOOL_DOUBLETAP) ||
         (levt->type == EV_KEY && levt->code == BTN_TOOL_TRIPLETAP)) {

          /* Check if button release is due. */
          if (state->fsm_state == TOUCHPAD_FSM_DRAG_START &&
              timeout_passed( &state->timeout, &levt->time )) {
               devt->flags     = DIEF_TIMESTAMP;
               devt->timestamp = state->timeout; /* timeout of levt->time? */
               devt->type      = DIET_BUTTONRELEASE;
               devt->button    = DIBI_FIRST;

               touchpad_fsm_init( state );
               return 1;
          }

          return 0;
     }

     /* Use translate_event() for other events. */
     if (!(levt->type == EV_KEY && levt->code == BTN_TOUCH) &&
         !(levt->type == EV_ABS && (levt->code == ABS_X || levt->code == ABS_Y)))
          return -1;

     switch (state->fsm_state) {
     case TOUCHPAD_FSM_START:
          if (touchpad_finger_landing( levt )) {
               state->fsm_state = TOUCHPAD_FSM_MAIN;
               state->timeout = levt->time;
               timeout_add( &state->timeout, &timeout );
          }
          return 0;

     case TOUCHPAD_FSM_MAIN:
          if (touchpad_finger_moving( levt )) {
               if (1){//timeout_passed( &state->timeout, &levt->time )) {
                    //timeout_clear( &state->timeout );
                    return touchpad_translate( state, levt, devt );
               }
          }
          else if (touchpad_finger_leaving( levt )) {
               if (!timeout_passed( &state->timeout, &levt->time )) {
                    devt->flags     = DIEF_TIMESTAMP;
                    devt->timestamp = levt->time;
                    devt->type      = DIET_BUTTONPRESS;
                    devt->button    = DIBI_FIRST;

                    touchpad_fsm_init( state );
                    state->fsm_state = TOUCHPAD_FSM_DRAG_START;
                    state->timeout = levt->time;
                    timeout_add( &state->timeout, &timeout );
                    return 1;
               }
               else {
                    touchpad_fsm_init( state );
               }
          }
          return 0;

     case TOUCHPAD_FSM_DRAG_START:
          if (timeout_passed( &state->timeout, &levt->time )){
               devt->flags     = DIEF_TIMESTAMP;
               devt->timestamp = state->timeout; /* timeout of levt->time? */
               devt->type      = DIET_BUTTONRELEASE;
               devt->button    = DIBI_FIRST;

               touchpad_fsm_init(state);
               return 1;
          }
          else {
               if (touchpad_finger_landing( levt )) {
                    state->fsm_state = TOUCHPAD_FSM_DRAG_MAIN;
                    state->timeout = levt->time;
                    timeout_add( &state->timeout, &timeout );
               }
          }
          return 0;

     case TOUCHPAD_FSM_DRAG_MAIN:
          if (touchpad_finger_moving( levt )) {
               if (1){//timeout_passed( &state->timeout, &levt->time )) {
                   //timeout_clear( &state->timeout );
                    return touchpad_translate( state, levt, devt );
               }
          }
          else if (touchpad_finger_leaving( levt )) {
               devt->flags     = DIEF_TIMESTAMP;
               devt->timestamp = levt->time;
               devt->type      = DIET_BUTTONRELEASE;
               devt->button    = DIBI_FIRST;

               touchpad_fsm_init( state );
               return 1;
          }
          return 0;

     default:
          return 0;
     }

     return 0;
}
