/*
 *  rtl_kbd.c 
 *
 * This file has several functions from oskit project
 *
 * Written by Miguel Masmano Tello <mmasmano@disca.upv.es>
 * Copyright (C) April, 2003 OCERA Consortium
 * Release under the terms of the GNU General Public License Version 2
 */

#include "include/kbd.h"
#include "include/vga.h"
#include "include/rtl_screen.h"
#include <pthread.h>
#include <rtl_core.h>
#include <linux/kbd_kern.h>
#include <linux/kbd_ll.h>
#include <linux/pc_keyb.h>

#define SHIFT -1
#define KEYBF1 -2
#define KEYBF2 -3
#define KEYBF3 -4
#define KEYBF4 -5
#define KEYBF5 -6
#define KEYBF6 -7
#define KEYBF7 -8
#define KEYBF8 -9
#define KEYBF9 -10
#define KEYBF10 -11

int terminal_state = DEACTIVATED;

static char keymap[128][2] = {
        {0},                    /* 0 */
        {27,    27},            /* 1 - ESC */
        {'1',   '!'},           /* 2 */
        {'2',   '@'},
        {'3',   '#'},
        {'4',   '$'},
        {'5',   '%'},
        {'6',   '^'},
        {'7',   '&'},
        {'8',   '*'},
        {'9',   '('},
        {'0',   ')'},
        {'-',   '_'},
        {'=',   '+'},
        {8,     8},             /* 14 - Backspace */
        {'\t',  '\t'},          /* 15 */
        {'q',   'Q'},
        {'w',   'W'},
        {'e',   'E'},
        {'r',   'R'},
        {'t',   'T'},
        {'y',   'Y'},
        {'u',   'U'},
        {'i',   'I'},
        {'o',   'O'},
        {'p',   'P'},
        {'[',   '{'},
        {']',   '}'},           /* 27 */
        {'\r',  '\r'},          /* 28 - Enter */
        {0,     0},             /* 29 - Ctrl */
        {'a',   'A'},           /* 30 */
        {'s',   'S'},
        {'d',   'D'},
        {'f',   'F'},
        {'g',   'G'},
        {'h',   'H'},
        {'j',   'J'},
        {'k',   'K'},
        {'l',   'L'},
        {';',   ':'},
        {'\'',  '"'},           /* 40 */
        {'`',   '~'},           /* 41 */
        {SHIFT, SHIFT},         /* 42 - Left Shift */
        {'\\',  '|'},           /* 43 */
	{'z',   'Z'},           /* 44 */
        {'x',   'X'},
        {'c',   'C'},
        {'v',   'V'},
        {'b',   'B'},
        {'n',   'N'},
        {'m',   'M'},
        {',',   '<'},
        {'.',   '>'},
        {'/',   '?'},           /* 53 */
        {SHIFT, SHIFT},         /* 54 - Right Shift */
        {0,     0},             /* 55 - Print Screen */
        {0,     0},             /* 56 - Alt */
        {' ',   ' '},           /* 57 - Space bar */
        {0,     0},             /* 58 - Caps Lock */
        {KEYBF1,     KEYBF1},             /* 59 - F1 */
        {KEYBF2,     KEYBF2},             /* 60 - F2 */
        {KEYBF3,     KEYBF3},             /* 61 - F3 */
        {KEYBF4,     KEYBF4},             /* 62 - F4 */
        {KEYBF5,     KEYBF5},             /* 63 - F5 */
        {KEYBF6,     KEYBF6},             /* 64 - F6 */
        {KEYBF7,     KEYBF7},             /* 65 - F7 */
        {KEYBF8,     KEYBF8},             /* 66 - F8 */
        {KEYBF9,     KEYBF9},             /* 67 - F9 */
        {KEYBF10,    KEYBF10},             /* 68 - F10 */
        {0,      0},            /* 69 - Num Lock */
        {0,      0},            /* 70 - Scroll Lock */
        {'7',   '7'},           /* 71 - Numeric keypad 7 */
        {'8',   '8'},           /* 72 - Numeric keypad 8 */
        {'9',   '9'},           /* 73 - Numeric keypad 9 */
        {'-',   '-'},           /* 74 - Numeric keypad '-' */
        {'4',   '4'},           /* 75 - Numeric keypad 4 */
        {'5',   '5'},           /* 76 - Numeric keypad 5 */
        {'6',   '6'},           /* 77 - Numeric keypad 6 */
        {'+',   '+'},           /* 78 - Numeric keypad '+' */
        {'1',   '1'},           /* 79 - Numeric keypad 1 */
        {'2',   '2'},           /* 80 - Numeric keypad 2 */
        {'3',   '3'},           /* 81 - Numeric keypad 3 */
        {'0',   '0'},           /* 82 - Numeric keypad 0 */
        {'.',   '.'},           /* 83 - Numeric keypad '.' */
};

