/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Generic Keyboard Support: Polled I/O support for kbtrans-supported keyboards.
 */

#define	KEYMAP_SIZE_VARIABLE

#include <sys/types.h>
#include <sys/stream.h>
#include <sys/kbd.h>
#include <sys/kbio.h>
#include <sys/vuid_event.h>
#include <sys/consdev.h>
#include <sys/kbtrans.h>
#include "kbtrans_lower.h"
#include "kbtrans_streams.h"

/*
 * Internal Function Prototypes
 */
static void kbtrans_polled_pressed(struct kbtrans *, uint_t, kbtrans_key_t,
			uint_t);
static void kbtrans_polled_released(struct kbtrans *, kbtrans_key_t);
static void kbtrans_polled_setled(struct kbtrans *);
static void kbtrans_polled_setup_repeat(struct kbtrans *, uint_t,
			kbtrans_key_t);
static void kbtrans_polled_cancel_repeat(struct kbtrans *);

/*
 * Functions to be called when a key is translated during polled
 * mode
 */
struct keyboard_callback kbtrans_polled_callbacks = {
	NULL,				/* keypressed_raw */
	NULL,				/* keyreleased_raw */
	kbtrans_polled_pressed,		/* keypressed */
	kbtrans_polled_released,	/* keyreleased */
	kbtrans_polled_setup_repeat,	/* setup_repeat */
	kbtrans_polled_cancel_repeat,	/* cancel_repeat */
	kbtrans_polled_setled,		/* setled */
};

/*
 * kbtrans_ischar:
 *	Return B_TRUE if character is pending, else return B_FALSE
 */
boolean_t
kbtrans_ischar(struct kbtrans *upper)
{
	struct kbtrans_callbacks *cb;
	struct kbtrans_hardware *hw;
	kbtrans_key_t key;
	enum keystate state;

	/*
	 * If we've still got input pending, say so.
	 */
	if (*upper->kbtrans_polled_pending_chars != '\0') {
		return (B_TRUE);
	}

	/*
	 * Reset to an empty buffer.
	 */
	upper->kbtrans_polled_buf[0] = '\0';
	upper->kbtrans_polled_pending_chars = upper->kbtrans_polled_buf;

	cb = upper->kbtrans_streams_hw_callbacks;
	hw = upper->kbtrans_streams_hw;

	/*
	 * Process scancodes until either we have input ready
	 * or we run out of scancodes.
	 */
	while (cb->kbtrans_polled_keycheck(hw, &key, &state)) {
		kbtrans_processkey(&upper->kbtrans_lower,
			&kbtrans_polled_callbacks, key, state);
		/*
		 * If that generated any input, we're ready.
		 */
		if (*upper->kbtrans_polled_pending_chars != '\0') {
			return (B_TRUE);
		}
	}

	return (B_FALSE);
}

/*
 * kbtrans_getchar:
 * 	Return a character
 */
int
kbtrans_getchar(struct kbtrans *upper)
{
	while (!kbtrans_ischar(upper))
		/* LOOP */;

	return (*upper->kbtrans_polled_pending_chars++);
}

void
kbtrans_polled_putcode(struct kbtrans *upper, char code)
{
	int i;

	/*
	 * NB:  KBTRANS_POLLED_BUF_SIZE is one smaller than
	 * the size of the buffer, to allow for a trailing
	 * null.
	 */
	for (i = 0; i < KBTRANS_POLLED_BUF_SIZE; i++) {
		if (upper->kbtrans_polled_buf[i] == '\0') {
			upper->kbtrans_polled_buf[i] = code;
			upper->kbtrans_polled_buf[i+1] = '\0';
			return;
		}
	}
	DPRINTF(PRINT_L2, PRINT_MASK_PACKET,
		(upper, "kbtrans_polled_pressed:  "
		"buffer overflow, character 0x%x discarded\n", code));
	/*
	 * Didn't fit, throw it on the floor.
	 */
}

/*
 * kbtrans_polled_pressed:
 *	This function is called when we are in polled mode and a key is
 * 	pressed.  The key is put into the kbtrans_polled_buf so that it
 * 	can be picked up later by kbtrans_ischar()
 */
/*ARGSUSED2*/
static void
kbtrans_polled_pressed(
    struct kbtrans *upper,
    uint_t entrytype,
    kbtrans_key_t key,
    uint_t entry)
{
	struct kbtrans_lower	*lower = &upper->kbtrans_lower;
	register char	*cp;

	/*
	 * Based on the type of key, we may need to do some ASCII
	 * specific post processing.
	 */
	switch (entrytype) {

	case BUCKYBITS:
	case SHIFTKEYS:
	case FUNNY:
		/*
		 * There is no ascii equivalent.  We will ignore these
		 * keys
		 */
		break;

	case FUNCKEYS:
		/*
		 * These will no doubt only cause problems.  Ignore them.
		 */

		break;

	case STRING:
		/*
		 * These are the multi byte keys (Home, Up, Down ...)
		 */
		cp = &lower->kbtrans_keystringtab[entry & 0x0F][0];

		/*
		 * Copy the string from the keystringtable, and send it
		 * upstream a character at a time.
		 */
		while (*cp != '\0') {
			kbtrans_polled_putcode(upper, *cp);
			cp++;
		}

		return;

	case PADKEYS:
		/*
		 * These are the keys on the keypad.  Look up the
		 * answer in the kb_numlock_table and send it upstream.
		 */
		kbtrans_polled_putcode(upper,
			lower->kbtrans_numlock_table[entry&0x1F]);

		break;

	case 0:	/* normal character */
	default:
		/*
		 * Send the byte upstream.
		 */
		kbtrans_polled_putcode(upper, (char)entry);
		break;
	}
}

/*
 * kbtrans_polled_released:
 *	This function is called when a key is released.  Nothing is
 * 	done.
 */
/*ARGSUSED*/
static void
kbtrans_polled_released(struct kbtrans *upper, kbtrans_key_t key)
{
	/* Nothing for now */
}

/*
 * kbtrans_polled_setled:
 *	This function is called to set the LEDs.
 */
static void
kbtrans_polled_setled(struct kbtrans *upper)
{
	struct kbtrans_callbacks *cb;
	struct kbtrans_hardware *hw;

	cb = upper->kbtrans_streams_hw_callbacks;
	hw = upper->kbtrans_streams_hw;

	cb->kbtrans_polled_setled(hw, upper->kbtrans_lower.kbtrans_led_state);
}

/*
 * kbtrans_polled_setup_repeat:
 *	Function to be called in order to handle a repeating key.
 *	Nothing is done.
 */
/*ARGSUSED*/
static void
kbtrans_polled_setup_repeat(
    struct kbtrans *upper,
    uint_t entrytype,
    kbtrans_key_t key)
{
	/* Nothing for now */
}

/*
 * kbtrans_polled_cancel_repeat:
 *	Function to be called to cancel a repeating key,
 *	so that we don't end up with an autorepeating key
 * 	on the stream because its release was handled by the
 * 	polled code.
 */
static void
kbtrans_polled_cancel_repeat(struct kbtrans *upper)
{
	/*
	 * Streams code will time out and will discard the
	 * autorepeat.
	 */
	upper->kbtrans_lower.kbtrans_repeatkey = 0;
}