summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/i386/pc386/console/mouse_parser.c
blob: ca068df87d60102d452a96539a1e45219dfa5535 (plain) (tree)















































































































































































































































































































































































































                                                                                                  
/*
 * Copyright (c) 1999 Greg Haerr <greg@censoft.com>
 * Portions Copyright (c) 1991 David I. Bell
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 *
 * UNIX Serial Port Mouse Driver
 * 
 * This driver opens a serial port directly, and interprets serial data.
 * Microsoft, PC, Logitech and PS/2 mice are supported.
 * The PS/2 mouse is only supported if the OS runs the mouse
 * byte codes through the serial port.
 *
 * The following environment variables control the mouse type expected
 * and the serial port to open.
 *
 * Environment Var	Default		Allowed
 * MOUSE_TYPE		pc		ms, pc, logi, ps2
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <rtems.h>
#include <bsp.h>
#include "keyboard.h"
#include "mouse_parser.h"
#include "serial_mouse.h"


/* NOTE NOTE NOTE NOTE:
   Select here the mouse type !!!!!
*/
#ifndef  MOUSE_TYPE
#define	MOUSE_TYPE	"ms"  /* default mouse type "ms","pc","ps2" */
#endif

/* states for the mouse*/
#define	IDLE			0		/* start of byte sequence */
#define	XSET			1		/* setting x delta */
#define	YSET			2		/* setting y delta */
#define	XADD			3		/* adjusting x delta */
#define	YADD			4		/* adjusting y delta */

/* values in the bytes returned by the mouse for the buttons*/
#define PC_LEFT_BUTTON		4
#define PC_MIDDLE_BUTTON	2
#define PC_RIGHT_BUTTON		1

#define MS_LEFT_BUTTON		2
#define MS_RIGHT_BUTTON		1

#define PS2_CTRL_BYTE		0x08
#define PS2_LEFT_BUTTON		1
#define PS2_RIGHT_BUTTON	2

/* Bit fields in the bytes sent by the mouse.*/
#define TOP_FIVE_BITS		0xf8
#define BOTTOM_THREE_BITS	0x07
#define TOP_BIT			0x80
#define SIXTH_BIT		0x40
#define BOTTOM_TWO_BITS		0x03
#define THIRD_FOURTH_BITS	0x0c
#define BOTTOM_SIX_BITS  	0x3f

/* local data*/
static int		state;		/* IDLE, XSET, ... */
static BUTTON	buttons;	/* current mouse buttons pressed*/
static BUTTON	availbuttons;	/* which buttons are available */
static COORD	xd;		/* change in x */
static COORD	yd;		/* change in y */

static int		left;		   /* because the button values change */
static int		middle;		/* between mice, the buttons are */
static int		right;		/* redefined */

static int		(*parse)( int );	/* parse routine */

/* local routines*/
static int  	ParsePC(int);		/* routine to interpret PC mouse */
static int  	ParseMS(int);		/* routine to interpret MS mouse */
static int  	ParsePS2(int);		/* routine to interpret PS/2 mouse */

extern void uart_set_driver_handler( int port, void ( *handler )( void *,  char *, int ) );
extern void kbd_set_driver_handler( void ( *handler )( void *,  unsigned short, unsigned long ) );
extern void ps2_set_driver_handler( int port, void ( *handler )( void *,  char *, int ) );

/*
 * Open up the mouse device.
 * Returns the fd if successful, or negative if unsuccessful.
 */
int MOU_Init()
{
	char	*type;

	/* get mouse type and port*/
	type = MOUSE_TYPE;

	/* set button bits and parse procedure*/
	if(!strcmp(type, "pc") || !strcmp(type, "logi")) {
		/* pc or logitech mouse*/
		left = PC_LEFT_BUTTON;
		middle = PC_MIDDLE_BUTTON;
		right = PC_RIGHT_BUTTON;
		parse = ParsePC;
	} else if (strcmp(type, "ms") == 0) {
		/* microsoft mouse*/
		left = MS_LEFT_BUTTON;
		right = MS_RIGHT_BUTTON;
		middle = 0;
		parse = ParseMS;
	} else if (strcmp(type, "ps2") == 0) {
		/* PS/2 mouse*/
		left = PS2_LEFT_BUTTON;
		right = PS2_RIGHT_BUTTON;
		middle = 0;
		parse = ParsePS2;
	} else
		return -1;

   printk("Device: /dev/mouse -- mouse type is: %s\n", MOUSE_TYPE );

	/* initialize data*/
	availbuttons = left | middle | right;
	state = IDLE;
	buttons = 0;
	xd = 0;
	yd = 0;
	return 0;
}

/*
 * Attempt to read bytes from the mouse and interpret them.
 * Returns -1 on error, 0 if either no bytes were read or not enough
 * was read for a complete state, or 1 if the new state was read.
 * When a new state is read, the current buttons and x and y deltas
 * are returned.  This routine does not block.
 */
int MOU_Data( int ch, COORD *dx, COORD *dy, COORD *dz, BUTTON *bptr)
{
	int b;

	/*
	 * Loop over all the bytes read in the buffer, parsing them.
	 * When a complete state has been read, return the results,
	 * leaving further bytes in the buffer for later calls.
	 */
	if( (*parse)( ch ) ) 
	{
		*dx = xd;
		*dy = yd;
		*dz = 0;
		b = 0;
		if(buttons & left)
			b |= LBUTTON;
		if(buttons & right)
			b |= RBUTTON;
		if(buttons & middle)
			b |= MBUTTON;
		*bptr = b;
		return 1;
	}
	return 0;
}

