/*******************************************************************
  Components for embedded applications builded for
  laboratory and medical instruments firmware  (COLAMI)
 
  kbd_mx1sl.c - periodically scanned keyboard MicroWindows driver
 
  Copyright holders and project originators
    (C) 2001-2004 by Pavel Pisa pisa@cmp.felk.cvut.cz
    (C) 2002-2004 by PiKRON Ltd. http://www.pikron.com

 The COLAMI components can be used and copied under next licenses
   - MPL - Mozilla Public License
   - GPL - GNU Public License
   - LGPL - Lesser GNU Public License
   - and other licenses added by project originators
 Code can be modified and re-distributed under any combination
 of the above listed licenses. If contributor does not agree with
 some of the licenses, he can delete appropriate line.
 Warning, if you delete all lines, you are not allowed to
 distribute code or build project.
 *******************************************************************/

#include <string.h>
#include <mwtypes.h>
#include "device.h"

#define WITH_RTEMS
#undef  DEBUG

#ifndef WITH_RTEMS

#include <types.h>
#include <cpu_def.h>
#include <system_def.h>
#include <mx1_reg.h>

#define KEY_TIMER msec_time
#define KEY_PUSH_T	20
#define KEY_RELEASE_T	10
#define KEY_REPFIRST_T	700
#define KEY_REPNEXT_T	200
typedef long mx1_kbd_interval_t;

#define KBD_DR _reg_PTD_DR
#define KBD_SSR _reg_PTD_SSR
#define KBD_DDIR _reg_PTD_DDIR
#define KBD_PUEN _reg_PTD_PUEN

typedef unsigned long kbdisr_lock_level_t;
#define kbdisr_lock   save_and_cli
#define	kbdisr_unlock restore_flags

#else /*WITH_RTEMS*/

#include <rtems.h>
#include <rtems/io.h>
#include <rtems/libio.h>
#include <rtems/error.h>
#include <rtems/mw_uid.h>
#include <stdio.h>
#include <irq.h>
#include <inttypes.h>
#include <mc9328mxl.h>

#define KEY_TIMER  ({rtems_interval ticks=0; \
	  rtems_clock_get(RTEMS_CLOCK_GET_TICKS_SINCE_BOOT,&ticks); \
	  ticks; \
	})
/*to obtain tick resolution use RTEMS_CLOCK_GET_TICKS_PER_SECOND*/
/*rtems/config.h: rtems_configuration_get_microseconds_per_tick()*/
#define KEY_PUSH_T	20
#define KEY_RELEASE_T	10
#define KEY_REPFIRST_T	700
#define KEY_REPNEXT_T	200
typedef rtems_interval mx1_kbd_interval_t;

#define KBD_DR      MC9328MXL_GPIOD_DR
#define KBD_SSR     MC9328MXL_GPIOD_SSR
#define KBD_DDIR    MC9328MXL_GPIOD_DDIR
#define KBD_PUEN    MC9328MXL_GPIOD_PUEN
#define KBD_IMR     MC9328MXL_GPIOD_IMR
#define KBD_ISR     MC9328MXL_GPIOD_ISR
#define KBD_ICR1    MC9328MXL_GPIOD_ICR1
#define KBD_ICR2    MC9328MXL_GPIOD_ICR2
#define KBD_IRQ_NUM BSP_INT_GPIO_PORTD

#define KBD_ISR_EVENT RTEMS_EVENT_26

typedef rtems_interrupt_level kbdisr_lock_level_t;
#define kbdisr_lock   rtems_interrupt_disable
#define	kbdisr_unlock rtems_interrupt_enable

#endif /*WITH_RTEMS*/


/*
 * MX_KBD matrix is 3 inputs x 8 outputs.
 * Outputs are PD23 to PD30
 * Inputs are PD20 to PD22.
 */

#define KBD_SCAN_CNT  8
#define KBD_SCAN_BIT0 23
#define KBD_RET_CNT   3
#define KBD_RET_BIT0  20

