View of xos/drivers/keyboard.c


XOS | Parent Directory | View | Download

/* Pilote pour le controleur de clavier Intel 8042 */
/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
#include "keymap.h"
#include "vc.h"
#include "vc_struct.h"
#include "tty.h"
#include "video_driver.h"
#include "tty_queue.h"
 
#include <panic.h>
#include <printk.h>
#include <asm.h>
#include <string.h>
 
/* commandes du 8042 */
#define CT_PULSE_PORTS  0xf0
 
/* commandes du 8048 */
#define KB_WRITE_LEDS   0xed
 
/* statut du 8048 */
#define KB_ACK          0xfa
#define KB_RESEND       0xfe
 
/* Traduction des scancodes en keycodes */
 
/* Les scancodes de 1 a 88 sont egaux a leur keycode. */
 
/* keycodes standards */
#define E0_KPENTER       96
#define E0_RCTRL         97
#define E0_KPSLASH       98
#define E0_PRSCR         99
#define E0_RALT         100
#define E0_BREAK        101
#define E0_HOME         102
#define E0_UP           103
#define E0_PGUP         104
#define E0_LEFT         105
#define E0_RIGHT        106
#define E0_END          107
#define E0_DOWN         108
#define E0_PGDN         109
#define E0_INS          110
#define E0_DEL          111
 
#define E1_PAUSE        119
 
/* Touches Windows.
 * e0 5b est le boutton "fenetre" gauche
 * e0 5c est le boutton "fenetre" droit
 * e0 5d est le bouton "menu" */
#define E0_MSLW         125
#define E0_MSRW         126
#define E0_MSTM         127
 
static unsigned char e0_keys[128] = {
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR,
  E0_RALT, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, E0_BREAK, E0_HOME,
  E0_UP, E0_PGUP, 0, E0_LEFT, 0, E0_RIGHT, 0, E0_END,
  E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,
  0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0
};
 
/* Traduction des keycodes en caracteres */
 
/* types de touches */
#define KT_LATIN         0
#define KT_FN            1
#define KT_SPEC          2
#define KT_PAD           3
#define KT_DEAD          4
#define KT_CONS          5
#define KT_CUR           6
#define KT_SHIFT         7
#define KT_META          8
#define KT_ASCII         9
#define KT_LOCK         10
#define KT_LETTER       11      /* symbole affecte par CapsLock */
#define KT_SLOCK        12
 
/* touches d'edition */
#define K_FIND          20
#define K_INSERT        21
#define K_REMOVE        22
#define K_SELECT        23
#define K_PGUP          24
#define K_PGDN          25
 
/* pave numerique */
#define K_P0             0
#define K_P1             1
#define K_P2             2
#define K_P3             3
#define K_P4             4
#define K_P5             5
#define K_P6             6
#define K_P7             7
#define K_P8             8
#define K_P9             9
#define K_PPLUS         10
#define K_PMINUS        11
#define K_PSTAR         12
#define K_PSLASH        13
#define K_PENTER        14
#define K_PDOT          16
 
/* fleches */
#define K_DOWN           0
#define K_LEFT           1
#define K_RIGHT          2
#define K_UP             3
 
/* touches de controle */
#define K_SHIFT          0
#define K_CTRL           2
#define K_ALT            2
#define K_ALTGR          1
#define K_SHIFTL         4
#define K_SHIFTR         5
#define K_CTRLL          6
#define K_CTRLR          7
#define K_CAPSSHIFT      8
 
static struct vc_struct dummy_con;
 
/* Variables d'etat */
 
static int shift_down[9];
static int dead_key_next;
static unsigned char diacr;
static int repeating;
static unsigned short prev_scancode;
static unsigned char prev_keycode;
static struct vc_struct *con;
static int char_put;
 
#define put_queue(c) ({ \
  tty_put(&con->tty, (c)); \
  char_put = 1; \
})
 
#define puts_queue(s) ({ \
  const char *tmp = (s); \
  while (*tmp) \
    put_queue(*tmp++); \
})
 
/* dummy_con */
 
static int dummy()
{
  return 0;
}
 
static const struct tty_vtbl_struct dummy_con_tty_vtbl = {
  .do_output = vc_tty_do_output,
  .do_stop = vc_tty_do_stop,
  .do_start = vc_tty_do_start,
  .do_ioctl = vc_tty_do_ioctl
};
 
