/* * This file contains the PS2 keyboard driver for the i8042 * * Note that this driver will only handle a single i8042 device * * COPYRIGHT (c) 1998 by Radstone Technology * * * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU. * * You are hereby granted permission to use, copy, modify, and distribute * this file, provided that this notice, plus the above copyright notice * and disclaimer, appears in all copies. Radstone Technology will provide * no support for this code. * * This driver uses the termios pseudo driver. * * $Id$ */ #include #include #include #include #include #include "console.h" #include "i8042_p.h" #include "vga_p.h" /* * UK style keyboard */ char pcUKNormalLookup[] = "1234567890-=\b\tqwertyuiop[]\n\0asdfghjkl;\'" "`\0#zxcvbnm,./\0*\0 \0\0\0\0\0\0\0\0\0\0\0\0" "\000789-456+1230.\0\0\\"; char pcUKShiftedLookup[]= "!\"£$%^&*()_+\b\tQWERTYUIOP{}\n\0ASDFGHJKL:" "@\0\0~ZXCVBNM<>?\0*\0 \0\0\0\0\0\0\0\0\0\0\0" "\0\000789-456+1230.\0\0|"; /* * US style keyboard */ char pcUSNormalLookup[] = "1234567890-=\b\tqwertyuiop[]\n\0asdfghjkl;\'" "`\0\\zxcvbnm,./\0*\0 \0\0\0\0\0\0\0\0\0\0\0" "\0\000789-456+1230.\0\0\\"; char pcUSShiftedLookup[]= "!@#$%^&*()_+\b\tQWERTYUIOP{}\n\0ASDFGHJKL:@~" "\0|ZXCVBNM<>?\0*\0 \0\0\0\0\0\0\0\0\0\0\0\0" "\000789-456+1230.\0\0|"; static char *pcNormalLookup; static char *pcShiftedLookup; /* * This is exported to vga.c to provide flow control */ volatile boolean bScrollLock=FALSE; /* * If multiple devices are to be supported then a private copy of * the following will be required for each instance */ static boolean bCtrlPressed=FALSE; static boolean bAltPressed=FALSE; static boolean bAltGrPressed=FALSE; static boolean bLShiftPressed=FALSE; static boolean bRShiftPressed=FALSE; static boolean bCapsLock=FALSE; static boolean bNumLock=FALSE; static boolean bTwoCode=FALSE; #if CONSOLE_USE_INTERRUPTS static volatile Ring_buffer_t KbdOutputBuffer; static volatile boolean bProcessInterruptInput=FALSE; static boolean bInterruptsEnabled=FALSE; static volatile boolean bReceivedAck=TRUE; static void i8042_process( int minor ); static void i8042_scan_code( int minor, unsigned8 ucScan ); #endif static volatile Ring_buffer_t KbdInputBuffer; /* * The following routines enable an interrupt driver to switch * to polled mode as required for command processing */ void i8042_polled_on( int minor ) { #if CONSOLE_USE_INTERRUPTS bProcessInterruptInput=FALSE; #endif } void i8042_polled_off( int minor ) { #if CONSOLE_USE_INTERRUPTS unsigned32 Irql; unsigned8 ucScan; /* * Make sure we have processed everything outstanding */ rtems_interrupt_disable(Irql); while(!Ring_buffer_Is_empty(&KbdInputBuffer)) { rtems_interrupt_enable(Irql); Ring_buffer_Remove_character(&KbdInputBuffer, ucScan); i8042_scan_code(minor, ucScan); rtems_interrupt_disable(Irql); } bProcessInterruptInput=TRUE; rtems_interrupt_enable(Irql); #endif } /* * Send data to the keyboard */ static rtems_status_code i8042_outbyte_raw( int minor, unsigned8 ucData ) { unsigned32 i; unsigned8 Status; #if CONSOLE_USE_INTERRUPTS unsigned32 Irql; if(bInterruptsEnabled) { Ring_buffer_Add_character(&KbdOutputBuffer, ucData); if(!Console_Port_Data[minor].bActive) { /* * Wake up the device */ rtems_interrupt_disable(Irql); Console_Port_Data[minor].bActive=TRUE; i8042_process(minor); rtems_interrupt_enable(Irql); } return RTEMS_SUCCESSFUL; } #endif for (i=0; i= 16) && (ucScan <= 25)) || ((ucScan >= 30) && (ucScan <= 38)) || ((ucScan >= 44) && (ucScan <= 50))) { if(bCtrlPressed) { cChar=pcNormalLookup[ucScan - 2]-'a'+1; } else { if(((bLShiftPressed || bRShiftPressed) && !bCapsLock) || (!(bLShiftPressed || bRShiftPressed) && bCapsLock)) { cChar=pcShiftedLookup[ucScan - 2]; } else { cChar=pcNormalLookup[ucScan - 2]; } } } else if((ucScan > 1) && (ucScan <= 0x56)) { /* * Its ASCII but not alpha, so don't shift on CapsLock. */ if(bLShiftPressed || bRShiftPressed) { cChar=pcShiftedLookup[ucScan - 2]; } else { cChar=pcNormalLookup[ucScan - 2]; } } /* * If we got a character then queue it */ if(cChar) { EnqueueKbdChar(minor, cChar); } } /* * Console Device Driver Entry Points */ boolean i8042_probe(int minor) { unsigned8 ucKeyboardAck; unsigned8 ucKeyboardID1, ucKeyboardID2; if(!vga_probe(minor)) { /* * There is no VGA adaptor so fail probe */ return(FALSE); } Ring_buffer_Initialize(&KbdInputBuffer); #if CONSOLE_USE_INTERRUPTS Ring_buffer_Initialize(&KbdOutputBuffer); #endif i8042_polled_on(minor); /* * Determine keyboard type */ i8042_outbyte_raw(minor, KBD_CMD_READ_ID); ucKeyboardAck=0; if((i8042_inbyte_raw(minor, &ucKeyboardAck)==RTEMS_TIMEOUT) || (ucKeyboardAck==KBD_CMD_RESEND)) { /* * No or incorrect keyboard response so fail probe */ return(FALSE); } i8042_inbyte_raw(minor, &ucKeyboardID1); i8042_inbyte_raw(minor, &ucKeyboardID2); if((ucKeyboardID1==0xab) && (ucKeyboardID2==0x41)) { pcNormalLookup=&pcUKNormalLookup[0]; pcShiftedLookup=&pcUKShiftedLookup[0]; } else { pcNormalLookup=&pcUSNormalLookup[0]; pcShiftedLookup=&pcUSShiftedLookup[0]; } i8042_polled_off(minor); return(TRUE); } void i8042_init(int minor) { unsigned8 ucKeyboardAck; vga_init(minor); i8042_polled_on(minor); /* * Switch all LEDs off */ i8042_outbyte_raw(minor, KBD_CMD_SET_LEDS); i8042_inbyte_raw(minor, &ucKeyboardAck); i8042_outbyte_raw(minor, 0); i8042_inbyte_raw(minor, &ucKeyboardAck); /* * Select scan code set 1 */ i8042_outbyte_raw(minor, KBD_CMD_SEL_SCAN_CODE); i8042_inbyte_raw(minor, &ucKeyboardAck); i8042_outbyte_raw(minor, 1); i8042_inbyte_raw(minor, &ucKeyboardAck); i8042_polled_off(minor); } /* PAGE * * i8042_inbyte_nonblocking_polled * * Console Termios polling input entry point. */ int i8042_inbyte_nonblocking_polled( int minor ) { unsigned8 ucScan; char ucData; if(i8042_inbyte_polled(minor, &ucScan)==RTEMS_SUCCESSFUL) { i8042_scan_code(minor, ucScan); } if(!Ring_buffer_Is_empty(&KbdInputBuffer)) { Ring_buffer_Remove_character(&KbdInputBuffer, ucData); return(ucData); } return(-1); } #if CONSOLE_USE_INTERRUPTS /* * i8042_isr * * This routine is the console interrupt handler for the keyboard * * Input parameters: * vector - vector number * * Output parameters: NONE * * Return values: NONE */ static void i8042_process( int minor ) { unsigned8 Status; unsigned8 ucData; inport_byte(Console_Port_Tbl[minor].ulCtrlPort1, Status); if(Status & KBD_OBF_MASK) { inport_byte(Console_Port_Tbl[minor].ulDataPort, ucData); if(bProcessInterruptInput) { /* * Every byte written to the keyboard should be followed * by an acknowledge */ if(ucData==KBD_CMD_ACK) { bReceivedAck=TRUE; } else { i8042_scan_code(minor, ucData); } } else { /* * Store the scan code into the ring buffer where it * can be read using i8042_inbyte_raw() */ Ring_buffer_Add_character(&KbdInputBuffer, ucData); } } if(((Status & KBD_IBF_MASK)==0) && bReceivedAck) { if(Ring_buffer_Is_empty(&KbdOutputBuffer)) { Console_Port_Data[minor].bActive=FALSE; } else { Ring_buffer_Remove_character(&KbdOutputBuffer, ucData); outport_byte(Console_Port_Tbl[minor].ulDataPort, ucData); bReceivedAck=FALSE; } } } static rtems_isr i8042_isr( rtems_vector_number vector ) { int minor; for(minor=0;minor