#define KBD_SCAN_MASK (((1<<KBD_SCAN_CNT)-1)<<KBD_SCAN_BIT0)
#define KBD_RET_MASK  (((1<<KBD_RET_CNT)-1)<<KBD_RET_BIT0)

/*
 *-----------------------------------------------------------
 */


static inline
unsigned char mx1_kbd_onerow(unsigned char scan)
{
	kbdisr_lock_level_t level;
	unsigned int scan_mask=KBD_SCAN_MASK;
	unsigned int scan_val=(~scan<<KBD_SCAN_BIT0) & scan_mask;
	unsigned int ret;
	int delay=10;

	kbdisr_lock(level);
	
	KBD_DR|=scan_val;
	KBD_DR&=scan_val|~scan_mask;
	kbdisr_unlock(level);

	while(delay--)
		ret=KBD_SSR;
	ret=KBD_SSR;

	kbdisr_lock(level);
	KBD_DR |= scan_mask;
	kbdisr_unlock(level);

	
	return (~ret>>KBD_RET_BIT0)&((1<<KBD_RET_CNT)-1);
}

static inline
void mx1_kbd_setio(void)
{
	kbdisr_lock_level_t level;
	unsigned int scan_mask=KBD_SCAN_MASK;
	unsigned int ret_mask=KBD_RET_MASK;

	kbdisr_lock(level);
	
	KBD_DR|=scan_mask;
	KBD_DDIR|=scan_mask;
	KBD_DDIR&=~ret_mask;
	KBD_PUEN|=ret_mask;

	kbdisr_unlock(level);
}


typedef struct {
	MWKEY bc;
	MWKEY sc;
} scan2mwkey_t;

typedef struct {
	int scan;
	int flag;
	MWKEYMOD is_mod;
	MWKEYMOD set_mod;
	MWKEYMOD xor_mod;
} scan2mwmod_t;

#define MWKMOD_SGM_SC		0x8000
#define MWKMOD_SGM_RELEASE	0x0080

#if 0

const scan2mwkey_t mx1_kbd_scan2mwkey_basic[]={
	[0x00]={0,0},
	[0x21]={MWKEY_F1,0},
	[0x22]={MWKEY_F2,0},
	[0x23]={MWKEY_F3,0},
	[0x24]={MWKEY_F4,0},
	[0x2c]={MWKEY_F5,0},
	[0x25]={MWKEY_F6,0},
	[0x26]={MWKEY_F7,0},
	[0x27]={MWKEY_F8,0},
	[0x28]={MWKEY_F9,0},
	[0x04]={MWKEY_KP0,MWKEY_INSERT},
	[0x03]={MWKEY_KP1,MWKEY_END},
	[0x0b]={MWKEY_KP2,MWKEY_DOWN},
	[0x13]={MWKEY_KP3,MWKEY_PAGEDOWN},
	[0x02]={MWKEY_KP4,MWKEY_LEFT},
	[0x0a]={MWKEY_KP5,MWKEY_BACKSPACE},
	[0x12]={MWKEY_KP6,MWKEY_RIGHT},
	[0x01]={MWKEY_KP7,MWKEY_HOME},
	[0x09]={MWKEY_KP8,MWKEY_UP},
	[0x11]={MWKEY_KP9,MWKEY_PAGEUP},
	[0x0c]={MWKEY_KP_PERIOD,MWKEY_DELETE},
	[0x14]={MWKEY_ENTER,MWKEY_ESCAPE},
	[0x16]={MWKEY_ENTER,MWKEY_ESCAPE},
};

const scan2mwmod_t mx1_kbd_scan2mwmod_basic[]={
	{0x28,0,0,MWKMOD_LSHIFT|MWKMOD_SGM_SC,0},
	{0,0,0,0}
};

#endif