static const struct vc_vtbl_struct dummy_con_vc_vtbl = {
  .do_stopped_changed = (void (*)())dummy
};
 
static const struct video_driver_struct dummy_video_driver = {
  .get_lines = (unsigned short (*)())dummy,
  .get_columns = (unsigned short (*)())dummy,
 
  .get_cursor_type = (unsigned char (*)())dummy,
  .set_cursor_type = (void (*)(unsigned char))dummy,
  .get_cursor_pos = (unsigned short (*)())dummy,
  .set_cursor_pos = (void (*)(unsigned short))dummy,
 
  .set_attr = (void (*)(const struct attr_mode_struct *))dummy,
  .putc = (void (*)(unsigned short, unsigned char))dummy,
  .clear = (void (*)(unsigned short, unsigned short))dummy,
  .scroll = (void (*)())dummy,
 
  .history_scroll = (void (*)(short))dummy,
  .history_cancel = (void (*)())dummy
};
 
static void dummy_con_init()
{
  dummy_con.tty.vptr = &dummy_con_tty_vtbl;
  dummy_con.vptr = &dummy_con_vc_vtbl;
  vc_init(&dummy_con, 0, 0);
  dummy_con.vt.do_kb_reset = (void (*)())dummy;
  dummy_con.vt.driver = &dummy_video_driver;
  dummy_con.vt.do_beep = (void (*)())dummy;
  dummy_con.lockstate = 0;
}
 
/* Routines du 8042 */
 
static unsigned char read_status()
{
  return inb(0x64);
}
 
/* Attend que le tampon de sortie du controleur ne soit pas vide. */
static inline int wait_input_full()
{
  int i;
 
  for (i = 0; i < 0x10000; i++)
    if (read_status() & 0x01)
      return 1;
  return 0;
}
 
/* Attend que le tampon d'entree du controleur soit vide. */
static inline int wait_output_empty()
{
  int i;
 
  for (i = 0; i < 0x10000; i++)
    if (!(read_status() & 0x02))
      return 1;
  return 0;
}
 
static void write_command(unsigned char command)
{
  wait_output_empty();
  outb(command, 0x64);
}
 
static unsigned char read_data()
{
  if (!wait_input_full())
    return 0;
  return inb(0x60);
}
 
static void write_data(unsigned char data)
{
  wait_output_empty();
  outb(data, 0x60);
}
 
static int send_data(unsigned char data)
{
  int try;
 
  for (try = 0; try < 3; try++) {
    write_data(data);
    switch (read_data()) {
    case KB_ACK:
      return 1;
    case KB_RESEND:
      continue;
    default:
      return 0;
    }
  }
  return 0;
}
 
/* Commandes de bas niveau */
 
static void set_leds(unsigned char leds)
{
  if (!send_data(KB_WRITE_LEDS))
    return;
  send_data(leds);
}
 
static void reset_cpu()
{
  write_command(CT_PULSE_PORTS | 0xe); /* pulse reset line */
}
 
/* Console */
 
/* Synchronise la console avec le pilote. */
static void synchronize()
{
  unsigned int i;
 
  con->modifiers = 0;
  for (i = 0; i < sizeof shift_down / sizeof *shift_down; i++)
    if (shift_down[i])
      con->modifiers |= 1 << i;
  set_leds(con->lockstate);
}
 
/* Utilitaires */
 
static void applkey(unsigned char value)
{
  put_queue('\033');
  put_queue('[');
  put_queue(value);
}
 
static unsigned char handle_diacr(unsigned char c)
{
  unsigned char d;
  unsigned int i;
 
  d = diacr;
  diacr = 0;
 
  for (i = 0; i < accent_table_size; i++)
    if (accent_table[i].diacr == d && accent_table[i].base == c)
      return accent_table[i].result;
 
  if (c == ' ' || c == d)
    return d;
 
  put_queue(d);
  return c;
}
 
static void __attribute__ ((noreturn)) reboot()
{
  reset_cpu();
  die();
}
 
/* Conversion des scancodes en keycodes */
 
