Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/drivers/input/keyboard_8042.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 #include "drivers/input/keyboard.h"
0019 #include "arch/i386.h"
0020 #include "sched/sched.h"
0021 #include "smp/smp.h"
0022 #include "smp/apic.h"
0023 #include "util/circular.h"
0024 #include "util/printf.h"
0025 
0026 /* Enable kernel debugging hotkeys */
0027 //#define DEBUG_KEYS
0028 
0029 /* Circular buffer storing incoming keyboard events. */
0030 static circular keyb_buffer;
0031 static key_event buffer_space[KEYBOARD_BUFFER_SIZE];
0032 
0033 /* Current state of the keyboard. */
0034 static key_event cur_event;
0035 
0036 /* Did we just receive an escape scancode, and if so, which one?  Not
0037  * to be confused with ESC key. */
0038 static uint8 escape;
0039 
0040 /* Scancodes are normally 1 byte, except when the code E0 or E1 is
0041  * received: that is the first byte of a 2-byte scancode. */
0042 
0043 static inline void
0044 clean_entry (int i)
0045 {
0046   cur_event.keys[i].scancode = 0;
0047   cur_event.keys[i].present  = 0;
0048   cur_event.keys[i].release  = 0;
0049   cur_event.keys[i].pressed  = 0;
0050   cur_event.keys[i].latest   = 0;
0051 }
0052 
0053 #ifdef DEBUG_KEYS
0054 static inline void
0055 check_debug_keys (void)
0056 {
0057   int i;
0058   for (i=0; i<KEY_EVENT_MAX; i++) {
0059     if (cur_event.keys[i].present && cur_event.keys[i].pressed) {
0060       if (cur_event.keys[i].scancode == 0x16) {
0061         /* pressed 'U' */
0062         void debug_dump_bulk_qhs (void);
0063         lock_kernel ();
0064         debug_dump_bulk_qhs ();
0065         unlock_kernel ();
0066       }
0067     }
0068   }
0069 }
0070 #endif
0071 
0072 static inline void
0073 check_control_alt_del (void)
0074 {
0075   bool ctrl, alt, del;
0076   int i;
0077   ctrl = alt = del = FALSE;
0078 
0079   for (i=0; i<KEY_EVENT_MAX; i++) {
0080     if (cur_event.keys[i].present && cur_event.keys[i].pressed) {
0081       switch (cur_event.keys[i].scancode) {
0082       case 0x001d:
0083       case 0xe01d:
0084         ctrl = TRUE; break;
0085       case 0x0038:
0086       case 0xe038:
0087         alt = TRUE; break;
0088       case 0x0053:
0089       case 0xe053:
0090         del = TRUE; break;
0091       }
0092     }
0093   }
0094 
0095   if (ctrl && alt && del) {
0096     uint8 state;
0097     printf ("REBOOTING...\n");
0098     com1_printf ("REBOOTING...\n");
0099     while (((state = inb (KEYBOARD_STATUS_PORT)) & 2) != 0);
0100     outb (0xFE, KEYBOARD_STATUS_PORT);
0101     asm volatile ("hlt");
0102   }
0103 }
0104 
0105 /* Process the keyboard IRQ. */
0106 static uint32
0107 kbd_irq_handler (uint8 vec)
0108 {
0109   uint8 code;
0110   int i;
0111 
0112   code = inb (KEYBOARD_DATA_PORT);
0113 
0114   if (escape) {
0115     /* Received 2-byte scancode. */
0116     code |= (escape << 8);
0117     escape = 0;
0118   }
0119 
0120   if (code == 0xE0 || code == 0xE1)
0121     escape = code;
0122   else if (code & 0x80) {
0123     /* Release key */
0124 
0125     code &= (~0x80);          /* unset "release" bit */
0126 
0127     /* If a key is released, there should be a Press event in the
0128      * cur_event buffer already. */
0129     for (i=0; i<KEY_EVENT_MAX; i++) {
0130       if (cur_event.keys[i].present == 1 &&
0131           cur_event.keys[i].scancode == code) {
0132 
0133         /* found previously inserted entry in event buffer: */
0134         cur_event.keys[i].release = 1;
0135         cur_event.keys[i].pressed = 0;
0136         cur_event.keys[i].latest  = 1;
0137 
0138         /* enqueue release event */
0139         if (circular_insert_nowait (&keyb_buffer, (void *)&cur_event) < 0) {
0140           lock_kernel();
0141           com1_printf ("keyboard_8042: dropped break code: %X\n", code);
0142           unlock_kernel();
0143         }
0144 
0145         clean_entry (i);      /* remove it from cur_event */
0146 
0147         break;
0148       }
0149     }
0150 
0151     /* No previous Press event found. */
0152     if (i == KEY_EVENT_MAX) {
0153       lock_kernel();
0154       com1_printf ("keyboard_8042: spurious break code: %X\n", code);
0155       unlock_kernel();
0156     }
0157 
0158   } else {
0159     /* Press key */
0160 
0161     for (i=0; i<KEY_EVENT_MAX; i++) {
0162       /* First, see if it is already in the table: e.g. repeating
0163        * keystrokes will send a stream of 'make' codes with no 'break'
0164        * codes. */
0165       if (cur_event.keys[i].present == 1 &&
0166           cur_event.keys[i].scancode == code) {
0167         break;
0168       }
0169     }
0170 
0171     if (i == KEY_EVENT_MAX) {
0172       /* It wasn't in the table already, so, find an empty entry */
0173       for (i=0; i<KEY_EVENT_MAX; i++) {
0174         if (cur_event.keys[i].present == 0) {
0175           break;
0176         }
0177       }
0178     }
0179 
0180     if (i == KEY_EVENT_MAX) {
0181       /* no free entry */
0182       lock_kernel();
0183       com1_printf ("keyboard_8042: too many keys: %X\n", code);
0184       unlock_kernel();
0185 
0186     } else {
0187       /* operate on entry: 0 <= i < KEY_EVENT_MAX */
0188 
0189       cur_event.keys[i].scancode = code;
0190       cur_event.keys[i].present  = 1;
0191       cur_event.keys[i].pressed  = 1;
0192       cur_event.keys[i].latest   = 1;
0193 
0194       /* enqueue press event */
0195       if (circular_insert_nowait (&keyb_buffer, (void *)&cur_event) < 0) {
0196         lock_kernel();
0197         com1_printf ("keyboard_8042: dropped make code: %X\n", code);
0198         unlock_kernel();
0199       }
0200 
0201       cur_event.keys[i].latest = 0;
0202     }
0203   }
0204 
0205   check_control_alt_del ();
0206 
0207 #ifdef DEBUG_KEYS
0208   check_debug_keys ();
0209 #endif
0210 
0211   return 0;
0212 }
0213 
0214 /* Setup the circular buffer and the IRQ handler. */
0215 bool
0216 init_keyboard_8042 (void)
0217 {
0218   int i;
0219 
0220   escape = 0;
0221 
0222   for (i=0; i<KEY_EVENT_MAX; i++)
0223     clean_entry (i);
0224 
0225   circular_init (&keyb_buffer,
0226                  (void *)buffer_space,
0227                  KEYBOARD_BUFFER_SIZE,
0228                  sizeof (key_event));
0229 
0230   if (mp_ISA_PC) {
0231     set_vector_handler (KEYBOARD_IRQ + PIC1_BASE_IRQ, kbd_irq_handler);
0232   } else {
0233     IOAPIC_map_GSI (IRQ_to_GSI (mp_ISA_bus_id, KEYBOARD_IRQ),
0234                     KEYBOARD_VECTOR, 0xFF00000000000800LL);
0235     set_vector_handler (KEYBOARD_VECTOR, kbd_irq_handler);
0236   }
0237   return TRUE;
0238 }
0239 
0240 /* Retrieve the next key event or block. */
0241 void
0242 keyboard_8042_next (key_event *e)
0243 {
0244   circular_remove (&keyb_buffer, (void *)e);
0245 }
0246 
0247 #include "module/header.h"
0248 
0249 static const struct module_ops mod_ops = {
0250   .init = init_keyboard_8042
0251 };
0252 
0253 DEF_MODULE (input___kb8042, "Keyboard (i8042) driver", &mod_ops, {});
0254 
0255 /*
0256  * Local Variables:
0257  * indent-tabs-mode: nil
0258  * mode: C
0259  * c-file-style: "gnu"
0260  * c-basic-offset: 2
0261  * End:
0262  */
0263 
0264 /* vi: set et sw=2 sts=2: */