const scan2mwkey_t mx1_kbd_scan2mwkey_basic[]={
	[0x00]={0,0},
	[0x0b]={'0',0},
	[0x01]={'1',0},
	[0x02]={'2',0},
	[0x03]={'3',0},
	[0x04]={'4',0},
	[0x05]={'5',0},
	[0x06]={'6',0},
	[0x07]={'7',0},
	[0x08]={'8',0},
	[0x09]={'9',0},
	[0x0a]={'.',0},
	[0x0c]={MWKEY_ESCAPE,0},

	[0x0f]={MWKEY_UP,0},
	[0x13]={MWKEY_DOWN,0},
	[0x11]={MWKEY_ENTER,0},

	[0x0d]={MWKEY_F1,0},
	[0x0e]={MWKEY_F2,0},
	[0x10]={MWKEY_F3,0},	/*MWKEY_LEFT*/
	[0x12]={MWKEY_F4,0},	/*MWKEY_RIGHT*/
	[0x14]={MWKEY_F5,0},
	[0x15]={MWKEY_F6,0},
	
	[0x16]={MWKEY_SUSPEND,0},
};

const scan2mwmod_t mx1_kbd_scan2mwmod_basic[]={
	{0,0,0,0}
};

scan2mwkey_t *mx1_kbd_scan2mwkey_tab=(scan2mwkey_t*)mx1_kbd_scan2mwkey_basic;
scan2mwmod_t *mx1_kbd_scan2mwmod_tab=(scan2mwmod_t*)mx1_kbd_scan2mwmod_basic;

/* State of keyboard matrix and key press reporting */

static unsigned char key_down_arr[KBD_SCAN_CNT];
static unsigned char key_chng_arr[KBD_SCAN_CNT];
static unsigned char key_hit;

static MWKEYMOD key_mod;

static int key_last_changed;

/* Internal state for repeat processing */

static short key_use_timer;
static short key_state;
static mx1_kbd_interval_t key_time;

#define KEY_STATE_IDLE     0
#define KEY_STATE_PUSH     1
#define KEY_STATE_RELEASE  2
#define KEY_STATE_REPEAT   4
#define KEY_STATE_NOISE    8
#define KEY_STATE_BUSY     (KEY_STATE_PUSH|KEY_STATE_RELEASE)


/**
 * mx1_kbd_scan - Scan keyboard matrix and report requests for state change
 *
 * Scans keyboard matrix connected row by row by calling function
 * mx1_kbd_onerow(). Number of scanned output lines is defined
 * by %KBD_SCAN_CNT. Checks read keyboard state against @key_down_arr
 * and updates @key_change_arr array. The @key_down_arr state is 
 * left unchanged. It is changed later by mx1_kbd_down() function.
 * Returns 0, if no keyboard activity is found. Returns 1
 * if at least one key is pressed. Returns 2 or 3 in case
 * of detected change.
 */
int 
mx1_kbd_scan()
{
	int i, ret=0;
	unsigned char mask, val, chng;
	for(i=0,mask=1;i<KBD_SCAN_CNT;i++,mask<<=1) {
		val=mx1_kbd_onerow(mask);
		chng=val^key_down_arr[i];
		key_chng_arr[i]=chng;
		if(val) ret|=1;
		if(chng) ret|=2;
	}
	/* mx1_kbd_onerow(~0); */
	return ret;
}


/**
 * mx1_kbd_scan2mod - Propagate keyboard matrix changes between modifiers
 * @scan_code:		Scan code of last detected key change
 *
 * Functions check keyboard matrix state in @key_down_arr.
 * It updates @key_mod according to @key_down_arr and 
 * modifiers transformations table @mx1_kbd_scan2mwmod_tab.
 */
void 
mx1_kbd_scan2mod(int scan_code)
{
	unsigned char val, chng;
	int s;
	scan2mwmod_t *mt=mx1_kbd_scan2mwmod_tab;

	for(;(s=mt->scan);mt++) {
		chng=(s==scan_code);
		s--;
		val=key_down_arr[s/KBD_RET_CNT]&(1<<(s%KBD_RET_CNT));
		if(val) {
			key_mod|=mt->set_mod;
			if(chng){
				key_mod^=mt->xor_mod;
			}
		} else {
			key_mod&=~mt->set_mod;
		}
	}
}