static int translate(unsigned char scancode, unsigned char *keycode)
{
  unsigned char up_flag;
 
  /* Scancodes etendus */
  if (scancode == 0xe0 || scancode == 0xe1) {
    prev_scancode = scancode;
    return 0;
  }
 
  /* On ignore 0xfa (acknowledge), 0xfe (resend),
     0x00 (keyboard buffer overflow) et 0xff (keyboard error) */
  if (scancode == 0xfa || scancode == 0xfe)
    return 0;
  if (scancode == 0x00 || scancode == 0xff) {
    prev_scancode = 0;
    return 0;
  }
 
  up_flag = scancode & 0x80;
  scancode &= 0x7f;
 
  if (prev_scancode) {
 
    /* La touche Pause genere e1 1d 45 e1 9d c5 quand elle est pressee, et rien
       quand elle est relachee */
    if (prev_scancode != 0xe0) {
      if (prev_scancode == 0xe1 && scancode == 0x1d) {
        prev_scancode = 0x100;
        return 0;
      }
      else if (prev_scancode == 0x100 && scancode == 0x45) {
        *keycode = E1_PAUSE;
        prev_scancode = 0;
      }
      else {
        prev_scancode = 0;
        return 0;
      }
    }
 
    else {
      prev_scancode = 0;
 
      /* On ignore les "fake shifts" */
      if (scancode == 0x2a || scancode == 0x36)
        return 0;
 
      if (e0_keys[scancode])
        *keycode = e0_keys[scancode];
      else
        return 0;
    }
  }
  else if (scancode >= 89)
    return 0;
  else
    *keycode = scancode;
 
  *keycode |= up_flag;
  repeating = *keycode == prev_keycode;
  prev_keycode = *keycode;
  return 1;
}
 
/* Gestionnaires des fonctions speciales */
 
static void fn_enter()
{
  if (diacr) {
    put_queue(diacr);
    diacr = 0;
  }
  put_queue('\r');
}
 
static void fn_caps_toggle()
{
  if (repeating)
    return;
 
  con->lockstate ^= CAPS_LOCK;
  set_leds(con->lockstate);
}
 
static void fn_hold()
{
  if (repeating)
    return;
 
  con->lockstate ^= SCROLL_LOCK;
  set_leds(con->lockstate);
  con->lockstate & SCROLL_LOCK ? tty_stop(&con->tty) : tty_start(&con->tty);
}
 
static void fn_num()
{
  if (repeating)
    return;
 
  con->lockstate ^= NUM_LOCK;
  set_leds(con->lockstate);
}
 
static void fn_history_pgdn()
{
  vc_history_pgdn(con);
}
 
static void fn_history_pgup()
{
  vc_history_pgup(con);
}
 
static void fn_history_down()
{
  vc_history_down(con);
}
 
static void fn_history_up()
{
  vc_history_up(con);
}
 
static void __attribute__ ((noreturn)) fn_reboot()
{
  reboot();
}
 
static void fn_compose()
{
  dead_key_next = 1;
}
 
static void fn_null() {}
 
static void (*fn_handler[])() = {
  fn_null, fn_enter, fn_null, fn_null,
  fn_null, fn_null, fn_null, fn_caps_toggle,
  fn_num, fn_hold, fn_history_pgdn, fn_history_pgup,
  fn_reboot, fn_null, fn_compose, fn_null,
  fn_null, fn_null, fn_null, fn_num,
  fn_history_down, fn_history_up
};
 
/* Gestionnaires des touches speciales */
 
static void k_self(unsigned char value, int up_flag)
{
  if (up_flag)
    return;
 
  if (diacr)
    value = handle_diacr(value);
  if (dead_key_next) {
    dead_key_next = 0;
    diacr = value;
    return;
  }
  put_queue(value);
}
 
static void k_fn(unsigned char value, int up_flag)
{
  if (up_flag)
    return;
 
  if (func_table[value])
    puts_queue(func_table[value]);
}
 
static void k_spec(unsigned char value, int up_flag)
{
  if (up_flag)
    return;
 
  if (value >= sizeof fn_handler / sizeof *fn_handler)
    return;
  fn_handler[value]();
}
 
static void k_cur(unsigned char value, int up_flag)
{
  static const char *cur_chars = "BDCA";
 
  if (up_flag)
    return;
 
  applkey(cur_chars[value]);
}
 