static unsigned char buffer_char = 0;
static int waiting_a_char = DEACTIVATED;

static pthread_mutex_t getchar_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t getchar2_mutex = PTHREAD_MUTEX_INITIALIZER;

int rt_terminal_getchar (void) {
  unsigned char temp = 0;
  
  pthread_mutex_lock (&getchar_mutex);
  waiting_a_char = ACTIVATED;

  while (waiting_a_char == ACTIVATED)
    pthread_mutex_lock (&getchar2_mutex);

  temp = buffer_char;
  pthread_mutex_unlock (&getchar_mutex);
 
  if (temp == '\r' || temp == '\n') return 10;
  return temp;
}
       
/* Translate keyboard Scancode to ASCII code */
static char scan_to_ascii(int scan_code) {
  static unsigned shift_state;
  char ch;
  
  /* Handle key releases - only release of SHIFT is important. */
  if (scan_code & 0x80) {
    scan_code &= 0x7f;
    if (keymap[scan_code][0] == SHIFT)
      shift_state = 0;
    ch = -1;
  } else {
    /* Translate the character through the keymap. */
    ch = keymap[scan_code][shift_state];
    if (ch == SHIFT) {
      shift_state = 1;
      ch = -1;
    } else if (ch == 0)
      ch = -1;
  }
  return ch;
}

/* Manage the scancode that we've read */
static int rtl_handle_scancode(unsigned char scancode) {
  char car = scan_to_ascii((int) scancode);
  static unsigned char c_map = 0x0;

  if (car == -1) return 0;
  switch (car) {
  case KEYBF1:
    if (terminal_state == ACTIVATED && waiting_a_char == ACTIVATED) {
      buffer_char = -1;
      waiting_a_char = DEACTIVATED;
      pthread_mutex_unlock (&getchar2_mutex);
      return 1;
    } 
    return 0;
    break;
  case KEYBF8: 
    if (terminal_state == ACTIVATED) {
      c_map ++;
      set_char_map_selec (c_map);
      return 1;
    } 
    return 0;
    break;
  case KEYBF9:
    if (terminal_state == ACTIVATED) {
      rt_clrscr ();
      return 1;
    }
    return 0;
    break;
  case KEYBF10: 
    if (terminal_state == DEACTIVATED) {
      console_on ();
      terminal_state = ACTIVATED;
      return 1;
    } else {
      terminal_state = DEACTIVATED;
      console_off ();
      return 1;
    }
    break;
  default:
    if (terminal_state == ACTIVATED && waiting_a_char == ACTIVATED) {
      rt_terminal_putchar (car);
      buffer_char = car;
      waiting_a_char = DEACTIVATED;
      pthread_mutex_unlock (&getchar2_mutex);
      return 1;
    }
    if (terminal_state == ACTIVATED) return 1;
    return 0; 
  }
  return 0;
}


#ifdef AS_BACKGROUND_APP
extern int (*rtl_intercept_scancode) (unsigned char scancode);

static int rtl_intercept_scancode_routine (unsigned char scancode) {
  return rtl_handle_scancode(scancode);
}
#else

inline unsigned char get_status (void) {
  return rtl_inb (0x64);
}

inline unsigned char get_scancode(void) {
  return rtl_inb (0x60);
}

static int linux_kbd_irq;
static unsigned char scancode = 0;

// This function is exported by the Linux Kernel
static void linux_kbd_handler (int irq, void *p, struct pt_regs *pr) {
  handle_scancode (scancode, !(scancode & 0x80));
  tasklet_schedule(&keyboard_tasklet);
}

static unsigned int kbd_handler (unsigned int irq, struct pt_regs *pr) {

  unsigned char status = get_status ();

  while (status & KBD_STAT_OBF) { 
    scancode = get_scancode ();
    if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR)))
      if (rtl_handle_scancode(scancode) == 0)
	rtl_global_pend_irq (linux_kbd_irq);
    status = get_status ();
  }
  
  rtl_hard_enable_irq (1);
  return 0;
}

#endif

int init_kbd (void){
  rtl_stop_interrupts ();
#ifdef AS_BACKGROUND_APP
  rtl_intercept_scancode = rtl_intercept_scancode_routine;
#else
  linux_kbd_irq = rtl_get_soft_irq (linux_kbd_handler, "RTL-Keyboard");
  if (linux_kbd_irq < 0 || rtl_request_irq (1, kbd_handler) < 0) {
    rtl_printf ("Problems installing irq handlers\n");
    return -1;
  }
#endif
  rtl_allow_interrupts ();
  return 0;
}

void close_kbd (void) {
  rtl_stop_interrupts ();
#ifdef AS_BACKGROUND_APP
  rtl_intercept_scancode = NULL;
#else
  rtl_free_soft_irq (linux_kbd_irq);
  rtl_free_irq (1);
#endif
  rtl_allow_interrupts ();
}
