/*
  Keyboard.c, Copyright  (c) by Lukas Ruf, lr@lpr.ch,
  Swiss Federal Institute of Technology,
  Computer Engineering and Networks Laboratory.
 
  TOPSY -- A Teachable Operating System.
           Implementation of a tiny and simple
           micro kernel for teaching purposes.
 
  For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
  This software is provided under the terms of the GNU General Public Licence.
 
  A full copy of the GNU GPL is provided in the file COPYING found in the 
  development root of Topsy.
*/
/*
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/IO/ia32/Drivers/Keyboard.c,v $
 	Author(s):             Lukas Ruf, lr@lpr.ch
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 3039 $
 	Creation Date:         
 	Last Date of Change:   $Date: 2007-06-12 21:43:31 +0200 (Die, 12 Jun 2007) $      by: $Author: sryffel $
*/
#include "keyboard.h"
#include "SupportAsm.h"

#include "Video.h"
#include "Topsy.h"
#include "TMHalAsm.h"
#include "video.h"

#ifdef APIC
#include "TMHalApic.h"
#endif

#define KEYECHO     TRUE
#define KEYDEBUG    FALSE
#define KEYGETDEBUG FALSE

/*  Translate
      Break-Key 
      PrtScr
    to unqiue "Hardware Make/Break Code" according coding scheme of my keyboard
    handling.
    See MakeKey.TBL, resp. Documentation to Keyboard-Handling 
    These codes are not used in the standard MF-II 102-Keyboard */

/* selfmade MAKE CODEs */
#define K_BREAK_MAKE_CODE  90
#define K_PRTSCR_MAKE_CODE 91
#define K_ENTER_MAKE_CODE  92
#define K_HOME_MAKE_CODE   94
#define K_UP_MAKE_CODE     95
#define K_PGUP_MAKE_CODE   98
#define K_LEFT_MAKE_CODE   99
#define K_RIGHT_MAKE_CODE  100
#define K_END_MAKE_CODE    101
#define K_DOWN_MAKE_CODE   102
#define K_PGDN_MAKE_CODE   103
#define K_INSERT_MAKE_CODE 104
#define K_DELETE_MAKE_CODE 105

/* MAKE CODES specially handled */
#define KEYCTRL                        29
#define KEYLEFTSHIFT                   42
#define KEYRIGHTSHIFT                  54
#define KEYALT                         56
#define KEYCAPSLOCK                    58
#define KEYNUMLOCK                     69
#define KEYSCROLLLOCK                  70
#define KEYALTSYSRQ                    84

#define KEYEXT2SIGN                    0xE1
#define KEYEXTSIGN                     0xE0
#define KBBSIZE            			   0x80

/* module global variables */
static unsigned char  kLEDSetting = 0x00;
static unsigned char  KBB[KBBSIZE];
static unsigned int   KBBHead;
static unsigned int   KBBTail;

/* This variable performs the mutex :-) */
static unsigned int KeyBoardLock;

#define HEADLOCK 0      /* set bit 0 */
#define TAILLOCK 1      /* set bit 1 */

#define K_SCLLOCK 1     /* SCROLL LOCK - LED */
#define K_NUMLOCK 2     /* NUM LOCK - LED */
#define K_CPSLOCK 4     /* CAPS LOCK - LED */

static unsigned char  *K_LHWMap;  /* Scan Code to Logical HW Key Code Map (7b -> 7b) */
static unsigned char  *K_LKCMap;  /* Logical HW Key Code to ASCII Map     (10b -> 8b) */
static unsigned short *K_LMSMap;  /* Key Message Map                      (10b -> 16b) */

/* Keyboard-Variables: */
static char KeyExt2Cnt;     /* Counter for two extended bytes */
static Boolean KeyExt2Set;     /* 0xE1 */
static Boolean KeyExtSet;        /* 0xE0 */
static Boolean KeyAltDown;
static Boolean KeyCtrlDown;
static Boolean KeyShiftDown;
static Boolean KeyCapsSet;
static Boolean KeyNumLockSet;
static Boolean KeyScrollSet;

Boolean _IsKeypressed()
{
  return KBBHead > KBBTail;
}

Boolean _GetChar(char *pCharToGet)
{
  Boolean xret = FALSE;
  *pCharToGet = 0;
  if (_IsKeypressed())
  {
    __mutex(&KeyBoardLock,TAILLOCK);
    *pCharToGet = (char)KBB[KBBTail % KBBSIZE];
    KBBTail++;
    __demutex(&KeyBoardLock,TAILLOCK);
    if (KEYGETDEBUG)
      kprintc(*pCharToGet);
    xret = TRUE;
  }
  return xret;
}

