2005-04-17 02:20:36 +04:00
/*
* Generic linux - input device driver for keyboard devices
*
* Copyright ( c ) 2001 Brian S . Julin
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification .
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` ` AS IS ' ' AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
*
* References :
* HP - HIL Technical Reference Manual . Hewlett Packard Product No . 45918 A
*
*/
# include <linux/hil.h>
# include <linux/input.h>
# include <linux/serio.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
2009-08-08 10:17:46 +04:00
# include <linux/completion.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/pci_ids.h>
# define PREFIX "HIL KEYB: "
# define HIL_GENERIC_NAME "HIL keyboard"
MODULE_AUTHOR ( " Brian S. Julin <bri@calyx.com> " ) ;
MODULE_DESCRIPTION ( HIL_GENERIC_NAME " driver " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
2008-12-29 15:44:44 +03:00
MODULE_ALIAS ( " serio:ty03pr25id00ex* " ) ;
2005-04-17 02:20:36 +04:00
# define HIL_KBD_MAX_LENGTH 16
# define HIL_KBD_SET1_UPBIT 0x01
# define HIL_KBD_SET1_SHIFT 1
2007-03-01 07:51:19 +03:00
static unsigned int hil_kbd_set1 [ HIL_KEYCODES_SET1_TBLSIZE ] __read_mostly =
2005-04-17 02:20:36 +04:00
{ HIL_KEYCODES_SET1 } ;
# define HIL_KBD_SET2_UPBIT 0x01
# define HIL_KBD_SET2_SHIFT 1
/* Set2 is user defined */
# define HIL_KBD_SET3_UPBIT 0x80
# define HIL_KBD_SET3_SHIFT 0
2007-03-01 07:51:19 +03:00
static unsigned int hil_kbd_set3 [ HIL_KEYCODES_SET3_TBLSIZE ] __read_mostly =
2005-04-17 02:20:36 +04:00
{ HIL_KEYCODES_SET3 } ;
2007-03-01 07:51:19 +03:00
static const char hil_language [ ] [ 16 ] = { HIL_LOCALE_MAP } ;
2005-04-17 02:20:36 +04:00
struct hil_kbd {
2006-03-26 18:41:55 +04:00
struct input_dev * dev ;
2005-04-17 02:20:36 +04:00
struct serio * serio ;
/* Input buffer and index for packets from HIL bus. */
hil_packet data [ HIL_KBD_MAX_LENGTH ] ;
int idx4 ; /* four counts per packet */
/* Raw device info records from HIL bus, see hil.h for fields. */
char idd [ HIL_KBD_MAX_LENGTH ] ; /* DID byte and IDD record */
char rsc [ HIL_KBD_MAX_LENGTH ] ; /* RSC record */
char exd [ HIL_KBD_MAX_LENGTH ] ; /* EXD record */
char rnm [ HIL_KBD_MAX_LENGTH + 1 ] ; /* RNM record + NULL term. */
2009-08-08 10:17:46 +04:00
struct completion cmd_done ;
2005-04-17 02:20:36 +04:00
} ;
2009-08-08 10:17:46 +04:00
static bool hil_kbd_is_command_response ( hil_packet p )
2005-04-17 02:20:36 +04:00
{
2009-08-08 10:17:46 +04:00
if ( ( p & ~ HIL_CMDCT_POL ) = = ( HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL ) )
return false ;
2005-04-17 02:20:36 +04:00
2009-08-08 10:17:46 +04:00
if ( ( p & ~ HIL_CMDCT_RPL ) = = ( HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL ) )
return false ;
return true ;
}
static void hil_kbd_handle_command_response ( struct hil_kbd * kbd )
{
hil_packet p ;
char * buf ;
int i , idx ;
2005-04-17 02:20:36 +04:00
2009-08-08 10:17:46 +04:00
idx = kbd - > idx4 / 4 ;
p = kbd - > data [ idx - 1 ] ;
2005-04-17 02:20:36 +04:00
switch ( p & HIL_PKT_DATA_MASK ) {
case HIL_CMD_IDD :
2009-08-08 10:17:46 +04:00
buf = kbd - > idd ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HIL_CMD_RSC :
2009-08-08 10:17:46 +04:00
buf = kbd - > rsc ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HIL_CMD_EXD :
2009-08-08 10:17:46 +04:00
buf = kbd - > exd ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HIL_CMD_RNM :
2009-08-08 10:17:46 +04:00
kbd - > rnm [ HIL_KBD_MAX_LENGTH ] = 0 ;
buf = kbd - > rnm ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
default :
/* These occur when device isn't present */
2009-08-08 10:17:46 +04:00
if ( p ! = ( HIL_ERR_INT | HIL_PKT_CMD ) ) {
/* Anything else we'd like to know about. */
printk ( KERN_WARNING PREFIX " Device sent unknown record %x \n " , p ) ;
}
goto out ;
2005-04-17 02:20:36 +04:00
}
2009-08-08 10:17:46 +04:00
for ( i = 0 ; i < idx ; i + + )
buf [ i ] = kbd - > data [ i ] & HIL_PKT_DATA_MASK ;
for ( ; i < HIL_KBD_MAX_LENGTH ; i + + )
buf [ i ] = 0 ;
out :
complete ( & kbd - > cmd_done ) ;
}
static void hil_kbd_handle_key_events ( struct hil_kbd * kbd )
{
struct input_dev * dev = kbd - > dev ;
int idx = kbd - > idx4 / 4 ;
int i ;
2005-04-17 02:20:36 +04:00
switch ( kbd - > data [ 0 ] & HIL_POL_CHARTYPE_MASK ) {
case HIL_POL_CHARTYPE_NONE :
2009-08-08 10:17:46 +04:00
return ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HIL_POL_CHARTYPE_ASCII :
2009-08-08 10:17:46 +04:00
for ( i = 1 ; i < idx - 1 ; i + + )
input_report_key ( dev , kbd - > data [ i ] & 0x7f , 1 ) ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HIL_POL_CHARTYPE_RSVD1 :
case HIL_POL_CHARTYPE_RSVD2 :
case HIL_POL_CHARTYPE_BINARY :
2009-08-08 10:17:46 +04:00
for ( i = 1 ; i < idx - 1 ; i + + )
input_report_key ( dev , kbd - > data [ i ] , 1 ) ;
2005-04-17 02:20:36 +04:00
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HIL_POL_CHARTYPE_SET1 :
2009-08-08 10:17:46 +04:00
for ( i = 1 ; i < idx - 1 ; i + + ) {
unsigned int key = kbd - > data [ i ] ;
int up = key & HIL_KBD_SET1_UPBIT ;
2005-04-17 02:20:36 +04:00
key & = ( ~ HIL_KBD_SET1_UPBIT & 0xff ) ;
key = hil_kbd_set1 [ key > > HIL_KBD_SET1_SHIFT ] ;
2009-08-08 10:17:46 +04:00
input_report_key ( dev , key , ! up ) ;
2005-04-17 02:20:36 +04:00
}
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HIL_POL_CHARTYPE_SET2 :
2009-08-08 10:17:46 +04:00
for ( i = 1 ; i < idx - 1 ; i + + ) {
unsigned int key = kbd - > data [ i ] ;
int up = key & HIL_KBD_SET2_UPBIT ;
2005-04-17 02:20:36 +04:00
key & = ( ~ HIL_KBD_SET1_UPBIT & 0xff ) ;
key = key > > HIL_KBD_SET2_SHIFT ;
2009-08-08 10:17:46 +04:00
input_report_key ( dev , key , ! up ) ;
2005-04-17 02:20:36 +04:00
}
break ;
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
case HIL_POL_CHARTYPE_SET3 :
2009-08-08 10:17:46 +04:00
for ( i = 1 ; i < idx - 1 ; i + + ) {
unsigned int key = kbd - > data [ i ] ;
int up = key & HIL_KBD_SET3_UPBIT ;
2005-04-17 02:20:36 +04:00
key & = ( ~ HIL_KBD_SET1_UPBIT & 0xff ) ;
key = hil_kbd_set3 [ key > > HIL_KBD_SET3_SHIFT ] ;
2009-08-08 10:17:46 +04:00
input_report_key ( dev , key , ! up ) ;
2005-04-17 02:20:36 +04:00
}
break ;
}
2009-08-08 10:17:46 +04:00
input_sync ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
static void hil_kbd_process_err ( struct hil_kbd * kbd )
{
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING PREFIX " errored HIL packet \n " ) ;
kbd - > idx4 = 0 ;
2009-08-08 10:17:46 +04:00
complete ( & kbd - > cmd_done ) ; /* just in case somebody is waiting */
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
static irqreturn_t hil_kbd_interrupt ( struct serio * serio ,
unsigned char data , unsigned int flags )
2005-04-17 02:20:36 +04:00
{
struct hil_kbd * kbd ;
hil_packet packet ;
int idx ;
2005-10-22 06:58:51 +04:00
kbd = serio_get_drvdata ( serio ) ;
2007-03-01 07:51:29 +03:00
BUG_ON ( kbd = = NULL ) ;
2005-04-17 02:20:36 +04:00
2009-08-08 10:17:46 +04:00
if ( kbd - > idx4 > = HIL_KBD_MAX_LENGTH * sizeof ( hil_packet ) ) {
2005-04-17 02:20:36 +04:00
hil_kbd_process_err ( kbd ) ;
2009-08-08 10:17:46 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2009-08-08 10:17:46 +04:00
idx = kbd - > idx4 / 4 ;
2007-03-01 07:51:29 +03:00
if ( ! ( kbd - > idx4 % 4 ) )
kbd - > data [ idx ] = 0 ;
2005-04-17 02:20:36 +04:00
packet = kbd - > data [ idx ] ;
packet | = ( ( hil_packet ) data ) < < ( ( 3 - ( kbd - > idx4 % 4 ) ) * 8 ) ;
kbd - > data [ idx ] = packet ;
/* Records of N 4-byte hil_packets must terminate with a command. */
2009-08-08 10:17:46 +04:00
if ( ( + + kbd - > idx4 % 4 ) = = 0 ) {
if ( ( packet & 0xffff0000 ) ! = HIL_ERR_INT ) {
hil_kbd_process_err ( kbd ) ;
} else if ( packet & HIL_PKT_CMD ) {
if ( hil_kbd_is_command_response ( packet ) )
hil_kbd_handle_command_response ( kbd ) ;
else
hil_kbd_handle_key_events ( kbd ) ;
kbd - > idx4 = 0 ;
}
2005-04-17 02:20:36 +04:00
}
2009-08-08 10:17:46 +04:00
out :
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
static void hil_kbd_disconnect ( struct serio * serio )
{
2009-08-08 10:17:46 +04:00
struct hil_kbd * kbd = serio_get_drvdata ( serio ) ;
2005-04-17 02:20:36 +04:00
2007-03-01 07:51:29 +03:00
BUG_ON ( kbd = = NULL ) ;
2005-04-17 02:20:36 +04:00
serio_close ( serio ) ;
2006-03-30 20:47:32 +04:00
input_unregister_device ( kbd - > dev ) ;
2005-04-17 02:20:36 +04:00
kfree ( kbd ) ;
}
2005-10-22 06:58:51 +04:00
static int hil_kbd_connect ( struct serio * serio , struct serio_driver * drv )
2005-04-17 02:20:36 +04:00
{
2009-08-08 10:17:46 +04:00
struct hil_kbd * kbd ;
struct input_dev * input_dev ;
uint8_t did , * idd ;
int i ;
int error ;
2006-04-02 09:08:05 +04:00
2006-03-14 08:09:16 +03:00
kbd = kzalloc ( sizeof ( * kbd ) , GFP_KERNEL ) ;
2009-08-08 10:17:46 +04:00
input_dev = input_allocate_device ( ) ;
if ( ! kbd | | ! input_dev ) {
error = - ENOMEM ;
2006-04-02 09:08:05 +04:00
goto bail0 ;
2009-08-08 10:17:46 +04:00
}
2006-04-02 09:08:05 +04:00
2005-04-17 02:20:36 +04:00
kbd - > serio = serio ;
2009-08-08 10:17:46 +04:00
kbd - > dev = input_dev ;
error = serio_open ( serio , drv ) ;
if ( error )
goto bail0 ;
2005-04-17 02:20:36 +04:00
2009-08-08 10:17:46 +04:00
serio_set_drvdata ( serio , kbd ) ;
2005-04-17 02:20:36 +04:00
/* Get device info. MLC driver supplies devid/status/etc. */
2009-08-08 10:17:46 +04:00
init_completion ( & kbd - > cmd_done ) ;
2009-08-05 11:30:26 +04:00
serio_write ( serio , 0 ) ;
serio_write ( serio , 0 ) ;
serio_write ( serio , HIL_PKT_CMD > > 8 ) ;
serio_write ( serio , HIL_CMD_IDD ) ;
2009-08-08 10:17:46 +04:00
error = wait_for_completion_killable ( & kbd - > cmd_done ) ;
if ( error )
goto bail1 ;
2005-04-17 02:20:36 +04:00
2009-08-08 10:17:46 +04:00
init_completion ( & kbd - > cmd_done ) ;
2009-08-05 11:30:26 +04:00
serio_write ( serio , 0 ) ;
serio_write ( serio , 0 ) ;
serio_write ( serio , HIL_PKT_CMD > > 8 ) ;
serio_write ( serio , HIL_CMD_RSC ) ;
2009-08-08 10:17:46 +04:00
error = wait_for_completion_killable ( & kbd - > cmd_done ) ;
if ( error )
goto bail1 ;
2005-04-17 02:20:36 +04:00
2009-08-08 10:17:46 +04:00
init_completion ( & kbd - > cmd_done ) ;
2009-08-05 11:30:26 +04:00
serio_write ( serio , 0 ) ;
serio_write ( serio , 0 ) ;
serio_write ( serio , HIL_PKT_CMD > > 8 ) ;
serio_write ( serio , HIL_CMD_RNM ) ;
2009-08-08 10:17:46 +04:00
error = wait_for_completion_killable ( & kbd - > cmd_done ) ;
if ( error )
goto bail1 ;
2005-04-17 02:20:36 +04:00
2009-08-08 10:17:46 +04:00
init_completion ( & kbd - > cmd_done ) ;
2009-08-05 11:30:26 +04:00
serio_write ( serio , 0 ) ;
serio_write ( serio , 0 ) ;
serio_write ( serio , HIL_PKT_CMD > > 8 ) ;
serio_write ( serio , HIL_CMD_EXD ) ;
2009-08-08 10:17:46 +04:00
error = wait_for_completion_killable ( & kbd - > cmd_done ) ;
if ( error )
goto bail1 ;
2005-04-17 02:20:36 +04:00
did = kbd - > idd [ 0 ] ;
idd = kbd - > idd + 1 ;
switch ( did & HIL_IDD_DID_TYPE_MASK ) {
case HIL_IDD_DID_TYPE_KB_INTEGRAL :
case HIL_IDD_DID_TYPE_KB_ITF :
case HIL_IDD_DID_TYPE_KB_RSVD :
case HIL_IDD_DID_TYPE_CHAR :
printk ( KERN_INFO PREFIX " HIL keyboard found (did = 0x%02x, lang = %s) \n " ,
did , hil_language [ did & HIL_IDD_DID_TYPE_KB_LANG_MASK ] ) ;
break ;
default :
2009-08-08 10:17:46 +04:00
goto bail1 ;
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
if ( HIL_IDD_NUM_BUTTONS ( idd ) | | HIL_IDD_NUM_AXES_PER_SET ( * idd ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO PREFIX " keyboards only, no combo devices supported. \n " ) ;
2009-08-08 10:17:46 +04:00
goto bail1 ;
2005-04-17 02:20:36 +04:00
}
2009-08-08 10:17:46 +04:00
input_dev - > evbit [ 0 ] = BIT_MASK ( EV_KEY ) | BIT_MASK ( EV_REP ) ;
input_dev - > ledbit [ 0 ] = BIT_MASK ( LED_NUML ) | BIT_MASK ( LED_CAPSL ) |
BIT_MASK ( LED_SCROLLL ) ;
input_dev - > keycodemax = HIL_KEYCODES_SET1_TBLSIZE ;
input_dev - > keycodesize = sizeof ( hil_kbd_set1 [ 0 ] ) ;
input_dev - > keycode = hil_kbd_set1 ;
input_dev - > name = strlen ( kbd - > rnm ) ? kbd - > rnm : HIL_GENERIC_NAME ;
input_dev - > phys = " hpkbd/input0 " ; /* XXX */
input_dev - > id . bustype = BUS_HIL ;
input_dev - > id . vendor = PCI_VENDOR_ID_HP ;
input_dev - > id . product = 0x0001 ; /* TODO: get from kbd->rsc */
input_dev - > id . version = 0x0100 ; /* TODO: get from kbd->rsc */
input_dev - > dev . parent = & serio - > dev ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 128 ; i + + ) {
2009-08-08 10:17:46 +04:00
__set_bit ( hil_kbd_set1 [ i ] , input_dev - > keybit ) ;
__set_bit ( hil_kbd_set3 [ i ] , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
}
2009-08-08 10:17:46 +04:00
__clear_bit ( KEY_RESERVED , input_dev - > keybit ) ;
2005-04-17 02:20:36 +04:00
2009-08-05 11:30:26 +04:00
serio_write ( serio , 0 ) ;
serio_write ( serio , 0 ) ;
serio_write ( serio , HIL_PKT_CMD > > 8 ) ;
serio_write ( serio , HIL_CMD_EK1 ) ; /* Enable Keyswitch Autorepeat 1 */
2009-08-08 10:17:46 +04:00
/* No need to wait for completion */
error = input_register_device ( kbd - > dev ) ;
if ( error )
goto bail1 ;
2005-04-17 02:20:36 +04:00
2005-10-22 06:58:51 +04:00
return 0 ;
2009-08-08 10:17:46 +04:00
bail1 :
2005-04-17 02:20:36 +04:00
serio_close ( serio ) ;
2006-04-03 17:44:17 +04:00
serio_set_drvdata ( serio , NULL ) ;
2005-04-17 02:20:36 +04:00
bail0 :
2009-08-08 10:17:46 +04:00
input_free_device ( input_dev ) ;
2005-04-17 02:20:36 +04:00
kfree ( kbd ) ;
2009-08-08 10:17:46 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
2005-10-22 06:58:51 +04:00
static struct serio_device_id hil_kbd_ids [ ] = {
{
. type = SERIO_HIL_MLC ,
. proto = SERIO_HIL ,
. id = SERIO_ANY ,
. extra = SERIO_ANY ,
} ,
{ 0 }
} ;
2005-04-17 02:20:36 +04:00
2007-03-01 07:51:19 +03:00
static struct serio_driver hil_kbd_serio_drv = {
2005-04-17 02:20:36 +04:00
. driver = {
. name = " hil_kbd " ,
} ,
. description = " HP HIL keyboard driver " ,
2005-10-22 06:58:51 +04:00
. id_table = hil_kbd_ids ,
2007-03-01 07:51:29 +03:00
. connect = hil_kbd_connect ,
. disconnect = hil_kbd_disconnect ,
. interrupt = hil_kbd_interrupt
2005-04-17 02:20:36 +04:00
} ;
static int __init hil_kbd_init ( void )
{
2006-11-24 07:35:10 +03:00
return serio_register_driver ( & hil_kbd_serio_drv ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
static void __exit hil_kbd_exit ( void )
{
serio_unregister_driver ( & hil_kbd_serio_drv ) ;
}
2007-03-01 07:51:29 +03:00
2005-04-17 02:20:36 +04:00
module_init ( hil_kbd_init ) ;
module_exit ( hil_kbd_exit ) ;