/////////////////////////////////////////////////////////////////////////////////////////////
//*																						  *//
//*      This is a serial mouse driver for eCos, writen by lihui<lhbyron@hotmail.com>     *//
//*                                                                                       *//
//*      The default setting is set mouse on com2("/dev/ser1"), and  mouse type is MS     *//
//*																						  *//
/////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <pkgconf/system.h>
#include <pkgconf/io_serial.h>
#include <pkgconf/io.h>
#include <cyg/io/serialio.h>
#include <cyg/io/io.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/io/devtab.h>
#include <cyg/io/serial.h>
#include <cyg/infra/diag.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/hal/hal_io.h>
#include <pkgconf/io_serial.h>

#define MAX_BYTES	128		/* number of bytes for buffer */
#define	SCALE		3	/* default scaling factor for acceleration */
#define	THRESH		5	/* default threshhold for acceleration */

/* 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	MS_LEFT_BUTTON		2
#define MS_RIGHT_BUTTON		1

/* 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

/* Mouse button bits*/
#define MWBUTTON_L	04
#define MWBUTTON_M	02
#define MWBUTTON_R 01

/* Mouse Port */
#define PORT  "/dev/ser1"

static int		state;		/* IDLE, XSET, ... */
static int		buttons;	/* current mouse buttons pressed*/
static int		availbuttons;	/* which buttons are available */
static long		xd;		/* change in x */
static long		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 unsigned char	*bp;		/* buffer pointer */
static int		nbytes;		/* number of bytes left */
static unsigned char	buffer[MAX_BYTES];	/* data bytes read */


Cyg_ErrNo error;		/* error output*/
cyg_io_handle_t handle;		/* mouse device handle */
cyg_serial_info_t info;		/* serial port config info */
cyg_uint32 len;				/* data length */
//unsigned char buf[6];		
//cyg_uint32 iBlock=1;		/* Set NonBlocking mode */

/* local routines*/
static cyg_io_handle_t  	MOU_Open();
static void 	MOU_Close(void);
static int  	MOU_GetButtonInfo(void);
static void		MOU_GetDefaultAccel(int *pscale,int *pthresh);
static int  	MOU_Read(long *dx, long *dy, long *dz, int *bptr);

static int  	ParseMS(int);		/* routine to interpret MS mouse */

/*
 * Open up the mouse device.
 * Returns the fd if successful, or negative if unsuccessful.
 */
static cyg_io_handle_t
MOU_Open()
{
	char	*type;
	char	*port;
	
	/* microsoft mouse*/
	left = MS_LEFT_BUTTON;
	right = MS_RIGHT_BUTTON;
	middle = 0;
	
	/* eCos mouse open */
	error=cyg_io_lookup(PORT, &handle);
	len=sizeof(info);

	/* set serial port configuration */
	error=cyg_io_get_config(handle,CYG_IO_GET_CONFIG_SERIAL_INFO,&info,&len);
	info.baud=CYGNUM_SERIAL_BAUD_1200;
	info.stop=CYGNUM_SERIAL_STOP_1;
	info.word_length=CYGNUM_SERIAL_WORD_LENGTH_8;
	error=cyg_io_set_config(handle,CYG_IO_SET_CONFIG_SERIAL_INFO, &info, &len);
	
	/* set nonblocking mode */
	//len=sizeof(cyg_uint32);
	//error=cyg_io_set_config(handle,CYG_IO_SET_CONFIG_SERIAL_READ_BLOCKING,&iBlock,&len);
	
	if (error < 0) {
		printf("Error %d opening serial mouse type %s on port %s.\n",
			errno, type, port);
 		return -1;
	}

	/* initialize data*/
	availbuttons = left | middle | right;
	state = IDLE;
	nbytes = 0;
	buttons = 0;
	xd = 0;
	yd = 0;
	return handle;
err:
	//close(mouse_fd);
	handle = 0;
	return -1;
}

/*
 * Close the mouse device.
 */
static void
MOU_Close(void)
{
	if (handle > 0) {
		//close(mouse_fd);
	}
	handle = 0;
}

/*
 * Get mouse buttons supported
 */
static int
MOU_GetButtonInfo(void)
{
	return availbuttons;
}

/*
 * Get default mouse acceleration settings
 */
static void
MOU_GetDefaultAccel(int *pscale,int *pthresh)
{
	*pscale = SCALE;
	*pthresh = THRESH;
}
/*
 * 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.
 */
static int
MOU_Read(long *dx, long *dy, long *dz, int *bptr)
{
	int	b;
	cyg_uint32 tmp;
	/*
	 * If there are no more bytes left, then read some more,
	 * waiting for them to arrive.  On a signal or a non-blocking
	 * error, return saying there is no new state available yet.
	 */
	if (nbytes <= 0) {
		bp = buffer;
		tmp=3;
		error = cyg_io_read(handle, bp, &tmp);
		nbytes=tmp;
		if (nbytes < 0) {
			if (error == EINTR || error == EAGAIN)
				return 0;

		}
	}

	/*
	 * 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.
	 */
	while (nbytes-- > 0) {
		if (ParseMS((int) *bp++)) {
			*dx = xd;
			*dy = yd;
			*dz = 0;
			b = 0;
			if(buttons & left)
				b |= MWBUTTON_L;
			if(buttons & right)
				b |= MWBUTTON_R;
			if(buttons & middle)
				b |= MWBUTTON_M;
			*bptr = b;
			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;
}

int main()
{
	long x, y, z;
	int	b;

	printf("Open Mouse\n");
	if( MOU_Open(0) < 0)
		printf("open failed mouse\n" );

	while(1) 
	{
		if(MOU_Read(&x, &y, &z, &b) == 1) 
		{
	     		printf("%d,%d,%d\n", x, y, b);
		}
	}
	
	
	return 0;
}