static void SetKeyLED(Boolean pSet, unsigned char pPos)
{
  if (pSet)
    kLEDSetting |= pPos;
  else
    kLEDSetting &= !pPos;
  kLEDSetting &= 7;   /* make sure only lower 3 bits are set */
  __outb(0xED,0x60);
  while (__inb(0x64) & 0x02)
    ;  /* wait until CPU->Keyboard Buffer empty */
  __outb(kLEDSetting,0x60);
  return;
}

/*********************/
/* KEYBOARD HANDLERS */
/*********************/
static void KeyInit()
{                      /* Initialize Keyboard Variables */
  KeyExt2Set    = FALSE;              /* ATTENTION Byte Marker (PrtScr) */
  KeyExtSet     = FALSE;
  KeyAltDown    = FALSE;              /* ALT   Key is not pressed */
  KeyCtrlDown   = FALSE;              /* CTRL */
  KeyShiftDown  = FALSE;              /* SHIFT */
  KeyCapsSet    = FALSE;              /* CAPS LOCK is not set */
  KeyNumLockSet = FALSE;              /* NUM  LOCK */
  KeyScrollSet  = FALSE;              /* SCROLL LOCK */
  K_LHWMap      = __addrHWKeyTrl();   /* Assign Address via function, as direct */
  K_LKCMap      = __addrKeyAscTrl();  /* assignment has not worked properly ??? */
  K_LMSMap      = __addrKeyMsgTrl();
  KBBHead       = 0;
  KBBTail       = 0;
  KeyBoardLock  = 0;
  return;
}

void KeyboardInit()
{
  KeyInit();
#ifdef APIC
  ioapic_enable_IRQ(0x21);
#else
  __enable_IRQ(0x21);
  __endOf_IRQ(0x21);
#endif
  return;
}

