2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2004 by Jan - Benedict Glaw < jbglaw @ lug - owl . de >
*/
/*
* LK keyboard driver for Linux , based on sunkbd . c ( C ) by Vojtech Pavlik
*/
/*
* DEC LK201 and LK401 keyboard driver for Linux ( primary for DECstations
* and VAXstations , but can also be used on any standard RS232 with an
* adaptor ) .
*
* DISCLAIMER : This works for _me_ . If you break anything by using the
* information given below , I will _not_ be liable !
*
* RJ10 pinout : To DE9 : Or DB25 :
2005-05-29 02:28:42 -05:00
* 1 - RxD < - - - - > Pin 3 ( TxD ) < - > Pin 2 ( TxD )
* 2 - GND < - - - - > Pin 5 ( GND ) < - > Pin 7 ( GND )
* 4 - TxD < - - - - > Pin 2 ( RxD ) < - > Pin 3 ( RxD )
* 3 - + 12 V ( from HDD drive connector ) , DON ' T connect to DE9 or DB25 ! ! !
2005-04-16 15:20:36 -07:00
*
* Pin numbers for DE9 and DB25 are noted on the plug ( quite small : ) . For
* RJ10 , it ' s like this :
*
* __ = __ Hold the plug in front of you , cable downwards ,
* / ___ / | nose is hidden behind the plug . Now , pin 1 is at
* | 1234 | | the left side , pin 4 at the right and 2 and 3 are
* | IIII | | in between , of course : )
* | | |
* | ____ | /
* | | So the adaptor consists of three connected cables
* | | for data transmission ( RxD and TxD ) and signal ground .
* Additionally , you have to get + 12 V from somewhere .
* Most easily , you ' ll get that from a floppy or HDD power connector .
* It ' s the yellow cable there ( black is ground and red is + 5 V ) .
*
* The keyboard and all the commands it understands are documented in
* " VCB02 Video Subsystem - Technical Manual " , EK - 104 AA - TM - 001. This
* document is LK201 specific , but LK401 is mostly compatible . It comes
* up in LK201 mode and doesn ' t report any of the additional keys it
* has . These need to be switched on with the LK_CMD_ENABLE_LK401
* command . You ' ll find this document ( scanned . pdf file ) on MANX ,
* a search engine specific to DEC documentation . Try
* http : //www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
*/
/*
* 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 , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Should you need to contact me , the author , you can do so either by
* email or by paper mail :
* Jan - Benedict Glaw , Lilienstra <EFBFBD> e 16 , 33790 H <EFBFBD> rste ( near Halle / Westf . ) ,
* Germany .
*/
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/serio.h>
# include <linux/workqueue.h>
# define DRIVER_DESC "LK keyboard driver"
MODULE_AUTHOR ( " Jan-Benedict Glaw <jbglaw@lug-owl.de> " ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* Known parameters :
* bell_volume
* keyclick_volume
* ctrlclick_volume
*
* Please notice that there ' s not yet an API to set these at runtime .
*/
static int bell_volume = 100 ; /* % */
module_param ( bell_volume , int , 0 ) ;
MODULE_PARM_DESC ( bell_volume , " Bell volume (in %). default is 100% " ) ;
static int keyclick_volume = 100 ; /* % */
module_param ( keyclick_volume , int , 0 ) ;
MODULE_PARM_DESC ( keyclick_volume , " Keyclick volume (in %), default is 100% " ) ;
static int ctrlclick_volume = 100 ; /* % */
module_param ( ctrlclick_volume , int , 0 ) ;
MODULE_PARM_DESC ( ctrlclick_volume , " Ctrlclick volume (in %), default is 100% " ) ;
2005-09-15 02:01:45 -05:00
static int lk201_compose_is_alt ;
2005-04-16 15:20:36 -07:00
module_param ( lk201_compose_is_alt , int , 0 ) ;
MODULE_PARM_DESC ( lk201_compose_is_alt , " If set non-zero, LK201' Compose key "
" will act as an Alt key " ) ;
# undef LKKBD_DEBUG
# ifdef LKKBD_DEBUG
# define DBG(x...) printk (x)
# else
# define DBG(x...) do {} while (0)
# endif
/* LED control */
# define LK_LED_WAIT 0x81
# define LK_LED_COMPOSE 0x82
# define LK_LED_SHIFTLOCK 0x84
# define LK_LED_SCROLLLOCK 0x88
# define LK_CMD_LED_ON 0x13
# define LK_CMD_LED_OFF 0x11
/* Mode control */
# define LK_MODE_DOWN 0x80
# define LK_MODE_AUTODOWN 0x82
# define LK_MODE_UPDOWN 0x86
# define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3))
/* Misc commands */
# define LK_CMD_ENABLE_KEYCLICK 0x1b
# define LK_CMD_DISABLE_KEYCLICK 0x99
# define LK_CMD_DISABLE_BELL 0xa1
# define LK_CMD_SOUND_BELL 0xa7
# define LK_CMD_ENABLE_BELL 0x23
# define LK_CMD_DISABLE_CTRCLICK 0xb9
# define LK_CMD_ENABLE_CTRCLICK 0xbb
# define LK_CMD_SET_DEFAULTS 0xd3
# define LK_CMD_POWERCYCLE_RESET 0xfd
# define LK_CMD_ENABLE_LK401 0xe9
# define LK_CMD_REQUEST_ID 0xab
/* Misc responses from keyboard */
# define LK_STUCK_KEY 0x3d
# define LK_SELFTEST_FAILED 0x3e
# define LK_ALL_KEYS_UP 0xb3
# define LK_METRONOME 0xb4
# define LK_OUTPUT_ERROR 0xb5
# define LK_INPUT_ERROR 0xb6
# define LK_KBD_LOCKED 0xb7
# define LK_KBD_TEST_MODE_ACK 0xb8
# define LK_PREFIX_KEY_DOWN 0xb9
# define LK_MODE_CHANGE_ACK 0xba
# define LK_RESPONSE_RESERVED 0xbb
# define LK_NUM_KEYCODES 256
# define LK_NUM_IGNORE_BYTES 6
typedef u_int16_t lk_keycode_t ;
static lk_keycode_t lkkbd_keycode [ LK_NUM_KEYCODES ] = {
[ 0x56 ] = KEY_F1 ,
[ 0x57 ] = KEY_F2 ,
[ 0x58 ] = KEY_F3 ,
[ 0x59 ] = KEY_F4 ,
[ 0x5a ] = KEY_F5 ,
[ 0x64 ] = KEY_F6 ,
[ 0x65 ] = KEY_F7 ,
[ 0x66 ] = KEY_F8 ,
[ 0x67 ] = KEY_F9 ,
[ 0x68 ] = KEY_F10 ,
[ 0x71 ] = KEY_F11 ,
[ 0x72 ] = KEY_F12 ,
[ 0x73 ] = KEY_F13 ,
[ 0x74 ] = KEY_F14 ,
[ 0x7c ] = KEY_F15 ,
[ 0x7d ] = KEY_F16 ,
[ 0x80 ] = KEY_F17 ,
[ 0x81 ] = KEY_F18 ,
[ 0x82 ] = KEY_F19 ,
[ 0x83 ] = KEY_F20 ,
[ 0x8a ] = KEY_FIND ,
[ 0x8b ] = KEY_INSERT ,
[ 0x8c ] = KEY_DELETE ,
[ 0x8d ] = KEY_SELECT ,
[ 0x8e ] = KEY_PAGEUP ,
[ 0x8f ] = KEY_PAGEDOWN ,
[ 0x92 ] = KEY_KP0 ,
[ 0x94 ] = KEY_KPDOT ,
[ 0x95 ] = KEY_KPENTER ,
[ 0x96 ] = KEY_KP1 ,
[ 0x97 ] = KEY_KP2 ,
[ 0x98 ] = KEY_KP3 ,
[ 0x99 ] = KEY_KP4 ,
[ 0x9a ] = KEY_KP5 ,
[ 0x9b ] = KEY_KP6 ,
[ 0x9c ] = KEY_KPCOMMA ,
[ 0x9d ] = KEY_KP7 ,
[ 0x9e ] = KEY_KP8 ,
[ 0x9f ] = KEY_KP9 ,
[ 0xa0 ] = KEY_KPMINUS ,
[ 0xa1 ] = KEY_PROG1 ,
[ 0xa2 ] = KEY_PROG2 ,
[ 0xa3 ] = KEY_PROG3 ,
[ 0xa4 ] = KEY_PROG4 ,
[ 0xa7 ] = KEY_LEFT ,
[ 0xa8 ] = KEY_RIGHT ,
[ 0xa9 ] = KEY_DOWN ,
[ 0xaa ] = KEY_UP ,
[ 0xab ] = KEY_RIGHTSHIFT ,
[ 0xac ] = KEY_LEFTALT ,
[ 0xad ] = KEY_COMPOSE , /* Right Compose, that is. */
[ 0xae ] = KEY_LEFTSHIFT , /* Same as KEY_RIGHTSHIFT on LK201 */
[ 0xaf ] = KEY_LEFTCTRL ,
[ 0xb0 ] = KEY_CAPSLOCK ,
[ 0xb1 ] = KEY_COMPOSE , /* Left Compose, that is. */
[ 0xb2 ] = KEY_RIGHTALT ,
[ 0xbc ] = KEY_BACKSPACE ,
[ 0xbd ] = KEY_ENTER ,
[ 0xbe ] = KEY_TAB ,
[ 0xbf ] = KEY_ESC ,
[ 0xc0 ] = KEY_1 ,
[ 0xc1 ] = KEY_Q ,
[ 0xc2 ] = KEY_A ,
[ 0xc3 ] = KEY_Z ,
[ 0xc5 ] = KEY_2 ,
[ 0xc6 ] = KEY_W ,
[ 0xc7 ] = KEY_S ,
[ 0xc8 ] = KEY_X ,
[ 0xc9 ] = KEY_102ND ,
[ 0xcb ] = KEY_3 ,
[ 0xcc ] = KEY_E ,
[ 0xcd ] = KEY_D ,
[ 0xce ] = KEY_C ,
[ 0xd0 ] = KEY_4 ,
[ 0xd1 ] = KEY_R ,
[ 0xd2 ] = KEY_F ,
[ 0xd3 ] = KEY_V ,
[ 0xd4 ] = KEY_SPACE ,
[ 0xd6 ] = KEY_5 ,
[ 0xd7 ] = KEY_T ,
[ 0xd8 ] = KEY_G ,
[ 0xd9 ] = KEY_B ,
[ 0xdb ] = KEY_6 ,
[ 0xdc ] = KEY_Y ,
[ 0xdd ] = KEY_H ,
[ 0xde ] = KEY_N ,
[ 0xe0 ] = KEY_7 ,
[ 0xe1 ] = KEY_U ,
[ 0xe2 ] = KEY_J ,
[ 0xe3 ] = KEY_M ,
[ 0xe5 ] = KEY_8 ,
[ 0xe6 ] = KEY_I ,
[ 0xe7 ] = KEY_K ,
[ 0xe8 ] = KEY_COMMA ,
[ 0xea ] = KEY_9 ,
[ 0xeb ] = KEY_O ,
[ 0xec ] = KEY_L ,
[ 0xed ] = KEY_DOT ,
[ 0xef ] = KEY_0 ,
[ 0xf0 ] = KEY_P ,
[ 0xf2 ] = KEY_SEMICOLON ,
[ 0xf3 ] = KEY_SLASH ,
[ 0xf5 ] = KEY_EQUAL ,
[ 0xf6 ] = KEY_RIGHTBRACE ,
[ 0xf7 ] = KEY_BACKSLASH ,
[ 0xf9 ] = KEY_MINUS ,
[ 0xfa ] = KEY_LEFTBRACE ,
[ 0xfb ] = KEY_APOSTROPHE ,
} ;
# define CHECK_LED(LED, BITS) do { \
2005-09-15 02:01:45 -05:00
if ( test_bit ( LED , lk - > dev - > led ) ) \
2005-04-16 15:20:36 -07:00
leds_on | = BITS ; \
else \
leds_off | = BITS ; \
} while ( 0 )
/*
* Per - keyboard data
*/
struct lkkbd {
lk_keycode_t keycode [ LK_NUM_KEYCODES ] ;
int ignore_bytes ;
unsigned char id [ LK_NUM_IGNORE_BYTES ] ;
2005-09-15 02:01:45 -05:00
struct input_dev * dev ;
2005-04-16 15:20:36 -07:00
struct serio * serio ;
struct work_struct tq ;
char name [ 64 ] ;
char phys [ 32 ] ;
char type ;
int bell_volume ;
int keyclick_volume ;
int ctrlclick_volume ;
} ;
/*
* Calculate volume parameter byte for a given volume .
*/
static unsigned char
volume_to_hw ( int volume_percent )
{
unsigned char ret = 0 ;
if ( volume_percent < 0 )
volume_percent = 0 ;
if ( volume_percent > 100 )
volume_percent = 100 ;
if ( volume_percent > = 0 )
ret = 7 ;
if ( volume_percent > = 13 ) /* 12.5 */
ret = 6 ;
if ( volume_percent > = 25 )
ret = 5 ;
if ( volume_percent > = 38 ) /* 37.5 */
ret = 4 ;
if ( volume_percent > = 50 )
ret = 3 ;
if ( volume_percent > = 63 ) /* 62.5 */
ret = 2 ; /* This is the default volume */
if ( volume_percent > = 75 )
ret = 1 ;
if ( volume_percent > = 88 ) /* 87.5 */
ret = 0 ;
ret | = 0x80 ;
return ret ;
}
static void
lkkbd_detection_done ( struct lkkbd * lk )
{
int i ;
/*
* Reset setting for Compose key . Let Compose be KEY_COMPOSE .
*/
lk - > keycode [ 0xb1 ] = KEY_COMPOSE ;
/*
* Print keyboard name and modify Compose = Alt on user ' s request .
*/
switch ( lk - > id [ 4 ] ) {
case 1 :
sprintf ( lk - > name , " DEC LK201 keyboard " ) ;
if ( lk201_compose_is_alt )
lk - > keycode [ 0xb1 ] = KEY_LEFTALT ;
break ;
case 2 :
sprintf ( lk - > name , " DEC LK401 keyboard " ) ;
break ;
default :
sprintf ( lk - > name , " Unknown DEC keyboard " ) ;
printk ( KERN_ERR " lkkbd: keyboard on %s is unknown, "
" please report to Jan-Benedict Glaw "
" <jbglaw@lug-owl.de> \n " , lk - > phys ) ;
printk ( KERN_ERR " lkkbd: keyboard ID'ed as: " ) ;
for ( i = 0 ; i < LK_NUM_IGNORE_BYTES ; i + + )
printk ( " 0x%02x " , lk - > id [ i ] ) ;
printk ( " \n " ) ;
break ;
}
printk ( KERN_INFO " lkkbd: keyboard on %s identified as: %s \n " ,
lk - > phys , lk - > name ) ;
/*
* Report errors during keyboard boot - up .
*/
switch ( lk - > id [ 2 ] ) {
case 0x00 :
/* All okay */
break ;
case LK_STUCK_KEY :
printk ( KERN_ERR " lkkbd: Stuck key on keyboard at "
" %s \n " , lk - > phys ) ;
break ;
case LK_SELFTEST_FAILED :
printk ( KERN_ERR " lkkbd: Selftest failed on keyboard "
" at %s, keyboard may not work "
" properly \n " , lk - > phys ) ;
break ;
default :
printk ( KERN_ERR " lkkbd: Unknown error %02x on "
" keyboard at %s \n " , lk - > id [ 2 ] ,
lk - > phys ) ;
break ;
}
/*
* Try to hint user if there ' s a stuck key .
*/
if ( lk - > id [ 2 ] = = LK_STUCK_KEY & & lk - > id [ 3 ] ! = 0 )
printk ( KERN_ERR " Scancode of stuck key is 0x%02x, keycode "
" is 0x%04x \n " , lk - > id [ 3 ] ,
lk - > keycode [ lk - > id [ 3 ] ] ) ;
return ;
}
/*
* lkkbd_interrupt ( ) is called by the low level driver when a character
* is received .
*/
static irqreturn_t
lkkbd_interrupt ( struct serio * serio , unsigned char data , unsigned int flags ,
struct pt_regs * regs )
{
struct lkkbd * lk = serio_get_drvdata ( serio ) ;
int i ;
DBG ( KERN_INFO " Got byte 0x%02x \n " , data ) ;
if ( lk - > ignore_bytes > 0 ) {
2005-09-15 02:01:45 -05:00
DBG ( KERN_INFO " Ignoring a byte on %s \n " , lk - > name ) ;
2005-04-16 15:20:36 -07:00
lk - > id [ LK_NUM_IGNORE_BYTES - lk - > ignore_bytes - - ] = data ;
if ( lk - > ignore_bytes = = 0 )
lkkbd_detection_done ( lk ) ;
return IRQ_HANDLED ;
}
switch ( data ) {
case LK_ALL_KEYS_UP :
2005-09-15 02:01:45 -05:00
input_regs ( lk - > dev , regs ) ;
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < ARRAY_SIZE ( lkkbd_keycode ) ; i + + )
if ( lk - > keycode [ i ] ! = KEY_RESERVED )
2005-09-15 02:01:45 -05:00
input_report_key ( lk - > dev , lk - > keycode [ i ] , 0 ) ;
input_sync ( lk - > dev ) ;
2005-04-16 15:20:36 -07:00
break ;
case LK_METRONOME :
2005-10-31 01:30:19 -05:00
DBG ( KERN_INFO " Got LK_METRONOME and don't "
2005-04-16 15:20:36 -07:00
" know how to handle... \n " ) ;
break ;
case LK_OUTPUT_ERROR :
DBG ( KERN_INFO " Got LK_OUTPUT_ERROR and don't "
" know how to handle... \n " ) ;
break ;
case LK_INPUT_ERROR :
DBG ( KERN_INFO " Got LK_INPUT_ERROR and don't "
" know how to handle... \n " ) ;
break ;
case LK_KBD_LOCKED :
DBG ( KERN_INFO " Got LK_KBD_LOCKED and don't "
" know how to handle... \n " ) ;
break ;
case LK_KBD_TEST_MODE_ACK :
DBG ( KERN_INFO " Got LK_KBD_TEST_MODE_ACK and don't "
" know how to handle... \n " ) ;
break ;
case LK_PREFIX_KEY_DOWN :
DBG ( KERN_INFO " Got LK_PREFIX_KEY_DOWN and don't "
" know how to handle... \n " ) ;
break ;
case LK_MODE_CHANGE_ACK :
DBG ( KERN_INFO " Got LK_MODE_CHANGE_ACK and ignored "
" it properly... \n " ) ;
break ;
case LK_RESPONSE_RESERVED :
DBG ( KERN_INFO " Got LK_RESPONSE_RESERVED and don't "
" know how to handle... \n " ) ;
break ;
case 0x01 :
DBG ( KERN_INFO " Got 0x01, scheduling re-initialization \n " ) ;
lk - > ignore_bytes = LK_NUM_IGNORE_BYTES ;
lk - > id [ LK_NUM_IGNORE_BYTES - lk - > ignore_bytes - - ] = data ;
schedule_work ( & lk - > tq ) ;
break ;
default :
if ( lk - > keycode [ data ] ! = KEY_RESERVED ) {
2005-09-15 02:01:45 -05:00
input_regs ( lk - > dev , regs ) ;
if ( ! test_bit ( lk - > keycode [ data ] , lk - > dev - > key ) )
input_report_key ( lk - > dev , lk - > keycode [ data ] , 1 ) ;
2005-04-16 15:20:36 -07:00
else
2005-09-15 02:01:45 -05:00
input_report_key ( lk - > dev , lk - > keycode [ data ] , 0 ) ;
input_sync ( lk - > dev ) ;
2005-04-16 15:20:36 -07:00
} else
printk ( KERN_WARNING " %s: Unknown key with "
" scancode 0x%02x on %s. \n " ,
__FILE__ , data , lk - > name ) ;
}
return IRQ_HANDLED ;
}
/*
* lkkbd_event ( ) handles events from the input module .
*/
static int
lkkbd_event ( struct input_dev * dev , unsigned int type , unsigned int code ,
int value )
{
struct lkkbd * lk = dev - > private ;
unsigned char leds_on = 0 ;
unsigned char leds_off = 0 ;
switch ( type ) {
case EV_LED :
CHECK_LED ( LED_CAPSL , LK_LED_SHIFTLOCK ) ;
CHECK_LED ( LED_COMPOSE , LK_LED_COMPOSE ) ;
CHECK_LED ( LED_SCROLLL , LK_LED_SCROLLLOCK ) ;
CHECK_LED ( LED_SLEEP , LK_LED_WAIT ) ;
if ( leds_on ! = 0 ) {
lk - > serio - > write ( lk - > serio , LK_CMD_LED_ON ) ;
lk - > serio - > write ( lk - > serio , leds_on ) ;
}
if ( leds_off ! = 0 ) {
lk - > serio - > write ( lk - > serio , LK_CMD_LED_OFF ) ;
lk - > serio - > write ( lk - > serio , leds_off ) ;
}
return 0 ;
case EV_SND :
switch ( code ) {
case SND_CLICK :
if ( value = = 0 ) {
DBG ( " %s: Deactivating key clicks \n " , __FUNCTION__ ) ;
lk - > serio - > write ( lk - > serio , LK_CMD_DISABLE_KEYCLICK ) ;
lk - > serio - > write ( lk - > serio , LK_CMD_DISABLE_CTRCLICK ) ;
} else {
DBG ( " %s: Activating key clicks \n " , __FUNCTION__ ) ;
lk - > serio - > write ( lk - > serio , LK_CMD_ENABLE_KEYCLICK ) ;
lk - > serio - > write ( lk - > serio , volume_to_hw ( lk - > keyclick_volume ) ) ;
lk - > serio - > write ( lk - > serio , LK_CMD_ENABLE_CTRCLICK ) ;
lk - > serio - > write ( lk - > serio , volume_to_hw ( lk - > ctrlclick_volume ) ) ;
}
return 0 ;
case SND_BELL :
if ( value ! = 0 )
lk - > serio - > write ( lk - > serio , LK_CMD_SOUND_BELL ) ;
return 0 ;
}
break ;
default :
printk ( KERN_ERR " %s (): Got unknown type %d, code %d, value %d \n " ,
__FUNCTION__ , type , code , value ) ;
}
return - 1 ;
}
/*
* lkkbd_reinit ( ) sets leds and beeps to a state the computer remembers they
* were in .
*/
static void
lkkbd_reinit ( void * data )
{
struct lkkbd * lk = data ;
int division ;
unsigned char leds_on = 0 ;
unsigned char leds_off = 0 ;
/* Ask for ID */
lk - > serio - > write ( lk - > serio , LK_CMD_REQUEST_ID ) ;
/* Reset parameters */
lk - > serio - > write ( lk - > serio , LK_CMD_SET_DEFAULTS ) ;
/* Set LEDs */
CHECK_LED ( LED_CAPSL , LK_LED_SHIFTLOCK ) ;
CHECK_LED ( LED_COMPOSE , LK_LED_COMPOSE ) ;
CHECK_LED ( LED_SCROLLL , LK_LED_SCROLLLOCK ) ;
CHECK_LED ( LED_SLEEP , LK_LED_WAIT ) ;
if ( leds_on ! = 0 ) {
lk - > serio - > write ( lk - > serio , LK_CMD_LED_ON ) ;
lk - > serio - > write ( lk - > serio , leds_on ) ;
}
if ( leds_off ! = 0 ) {
lk - > serio - > write ( lk - > serio , LK_CMD_LED_OFF ) ;
lk - > serio - > write ( lk - > serio , leds_off ) ;
}
/*
* Try to activate extended LK401 mode . This command will
* only work with a LK401 keyboard and grants access to
* LAlt , RAlt , RCompose and RShift .
*/
lk - > serio - > write ( lk - > serio , LK_CMD_ENABLE_LK401 ) ;
/* Set all keys to UPDOWN mode */
for ( division = 1 ; division < = 14 ; division + + )
lk - > serio - > write ( lk - > serio , LK_CMD_SET_MODE ( LK_MODE_UPDOWN ,
division ) ) ;
/* Enable bell and set volume */
lk - > serio - > write ( lk - > serio , LK_CMD_ENABLE_BELL ) ;
lk - > serio - > write ( lk - > serio , volume_to_hw ( lk - > bell_volume ) ) ;
/* Enable/disable keyclick (and possibly set volume) */
2005-09-15 02:01:45 -05:00
if ( test_bit ( SND_CLICK , lk - > dev - > snd ) ) {
2005-04-16 15:20:36 -07:00
lk - > serio - > write ( lk - > serio , LK_CMD_ENABLE_KEYCLICK ) ;
lk - > serio - > write ( lk - > serio , volume_to_hw ( lk - > keyclick_volume ) ) ;
lk - > serio - > write ( lk - > serio , LK_CMD_ENABLE_CTRCLICK ) ;
lk - > serio - > write ( lk - > serio , volume_to_hw ( lk - > ctrlclick_volume ) ) ;
} else {
lk - > serio - > write ( lk - > serio , LK_CMD_DISABLE_KEYCLICK ) ;
lk - > serio - > write ( lk - > serio , LK_CMD_DISABLE_CTRCLICK ) ;
}
/* Sound the bell if needed */
2005-09-15 02:01:45 -05:00
if ( test_bit ( SND_BELL , lk - > dev - > snd ) )
2005-04-16 15:20:36 -07:00
lk - > serio - > write ( lk - > serio , LK_CMD_SOUND_BELL ) ;
}
/*
* lkkbd_connect ( ) probes for a LK keyboard and fills the necessary structures .
*/
static int
lkkbd_connect ( struct serio * serio , struct serio_driver * drv )
{
struct lkkbd * lk ;
2005-09-15 02:01:45 -05:00
struct input_dev * input_dev ;
2005-04-16 15:20:36 -07:00
int i ;
int err ;
2005-09-15 02:01:45 -05:00
lk = kzalloc ( sizeof ( struct lkkbd ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! lk | | ! input_dev ) {
err = - ENOMEM ;
goto fail ;
}
2005-04-16 15:20:36 -07:00
lk - > serio = serio ;
2005-09-15 02:01:45 -05:00
lk - > dev = input_dev ;
2005-04-16 15:20:36 -07:00
INIT_WORK ( & lk - > tq , lkkbd_reinit , lk ) ;
lk - > bell_volume = bell_volume ;
lk - > keyclick_volume = keyclick_volume ;
lk - > ctrlclick_volume = ctrlclick_volume ;
2005-09-15 02:01:45 -05:00
memcpy ( lk - > keycode , lkkbd_keycode , sizeof ( lk_keycode_t ) * LK_NUM_KEYCODES ) ;
2005-04-16 15:20:36 -07:00
2005-09-15 02:01:45 -05:00
strlcpy ( lk - > name , " DEC LK keyboard " , sizeof ( lk - > name ) ) ;
snprintf ( lk - > phys , sizeof ( lk - > phys ) , " %s/input0 " , serio - > phys ) ;
input_dev - > name = lk - > name ;
input_dev - > phys = lk - > phys ;
input_dev - > id . bustype = BUS_RS232 ;
input_dev - > id . vendor = SERIO_LKKBD ;
input_dev - > id . product = 0 ;
input_dev - > id . version = 0x0100 ;
input_dev - > cdev . dev = & serio - > dev ;
input_dev - > event = lkkbd_event ;
input_dev - > private = lk ;
set_bit ( EV_KEY , input_dev - > evbit ) ;
set_bit ( EV_LED , input_dev - > evbit ) ;
set_bit ( EV_SND , input_dev - > evbit ) ;
set_bit ( EV_REP , input_dev - > evbit ) ;
set_bit ( LED_CAPSL , input_dev - > ledbit ) ;
set_bit ( LED_SLEEP , input_dev - > ledbit ) ;
set_bit ( LED_COMPOSE , input_dev - > ledbit ) ;
set_bit ( LED_SCROLLL , input_dev - > ledbit ) ;
set_bit ( SND_BELL , input_dev - > sndbit ) ;
set_bit ( SND_CLICK , input_dev - > sndbit ) ;
input_dev - > keycode = lk - > keycode ;
input_dev - > keycodesize = sizeof ( lk_keycode_t ) ;
input_dev - > keycodemax = LK_NUM_KEYCODES ;
for ( i = 0 ; i < LK_NUM_KEYCODES ; i + + )
set_bit ( lk - > keycode [ i ] , input_dev - > keybit ) ;
2005-04-16 15:20:36 -07:00
serio_set_drvdata ( serio , lk ) ;
err = serio_open ( serio , drv ) ;
2005-09-15 02:01:45 -05:00
if ( err )
goto fail ;
2005-04-16 15:20:36 -07:00
2005-09-15 02:01:45 -05:00
input_register_device ( lk - > dev ) ;
2005-04-16 15:20:36 -07:00
lk - > serio - > write ( lk - > serio , LK_CMD_POWERCYCLE_RESET ) ;
return 0 ;
2005-09-15 02:01:45 -05:00
fail : serio_set_drvdata ( serio , NULL ) ;
input_free_device ( input_dev ) ;
kfree ( lk ) ;
return err ;
2005-04-16 15:20:36 -07:00
}
/*
* lkkbd_disconnect ( ) unregisters and closes behind us .
*/
static void
lkkbd_disconnect ( struct serio * serio )
{
struct lkkbd * lk = serio_get_drvdata ( serio ) ;
2005-09-15 02:01:45 -05:00
input_get_device ( lk - > dev ) ;
input_unregister_device ( lk - > dev ) ;
2005-04-16 15:20:36 -07:00
serio_close ( serio ) ;
serio_set_drvdata ( serio , NULL ) ;
2005-09-15 02:01:45 -05:00
input_put_device ( lk - > dev ) ;
2005-04-16 15:20:36 -07:00
kfree ( lk ) ;
}
static struct serio_device_id lkkbd_serio_ids [ ] = {
{
. type = SERIO_RS232 ,
. proto = SERIO_LKKBD ,
. id = SERIO_ANY ,
. extra = SERIO_ANY ,
} ,
{ 0 }
} ;
MODULE_DEVICE_TABLE ( serio , lkkbd_serio_ids ) ;
static struct serio_driver lkkbd_drv = {
. driver = {
. name = " lkkbd " ,
} ,
. description = DRIVER_DESC ,
. id_table = lkkbd_serio_ids ,
. connect = lkkbd_connect ,
. disconnect = lkkbd_disconnect ,
. interrupt = lkkbd_interrupt ,
} ;
/*
* The functions for insering / removing us as a module .
*/
static int __init
lkkbd_init ( void )
{
serio_register_driver ( & lkkbd_drv ) ;
return 0 ;
}
static void __exit
lkkbd_exit ( void )
{
serio_unregister_driver ( & lkkbd_drv ) ;
}
module_init ( lkkbd_init ) ;
module_exit ( lkkbd_exit ) ;