/*
 * Copyright (c) 1995, 1994, 1993, 1992, 1991, 1990  
 * Open Software Foundation, Inc. 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation, and that the name of ("OSF") or Open Software 
 * Foundation not be used in advertising or publicity pertaining to 
 * distribution of the software without specific, written prior permission. 
 *  
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OSF BE LIABLE FOR ANY 
 * SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
 * ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING 
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE 
 */
/*
 * OSF Research Institute MK6.1 (unencumbered) 1/31/1995
 */
/*
 * Mach Operating System
 * Copyright (c) 1992 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
/* CMU_HIST */
/*
 * Revision 2.5  92/05/22  15:47:38  jfriedl
 * 	Got autorepeat to work under X11: just generate two events
 * 	(up+down) instead of the single metronome one that would
 * 	not really work.
 * 	[92/05/20  22:43:35  af]
 * 
 * Revision 2.3.1.1  92/05/04  14:16:54  af
 * 	Fixed to not call autorepeat when invoked from debugger.
 * 	It might be too early (e.g. boothowto=0xc) for timeout().
 * 	[92/05/04            af]
 * 
 * Revision 2.3  92/03/05  17:08:21  rpd
 * 	Changed locators handler to always pass relative coords
 * 	to screen_motion_event.
 * 	[92/03/05            af]
 * 
 * Revision 2.2  92/03/02  18:32:22  rpd
 * 	Created from DEC specs:
 * 	"DESKTOPinterconnect Description and Protocol Specification"
 * 	Version 0.9, Jun 17 1991
 * 	"Open Desktop Bus, Locator Device Protocol Specification"
 * 	Version 0.4, Dec 13 1990
 * 	"Open Desktop Bus, Keyboard Device Protocol Specification"
 * 	Version 0.7, Jan 9 1991
 * 	[92/01/19            af]
 * 
 */
/* CMU_ENDHIST */
/*
 *	File: dtop_handlers.c
 * 	Author: Alessandro Forin, Carnegie Mellon University
 *	Date:	1/92
 *
 *	Handler functions for devices attached to the DESKTOP bus.
 */

#include <dtop.h>
#if	NDTOP > 0

#include <mach_kdb.h>

#include <machine/machparam.h>		/* spl definitions */
#include <sys/types.h>
#include <mach/std_types.h>
#include <device/io_req.h>
#include <device/tty.h>

#include <chips/busses.h>
#include <chips/serial_defs.h>
#include <chips/screen_defs.h>
#include <mips/PMAX/tc.h>

#include <chips/dtop.h>
#include <chips/lk201.h>

/*
 * Default handler function
 */
int
dtop_null_device_handler(
	 dtop_device_t	dev,
	 dtop_message_t	msg,
	 int		event,
	 unsigned char	outc)
{
	/* See if the message was to the default address (powerup) */

	/* Uhmm, donno how to handle this. Drop it */
	if (event == DTOP_EVENT_RECEIVE_PACKET)
		dev->unknown_report = *msg;
	return 0;
}

/*
 * Handler for locator devices (mice)
 */
int
dtop_locator_handler(
	 dtop_device_t	dev,
	 dtop_message_t	msg,
	 int		event,
	 unsigned char	outc)
{
	register unsigned short	buttons;
	register short		coord;
#if	BYTE_MSF
#	define	get_short(b0,b1)	(((b1)<<8)|(b0))
#else
#	define	get_short(b0,b1)	(((b0)<<8)|(b1))
#endif

	/*
	 * Do the position first
	 */
	{
		register int		i;
		register boolean_t	moved;
		int			delta_coords[L_COORD_MAX];

		/*
		 * Get all coords, see if it moved at all (buttons!)
		 */
		moved = FALSE;
		for (i = 0; i < dev->locator.n_coords; i++) {

			coord = get_short(msg->body[2+(i<<1)],
					  msg->body[3+(i<<1)]);

			if (dev->locator.relative) {
				/*
				 * Flame on
				 * I am getting tired of this, why do they have to
				 * keep this bug around ?  Religion ?  Damn, they
				 * design a keyboard for X11 use and forget the mouse ?
				 * Flame off
				 */
#define	BOGUS_DEC_X_AXIS
#ifdef	BOGUS_DEC_X_AXIS
				if (i == 1) coord = - coord;
#endif	/* BOGUS_DEC_X_AXIS */
				/* dev->locator.coordinate[i] += coord; */
			} else {
				register unsigned int	old_coord;

				old_coord = dev->locator.coordinate[i];
				dev->locator.coordinate[i] = coord;
				coord = old_coord - coord;
			}
			delta_coords[i] = coord;
			if (coord != 0)
				moved = TRUE;
		}
		if (moved) {
			/* scale and threshold done higher up */
			screen_motion_event( 0,
				dev->locator.type,
				delta_coords[0],
				delta_coords[1]);
		}
	}

	/*
	 * Time for the buttons now
	 */
#define	new_buttons	coord
	new_buttons = get_short(msg->body[0],msg->body[1]);
	buttons = new_buttons ^ dev->locator.prev_buttons;
	if (buttons) {
		register int	i, type;

		dev->locator.prev_buttons = new_buttons;
		for (i = 0; buttons; i++, buttons >>= 1) {

			if ((buttons & 1) == 0) continue;

			type = (new_buttons & (1<<i)) ? 
				EVT_BUTTON_DOWN : EVT_BUTTON_UP;
			screen_keypress_event(	0,
						dev->locator.type,
						dev->locator.button_code[i],
						type);
		}
	}
#undef	new_buttons
}