/**
 * mx1_kbd_down - Detects changed key scancode and applies changes to matrix state
 *
 * Functions check @key_chng_arr and process changes.
 * It updates its internal state @key_state, does
 * noise cancellation and repeat timing, then updates 
 * @key_down_arr, stores detected scancode to @key_last_changed
 * and calls modifiers processing mx1_kbd_scan2mod().
 * Return value is zero if no change is detected. 
 * In other case evaluated scancode is returned.
 * Variable @key_hit signals by value 1 pressed key, by value
 * 2 key release.
 */
int
mx1_kbd_down()
{
	int i, j=0;
	unsigned char val;
	
        if(!(key_state&KEY_STATE_BUSY)){
		for(i=0;i<KBD_SCAN_CNT;i++) {
			if(!(val=key_chng_arr[i])) continue;
			for(j=0;!(val&1);j++) val>>=1;
			key_last_changed=i*KBD_RET_CNT+j+1;
			if(key_down_arr[i]&(1<<j)){
				key_time=KEY_TIMER+KEY_PUSH_T;
				key_state=KEY_STATE_RELEASE;
			}else{
				key_time=KEY_TIMER+KEY_RELEASE_T;
				key_state=KEY_STATE_PUSH;
			}
			break;
		}
		if(key_state==KEY_STATE_IDLE)
			return 0;
	} else {
		if(!key_last_changed){
			key_state=KEY_STATE_IDLE;
	  		return 0;
		}
		i=(key_last_changed-1)/KBD_RET_CNT;
		j=(key_last_changed-1)%KBD_RET_CNT;
		if(!(key_chng_arr[i]&(1<<j))){
			/* Noise detected */
			if(!(key_state&KEY_STATE_NOISE)){
			        key_time=KEY_TIMER+KEY_RELEASE_T;
			        key_state|=KEY_STATE_NOISE;
			}
		}
	}

	if(!key_use_timer){
		if(KEY_TIMER) key_use_timer=1;
		if(key_state&KEY_STATE_REPEAT) return 0;
	}else{
		if((long)(KEY_TIMER-key_time)<0) return 0;
	}
	
	if(key_state==KEY_STATE_PUSH) {
		key_down_arr[i]|=1<<j;
		mx1_kbd_scan2mod(key_last_changed);
		key_state=KEY_STATE_REPEAT;
		key_time=KEY_TIMER+KEY_REPFIRST_T;
		key_hit=1;
		return key_last_changed;
	} else if(key_state==KEY_STATE_REPEAT) {
		key_time=KEY_TIMER+KEY_REPNEXT_T;
		key_hit=1;
		return key_last_changed;
	} else if(key_state==KEY_STATE_RELEASE) {
		key_down_arr[i]&=~(1<<j);
		mx1_kbd_scan2mod(key_last_changed);
	        key_state=KEY_STATE_IDLE;
		key_hit=2;
		return key_last_changed;
	} 
	key_state=KEY_STATE_IDLE;
	return 0;
}

/**
 * mx1_kbd_scan2mwkey - Converts scancode to MWKEY keyboard values
 * @scan:	Detected scancode
 *
 * Computes MWKEY value for detected scancode.
 * Uses @mx1_kbd_scan2mwkey_tab transformation table
 * and @key_mod modifiers information.
 */
MWKEY mx1_kbd_scan2mwkey(int scan)
{
	if((key_mod&MWKMOD_SGM_SC)&&mx1_kbd_scan2mwkey_tab[scan].sc)
		return mx1_kbd_scan2mwkey_tab[scan].sc;
	return mx1_kbd_scan2mwkey_tab[scan].bc;
}

/*
 *-----------------------------------------------------------
 */

#ifdef WITH_RTEMS

static rtems_id mx1_kbd_task=(rtems_id)0;

static rtems_device_driver mx1_kbd_init(rtems_device_major_number,rtems_device_minor_number,void *);
static rtems_device_driver mx1_kbd_open(rtems_device_major_number,rtems_device_minor_number,void *);
static rtems_device_driver mx1_kbd_close(rtems_device_major_number,rtems_device_minor_number,void *);
static rtems_device_driver mx1_kbd_read(rtems_device_major_number,rtems_device_minor_number,void *);
static rtems_device_driver mx1_kbd_write(rtems_device_major_number,rtems_device_minor_number,void *);
static rtems_device_driver mx1_kbd_ioctl(rtems_device_major_number,rtems_device_minor_number,void *);

