2005-04-17 02:20:36 +04:00
/*
*
* BRIEF MODULE DESCRIPTION
* Qtronix 990 P infrared keyboard driver .
*
*
* Copyright 2001 MontaVista Software Inc .
* Author : MontaVista Software , Inc .
* ppopov @ mvista . com or source @ mvista . com
*
*
* The bottom portion of this driver was take from
* pc_keyb . c Please see that file for copyrights .
*
* 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 SOFTWARE IS PROVIDED ` ` 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 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 OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* 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 . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/config.h>
/*
* NOTE :
*
* This driver has only been tested with the Consumer IR
* port of the ITE 8172 system controller .
*
* You do not need this driver if you are using the ps / 2 or
* USB adapter that the keyboard ships with . You only need
* this driver if your board has a IR port and the keyboard
* data is being sent directly to the IR . In that case ,
* you also need some low - level IR support . See it8172_cir . c .
*
*/
# ifdef CONFIG_QTRONIX_KEYBOARD
# include <linux/module.h>
# include <linux/types.h>
# include <linux/pci.h>
# include <linux/kernel.h>
# include <asm/it8172/it8172.h>
# include <asm/it8172/it8172_int.h>
# include <asm/it8172/it8172_cir.h>
# include <linux/spinlock.h>
# include <linux/sched.h>
# include <linux/interrupt.h>
# include <linux/tty.h>
# include <linux/mm.h>
# include <linux/signal.h>
# include <linux/init.h>
# include <linux/kbd_ll.h>
# include <linux/delay.h>
# include <linux/poll.h>
# include <linux/miscdevice.h>
# include <linux/slab.h>
# include <linux/kbd_kern.h>
# include <linux/smp_lock.h>
# include <asm/io.h>
# include <linux/pc_keyb.h>
# include <asm/keyboard.h>
# include <linux/bitops.h>
# include <asm/uaccess.h>
# include <asm/irq.h>
# include <asm/system.h>
# define leading1 0
# define leading2 0xF
# define KBD_CIR_PORT 0
# define AUX_RECONNECT 170 /* scancode when ps2 device is plugged (back) in */
static int data_index ;
struct cir_port * cir ;
static unsigned char kbdbytes [ 5 ] ;
static unsigned char cir_data [ 32 ] ; /* we only need 16 chars */
static void kbd_int_handler ( int irq , void * dev_id , struct pt_regs * regs ) ;
static int handle_data ( unsigned char * p_data ) ;
static inline void handle_mouse_event ( unsigned char scancode ) ;
static inline void handle_keyboard_event ( unsigned char scancode , int down ) ;
static int __init psaux_init ( void ) ;
static struct aux_queue * queue ; /* Mouse data buffer. */
static int aux_count = 0 ;
/*
* Keys accessed through the ' Fn ' key
* The Fn key does not produce a key - up sequence . So , the first
* time the user presses it , it will be key - down event . The key
* stays down until the user presses it again .
*/
# define NUM_FN_KEYS 56
static unsigned char fn_keys [ NUM_FN_KEYS ] = {
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 0 7 */
8 , 9 , 10 , 93 , 0 , 0 , 0 , 0 , /* 8 15 */
0 , 0 , 0 , 0 , 0 , 0 , 0 , 5 , /* 16 23 */
6 , 7 , 91 , 0 , 0 , 0 , 0 , 0 , /* 24 31 */
0 , 0 , 0 , 0 , 0 , 2 , 3 , 4 , /* 32 39 */
92 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , /* 40 47 */
0 , 0 , 0 , 0 , 11 , 0 , 94 , 95 /* 48 55 */
} ;
void __init init_qtronix_990P_kbd ( void )
{
int retval ;
cir = ( struct cir_port * ) kmalloc ( sizeof ( struct cir_port ) , GFP_KERNEL ) ;
if ( ! cir ) {
printk ( " Unable to initialize Qtronix keyboard \n " ) ;
return ;
}
/*
* revisit
* this should be programmable , somehow by the , by the user .
*/
cir - > port = KBD_CIR_PORT ;
cir - > baud_rate = 0x1d ;
cir - > rdwos = 0 ;
cir - > rxdcr = 0x3 ;
cir - > hcfs = 0 ;
cir - > fifo_tl = 0 ;
cir - > cfq = 0x1d ;
cir_port_init ( cir ) ;
retval = request_irq ( IT8172_CIR0_IRQ , kbd_int_handler ,
( unsigned long ) ( SA_INTERRUPT | SA_SHIRQ ) ,
( const char * ) " Qtronix IR Keyboard " , ( void * ) cir ) ;
if ( retval ) {
printk ( " unable to allocate cir %d irq %d \n " ,
cir - > port , IT8172_CIR0_IRQ ) ;
}
# ifdef CONFIG_PSMOUSE
psaux_init ( ) ;
# endif
}
static inline unsigned char BitReverse ( unsigned short key )
{
unsigned char rkey = 0 ;
rkey | = ( key & 0x1 ) < < 7 ;
rkey | = ( key & 0x2 ) < < 5 ;
rkey | = ( key & 0x4 ) < < 3 ;
rkey | = ( key & 0x8 ) < < 1 ;
rkey | = ( key & 0x10 ) > > 1 ;
rkey | = ( key & 0x20 ) > > 3 ;
rkey | = ( key & 0x40 ) > > 5 ;
rkey | = ( key & 0x80 ) > > 7 ;
return rkey ;
}
static inline u_int8_t UpperByte ( u_int8_t data )
{
return ( data > > 4 ) ;
}
static inline u_int8_t LowerByte ( u_int8_t data )
{
return ( data & 0xF ) ;
}
int CheckSumOk ( u_int8_t byte1 , u_int8_t byte2 ,
u_int8_t byte3 , u_int8_t byte4 , u_int8_t byte5 )
{
u_int8_t CheckSum ;
CheckSum = ( byte1 & 0x0F ) + byte2 + byte3 + byte4 + byte5 ;
if ( LowerByte ( UpperByte ( CheckSum ) + LowerByte ( CheckSum ) ) ! = UpperByte ( byte1 ) )
return 0 ;
else
return 1 ;
}
static void kbd_int_handler ( int irq , void * dev_id , struct pt_regs * regs )
{
struct cir_port * cir ;
int j ;
unsigned char int_status ;
cir = ( struct cir_port * ) dev_id ;
int_status = get_int_status ( cir ) ;
if ( int_status & 0x4 ) {
clear_fifo ( cir ) ;
return ;
}
while ( cir_get_rx_count ( cir ) ) {
cir_data [ data_index ] = cir_read_data ( cir ) ;
if ( data_index = = 0 ) { /* expecting first byte */
if ( cir_data [ data_index ] ! = leading1 ) {
//printk("!leading byte %x\n", cir_data[data_index]);
set_rx_active ( cir ) ;
clear_fifo ( cir ) ;
continue ;
}
}
if ( data_index = = 1 ) {
if ( ( cir_data [ data_index ] & 0xf ) ! = leading2 ) {
set_rx_active ( cir ) ;
data_index = 0 ; /* start over */
clear_fifo ( cir ) ;
continue ;
}
}
if ( ( cir_data [ data_index ] = = 0xff ) ) { /* last byte */
//printk("data_index %d\n", data_index);
set_rx_active ( cir ) ;
#if 0
for ( j = 0 ; j < = data_index ; j + + ) {
printk ( " rx_data %d: %x \n " , j , cir_data [ j ] ) ;
}
# endif
data_index = 0 ;
handle_data ( cir_data ) ;
return ;
}
else if ( data_index > 16 ) {
set_rx_active ( cir ) ;
#if 0
printk ( " warning: data_index %d \n " , data_index ) ;
for ( j = 0 ; j < = data_index ; j + + ) {
printk ( " rx_data %d: %x \n " , j , cir_data [ j ] ) ;
}
# endif
data_index = 0 ;
clear_fifo ( cir ) ;
return ;
}
data_index + + ;
}
}
# define NUM_KBD_BYTES 5
static int handle_data ( unsigned char * p_data )
{
u_int32_t bit_bucket ;
u_int32_t i , j ;
u_int32_t got_bits , next_byte ;
int down = 0 ;
/* Reorganize the bit stream */
for ( i = 0 ; i < 16 ; i + + )
p_data [ i ] = BitReverse ( ~ p_data [ i ] ) ;
/*
* We ' ve already previously checked that p_data [ 0 ]
* is equal to leading1 and that ( p_data [ 1 ] & 0xf )
* is equal to leading2 . These twelve bits are the
* leader code . We can now throw them away ( the 12
* bits ) and continue parsing the stream .
*/
bit_bucket = p_data [ 1 ] < < 12 ;
got_bits = 4 ;
next_byte = 2 ;
/*
* Process four bits at a time
*/
for ( i = 0 ; i < NUM_KBD_BYTES ; i + + ) {
kbdbytes [ i ] = 0 ;
for ( j = 0 ; j < 8 ; j + + ) /* 8 bits per byte */
{
if ( got_bits < 4 ) {
bit_bucket | = ( p_data [ next_byte + + ] < < ( 8 - got_bits ) ) ;
got_bits + = 8 ;
}
if ( ( bit_bucket & 0xF000 ) = = 0x8000 ) {
/* Convert 1000b to 1 */
kbdbytes [ i ] = 0x80 | ( kbdbytes [ i ] > > 1 ) ;
got_bits - = 4 ;
bit_bucket = bit_bucket < < 4 ;
}
else if ( ( bit_bucket & 0xC000 ) = = 0x8000 ) {
/* Convert 10b to 0 */
kbdbytes [ i ] = kbdbytes [ i ] > > 1 ;
got_bits - = 2 ;
bit_bucket = bit_bucket < < 2 ;
}
else {
/* bad serial stream */
return 1 ;
}
if ( next_byte > 16 ) {
//printk("error: too many bytes\n");
return 1 ;
}
}
}
if ( ! CheckSumOk ( kbdbytes [ 0 ] , kbdbytes [ 1 ] ,
kbdbytes [ 2 ] , kbdbytes [ 3 ] , kbdbytes [ 4 ] ) ) {
//printk("checksum failed\n");
return 1 ;
}
if ( kbdbytes [ 1 ] & 0x08 ) {
//printk("m: %x %x %x\n", kbdbytes[1], kbdbytes[2], kbdbytes[3]);
handle_mouse_event ( kbdbytes [ 1 ] ) ;
handle_mouse_event ( kbdbytes [ 2 ] ) ;
handle_mouse_event ( kbdbytes [ 3 ] ) ;
}
else {
if ( kbdbytes [ 2 ] = = 0 ) down = 1 ;
#if 0
if ( down )
printk ( " down %d \n " , kbdbytes [ 3 ] ) ;
else
printk ( " up %d \n " , kbdbytes [ 3 ] ) ;
# endif
handle_keyboard_event ( kbdbytes [ 3 ] , down ) ;
}
return 0 ;
}
DEFINE_SPINLOCK ( kbd_controller_lock ) ;
static unsigned char handle_kbd_event ( void ) ;
int kbd_setkeycode ( unsigned int scancode , unsigned int keycode )
{
printk ( " kbd_setkeycode scancode %x keycode %x \n " , scancode , keycode ) ;
return 0 ;
}
int kbd_getkeycode ( unsigned int scancode )
{
return scancode ;
}
int kbd_translate ( unsigned char scancode , unsigned char * keycode ,
char raw_mode )
{
static int prev_scancode = 0 ;
if ( scancode = = 0x00 | | scancode = = 0xff ) {
prev_scancode = 0 ;
return 0 ;
}
/* todo */
if ( ! prev_scancode & & scancode = = 160 ) { /* Fn key down */
//printk("Fn key down\n");
prev_scancode = 160 ;
return 0 ;
}
else if ( prev_scancode & & scancode = = 160 ) { /* Fn key up */
//printk("Fn key up\n");
prev_scancode = 0 ;
return 0 ;
}
/* todo */
if ( prev_scancode = = 160 ) {
if ( scancode < = NUM_FN_KEYS ) {
* keycode = fn_keys [ scancode ] ;
//printk("fn keycode %d\n", *keycode);
}
else
return 0 ;
}
else if ( scancode < = 127 ) {
* keycode = scancode ;
}
else
return 0 ;
return 1 ;
}
char kbd_unexpected_up ( unsigned char keycode )
{
//printk("kbd_unexpected_up\n");
return 0 ;
}
static unsigned char kbd_exists = 1 ;
static inline void handle_keyboard_event ( unsigned char scancode , int down )
{
kbd_exists = 1 ;
handle_scancode ( scancode , down ) ;
tasklet_schedule ( & keyboard_tasklet ) ;
}
void kbd_leds ( unsigned char leds )
{
}
/* dummy */
void kbd_init_hw ( void )
{
}
static inline void handle_mouse_event ( unsigned char scancode )
{
if ( scancode = = AUX_RECONNECT ) {
queue - > head = queue - > tail = 0 ; /* Flush input queue */
// __aux_write_ack(AUX_ENABLE_DEV); /* ping the mouse :) */
return ;
}
if ( aux_count ) {
int head = queue - > head ;
queue - > buf [ head ] = scancode ;
head = ( head + 1 ) & ( AUX_BUF_SIZE - 1 ) ;
if ( head ! = queue - > tail ) {
queue - > head = head ;
kill_fasync ( & queue - > fasync , SIGIO , POLL_IN ) ;
wake_up_interruptible ( & queue - > proc_list ) ;
}
}
}
static unsigned char get_from_queue ( void )
{
unsigned char result ;
unsigned long flags ;
spin_lock_irqsave ( & kbd_controller_lock , flags ) ;
result = queue - > buf [ queue - > tail ] ;
queue - > tail = ( queue - > tail + 1 ) & ( AUX_BUF_SIZE - 1 ) ;
spin_unlock_irqrestore ( & kbd_controller_lock , flags ) ;
return result ;
}
static inline int queue_empty ( void )
{
return queue - > head = = queue - > tail ;
}
static int fasync_aux ( int fd , struct file * filp , int on )
{
int retval ;
//printk("fasync_aux\n");
retval = fasync_helper ( fd , filp , on , & queue - > fasync ) ;
if ( retval < 0 )
return retval ;
return 0 ;
}
/*
* Random magic cookie for the aux device
*/
# define AUX_DEV ((void *)queue)
static int release_aux ( struct inode * inode , struct file * file )
{
fasync_aux ( - 1 , file , 0 ) ;
aux_count - - ;
return 0 ;
}
static int open_aux ( struct inode * inode , struct file * file )
{
if ( aux_count + + ) {
return 0 ;
}
queue - > head = queue - > tail = 0 ; /* Flush input queue */
return 0 ;
}
/*
* Put bytes from input queue to buffer .
*/
static ssize_t read_aux ( struct file * file , char * buffer ,
size_t count , loff_t * ppos )
{
DECLARE_WAITQUEUE ( wait , current ) ;
ssize_t i = count ;
unsigned char c ;
if ( queue_empty ( ) ) {
if ( file - > f_flags & O_NONBLOCK )
return - EAGAIN ;
add_wait_queue ( & queue - > proc_list , & wait ) ;
repeat :
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( queue_empty ( ) & & ! signal_pending ( current ) ) {
schedule ( ) ;
goto repeat ;
}
current - > state = TASK_RUNNING ;
remove_wait_queue ( & queue - > proc_list , & wait ) ;
}
while ( i > 0 & & ! queue_empty ( ) ) {
c = get_from_queue ( ) ;
put_user ( c , buffer + + ) ;
i - - ;
}
if ( count - i ) {
struct inode * inode = file - > f_dentry - > d_inode ;
inode - > i_atime = current_fs_time ( inode - > i_sb ) ;
return count - i ;
}
if ( signal_pending ( current ) )
return - ERESTARTSYS ;
return 0 ;
}
/*
* Write to the aux device .
*/
static ssize_t write_aux ( struct file * file , const char * buffer ,
size_t count , loff_t * ppos )
{
/*
* The ITE boards this was tested on did not have the
* transmit wires connected .
*/
return count ;
}
static unsigned int aux_poll ( struct file * file , poll_table * wait )
{
poll_wait ( file , & queue - > proc_list , wait ) ;
if ( ! queue_empty ( ) )
return POLLIN | POLLRDNORM ;
return 0 ;
}
struct file_operations psaux_fops = {
. read = read_aux ,
. write = write_aux ,
. poll = aux_poll ,
. open = open_aux ,
. release = release_aux ,
. fasync = fasync_aux ,
} ;
/*
* Initialize driver .
*/
static struct miscdevice psaux_mouse = {
PSMOUSE_MINOR , " psaux " , & psaux_fops
} ;
static int __init psaux_init ( void )
{
int retval ;
retval = misc_register ( & psaux_mouse ) ;
if ( retval < 0 )
return retval ;
queue = ( struct aux_queue * ) kmalloc ( sizeof ( * queue ) , GFP_KERNEL ) ;
2005-03-10 20:34:03 +03:00
if ( ! queue ) {
misc_deregister ( & psaux_mouse ) ;
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
memset ( queue , 0 , sizeof ( * queue ) ) ;
queue - > head = queue - > tail = 0 ;
init_waitqueue_head ( & queue - > proc_list ) ;
return 0 ;
}
module_init ( init_qtronix_990P_kbd ) ;
# endif