/*
 * Input routine for PC mouse.
 * Returns nonzero when a new mouse state has been completed.
 */
static int ParsePC(int byte)
{
	int	sign;			/* sign of movement */

	switch (state) {
		case IDLE:
			if ((byte & TOP_FIVE_BITS) == TOP_BIT) {
				buttons = ~byte & BOTTOM_THREE_BITS;
				state = XSET;
			}
			break;

		case XSET:
			sign = 1;
			if (byte > 127) {
				byte = 256 - byte;
				sign = -1;
			}
			xd = byte * sign;
			state = YSET;
			break;

		case YSET:
			sign = 1;
			if (byte > 127) {
				byte = 256 - byte;
				sign = -1;
			}
			yd = -byte * sign;
			state = XADD;
			break;

		case XADD:
			sign = 1;
			if (byte > 127) {
				byte = 256 - byte;
				sign = -1;
			}
			xd += byte * sign;
			state = YADD;
			break;

		case YADD:
			sign = 1;
			if (byte > 127) {
				byte = 256 - byte;
				sign = -1;
			}
			yd -= byte * sign;
			state = IDLE;
			return 1;
	}
	return 0;
}


/*
 * Input routine for Microsoft mouse.
 * Returns nonzero when a new mouse state has been completed.
 */
static int ParseMS(int byte)
{
	switch (state) {
		case IDLE:
			if (byte & SIXTH_BIT) {
				buttons = (byte >> 4) & BOTTOM_TWO_BITS;
				yd = ((byte & THIRD_FOURTH_BITS) << 4);
				xd = ((byte & BOTTOM_TWO_BITS) << 6);
				state = XADD;
			}
			break;

		case XADD:
			xd |= (byte & BOTTOM_SIX_BITS);
			state = YADD;
			break;

		case YADD:
			yd |= (byte & BOTTOM_SIX_BITS);
			state = IDLE;
			if (xd > 127)
				xd -= 256;
			if (yd > 127)
				yd -= 256;
			return 1;
	}
	return 0;
}

/*
 * Input routine for PS/2 mouse.
 * Returns nonzero when a new mouse state has been completed.
 */
static int ParsePS2(int byte)
{
	switch (state) {
		case IDLE:
			if (byte & PS2_CTRL_BYTE) {
				buttons = byte & 
					(PS2_LEFT_BUTTON|PS2_RIGHT_BUTTON);
				state = XSET;
			}
			break;

		case XSET:
			if(byte > 127)
				byte -= 256;
			xd = byte;
			state = YSET;
			break;

		case YSET:
			if(byte > 127)
				byte -= 256;
			yd = -byte;
			state = IDLE;
			return 1;
	}
	return 0;
}

static rtems_id queue_id = 0;

/* generic keyboard parser */
static void mouse_parser( void *ptr, char *buffer, int size )
{
   COORD dx;
   COORD dy;
   COORD dz;
   BUTTON bptr;
   while( size-- )
   {
      if( MOU_Data( *buffer++, &dx, &dy, &dz, &bptr ) )
      {
         struct MW_UID_MESSAGE m;
         m.type = MV_UID_REL_POS;
         /* buttons definitons have been selected to match */
         m.m.pos.btns = bptr;
         m.m.pos.x  = dx;
         m.m.pos.y  = dy;
         m.m.pos.z  = dz;
/*       printk( "Mouse: msg: dx=%d, dy=%d, btn=%X\n", dx, dy, bptr ); */
         rtems_message_queue_send( queue_id, ( void * )&m, sizeof( struct MW_UID_MESSAGE ) );
      }
   }
}

/* enable the mouse to add messages to the queue */
void register_mou_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, &queue_id );
   if( status != RTEMS_SUCCESSFUL )
   {
      printk( "UID_Queue: error open queue: %d\n", status );
      return;
   }
   MOU_Init();
   if( port == -1 )
   {
      /* we know the mouse type in this case, let's initialize everything */
		left = PS2_LEFT_BUTTON;
		right = PS2_RIGHT_BUTTON;
		middle = 0;
		parse = ParsePS2;
      ps2_set_driver_handler( port, mouse_parser );
   }
   else
   {
      uart_set_driver_handler( port, mouse_parser );
   }
}

/* stop the mouse from adding messages to the queue */
void unregister_mou_msg_queue( int port )
{
   if( port == -1 )
   {
      ps2_set_driver_handler( port, NULL );
   }
   else
   {
      uart_set_driver_handler( port, NULL );
   }
}

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

    m.type = MV_UID_KBD;
    m.m.kbd.code       = keycode;
    m.m.kbd.modifiers  = kbd->ledflagstate;
    m.m.kbd.mode       = kbd->kbdmode;
    /*  printk( "kbd: msg: keycode=%X, mod=%X\n", keycode, mods );  */
    rtems_message_queue_send( queue_id, ( void * )&m, 
                              sizeof( struct MW_UID_MESSAGE ) );
}

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, &queue_id );
   if( status != RTEMS_SUCCESSFUL )
   {
      printk( "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 );
}