static rtems_device_major_number mx1_kbd_major;
static rtems_driver_address_table mx1_kbd_driver_table={
		.initialization_entry= mx1_kbd_init, /* initialization procedure */
		.open_entry= mx1_kbd_open,	/* open request procedure */
		.close_entry= mx1_kbd_close,	/* close request procedure */
		.read_entry= mx1_kbd_read,	/* read request procedure */
		.write_entry= mx1_kbd_write,	/* write request procedure */
		.control_entry= mx1_kbd_ioctl,	/* special functions procedure */
	};

static rtems_isr mx1_kbd_isr(rtems_vector_number vector);
static void mx1_kbd_isr_on(const rtems_irq_connect_data *unused);
static void mx1_kbd_isr_off(const rtems_irq_connect_data *unused);
static int mx1_kbd_isr_is_on(const rtems_irq_connect_data *irq);


/* Replace the first value with the clock's interrupt name. */
rtems_irq_connect_data mx1_kbd_isr_data = {
		KBD_IRQ_NUM,
		(rtems_irq_hdl)mx1_kbd_isr,
		mx1_kbd_isr_on,
		mx1_kbd_isr_off,
		mx1_kbd_isr_is_on,
		3,	/* unused for ARM */
		0	/* unused for ARM */
	};  


static void (*mx1_kbd_parser_fnc)( void *ptr, unsigned short keycode, unsigned long mods);
static rtems_id mx1_kbd_queue_id;


/* Enables mx1_kbd interrupts. */
static void 
mx1_kbd_isr_on(const rtems_irq_connect_data *unused)
{
	/* Enable interrupts */
	MC9328MXL_AITC_INTENNUM = KBD_IRQ_NUM;

	return;
}

/* Disables enet interrupts */
static void
mx1_kbd_isr_off(const rtems_irq_connect_data *unused)
{
	/* disable all various TX/RX interrupts */
	MC9328MXL_AITC_INTDISNUM = KBD_IRQ_NUM;

	return;
}

/* Tests to see if mx1_kbd interrupts are enabled, and returns non-0 if so.
 * If interrupt is not enabled, returns 0.
 */
static int
mx1_kbd_isr_is_on(const rtems_irq_connect_data *irq)
{

	if(KBD_IRQ_NUM<32)
		return MC9328MXL_AITC_INTENABLEL & (1 << (KBD_IRQ_NUM));
	else
		return MC9328MXL_AITC_INTENABLEH & (1 << (KBD_IRQ_NUM - 32));
}

/* interrupt handler */
static rtems_isr
mx1_kbd_isr(rtems_vector_number v)
{
	kbdisr_lock_level_t level;

	kbdisr_lock(level);
	KBD_ISR |= KBD_RET_MASK;
	KBD_IMR &= ~KBD_RET_MASK;
	kbdisr_unlock(level);
	
	rtems_event_send(mx1_kbd_task, KBD_ISR_EVENT);
}