unsigned long KeyModifier(unsigned char pRawKey)
{
  Boolean xbreak, xerr;
  unsigned long xret = 1;
  if (KEYDEBUG)
    Debug("KeyModifier called: 0x%x (%i) -- ",pRawKey,(int)pRawKey);
  if (KeyAltDown && KeyCtrlDown && (pRawKey == 0x53)) /* Alt-Ctrl-Delete */
    __reboot();
  if (pRawKey == 0xFA)
  {
    /* if (KEYDEBUG) Debug("(=ACK)\n"); */
    return 0x00;
  }
  if (KeyExt2Set)
  {   /* 1st byte after 0xE1 */
    /* Break was pressed -> 0xE1 0x1D 0x45 */
    /*       or released -> 0xE1 0x9D 0xC5 */
    xerr = FALSE;
    switch (pRawKey & 0x7F)
    {
    case 0x1D :
      KeyExt2Cnt = 1;
      break;
    case 0x45 :
      KeyExt2Cnt = 2;
      break;
    default   :
      xerr = TRUE;
    }
    if (KEYDEBUG)
      Debug(" \"Break\" ");
    KeyExt2Set = (KeyExt2Cnt < 2);
    if (KeyExt2Set)
      xret = 0; /* disable key output until all bytes passed */
    else
      pRawKey = K_BREAK_MAKE_CODE | (pRawKey & 0x80);
    if (xerr)
    {
      (" Warning !!! Unknown Raw Key Code 0x%x\n",(int)pRawKey);
      xret = 0;
    }
  }
  else
    if (KeyExtSet)
    { /* byte after 0xE0  */
      /* PrtScr Press:  -> 0xE0 0x2A 0xE0 0x37 --> Repeated only "0xE0 0x37" */
      /*      Release:  -> 0xE0 0xB7 0xE0 0xAA */
      /* Ctrl-PrtScr :  -> 0xE0 0x37 */
      /*      Release:  -> 0xE0 0xB7 */
      if ((pRawKey & 0x7F) == 0x2A) /* discard envelopping 0xE0 0x2A resp. 0xAA */
        xret = 0;
      else /* translate extended keycodes to unique Topsy HW KeyCode !! */
        switch (pRawKey & 0x7F)
        {
        case 0x28 : /* NumPad Enter */
          pRawKey = K_ENTER_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"NumPad Enter\" ");
          break;
        case 0x37 : /* PrtScr */
          pRawKey = K_PRTSCR_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"PrtScr\" ");
          break;
        case 0x46 : /* Break (resp. Ctrl-Break) */
          pRawKey = K_BREAK_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"Break\" ");
          break;
        case 0x47 : /* Middle Home */
          pRawKey = K_HOME_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"Home\" ");
          break;
        case 0x48 : /* Middle Up */
          pRawKey = K_UP_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"Up\" ");
          break;
        case 0x49 : /* Middle PgUp */
          pRawKey = K_PGUP_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"PgUp\" ");
          break;
        case 0x4B : /* Middle Left */
          pRawKey = K_LEFT_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"Left\" ");
          break;
        case 0x4D : /* Middle Right */
          pRawKey = K_RIGHT_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"Right\" ");
          break;
        case 0x4F : /* Middle End */
          pRawKey = K_END_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"End\" ");
          break;
        case 0x50 : /* Middle Down */
          pRawKey = K_DOWN_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"Down\" ");
          break;
        case 0x51 : /* Middle PgDn */
          pRawKey = K_PGDN_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"PgDn\" ");
          break;
        case 0x52 : /* Middle Insert */
          pRawKey = K_INSERT_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"Insert\" ");
          break;
        case 0x53 : /* Middle Delete */
          pRawKey = K_DELETE_MAKE_CODE | (pRawKey & 0x80);
          if (KEYDEBUG)
            Debug(" \"Delete\" ");
          break;
        }
      KeyExtSet = FALSE;
    }
  if (xret && !KeyExt2Set)
  {
    KeyExtSet = (pRawKey == 0xE0);
    KeyExt2Set= (pRawKey == 0xE1);
    xbreak    = ((pRawKey & 0x80) == 0x80); /* is it break code */
    xret      = 0;
    if (!KeyExtSet && !KeyExt2Set)
      if (xbreak)
        switch (pRawKey & 0x7F)
        {
        case KEYALT :                   /* left or right ALT Key */
          KeyAltDown     = FALSE;       /* Toggle Flag */
          if (KEYDEBUG)
            Debug(" KeyALT released ");
          break;
        case KEYCTRL:                   /* left or right CTRL Key */
          KeyCtrlDown    = FALSE;       /* Toggle Flag */
          if (KEYDEBUG)
            Debug(" KeyCTRL released ");
          break;
        case KEYRIGHTSHIFT :            /* right SHIFT Key */
          KeyShiftDown   = FALSE;       /* Toggle Flag */
          if (KEYDEBUG)
            Debug(" KeyRIGHTSHIFT released ");
          break;
        case KEYLEFTSHIFT :             /* left SHIFT Key */
          KeyShiftDown   = FALSE;       /* Toggle Flag */
          if (KEYDEBUG)
            Debug(" KeyLEFTSHIFT released ");
          break;
        }
      else
        switch (pRawKey)
        {
        case KEYALT :                     /* left or right ALT Key */
          KeyAltDown     = TRUE;          /* Toggle Flag */
          if (KEYDEBUG)
            Debug(" KeyALT pressed ");
          break;
        case KEYCTRL:                     /* left or right CTRL Key */
          KeyCtrlDown    = TRUE;          /* Toggle Flag */
          if (KEYDEBUG)
            Debug(" KeyCTRL pressed ");
          break;
        case KEYRIGHTSHIFT :              /* right SHIFT Key */
          KeyShiftDown   = TRUE;          /* Toggle Flag */
          if (KEYDEBUG)
            Debug(" KeyRIGHTSHIFT pressed ");
          break;
        case KEYLEFTSHIFT :               /* left SHIFT Key */
          KeyShiftDown   = TRUE;        /* Toggle Flag */
          if (KEYDEBUG)
            Debug(" KeyLEFTSHIFT pressed ");
          break;
        case KEYCAPSLOCK :
          KeyCapsSet = (KeyCapsSet) ? FALSE : TRUE;
          SetKeyLED(KeyCapsSet,K_CPSLOCK);
          if (KEYDEBUG)
            Debug(" CapsLock Toggled ");
          break;
        case KEYNUMLOCK :                 /* NumLock */
          KeyNumLockSet = (KeyNumLockSet) ? FALSE : TRUE;
          SetKeyLED(KeyNumLockSet,K_NUMLOCK);
          if (KEYDEBUG)
            Debug(" NumLock Toggled ");
          break;
        case KEYSCROLLLOCK :              /* Scroll Lock Key */
          KeyScrollSet = (KeyScrollSet) ? FALSE : TRUE;
          SetKeyLED(KeyScrollSet,K_SCLLOCK);
          if (KEYDEBUG)
            Debug(" Scroll Lock toggled ");
          break;
        case KEYALTSYSRQ :                /* Alt-PrtScr == Alt-SysRq */
          xret = K_PRTSCR_MAKE_CODE;
          if (KEYDEBUG)
            Debug(" \"Alt-PrtScr\" ");

          break;
        case K_BREAK_MAKE_CODE :
        case K_PRTSCR_MAKE_CODE :
        case K_ENTER_MAKE_CODE :
        case K_HOME_MAKE_CODE :
        case K_UP_MAKE_CODE :
        case K_PGUP_MAKE_CODE :
        case K_LEFT_MAKE_CODE :
        case K_RIGHT_MAKE_CODE :
        case K_END_MAKE_CODE :
        case K_DOWN_MAKE_CODE :
        case K_PGDN_MAKE_CODE :
        case K_INSERT_MAKE_CODE :
        case K_DELETE_MAKE_CODE :
          xret = (pRawKey & 0x7F);
          if (KEYDEBUG)
            Debug(" \"Selfmade\" MAKE CODE processed ");
          break;
        default     :
          xret = 1;
        }
    else
      KeyExt2Cnt = 0;
  }
  if (KEYDEBUG)
    Debug(" --> xret = 0x%x\n",(int)xret);
  __printchar((24*80+79-6)*2,0x1E,KeyAltDown      ? 'A' : ' '); /* ALT */
  __printchar((24*80+79-5)*2,0x1E,KeyCtrlDown     ? 'C' : ' '); /* CTRL */
  __printchar((24*80+79-4)*2,0x1E,KeyShiftDown    ? 'S' : ' '); /* SHIFT  */
  __printchar((24*80+79-3)*2,0x1E,KeyCapsSet      ? 'L' : ' '); /* Caps Lock */
  __printchar((24*80+79-2)*2,0x1E,KeyScrollSet    ? 'R' : ' '); /* Scroll Lock */
  __printchar((24*80+79-1)*2,0x1E,KeyNumLockSet   ? 'N' : ' '); /* Num Lock */
  return xret;
}


