2011-07-20 15:32:52 +00:00
/*
Copyright 2011 Jun Wako < wakojun @ gmail . com >
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 2 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/>.
*/
2011-01-17 12:51:51 +00:00
# include <stdint.h>
# include <stdbool.h>
2013-11-25 08:43:26 +00:00
# include "action.h"
2011-01-17 12:51:51 +00:00
# include "print.h"
# include "util.h"
# include "debug.h"
# include "ps2.h"
2011-02-21 06:43:17 +00:00
# include "matrix.h"
2011-01-17 12:51:51 +00:00
2011-12-04 17:53:15 +00:00
static void matrix_make ( uint8_t code ) ;
static void matrix_break ( uint8_t code ) ;
2013-11-28 06:50:17 +00:00
static void matrix_clear ( void ) ;
2011-12-04 17:53:15 +00:00
# ifdef MATRIX_HAS_GHOST
static bool matrix_has_ghost_in_row ( uint8_t row ) ;
2011-01-17 12:51:51 +00:00
# endif
/*
2011-12-04 17:53:15 +00:00
* Matrix Array usage :
* ' Scan Code Set 2 ' is assigned into 256 ( 32 x8 ) cell matrix .
* Hmm , it is very sparse and not efficient : (
*
* Notes :
* Both ' Hanguel / English ' ( F1 ) and ' Hanja ' ( F2 ) collide with ' Delete ' ( E0 71 ) and ' Down ' ( E0 72 ) .
* These two Korean keys need exceptional handling and are not supported for now . Sorry .
2011-01-17 12:51:51 +00:00
*
2011-12-04 17:53:15 +00:00
* 8 bit wide
* + - - - - - - - - - +
2011-01-17 12:51:51 +00:00
* 0 | |
2011-01-24 15:53:49 +00:00
* : | XX | 00 - 7F for normal codes ( without E0 - prefix )
2011-01-17 12:51:51 +00:00
* f | _________ |
* 10 | |
2011-12-04 17:53:15 +00:00
* : | E0 YY | 80 - FF for E0 - prefixed codes
* 1f | | ( < YY > | 0x80 ) is used as matrix position .
* + - - - - - - - - - +
*
* Exceptions :
* 0x83 : F7 ( 0x83 ) This is a normal code but beyond 0x7F .
* 0xFC : PrintScreen
* 0xFE : Pause
2011-01-17 12:51:51 +00:00
*/
2011-12-04 17:53:15 +00:00
static uint8_t matrix [ MATRIX_ROWS ] ;
2011-01-24 15:53:49 +00:00
# define ROW(code) (code>>3)
# define COL(code) (code&0x07)
2011-01-17 12:51:51 +00:00
2011-12-04 17:53:15 +00:00
// matrix positions for exceptional keys
# define F7 (0x83)
# define PRINT_SCREEN (0xFC)
# define PAUSE (0xFE)
2011-01-17 12:51:51 +00:00
2011-12-04 17:53:15 +00:00
static bool is_modified = false ;
2011-01-17 12:51:51 +00:00
inline
uint8_t matrix_rows ( void )
{
return MATRIX_ROWS ;
}
inline
uint8_t matrix_cols ( void )
{
return MATRIX_COLS ;
}
void matrix_init ( void )
{
2013-11-28 06:50:17 +00:00
debug_enable = true ;
2011-01-17 12:51:51 +00:00
ps2_host_init ( ) ;
// initialize matrix state: all keys off
2011-01-24 15:53:49 +00:00
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) matrix [ i ] = 0x00 ;
2011-01-17 12:51:51 +00:00
return ;
}
2011-01-24 15:53:49 +00:00
/*
* PS / 2 Scan Code Set 2 : Exceptional Handling
*
* There are several keys to be handled exceptionally .
* The scan code for these keys are varied or prefix / postfix ' d
* depending on modifier key state .
*
2011-12-04 17:53:15 +00:00
* Keyboard Scan Code Specification :
2011-01-24 15:53:49 +00:00
* http : //www.microsoft.com/whdc/archive/scancode.mspx
* http : //download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc
*
*
2011-12-04 17:53:15 +00:00
* 1 ) Insert , Delete , Home , End , PageUp , PageDown , Up , Down , Right , Left
* a ) when Num Lock is off
2011-01-24 15:53:49 +00:00
* modifiers | make | break
* - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - -
* Ohter | < make > | < break >
* LShift | E0 F0 12 < make > | < break > E0 12
* RShift | E0 F0 59 < make > | < break > E0 59
* L + RShift | E0 F0 12 E0 F0 59 < make > | < break > E0 59 E0 12
*
2011-12-04 17:53:15 +00:00
* b ) when Num Lock is on
2011-01-24 15:53:49 +00:00
* modifiers | make | break
* - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - -
* Other | E0 12 < make > | < break > E0 F0 12
* Shift ' d | < make > | < break >
*
2011-12-04 17:53:15 +00:00
* Handling : These prefix / postfix codes are ignored .
2011-01-24 15:53:49 +00:00
*
*
2011-12-04 17:53:15 +00:00
* 2 ) Keypad /
2011-01-24 15:53:49 +00:00
* modifiers | make | break
* - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - -
* Ohter | < make > | < break >
* LShift | E0 F0 12 < make > | < break > E0 12
* RShift | E0 F0 59 < make > | < break > E0 59
* L + RShift | E0 F0 12 E0 F0 59 < make > | < break > E0 59 E0 12
*
2011-12-04 17:53:15 +00:00
* Handling : These prefix / postfix codes are ignored .
2011-01-24 15:53:49 +00:00
*
*
2011-12-04 17:53:15 +00:00
* 3 ) PrintScreen
2011-01-24 15:53:49 +00:00
* modifiers | make | break
* - - - - - - - - - - + - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Other | E0 12 E0 7 C | E0 F0 7 C E0 F0 12
* Shift ' d | E0 7 C | E0 F0 7 C
* Control ' d | E0 7 C | E0 F0 7 C
* Alt ' d | 84 | F0 84
*
2011-12-04 17:53:15 +00:00
* Handling : These prefix / postfix codes are ignored , and both scan codes
* ' E0 7 C ' and 84 are seen as PrintScreen .
2011-01-24 15:53:49 +00:00
*
2011-12-04 17:53:15 +00:00
* 4 ) Pause
2011-01-24 15:53:49 +00:00
* modifiers | make ( no break code )
* - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2011-12-04 17:53:15 +00:00
* Other | E1 14 77 E1 F0 14 F0 77
2011-01-24 15:53:49 +00:00
* Control ' d | E0 7 E E0 F0 7 E
*
2011-12-04 17:53:15 +00:00
* Handling : Both code sequences are treated as a whole .
* And we need a ad hoc ' pseudo break code ' hack to get the key off
* because it has no break code .
2011-01-24 15:53:49 +00:00
*
*/
2011-01-17 12:51:51 +00:00
uint8_t matrix_scan ( void )
{
2011-12-04 17:53:15 +00:00
// scan code reading states
2011-01-17 12:51:51 +00:00
static enum {
INIT ,
2011-01-24 15:53:49 +00:00
F0 ,
2011-01-17 12:51:51 +00:00
E0 ,
E0_F0 ,
2011-12-04 17:53:15 +00:00
// Pause
2011-01-17 12:51:51 +00:00
E1 ,
E1_14 ,
E1_14_77 ,
E1_14_77_E1 ,
E1_14_77_E1_F0 ,
E1_14_77_E1_F0_14 ,
E1_14_77_E1_F0_14_F0 ,
2011-12-04 17:53:15 +00:00
// Control'd Pause
E0_7E ,
E0_7E_E0 ,
E0_7E_E0_F0 ,
2011-01-17 12:51:51 +00:00
} state = INIT ;
2011-01-24 15:53:49 +00:00
is_modified = false ;
2011-01-17 12:51:51 +00:00
2011-12-04 17:53:15 +00:00
// 'pseudo break code' hack
2011-01-24 15:53:49 +00:00
if ( matrix_is_on ( ROW ( PAUSE ) , COL ( PAUSE ) ) ) {
matrix_break ( PAUSE ) ;
2011-01-17 12:51:51 +00:00
}
2013-11-25 08:43:26 +00:00
uint8_t code = ps2_host_recv ( ) ;
2014-06-19 07:13:35 +00:00
if ( code ) xprintf ( " %i \r \n " , code ) ;
2013-11-25 08:43:26 +00:00
if ( ! ps2_error ) {
2011-01-17 12:51:51 +00:00
switch ( state ) {
case INIT :
switch ( code ) {
2011-12-04 17:53:15 +00:00
case 0xE0 :
2011-01-17 12:51:51 +00:00
state = E0 ;
break ;
2011-12-04 17:53:15 +00:00
case 0xF0 :
2011-01-24 15:53:49 +00:00
state = F0 ;
2011-01-17 12:51:51 +00:00
break ;
2011-12-04 17:53:15 +00:00
case 0xE1 :
2011-01-17 12:51:51 +00:00
state = E1 ;
break ;
2011-12-04 17:53:15 +00:00
case 0x83 : // F7
matrix_make ( F7 ) ;
2011-01-24 15:53:49 +00:00
state = INIT ;
break ;
2011-12-04 17:53:15 +00:00
case 0x84 : // Alt'd PrintScreen
2011-01-24 15:53:49 +00:00
matrix_make ( PRINT_SCREEN ) ;
state = INIT ;
break ;
2013-11-25 08:43:26 +00:00
case 0x00 : // Overrun [3]p.25
2013-11-28 06:50:17 +00:00
matrix_clear ( ) ;
2013-11-25 08:43:26 +00:00
clear_keyboard ( ) ;
2013-11-28 06:50:17 +00:00
print ( " Overrun \n " ) ;
2013-11-25 08:43:26 +00:00
state = INIT ;
break ;
2011-01-17 12:51:51 +00:00
default : // normal key make
if ( code < 0x80 ) {
2011-01-24 15:53:49 +00:00
matrix_make ( code ) ;
2011-01-17 12:51:51 +00:00
} else {
2013-11-28 06:50:17 +00:00
matrix_clear ( ) ;
2013-11-25 08:43:26 +00:00
clear_keyboard ( ) ;
2013-11-28 06:50:17 +00:00
xprintf ( " unexpected scan code at INIT: %02X \n " , code ) ;
2011-01-17 12:51:51 +00:00
}
state = INIT ;
}
break ;
2011-12-04 17:53:15 +00:00
case E0 : // E0-Prefixed
2011-01-17 12:51:51 +00:00
switch ( code ) {
2011-12-04 17:53:15 +00:00
case 0x12 : // to be ignored
case 0x59 : // to be ignored
2011-01-17 12:51:51 +00:00
state = INIT ;
break ;
2011-12-04 17:53:15 +00:00
case 0x7E : // Control'd Pause
state = E0_7E ;
2011-01-24 15:53:49 +00:00
break ;
2011-12-04 17:53:15 +00:00
case 0xF0 :
2011-01-17 12:51:51 +00:00
state = E0_F0 ;
break ;
2011-12-04 17:53:15 +00:00
default :
2011-01-17 12:51:51 +00:00
if ( code < 0x80 ) {
2011-01-24 15:53:49 +00:00
matrix_make ( code | 0x80 ) ;
2011-01-17 12:51:51 +00:00
} else {
2013-11-28 06:50:17 +00:00
matrix_clear ( ) ;
2013-11-25 08:43:26 +00:00
clear_keyboard ( ) ;
2013-11-28 06:50:17 +00:00
xprintf ( " unexpected scan code at E0: %02X \n " , code ) ;
2011-01-17 12:51:51 +00:00
}
state = INIT ;
}
break ;
2011-12-04 17:53:15 +00:00
case F0 : // Break code
2011-01-17 12:51:51 +00:00
switch ( code ) {
2011-12-04 17:53:15 +00:00
case 0x83 : // F7
matrix_break ( F7 ) ;
2011-01-17 12:51:51 +00:00
state = INIT ;
break ;
2011-12-04 17:53:15 +00:00
case 0x84 : // Alt'd PrintScreen
2011-01-24 15:53:49 +00:00
matrix_break ( PRINT_SCREEN ) ;
2011-01-17 12:51:51 +00:00
state = INIT ;
break ;
2013-11-28 06:50:17 +00:00
case 0xF0 :
matrix_clear ( ) ;
clear_keyboard ( ) ;
xprintf ( " unexpected scan code at F0: F0(clear and cont.) \n " ) ;
break ;
2011-01-17 12:51:51 +00:00
default :
2011-01-24 15:53:49 +00:00
if ( code < 0x80 ) {
matrix_break ( code ) ;
} else {
2013-11-28 06:50:17 +00:00
matrix_clear ( ) ;
2013-11-25 08:43:26 +00:00
clear_keyboard ( ) ;
2013-11-28 06:50:17 +00:00
xprintf ( " unexpected scan code at F0: %02X \n " , code ) ;
2011-01-24 15:53:49 +00:00
}
state = INIT ;
2011-01-17 12:51:51 +00:00
}
break ;
2011-12-04 17:53:15 +00:00
case E0_F0 : // Break code of E0-prefixed
2011-01-17 12:51:51 +00:00
switch ( code ) {
2011-12-04 17:53:15 +00:00
case 0x12 : // to be ignored
case 0x59 : // to be ignored
2011-01-17 12:51:51 +00:00
state = INIT ;
break ;
default :
2011-01-24 15:53:49 +00:00
if ( code < 0x80 ) {
matrix_break ( code | 0x80 ) ;
} else {
2013-11-28 06:50:17 +00:00
matrix_clear ( ) ;
2013-11-25 08:43:26 +00:00
clear_keyboard ( ) ;
2013-11-28 06:50:17 +00:00
xprintf ( " unexpected scan code at E0_F0: %02X \n " , code ) ;
2011-01-24 15:53:49 +00:00
}
2011-01-17 12:51:51 +00:00
state = INIT ;
}
break ;
2011-12-04 17:53:15 +00:00
// following are states of Pause
2011-01-17 12:51:51 +00:00
case E1 :
switch ( code ) {
case 0x14 :
state = E1_14 ;
break ;
default :
state = INIT ;
}
break ;
case E1_14 :
switch ( code ) {
case 0x77 :
state = E1_14_77 ;
break ;
default :
state = INIT ;
}
break ;
case E1_14_77 :
switch ( code ) {
case 0xE1 :
state = E1_14_77_E1 ;
break ;
default :
state = INIT ;
}
break ;
case E1_14_77_E1 :
switch ( code ) {
case 0xF0 :
state = E1_14_77_E1_F0 ;
break ;
default :
state = INIT ;
}
break ;
case E1_14_77_E1_F0 :
switch ( code ) {
case 0x14 :
state = E1_14_77_E1_F0_14 ;
break ;
default :
state = INIT ;
}
break ;
case E1_14_77_E1_F0_14 :
switch ( code ) {
case 0xF0 :
state = E1_14_77_E1_F0_14_F0 ;
break ;
default :
state = INIT ;
}
break ;
case E1_14_77_E1_F0_14_F0 :
switch ( code ) {
case 0x77 :
2011-01-24 15:53:49 +00:00
matrix_make ( PAUSE ) ;
2011-01-17 12:51:51 +00:00
state = INIT ;
break ;
default :
state = INIT ;
}
break ;
2011-12-04 17:53:15 +00:00
// Following are states of Control'd Pause
case E0_7E :
if ( code = = 0xE0 )
state = E0_7E_E0 ;
else
state = INIT ;
break ;
case E0_7E_E0 :
if ( code = = 0xF0 )
state = E0_7E_E0_F0 ;
else
state = INIT ;
break ;
case E0_7E_E0_F0 :
if ( code = = 0x7E )
matrix_make ( PAUSE ) ;
state = INIT ;
break ;
2011-01-17 12:51:51 +00:00
default :
state = INIT ;
}
2013-11-25 08:43:26 +00:00
}
2013-11-28 06:50:17 +00:00
// TODO: request RESEND when error occurs?
/*
if ( PS2_IS_FAILED ( ps2_error ) ) {
2013-11-25 08:43:26 +00:00
uint8_t ret = ps2_host_send ( PS2_RESEND ) ;
2013-11-26 05:31:57 +00:00
xprintf ( " Resend: %02X \n " , ret ) ;
2011-01-17 12:51:51 +00:00
}
2013-11-28 06:50:17 +00:00
*/
2011-01-17 12:51:51 +00:00
return 1 ;
}
bool matrix_is_modified ( void )
{
2011-01-24 15:53:49 +00:00
return is_modified ;
2011-01-17 12:51:51 +00:00
}
inline
bool matrix_has_ghost ( void )
{
# ifdef MATRIX_HAS_GHOST
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) {
if ( matrix_has_ghost_in_row ( i ) )
return true ;
}
# endif
return false ;
}
inline
bool matrix_is_on ( uint8_t row , uint8_t col )
{
return ( matrix [ row ] & ( 1 < < col ) ) ;
}
inline
uint8_t matrix_get_row ( uint8_t row )
{
return matrix [ row ] ;
}
void matrix_print ( void )
{
print ( " \n r/c 01234567 \n " ) ;
for ( uint8_t row = 0 ; row < matrix_rows ( ) ; row + + ) {
phex ( row ) ; print ( " : " ) ;
pbin_reverse ( matrix_get_row ( row ) ) ;
# ifdef MATRIX_HAS_GHOST
if ( matrix_has_ghost_in_row ( row ) ) {
print ( " <ghost " ) ;
}
# endif
print ( " \n " ) ;
}
}
uint8_t matrix_key_count ( void )
{
uint8_t count = 0 ;
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) {
count + = bitpop ( matrix [ i ] ) ;
}
return count ;
}
# ifdef MATRIX_HAS_GHOST
inline
static bool matrix_has_ghost_in_row ( uint8_t row )
{
// no ghost exists in case less than 2 keys on
if ( ( ( matrix [ row ] - 1 ) & matrix [ row ] ) = = 0 )
return false ;
// ghost exists in case same state as other row
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) {
if ( i ! = row & & ( matrix [ i ] & matrix [ row ] ) = = matrix [ row ] )
return true ;
}
return false ;
}
# endif
inline
2011-01-24 15:53:49 +00:00
static void matrix_make ( uint8_t code )
2011-01-17 12:51:51 +00:00
{
2011-01-24 15:53:49 +00:00
if ( ! matrix_is_on ( ROW ( code ) , COL ( code ) ) ) {
matrix [ ROW ( code ) ] | = 1 < < COL ( code ) ;
is_modified = true ;
2011-01-17 12:51:51 +00:00
}
}
inline
2011-01-24 15:53:49 +00:00
static void matrix_break ( uint8_t code )
2011-01-17 12:51:51 +00:00
{
2011-01-24 15:53:49 +00:00
if ( matrix_is_on ( ROW ( code ) , COL ( code ) ) ) {
matrix [ ROW ( code ) ] & = ~ ( 1 < < COL ( code ) ) ;
is_modified = true ;
2011-01-17 12:51:51 +00:00
}
}
2013-11-28 06:50:17 +00:00
inline
static void matrix_clear ( void )
{
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) matrix [ i ] = 0x00 ;
}