static rtems_task
mx1_kbd_task_proc(rtems_task_argument argument)
{
	kbdisr_lock_level_t level;
	rtems_event_set   events;
	rtems_interval    ticks;
	rtems_status_code status;
	MWKEY keycode;
	MWKEYMOD modifiers;
	MWSCANCODE scancode;
	
	while (1) {

		if(!key_hit) {
			if(mx1_kbd_scan() || (key_state!=KEY_STATE_IDLE)){
				mx1_kbd_down();
			}
		}

		if(key_hit){
			modifiers = key_mod&~MWKMOD_SGM_SC;
			scancode = key_last_changed;
			keycode = mx1_kbd_scan2mwkey(key_last_changed);
			
		    #ifdef DEBUG
			printf("MXKB: hit %d key %2d mod 0x%04x scan %2d\n",
				key_hit, keycode, modifiers, scancode);
		    #endif /*DEBUG*/
			if(mx1_kbd_parser_fnc){
				if(key_hit==2)
					modifiers |= MWKMOD_SGM_RELEASE;
				mx1_kbd_parser_fnc(NULL, keycode, modifiers);
			}

			key_hit = 0;
			continue;
		}
	
		
		if(key_state==KEY_STATE_IDLE){
			uint32_t press;

			ticks = RTEMS_NO_TIMEOUT;

			kbdisr_lock(level);
			KBD_DR &= ~KBD_SCAN_MASK;
			KBD_IMR |= KBD_RET_MASK;
			press = KBD_SSR;
			kbdisr_unlock(level);

			if(~press&KBD_RET_MASK)
				ticks=50;
		}else{
			ticks=key_time-KEY_TIMER;
			if((long)ticks<=0)
				ticks=10;
			if(ticks>50)
				ticks=50;
		}
		
		status=rtems_event_receive (KBD_ISR_EVENT, RTEMS_EVENT_ANY|RTEMS_WAIT, ticks, &events);


	    #ifdef DEBUG
		if(0){
			static int i=0;
			if(status==RTEMS_SUCCESSFUL)
				printf("K%d.%d\n",i,key_state);
			else
				printf("_%d.%d\n",i,key_state);
			if(++i>=10)i=0;
		}
	    #endif /*DEBUG*/
		
		kbdisr_lock(level);
		KBD_IMR &= ~KBD_RET_MASK;
		KBD_DR |= KBD_SCAN_MASK;
		kbdisr_unlock(level);
	}
}

static rtems_status_code
mx1_kbd_hw_init(void)
{
	kbdisr_lock_level_t level;
	rtems_status_code  status;
	/* Set for negative edge triggered interupt => mode 1 */
	uint32_t irq_mode=1;
	uint32_t irq_mask=(1<<(2*KBD_RET_CNT))-1;
	irq_mode = (irq_mode*0x55555555)&irq_mask;
	int kbd_ret_bit0 = KBD_RET_BIT0;
	
	mx1_kbd_setio();
	
	/* Set for negative edge triggered interupt => mode 1 */
	KBD_ICR1 &= ~(irq_mask<<(kbd_ret_bit0*2));
	KBD_ICR1 |= (irq_mode<<(kbd_ret_bit0*2));
	
	if(kbd_ret_bit0<16){
		irq_mask >>= 2*(16-kbd_ret_bit0);
		irq_mode >>= 2*(16-kbd_ret_bit0);
	}else{
		irq_mask <<= 2*(kbd_ret_bit0-16);
		irq_mode <<= 2*(kbd_ret_bit0-16);
	}
	
	KBD_ICR2 &= ~irq_mask;
	KBD_ICR2 |= irq_mode;

	if(!mx1_kbd_task){
	
		status=rtems_task_create (rtems_build_name('M','X','K','B'),
                	/*priority*/ 2,
                	/*stacksize*/ RTEMS_MINIMUM_STACK_SIZE+1024,
                	RTEMS_DEFAULT_MODES /*RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|RTEMS_INTERRUPT_LEVEL(0)*/,
                	RTEMS_DEFAULT_ATTRIBUTES /*RTEMS_NO_FLOATING_POINT|RTEMS_LOCAL*/,
                	&mx1_kbd_task);
        	if (status != RTEMS_SUCCESSFUL){
			printf("MX1_KBD: rtems_task_create %s\n",rtems_status_text(status));
			return status;
		}
		
		/* Install the interrupt handler */
       		if (!BSP_install_rtems_irq_handler(&mx1_kbd_isr_data)){
			printf("MX1_KBD: BSP_install_rtems_irq_handler failed\n");
			return RTEMS_UNSATISFIED;
		}

		status = rtems_task_start (mx1_kbd_task, mx1_kbd_task_proc, (rtems_task_argument)0);
        	if (status != RTEMS_SUCCESSFUL){
			printf("MX1_KBD: rtems_task_start %s\n",rtems_status_text(status));
			return status;
		}

	}

	/*   Enable GPIO port A3 interrupt */
	kbdisr_lock(level);
	KBD_DR &= ~KBD_SCAN_MASK;
	KBD_IMR |= KBD_RET_MASK;
	kbdisr_unlock(level);

	return RTEMS_SUCCESSFUL;

}