static void KeyIntoBuffer(unsigned char pASCII)
{
  if (KEYDEBUG)
    Debug("KeyIntoBuffer: %c\n",(char)pASCII);
  __mutex(&KeyBoardLock,HEADLOCK);
  if (KEYECHO)
  {
    if (pASCII==13)
      kprintc(10);
    else
      kprintc(pASCII);
  }
  KBB[KBBHead % KBBSIZE] = pASCII;
  KBBHead++;
  __demutex(&KeyBoardLock,HEADLOCK);
  return;
}

/* return from SCANCODE an "eineindeutig", continously ranged 7b-Keycode  */
/* -> so called Logical HW Key (=7b) */
static unsigned char KeyMappedCode(unsigned long pSCANCODE)
{
  unsigned char xc;
  if (KEYDEBUG)
    Debug("-> KeyMappedCode: %i -- ",pSCANCODE);
  xc = K_LHWMap[pSCANCODE & 0x7F];
  if (KEYDEBUG)
    Debug("<- KeyMappedCode: %i \n",(int)xc);
  return xc;
}

/* Translate Logical HW Key into KEYCODE depending on Modifier state */
/* -> so called Key Code (=10b) */
static unsigned short KeyGetCode(unsigned char pLHWKey)
{
  unsigned short xret = 0;
  Boolean oldState = KeyShiftDown;
  if (KeyCapsSet && (pLHWKey >= 61) && (pLHWKey <= 86))
    KeyShiftDown = TRUE;

  xret = pLHWKey + (unsigned short)KeyAltDown*0x200 + (unsigned short)KeyCtrlDown*0x100 +
         (unsigned short)KeyShiftDown*0x80;
  KeyShiftDown = oldState;
  return xret;
}

static unsigned char KeyASCIICode(unsigned short pKeyCode)
{
  unsigned char xc;
  if (KEYDEBUG)
    Debug("->  KeyASCIICode: %i -- ",(int)pKeyCode);
  xc = K_LKCMap[pKeyCode & 0x3FF];
  if (KEYDEBUG)
    Debug("<-  KeyASCIICode: %i \n",(int)xc);
  return xc;
}

static void KeyMsgSender(unsigned short pKeyMsg)
{
  /* Send Message K_LMSMap[pKeyMsg & 0x3FF];*/
  return;
}

void _KeyboardISR()
{
  unsigned long lksl, lkmr;
  unsigned short lkoc;
  unsigned char lksb;
  lksb  = __inb(0x60);              /* read Keyboard; */
  lksl  = lksb;                     /* convert to long */
  lkmr  = KeyModifier(lksb);        /* handle key struck */
  if (lkmr != 0)
  {                        /* if "normal" key was pressed */
    if (lkmr != 1)
      lksl = lkmr;           /* if "selfmade" SCAN CODE  */
    lksb = KeyMappedCode(lksl);           /* get code mapped to continous ranges (1st translation) ( -> unequivocal code (7b)) */
    if (lksb < 0x80)
    {
      lkoc = KeyGetCode(lksb);            /* get one of 1024 Topsy Key Codes     (2nd translation, considers modifiers) (7b -> 10b) */
      lksb = KeyASCIICode(lkoc);          /* translate Topsy Key Code to ASCII (10b -> 8b) */
      if ((lksb > 0x00) && (lksb < 0xFF)) /* 0x00 = abort, 0xFF = send associated message */
        KeyIntoBuffer(lksb);              /* insert ascii code into keyboard buffer */
      else
        KeyMsgSender(lkoc);               /* or send an associated message to running process */
    }
  }
  return ;
}
/**********************************************************/
/* END OF KEYBOARD HANDLING                               */
/**********************************************************/