/*
 * Handler for keyboard devices
 * Special case: outc set for recv packet means
 * we are inside the kernel debugger
 */
int
dtop_keyboard_handler(
	 dtop_device_t	dev,
	 dtop_message_t	msg,
	 int		event,
	 unsigned char	outc)
{
	char		save[11];
	register int	msg_len, c;

	/*
	 * Well, really this code handles just an lk401 and in
	 * a very primitive way at that.  Should at least emulate
	 * an lk201 decently, and make that a pluggable module.
	 * Sigh.
	 */

	if (event != DTOP_EVENT_RECEIVE_PACKET) {
		switch (event) {
		case DTOP_EVENT_POLL:
		    {
			register unsigned int	t, t0;

			/*
			 * Note we will always have at least the
			 * end-of-list marker present (a zero)
			 * Here stop and trigger of autorepeat.
			 * Do not repeat shift keys, either.
			 */
			{
				register unsigned char	uc, i = 0;

rpt_char:
				uc = dev->keyboard.last_codes[i];

				if (uc == DTOP_KBD_EMPTY) {
					dev->keyboard.k_ar_state = K_AR_OFF;
					return 0;
				}
				if ((uc >= LK_R_SHIFT) && (uc <= LK_R_ALT)) {
					/* sometimes swapped. Grrr. */
					if (++i < dev->keyboard.last_codes_count) 
						goto rpt_char;
					dev->keyboard.k_ar_state = K_AR_OFF;
					return 0;
				}
				c = uc;
			}

			/*
			 * Got a char. See if enough time from stroke,
			 * or from last repeat.
			 */
			t0 = (dev->keyboard.k_ar_state == K_AR_TRIGGER) ? 30 : 500;
			t = approx_time_in_msec();
			if ((t - dev->keyboard.last_msec) < t0)
				return 0;

			dev->keyboard.k_ar_state = K_AR_TRIGGER;

			/*
			 * Simplest thing to do is to mimic lk201
			 */
			outc = lk201_input(0, LK_REPEAT);
			if ( ! screen_keypress_event(	0,
							DEV_KEYBD,
							c,
							EVT_BUTTON_UP)) {
				if (outc > 0) cons_input(0, outc, 0);
			} else
				screen_keypress_event(	0,
							DEV_KEYBD,
							c,
							EVT_BUTTON_DOWN);
			return 0;
		    }
		default:	gimmeabreak();/*fornow*/
		}
		return -1;
	}

	msg_len = msg->code.val.len;

	/* Check for errors */
	c = msg->body[0];
	if ((c < DTOP_KBD_KEY_MIN) && (c != DTOP_KBD_EMPTY)) {
		printf("Keyboard error: %x %x %x..\n", msg_len, c, msg->body[1]);
		if (c != DTOP_KBD_OUT_ERR) return -1;
		/* spec sez if scan list overflow still there is data */
		msg->body[0] = 0;
	}

	dev->keyboard.last_msec = approx_time_in_msec();

	switch (dev->keyboard.k_ar_state) {
	case K_AR_IDLE:
		if (outc != 0xff) /* from debugger, might be too early */
			dtop_keyboard_autorepeat( dev );
		/* fall through */
	case K_AR_TRIGGER:
		dev->keyboard.k_ar_state = K_AR_ACTIVE;
		break;
	case K_AR_ACTIVE:
		break;
	case K_AR_OFF:	gimmeabreak();	/* ??? */
		dev->keyboard.k_ar_state = K_AR_IDLE;
	}

	/*
	 * We can only assume that pressed keys are reported in the
	 * same order (a minimum of sanity, please) across scans.
	 * To make things readable, do a first pass cancelling out
	 * all keys that are still pressed, and a second one generating
	 * events.  While generating events, do the upstrokes first
	 * from oldest to youngest, then the downstrokes from oldest
	 * to youngest.  This copes with lost packets and provides
	 * a reasonable model even if scans are too slow.
	 */

	/* make a copy of new state first */
	{
		register char	*p, *q, *e;

		p = save;
		q = (char*)msg->body;
		e = (char*)&msg->body[msg_len];

		while (q < e)
			*p++ = *q++;
	}

	/*
	 * Do the cancelling pass
	 */
	{
		register char	*ls, *le, *ns, *ne, *sync;

		ls = (char*)dev->keyboard.last_codes;
		le = (char*)&dev->keyboard.last_codes[dev->keyboard.last_codes_count];
		ns = (char*)msg->body;
		ne = (char*)&msg->body[msg_len];

		/* sync marks where to restart scanning, saving
		   time thanks to ordering constraints */
		for (sync = ns; ls < le; ls++) {
			register char	c = *ls;
			for (ns = sync; ns < ne; ns++)
				if (c == *ns) {
					*ls = *ns = 0;
					sync = ns + 1;
					break;
				}
			/* we could already tell if c is an upstroke,
			   but see the above discussion about errors */
		}
	}
	/*
	 * Now generate all upstrokes
	 */
	{
		register char	*ls, *le;
		register unsigned char	c;

		le = (char*)dev->keyboard.last_codes;
		ls = (char*)&dev->keyboard.last_codes[dev->keyboard.last_codes_count - 1];

		for ( ; ls >= le; ls--)
		    if (c = *ls) {
			/* keep kernel notion of lk201 state consistent */
			(void) lk201_input(0,c);			

			if (outc == 0)
			    screen_keypress_event(0,
						  DEV_KEYBD,
						  c,
						  EVT_BUTTON_UP);
		    }
	}
	/*
	 * And finally the downstrokes
	 */
	{
		register char	*ns, *ne, c, retc;

		ne = (char*)msg->body;
		ns = (char*)&msg->body[msg_len - 1];
		retc = 0;

		for ( ; ns >= ne; ns--)
		    if (c = *ns) {
			register unsigned char	data;

			data = c;
			c = lk201_input(0, data);

			/*
			 * If X11 had better code for the keyboard this
			 * would be an EVT_BUTTON_DOWN.  But that would
			 * screwup the REPEAT function. Grrr.
			 */
			/* outc non zero sez we are in the debugger */
			if (outc == 0) {
			    if (screen_keypress_event(0,
						  DEV_KEYBD,
						  data,
						  EVT_BUTTON_DOWN))
				c = -1; /* consumed by X */
			    else
				if (c > 0) cons_input(0, c, 0);
			}
			/* return the xlated keycode anyways */
			if ((c > 0) && (retc == 0))
			    retc = c;
		    }
		outc = retc;
	}
	/* install new scan state */
	{
		register char	*p, *q, *e;

		p = (char*)dev->keyboard.last_codes;
		q = (char*)save;
		e = (char*)&save[msg_len];

		while (q < e)
			*p++ = *q++;
		dev->keyboard.last_codes_count = msg_len;
	}
	return outc;
}

/*
 * Polled operations: we must do autorepeat by hand. Sigh.
 */
dtop_keyboard_autorepeat(
	dtop_device_t	dev)
{
	unsigned int	s = spltty();

	if (dev->keyboard.k_ar_state != K_AR_IDLE)
		dtop_keyboard_handler( dev, 0, DTOP_EVENT_POLL, 0);

	if (dev->keyboard.k_ar_state == K_AR_OFF)
		dev->keyboard.k_ar_state = K_AR_IDLE;
	else
		timeout( dtop_keyboard_autorepeat, dev, dev->keyboard.poll_frequency);

	splx(s);
}

#endif	/*NDTOP>0*/