static rtems_device_driver 
mx1_kbd_init(rtems_device_major_number major,rtems_device_minor_number minor,void *arg)
{
	/*rtems_libio_open_close_args_t* argp = (rtems_libio_open_close_args_t*)arg;*/
	
	return mx1_kbd_hw_init();  
}

static rtems_device_driver 
mx1_kbd_open(rtems_device_major_number major,rtems_device_minor_number minor,void *arg)
{
	/*rtems_libio_open_close_args_t* argp = (rtems_libio_open_close_args_t*)arg;*/
	
	if(minor!=0)
		return RTEMS_UNSATISFIED;  
	
	return RTEMS_SUCCESSFUL;
}

static rtems_device_driver
mx1_kbd_close(rtems_device_major_number major,rtems_device_minor_number minor,void *arg)
{
	/*rtems_libio_open_close_args_t* argp = (rtems_libio_open_close_args_t*)arg;*/
	
	return RTEMS_SUCCESSFUL;
}

static rtems_device_driver
mx1_kbd_read(rtems_device_major_number major,rtems_device_minor_number minor,void *arg)
{
	rtems_libio_rw_args_t* argp = (rtems_libio_rw_args_t*)arg;
	char *buf = argp->buffer;
	int count = argp->count;
	
	argp->bytes_moved = 0;
	
	return RTEMS_SUCCESSFUL;
}

static rtems_device_driver
mx1_kbd_write(rtems_device_major_number major,rtems_device_minor_number minor,void *arg)
{
	/*rtems_libio_rw_args_t* argp = (rtems_libio_rw_args_t*)arg;*/
	
	return RTEMS_UNSATISFIED;  
}

/* adds a kbd message to the queue */
static void
kbd_parser( void *ptr, unsigned short keycode, unsigned long modifiers)
{
	struct MW_UID_MESSAGE m;

	m.type = MV_UID_KBD;
	m.m.kbd.code       = keycode;
	m.m.kbd.modifiers  = modifiers;
	m.m.kbd.mode       = MV_KEY_MODE_ASCII; /*MV_KEY_MODE_SCANCODE*/
	/*  printf( "kbd: msg: keycode=%X, mod=%X\n", keycode, mods );  */
	rtems_message_queue_send( mx1_kbd_queue_id, ( void * )&m,
                        	  sizeof( struct MW_UID_MESSAGE ) );
}

void kbd_set_driver_handler( void ( *handler )( void *, unsigned short, unsigned long ) )
{
  mx1_kbd_parser_fnc = handler;
}

void
register_kbd_msg_queue( char *q_name, int port )
{
	rtems_name queue_name;
	rtems_status_code status;

	queue_name = rtems_build_name( q_name[0],
                        	       q_name[1],
                        	       q_name[2],
                        	       q_name[3] );
	status = rtems_message_queue_ident( queue_name, RTEMS_LOCAL, &mx1_kbd_queue_id );
	if( status != RTEMS_SUCCESSFUL )
	{
		printf( "UID_Queue: error open queue: %d\n", status );
		return;
	}
	kbd_set_driver_handler( kbd_parser );
}

void
unregister_kbd_msg_queue( int port )
{
	kbd_set_driver_handler( NULL );
}


static rtems_device_driver
mx1_kbd_ioctl(rtems_device_major_number major,rtems_device_minor_number minor,void *arg)
{
	rtems_libio_ioctl_args_t* argp = (rtems_libio_ioctl_args_t*)arg;
	
	switch(argp->command){
	    case MW_UID_REGISTER_DEVICE:
		register_kbd_msg_queue( argp->buffer, -1 );
                break;

	    case MW_UID_UNREGISTER_DEVICE:
		unregister_kbd_msg_queue( -1 );

	    default:
		return RTEMS_INVALID_NAME;
	}


	argp->ioctl_return = 0;
	return RTEMS_SUCCESSFUL;
}