static void k_shift(unsigned char value, int up_flag)
{
  if (repeating)
    return;
 
  if (value == K_CAPSSHIFT) {
    value = K_SHIFT;
    con->lockstate &= ~CAPS_LOCK;
    set_leds(con->lockstate);
  }
 
  if (up_flag) {
    if (shift_down[value])
      shift_down[value]--;
  }
  else
    shift_down[value]++;
 
  if (shift_down[value])
    con->modifiers |= (1 << value);
  else
    con->modifiers &= ~(1 << value);
}
 
static void k_meta(unsigned char value, int up_flag)
{
  if (up_flag)
    return;
 
  put_queue('\033');
  put_queue(value);
}
 
static void k_dead(unsigned char value, int up_flag)
{
  if (up_flag)
    return;
 
  diacr = diacr ? handle_diacr(value) : value;
}
 
static void k_ignore(unsigned char value, int up_flag) {}
 
static void k_pad(unsigned char value, int up_flag)
{
  static const char *pad_chars = "0123456789+-*/\015,.?()#";
 
  if (up_flag)
    return;
 
  if (!(con->lockstate & NUM_LOCK))
    switch (value) {
    case K_P0:
      return k_fn(K_INSERT, 0);
    case K_P1:
      return k_fn(K_SELECT, 0);
    case K_P2:
      return k_cur(K_DOWN, 0);
    case K_P3:
      return k_fn(K_PGDN, 0);
    case K_P4:
      return k_cur(K_LEFT, 0);
    case K_P5:
      return applkey('G');
    case K_P6:
      return k_cur(K_RIGHT, 0);
    case K_P7:
      return k_fn(K_FIND, 0);
    case K_P8:
      return k_cur(K_UP, 0);
    case K_P9:
      return k_fn(K_PGUP, 0);
    case K_PDOT:
      return k_fn(K_REMOVE, 0);
    }
 
  put_queue(pad_chars[value]);
}
 
static void (*const k_handler[16])(unsigned char value, int up_flag) = {
  k_self, k_fn, k_spec, k_pad,
  k_ignore, k_ignore, k_cur, k_shift,
  k_meta, k_ignore, k_ignore, k_ignore,
  k_ignore, k_dead, k_ignore, k_ignore
};
 
/* Traitement des keycodes */
 
static void process_keycode(unsigned char keycode)
{
  unsigned char up_flag;
  const unsigned short *key_map;
  unsigned short keysym;
  unsigned char type;
 
  up_flag = keycode & 0x80;
  keycode &= 0x7f;
 
  if (con->modifiers >= sizeof key_maps / sizeof *key_maps)
    return;
  key_map = key_maps[con->modifiers];
  if (!key_map)
    return;
  keysym = key_map[keycode];
 
  type = keysym >> 8;
  if (type < 0xf0)
    return;
  type -= 0xf0;
 
  if (type == KT_LETTER) {
    if (con->lockstate & CAPS_LOCK) {
      key_map = key_maps[con->modifiers ^ SHIFT];
      if (key_map)
        keysym = key_map[keycode];
    }
    type = KT_LATIN;
  }
 
  char_put = 0;
  (*k_handler[type])(keysym & 0xff, up_flag);
  if (char_put) {
    vc_history_cancel(con);
    tty_process_input_queue(&con->tty);
  }
}
 
/* Fonctions exportees */
 
void kb_init()
{
  dummy_con_init();
 
  memset(shift_down, 0, sizeof shift_down / sizeof *shift_down);
  dead_key_next = 0;
  diacr = 0;
  repeating = 0;
  prev_scancode = 0;
  prev_keycode = 0;
  con = &dummy_con;
 
  synchronize();
}
 
void kb_print_info()
{
  printk("Keyboard controller: Intel 8042\n");
  printk("Keymap: " KEYMAP_NAME "\n");
}
 
void kb_set_con(struct vc_struct *vc)
{
  int intr;
 
  disable_intr(intr); /* pas d'interruption clavier pendant la synchronisation */
  con = vc;
  synchronize();
  restore_intr(intr);
}
 
void kb_reset()
{
  int intr;
 
  disable_intr(intr); /* pas d'interruption clavier pendant la reinitialisation */
  con->lockstate = 0;
  synchronize();
  restore_intr(intr);
}
 
void __attribute__ ((noreturn)) kb_reboot()
{
  cli();
  reboot();
}
 
void do_keyboard_interrupt()
{
  unsigned char scancode, keycode;
 
  if (!(scancode = read_data()))
    return;
  if (translate(scancode, &keycode))
    process_keycode(keycode);
}