rtems_status_code
mx1_kbd_driver_init(void)
{
	rtems_status_code  status;

	status = rtems_io_register_driver(0,&mx1_kbd_driver_table,&mx1_kbd_major);
        if (status != RTEMS_SUCCESSFUL){
		printf("MX1_KBD: rtems_io_register_driver %s\n",rtems_status_text(status));
		return status;
	}

	status = rtems_io_register_name( "/dev/kbd", mx1_kbd_major, 0);
        if (status != RTEMS_SUCCESSFUL){
		printf("MX1_KBD: rtems_io_register_name %s\n",rtems_status_text(status));
		return status;
	}

	return RTEMS_SUCCESSFUL;
}

#endif /* WITH_RTEMS */


/*
 *-----------------------------------------------------------
 */

#ifndef WITH_RTEMS

static int  mx1_kbd_Open(KBDDEVICE *pkd);
static void mx1_kbd_Close(void);
static void mx1_kbd_GetModifierInfo(MWKEYMOD *modifiers, MWKEYMOD *curmodifiers);
static int  mx1_kbd_Read(MWKEY *buf, MWKEYMOD *modifiers, MWSCANCODE *scancode);
static int  mx1_kbd_Poll(void);

KBDDEVICE kbddev = {
	mx1_kbd_Open,
	mx1_kbd_Close,
	mx1_kbd_GetModifierInfo,
	mx1_kbd_Read,
	mx1_kbd_Poll
};


/**
 * mx1_kbd_scan2mwkey - Open the keyboard
 * @pkd:	Pointer to keyboard device
 */
static int
mx1_kbd_Open(KBDDEVICE *pkd)
{
	key_last_changed=0;
	key_mod=0;
	key_hit=0;
	key_use_timer=0;
	memset(key_down_arr,0,sizeof(key_down_arr));
	mx1_kbd_setio();
	return 0;
}

/**
 * mx1_kbd_Close - Closes keyboard
 */
static void
mx1_kbd_Close(void)
{
}

/**
 * mx1_kbd_Poll - Polls for keyboard events
 *
 * Returns non-zero value if change is detected.
 */
static int
mx1_kbd_Poll(void)
{
	if(key_hit)
		return 1;
	if(mx1_kbd_scan())
		mx1_kbd_down();
	return key_hit?1:0;
}

/**
 * mx1_kbd_GetModifierInfo - Returns the possible modifiers for the keyboard
 * @modifiers:		If non-NULL, ones in defined modifiers bits are returned.
 * @curmodifiers:	If non-NULL, ones in actually active modifiers
 *			bits are returned.
 */
static  void
mx1_kbd_GetModifierInfo(MWKEYMOD *modifiers, MWKEYMOD *curmodifiers)
{
	if (modifiers)
		*modifiers = 0;		/* no modifiers available */
	if (curmodifiers)
		*curmodifiers = key_mod&~MWKMOD_SGM_SC;
}

/**
 * mx1_kbd_Read - Reads resolved MWKEY value, modifiers and scancode
 * @buf:		If non-NULL, resolved MWKEY is stored here
 * @modifiers:		If non-NULL, ones in actually active modifiers
 *			bits are returned
 * @scancode:		If non-NULL, scancode of resolved key is stored
 *			here
 *
 * This function reads one keystroke from the keyboard, and the current state
 * of the modifier keys (ALT, SHIFT, etc).  Returns -1 on error, 0 if no data
 * is ready, 1 on a keypress, and 2 on keyrelease.
 * This is a non-blocking call.
 */
static int
mx1_kbd_Read(MWKEY *buf, MWKEYMOD *modifiers, MWSCANCODE *scancode)
{
        int ret;
	if(!key_hit) {
		if(mx1_kbd_scan()){
			mx1_kbd_down();
		}
	}
	if(modifiers)
		*modifiers = key_mod&~MWKMOD_SGM_SC;
	if(!key_hit)
		return 0;
	if(scancode)
		*scancode = key_last_changed;
	if(buf)
		*buf = mx1_kbd_scan2mwkey(key_last_changed);
	ret=key_hit;
	key_hit=0;
	return ret;
}


#endif /* !WITH_RTEMS */
