/* $XConsortium: xf86SpOrb.c blah blah RCS codes go here */
/*
 * Copyright 1997 by Louis Kruger. <louie@alumni.princeton.edu>
 * based on the Wacom Tablet driver: xf86Wacom.c
 *
 * Information and code on the SpaceOrb's protocol courtesy of Brett Viren,
 * author of the SpaceOrb Programming Secrets page:
 *   http://bock.physics.sunysb.edu/~bviren/orb/
 *                                                                            
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is  hereby granted without fee, provided that
 * the  above copyright   notice appear  in   all  copies and  that both  that
 * copyright  notice   and   this  permission   notice  appear  in  supporting
 * documentation, and that   the  name of  Louis   Kruger    not  be  used  in
 * advertising or publicity pertaining to distribution of the software without
 * specific,  written      prior  permission.     Louis   Kruger     makes  no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.                   
 *                                                                            
 * LOUIS  KRUGER    DISCLAIMS ALL   WARRANTIES WITH REGARD  TO  THIS SOFTWARE,
 * INCLUDING ALL IMPLIED   WARRANTIES OF MERCHANTABILITY  AND   FITNESS, IN NO
 * EVENT  SHALL   LOUIS   KRUGER BE   LIABLE   FOR ANY  SPECIAL, INDIRECT   OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA  OR PROFITS, WHETHER  IN  AN ACTION OF  CONTRACT,  NEGLIGENCE OR OTHER
 * TORTIOUS  ACTION, ARISING    OUT OF OR   IN  CONNECTION  WITH THE USE    OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 */

/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86SpOrb.c,v 3.25.2.1 1997/05/12 12:52:28 hohndel Exp $ */

#include "Xos.h"
#include <signal.h>

#define NEED_EVENTS
#include "X.h"
#include "Xproto.h"
#include "misc.h"
#include "inputstr.h"
#include "scrnintstr.h"
#include "XI.h"
#include "XIproto.h"
#include "keysym.h"

#if defined(sun) && !defined(i386)
#define POSIX_TTY
#include <errno.h>
#include <termio.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdio.h>

#include "extio.h"
#else
#include "compiler.h"

#include "xf86.h"
#include "xf86Procs.h"
#include "xf86_OSlib.h"
#include "xf86_Config.h"
#include "xf86Xinput.h"
#include "atKeynames.h"
#include "xf86Version.h"
#endif

#if !defined(sun) || defined(i386)
#include "osdep.h"
#include "exevents.h"

#include "extnsionst.h"
#include "extinit.h"
#endif

/******************************************************************************
 * debugging macro
 *****************************************************************************/
#ifdef DBG
#undef DBG
#endif
#ifdef DEBUG
#undef DEBUG
#endif

static int      debug_level = 0;
#define DEBUG 1
#if DEBUG
#define DBG(lvl, f) {if ((lvl) <= debug_level) f;}
#else
#define DBG(lvl, f)
#endif

/******************************************************************************
 * SpOrbDeviceRec flags
 *****************************************************************************/
#define DEVICE_ID(flags) ((flags) & 0x07)
#define SPORB_ID   1	

#define ABSOLUTE_FLAG	8

#define SORB_BUTTON_SIZE 5      /* The packet size for no motion */
#define SORB_BUTTON_NUMBER 75   /* The flag for a no-motion-packet */
#define SORB_MOTION_SIZE 12     /* The packet size for motion */
#define SORB_MOTION_NUMBER 68   /* The flag for a motion-packet */
#define SORB_GREETING_SIZE 53     /* The packet size for motion */
#define SORB_GREETING_NUMBER 'R'   /* The flag for a motion-packet */

/******************************************************************************
 * SpOrbCommonRec flags
 *****************************************************************************/

typedef struct
{
    /* configuration fields */
    unsigned char 	flags;
    double		factorX;
    double		factorY;
    int			offsetX;
    int			offsetY;

    struct _SpOrbCommonRec *common;	/* common info pointer */
    
    /* state fields */
    int			x;
    int			y;
    int			z;
    int			u;
    int			v;
    int			r;
    int			oldx;
    int			oldy;
    int			oldz;
    int			oldu;
    int			oldv;
    int			oldr;
    int			oldButtons;
    

} SpOrbDeviceRec, *SpOrbDevicePtr;

typedef struct _SpOrbCommonRec 
{
    char		*srbDevice;	/* device file name */
    LocalDevicePtr	*srbDevices;	/* array of all devices sharing the same port */
    char		srbFlags;	/* unused */
    int			srbNumDevices;	/* number of devices */
    int			srbIndex;	/* number of bytes read */
    int			srbPktLength[3];	/* length of a packet */
    unsigned char	srbData[64];	/* data read on the device */
} SpOrbCommonRec, *SpOrbCommonPtr;

/******************************************************************************
 * configuration stuff
 *****************************************************************************/
#define SPORB_SECTION_NAME "SpaceOrb"
#define PORT		1
#define DEVICENAME	2
#define THE_MODE	3
#define SUPPRESS	4
#define DEBUG_LEVEL     5
#define HISTORY_SIZE	7
#define ALWAYS_CORE	8
#define AXIS_MAP	9
#define BUTTON_MAP	10
#define XSENSITIVITY	11
#define YSENSITIVITY	12
#define XOFFSET		13
#define YOFFSET		14

#if !defined(sun) || defined(i386)
static SymTabRec srbTab[] = {
  { ENDSUBSECTION,	"endsubsection" },
  { PORT,		"port" },
  { DEVICENAME,		"devicename" },
  { THE_MODE,		"mode" },
  { SUPPRESS,		"suppress" },
  { DEBUG_LEVEL,	"debuglevel" },
  { HISTORY_SIZE,	"historysize" },
  { ALWAYS_CORE,	"alwayscore" },
  { AXIS_MAP,		"axismap" },
/*  { BUTTON_MAP,	"buttonmap" }, */
  { XSENSITIVITY,	"xsensitivity" },
  { YSENSITIVITY,	"ysensitivity" },
  { XOFFSET,		"xoffset" },
  { YOFFSET,		"yoffset" },
  { -1,			"" }
};

#define RELATIVE	1
#define ABSOLUTE	2

static SymTabRec ModeTabRec[] = {
  { RELATIVE,	"relative" },
  { ABSOLUTE,	"absolute" },
  { -1,		"" }
};
  
#endif

/******************************************************************************
 * constant and macros declarations
 *****************************************************************************/
#define BUFFER_SIZE 256		/* size of reception buffer */
#define XI_SPORB   "SpaceOrb"	/* X device name for the stylus */
#define MAX_VALUE 1024           /* number of positions */
#define MAXTRY 50               /* max number of try to receive magic number */
#define SYSCALL(call) while(((call) == -1) && (errno == EINTR))

#define COMMAND_SET_MASK	0xc0
#define BAUD_RATE_MASK		0x0a
#define PARITY_MASK		0x30
#define DATA_LENGTH_MASK	0x40
#define STOP_BIT_MASK		0x80

#define HEADER_BIT	0x80
#define ZAXIS_SIGN_BIT	0x40
#define ZAXIS_BIT    	0x04
#define ZAXIS_BITS    	0x7f
#define POINTER_BIT     0x20
#define PROXIMITY_BIT   0x40
#define BUTTON_FLAG	0x08
#define BUTTONS_BITS	0x78
#define TILT_SIGN_BIT	0x40
#define TILT_BITS	0x7f

#define HANDLE_TILT(comm) ((comm)->srbPktLength == 9)

#define mils(res) (res * 1000 / 2.54) /* resolution */

/******************************************************************************
 * Function/Macro keys variables
 *****************************************************************************/
static KeySym SpOrb_map[] = 
{
    NoSymbol,	/* 0x00 */
    1,
    2,
    3,
    4,
    5,
    6,
    7,
};

static int SpOrb_Axis_map[6] = {0, 1, 2, 3, 4, 5};
static int SpOrb_Axis_rev[6] = {0, 0, 0, 0, 0, 0};

/* minKeyCode = 8 because this is the min legal key code */
static KeySymsRec SpOrb_keysyms = {
  /* map	minKeyCode	maxKC	width */
  SpOrb_map,	8,		0x20,	1
};

/******************************************************************************
 * external declarations
 *****************************************************************************/
#if defined(sun) && !defined(i386)
#define ENQUEUE suneqEnqueue
#else
#define ENQUEUE xf86eqEnqueue

extern void xf86eqEnqueue(
#if NeedFunctionPrototypes
    xEventPtr /*e*/
#endif
);
#endif

extern void miPointerDeltaCursor(
#if NeedFunctionPrototypes
    int /*dx*/,
    int /*dy*/,
    unsigned long /*time*/
#endif
);

#if NeedFunctionPrototypes
static LocalDevicePtr xf86srbAllocateSpball(void);
#endif

#if !defined(sun) || defined(i386)

static void SpOrbInvertMap()
{
    int map[6];
    int i;
    for(i=0; i<6; ++i) {
      map[i]=SpOrb_Axis_map[i];
    }
    for(i=0; i<6; ++i) {
      SpOrb_Axis_map[map[i]]=i;
    }
}

/*
 ***************************************************************************
 *
 * xf86srbConfig --
 *	Configure the device.
 *
 ***************************************************************************
 */
static Bool
xf86srbConfig(LocalDevicePtr    *array,
              int               inx,
              int               max,
	      LexPtr            val)
{
    LocalDevicePtr      dev = array[inx];
    SpOrbDevicePtr	priv = (SpOrbDevicePtr)(dev->private);
    SpOrbCommonPtr	common = priv->common;
    int			token;
    int			mtoken;
    int			tmp,i;
    
    DBG(1, ErrorF("xf86srbConfig\n"));
    
    while ((token = xf86GetToken(srbTab)) != ENDSUBSECTION) {
	switch(token) {
	case DEVICENAME:
	    if (xf86GetToken(NULL) != STRING)
		xf86ConfigError("Option string expected");
	    dev->name = strdup(val->str);
	    if (xf86Verbose)
		ErrorF("%s SpOrb X device name is %s\n", XCONFIG_GIVEN,
		       dev->name);
	    break;
	    
	case PORT:
	    if (xf86GetToken(NULL) != STRING)
		xf86ConfigError("Option string expected");
	    else {
		int     loop;
		
		/* try to find another SpOrb device which share the same port */
		for(loop=0; loop<max; loop++) {
		    if (loop == inx)
			continue;
		    if ((array[loop]->device_config == xf86srbConfig) &&
			(strcmp(((SpOrbDevicePtr)array[loop]->private)->common->srbDevice, val->str) == 0)) {
			DBG(2, ErrorF("xf86srbConfig SpOrb port share between"
				      " %s and %s\n",
				      dev->name, array[loop]->name));
			xfree(common->srbDevices);
			xfree(common);
			common = priv->common = ((SpOrbDevicePtr) array[loop]->private)->common;
			common->srbNumDevices++;
			common->srbDevices = (LocalDevicePtr *) xrealloc(common->srbDevices,
									 sizeof(LocalDevicePtr) * common->srbNumDevices);
			common->srbDevices[common->srbNumDevices - 1] = dev;
			break;
		    }
		}
		if (loop == max) {
		    common->srbDevice = strdup(val->str);
		    if (xf86Verbose)
			ErrorF("%s SpOrb port is %s\n", XCONFIG_GIVEN,
			       common->srbDevice);
		}
	    }
	    break;
	    
	case THE_MODE:
	    mtoken = xf86GetToken(ModeTabRec);
	    if ((mtoken == EOF) || (mtoken == STRING) || (mtoken == NUMBER)) 
		xf86ConfigError("Mode type token expected");
	    else {
		switch (mtoken) {
		case ABSOLUTE:
		    priv->flags = priv->flags | ABSOLUTE_FLAG;
		    break;
		case RELATIVE:
		    priv->flags = priv->flags & ~ABSOLUTE_FLAG; 
		    break;
		default:
		    xf86ConfigError("Illegal Mode type");
		    break;
		}
	    }
	    break;
	    
	case DEBUG_LEVEL:
	    if (xf86GetToken(NULL) != NUMBER)
		xf86ConfigError("Option number expected");
	    debug_level = val->num;
	    if (xf86Verbose) {
#if DEBUG
		ErrorF("%s SpOrb debug level sets to %d\n", XCONFIG_GIVEN,
		       debug_level);      
#else
		ErrorF("%s SpOrb debug level not sets to %d because"
		       " debugging is not compiled\n", XCONFIG_GIVEN,
		       debug_level);      
#endif
	    }
	    break;

	case HISTORY_SIZE:
	    if (xf86GetToken(NULL) != NUMBER)
		xf86ConfigError("Option number expected");
	    dev->history_size = val->num;
	    if (xf86Verbose)
		ErrorF("%s SpOrb Motion history size is %d\n", XCONFIG_GIVEN,
		       dev->history_size);      
	    break;

	case ALWAYS_CORE:
	    xf86AlwaysCore(dev, TRUE);
	    if (xf86Verbose)
		ErrorF("%s SpOrb device always stays core pointer\n",
		       XCONFIG_GIVEN);
	    break;

        case XSENSITIVITY:
	    if (xf86GetToken(NULL) != NUMBER)
		xf86ConfigError("Option number expected");
            priv->factorX = val->realnum;
            if(xf86Verbose)
                ErrorF("%s SpOrb X sensitivity set to %f",XCONFIG_GIVEN,priv->factorX);
            break;

        case YSENSITIVITY:
	    if (xf86GetToken(NULL) != NUMBER)
		xf86ConfigError("Option number expected");
            priv->factorY = val->realnum;
            if(xf86Verbose)
                ErrorF("%s SpOrb Y sensitivity set to %f",XCONFIG_GIVEN,priv->factorY);
            break;

        case XOFFSET:
	    if (xf86GetToken(NULL) != NUMBER)
		xf86ConfigError("Option number expected");
            priv->offsetX = val->num;
            if(xf86Verbose)
                ErrorF("%s SpOrb X offset set to %d",XCONFIG_GIVEN,priv->offsetX);
            break;

        case YOFFSET:
	    if (xf86GetToken(NULL) != NUMBER)
		xf86ConfigError("Option number expected");
            priv->offsetY = val->num;
            if(xf86Verbose)
                ErrorF("%s SpOrb Y offset set to %d",XCONFIG_GIVEN,priv->offsetY);
            break;

        case AXIS_MAP:
	    if (xf86GetToken(NULL) != STRING)
		xf86ConfigError("Option string expected");
            if(strlen(val->str) != 6)
		xf86ConfigError("AxisMap string should be 6 characters");
            tmp = 0;
            for(i=0; i<6; ++i) {
	        if(val->str[i]>='A' && val->str[i]<='Z')
		    SpOrb_Axis_rev[i] = 1;
		else
		    SpOrb_Axis_rev[i] = 0;
	        switch(val->str[i]) {
                  default:
		    xf86ConfigError("AxisMap string should permute XYZUVR");
		  case 'x':
		  case 'X':
		    SpOrb_Axis_map[i] = 0;
		    tmp |= 1;
		    break;
		  case 'y':
		  case 'Y':
		    SpOrb_Axis_map[i] = 1;
		    tmp |= 2;
		    break;
		  case 'z':
		  case 'Z':
		    SpOrb_Axis_map[i] = 2;
		    tmp |= 4;
		    break;
		  case 'u':
		  case 'U':
		    SpOrb_Axis_map[i] = 3;
		    tmp |= 8;
		    break;
		  case 'v':
		  case 'V':
		    SpOrb_Axis_map[i] = 4;
		    tmp |= 16;
		    break;
		  case 'r':
		  case 'R':
		    SpOrb_Axis_map[i] = 5;
		    tmp |= 32;
		    break;
		}
            }
		    
            if (tmp != 63)
		xf86ConfigError("AxisMap string should permute XYZUVR");

            SpOrbInvertMap();
	    if (xf86Verbose)
		ErrorF("%s SpOrb Axis Map (%d %d %d %d %d %d)\n", XCONFIG_GIVEN,
                       SpOrb_Axis_map[0],
                       SpOrb_Axis_map[1],
                       SpOrb_Axis_map[2],
                       SpOrb_Axis_map[3],
                       SpOrb_Axis_map[4],
                       SpOrb_Axis_map[5]);
           
	    break;
            

	case EOF:
	    FatalError("Unexpected EOF (missing EndSubSection)");
	    break;
	    
	default:
	    xf86ConfigError("SpOrb subsection keyword expected");
	    break;
	}
    }
    
    DBG(1, ErrorF("xf86srbConfig name=%s\n", common->srbDevice));
    
    return Success;
}
#endif

/*
 ***************************************************************************
 *
 * wait_for_fd --
 *
 *	Wait one second that the file descriptor becomes readable.
 *
 ***************************************************************************
 */
static int
wait_for_fd(int	fd)
{
    int			err;
    fd_set		readfds;
    struct timeval	timeout;
    
    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);
    
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    SYSCALL(err = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout));

    return err;
}

static void
xf86srbSendEvents(LocalDevicePtr	local,
		  int			is_ball,
		  int			x,
		  int			y,
		  int			z,
		  int			u,
		  int			v,
		  int			r,
		  int			buttons)
{
    SpOrbDevicePtr	priv = (SpOrbDevicePtr) local->private;
    SpOrbCommonPtr	common = priv->common;
    int			i;
    int			rx, ry, rz, ru, rv, rr;
    int			valuator[7];
    int			is_core_pointer, is_absolute;

    DBG(6, ErrorF("[%s] \tx=%d\ty=%d\tz=%d\tu=%d\tv=%d\tr=%d\tbuttons=%d\n",
		  is_ball ? "ball" : "buttons",
		  x, y, z, u, v, r, buttons));

    /* Translate coordinates according to Top and Bottom points
     * if we are outside the zone do as a ProximityOut event.
     */

      
    is_absolute = (priv->flags & ABSOLUTE_FLAG);
    is_core_pointer = xf86IsCorePointer(local->dev);


    if(is_ball) {
      /* sets rx and ry according to the mode */
  	rx = x; ry = y; rz = z;
  	ru = u; rv = v; rr = r;
    } else {
  	rx = priv->oldx;
  	ry = priv->oldy;
  	rz = priv->oldz;  
	ru = priv->oldu;  
  	rv = priv->oldv;  
  	rr = priv->oldr;  
    }  

    /* handle axis_map */
	valuator[SpOrb_Axis_map[0]] = rx;
        valuator[SpOrb_Axis_map[1]] = ry;
        valuator[SpOrb_Axis_map[2]] = rz;
        valuator[SpOrb_Axis_map[3]] = ru;
        valuator[SpOrb_Axis_map[4]] = rv;
        valuator[SpOrb_Axis_map[5]] = rr;
        for(i=0; i<6; ++i)
          if(SpOrb_Axis_rev[i])
            valuator[i] = -valuator[i]-1;
        
    if (is_core_pointer) {
	valuator[0] *= priv->factorX;
	valuator[1] *= priv->factorY;
    }

    if (is_absolute) {
        valuator[0] += priv->offsetX;
        valuator[1] += priv->offsetY;
    }

    /* coordonates are ready we can send events */
        if(is_ball) {
          DBG(6, 
            ErrorF("[move] \tx=%d\ty=%d\tz=%d\tu=%d\tv=%d\tr=%d\tbuttons=%d\n",
		  valuator[0],
		  valuator[1],
		  valuator[2],
		  valuator[3],
		  valuator[4],
		  valuator[5], buttons));
	  xf86PostMotionEvent(local->dev, is_absolute, 
                  0, 6, valuator[0],
                        valuator[1],
                        valuator[2],
                        valuator[3],
                        valuator[4],
                        valuator[5]);

          priv->oldx = rx;
          priv->oldy = ry;
          priv->oldz = rz;
          priv->oldu = ru;
          priv->oldv = rv;
          priv->oldr = rr;
        } else {
          for(i=0; i<7; ++i) {
            if((priv->oldButtons&(1<<i)) != (buttons&(1<<i))) {
              DBG(6, ErrorF("[button] \t%d %d = %d\n",i,buttons&(1<<i),
                  SpOrb_map[i+1]));
	      xf86PostButtonEvent(local->dev, is_absolute, SpOrb_map[i+1], 
                  buttons&(1<<i), 0, 6, valuator[0],
                                        valuator[1],
                                        valuator[2],
                                        valuator[3],
                                        valuator[4],
                                        valuator[5]);
            }
          }
          priv->oldButtons = buttons;
        }
}

/*
 ***************************************************************************
 *
 * xf86srbReadInput --
 *	Read the new events from the device, and enqueue them.
 *
 ***************************************************************************
 */
static void
xf86srbReadInput(LocalDevicePtr         local)
{
    SpOrbDevicePtr	priv = (SpOrbDevicePtr) local->private;
    SpOrbCommonPtr	common = priv->common;
    int			len, loop, idx;
    int			x=31, y=32, z=33, u=34, v=35, r=36, buttons=37;
    int			*px, *py, *pz, *pbuttons, *pprox;
    static int			packettype;
    int			i;
    unsigned char	buffer[BUFFER_SIZE];
    static char SpaceWare[] = "SpaceWare!";
  
    DBG(7, ErrorF("xf86srbReadInput BEGIN device=%s fd=%d\n",
		  common->srbDevice, local->fd));

    SYSCALL(len = read(local->fd, buffer, sizeof(buffer)));

    if (len <= 0) {
	ErrorF("Error reading SpOrb device : %s\n", strerror(errno));
	return;
    } else {
	DBG(10, ErrorF("xf86srbReadInput read %d bytes\n", len));
    }

    for(loop=0; loop<len; loop++) {

	if (common->srbIndex == 0) {
          if(buffer[loop] == SORB_BUTTON_NUMBER)  {
            DBG(9, ErrorF("Got Button Packet\n"))
            packettype = 0;
          } else if(buffer[loop] == SORB_MOTION_NUMBER) {
            DBG(9, ErrorF("Got Motion Packet\n"))
            packettype = 1;
          } else if(buffer[loop] == SORB_GREETING_NUMBER) {
            DBG(9, ErrorF("Got Greeting Packet\n"))
            packettype = 2;
          } else if(buffer[loop] == '\r') {
            continue;	/* CRs are normal */
          } else { /* first byte is not OK */
	    DBG(6, ErrorF("xf86srbReadInput bad magic number 0x%x\n",
			  buffer[loop]));
	    continue;
          }
	}

	common->srbData[common->srbIndex++] = buffer[loop] & 0177;

        if(packettype!=0 && packettype!=1 && packettype!=2) {
          ErrorF("SpOrb: bad bad bad... Processing bad packet %d\n",packettype);
          continue;
        }

	if (common->srbIndex == common->srbPktLength[packettype]) {
	    /* the packet is OK */

	    /* reset char count for next read */
	    common->srbIndex = 0;

            if(xf86Verbose && packettype==2)  {
              char tmpbuffer[64];
              strncpy(tmpbuffer,(char *)common->srbData,SORB_GREETING_SIZE);
              ErrorF("%s SpaceOrb: %s\n",XCONFIG_PROBED,tmpbuffer);
              continue;
            }

	    /* decode data */
            /* x=, y,z,u,v,r */

            switch(packettype) {
	      case 1:			/* ball movement */

                /* Strip the MSB, which is a parity bit */
                for (i = 0; i < 9; ++i) {
                    common->srbData[i+2] &= 0177;          /* Make sure everything is 7bit */
                    common->srbData[i+2] ^= SpaceWare[i];  /* XOR "encryption key" */
                }
            
                /* Turn chars into 10 bit integers */
                x = ((common->srbData[2] & 0177)<<3)|((common->srbData[3] & 0160)>>4);
                y = ((common->srbData[3] & 0017)<<6)|((common->srbData[4] & 0176)>>1);
                z = ((common->srbData[4] & 0001)<<9)|((common->srbData[5] & 0177)<<2)|((common->srbData[6] & 0140)>>5);
                u = ((common->srbData[6] & 0037)<<5)|((common->srbData[7] & 0174)>>2);
                v = ((common->srbData[7] & 0003)<<8)|((common->srbData[8] & 0177)<<1)|((common->srbData[9] & 0100)>>6);
                r = ((common->srbData[9] & 0077)<<4)|((common->srbData[10] & 0170)>>3);
            
                /* Get the sign right. */
                if (x > 511) x -= 1024;
                if (y > 511) y -= 1024;
                if (z > 511) z -= 1024;
                if (u > 511) u -= 1024;
                if (v > 511) v -= 1024;
                if (r > 511) r -= 1024;

		buttons = common->srbData[1];
              break;

	      case 0:			/* button packet */
                DBG(5,ErrorF("Buttons: %d %d %d %d %d\n",
                  common->srbData[0],
                  common->srbData[1],
                  common->srbData[2],
                  common->srbData[3],
                  common->srbData[4]));

                buttons = common->srbData[2];
                x = priv->oldx;
                y = priv->oldr;
                z = priv->oldz;
                u = priv->oldu;
                v = priv->oldv;
                r = priv->oldr;
              break;
            }

	    for(idx=0; idx<common->srbNumDevices; idx++) {
		DBG(7, ErrorF("xf86srbReadInput trying to send to %s\n",
			      common->srbDevices[idx]->name));
		
		xf86srbSendEvents(common->srbDevices[idx],
				  packettype,
				  x, y, z, u, v, r, buttons);
	    }
	}
    }
    DBG(7, ErrorF("xf86srbReadInput END   local=0x%x priv=0x%x\n",
		  local, priv));
}

/*
 ***************************************************************************
 *
 * xf86srbControlProc --
 *
 ***************************************************************************
 */
static void
xf86srbControlProc(DeviceIntPtr	device,
		   PtrCtrl	*ctrl)
{
  DBG(2, ErrorF("xf86srbControlProc\n"));
}

/*
 ***************************************************************************
 *
 * xf86srbOpen --
 *
 ***************************************************************************
 */
static Bool
xf86srbOpen(LocalDevicePtr	local)
{
    struct termios	termios_tty;
    struct timeval	timeout;
    char		buffer[256];
    int			err;
    SpOrbDevicePtr	priv = (SpOrbDevicePtr)local->private;
    SpOrbCommonPtr	common = priv->common;
    int			a, b;
    int			loop, idx;
    float		version = 0.0;
  
    DBG(1, ErrorF("opening %s\n", common->srbDevice));

    SYSCALL(local->fd = open(common->srbDevice, O_RDWR|O_NDELAY|O_NOCTTY));
    if (local->fd == -1) {
	ErrorF("Error opening %s : %s\n", common->srbDevice, strerror(errno));
	return !Success;
    }

#ifdef POSIX_TTY
    SYSCALL(err = tcgetattr(local->fd, &termios_tty));

    if (err == -1) {
	ErrorF("SpOrb tcgetattr error : %s\n", strerror(errno));
	return !Success;
    }
    termios_tty.c_iflag = IXOFF;
    termios_tty.c_oflag = 0;
    termios_tty.c_cflag = B9600|CS8|CREAD|CLOCAL;
    termios_tty.c_lflag = 0;

    termios_tty.c_cc[VINTR] = 0;
    termios_tty.c_cc[VQUIT] = 0;
    termios_tty.c_cc[VERASE] = 0;
    termios_tty.c_cc[VEOF] = 0;
#ifdef VWERASE
    termios_tty.c_cc[VWERASE] = 0;
#endif
#ifdef VREPRINT
    termios_tty.c_cc[VREPRINT] = 0;
#endif
    termios_tty.c_cc[VKILL] = 0;
    termios_tty.c_cc[VEOF] = 0;
    termios_tty.c_cc[VEOL] = 0;
#ifdef VEOL2
    termios_tty.c_cc[VEOL2] = 0;
#endif
    termios_tty.c_cc[VSUSP] = 0;
#ifdef VDSUSP
    termios_tty.c_cc[VDSUSP] = 0;
#endif
#ifdef VDISCARD
    termios_tty.c_cc[VDISCARD] = 0;
#endif
#ifdef VLNEXT
    termios_tty.c_cc[VLNEXT] = 0; 
#endif
	
    /* minimum 1 character in one read call and timeout to 100 ms */
    termios_tty.c_cc[VMIN] = 1;
    termios_tty.c_cc[VTIME] = 10;

    SYSCALL(err = tcsetattr(local->fd, TCSANOW, &termios_tty));
    if (err == -1) {
	ErrorF("SpOrb tcsetattr TCSANOW error : %s\n", strerror(errno));
	return !Success;
    }

#else
    Code for OSs without POSIX tty functions
#endif

    DBG(1, ErrorF("initializing SpaceOrb\n"));
    
  
#ifdef POSIX_TTY
    /* send a START just for the case the tablet would have been stopped */
    SYSCALL(err = tcflow(local->fd, TCION));
    if (err == -1) {
	ErrorF("SpOrb tcflow TCION error : %s\n", strerror(errno));
    }

    /* flush input and output */
    SYSCALL(err = tcflush(local->fd, TCIOFLUSH));
    if (err == -1) {
	ErrorF("SpOrb tcflush TCIOFLUSH error : %s\n", strerror(errno));
    }
#else
    Code for OSs without POSIX tty functions
#endif

    /* maybe read greet packet */

    DBG(2,ErrorF("SpOrb init err = %d\n",err))
  
    if (err < 0) {
	SYSCALL(close(local->fd));
	return !Success;
    }

    if (xf86Verbose)
	ErrorF("%s SpaceOrb Initialized\n",
	       XCONFIG_PROBED);

    return Success;
}

/*
 ***************************************************************************
 *
 * xf86srbOpenDevice --
 *	Open the physical device and init information structs.
 *
 ***************************************************************************
 */
static int
xf86srbOpenDevice(DeviceIntPtr       psrb)
{
    LocalDevicePtr	local = (LocalDevicePtr)psrb->public.devicePrivate;
    SpOrbDevicePtr	priv = (SpOrbDevicePtr)PRIVATE(psrb);
    SpOrbCommonPtr	common = priv->common;
    double		screenRatio, tabletRatio;
    int			gap;
    int			loop;
    
    if (local->fd < 0) {
	if (xf86srbOpen(local) != Success) {
	    if (local->fd >= 0) {
		SYSCALL(close(local->fd));
	    }
	    local->fd = -1;
	}
	else {
	    /* report the file descriptor to all devices */
	    for(loop=0; loop<common->srbNumDevices; loop++) {
		common->srbDevices[loop]->fd = local->fd;
	    }
	}
    }

    /* Set the real values */
    InitValuatorAxisStruct(psrb, 0, -512, 511, 9600,0,9600);
    InitValuatorAxisStruct(psrb, 1, -512, 511, 9600,0,9600);
    InitValuatorAxisStruct(psrb, 2, -512, 511, 9600,0,9600);
    InitValuatorAxisStruct(psrb, 3, -512, 511, 9600,0,9600);
    InitValuatorAxisStruct(psrb, 4, -512, 511, 9600,0,9600);
    InitValuatorAxisStruct(psrb, 5, -512, 511, 9600,0,9600);
    
    return (local->fd != -1);
}

/*
 ***************************************************************************
 *
 * xf86srbClose --
 *
 ***************************************************************************
 */
static void
xf86srbClose(LocalDevicePtr	local)
{
    SpOrbDevicePtr	priv = (SpOrbDevicePtr)local->private;
    SpOrbCommonPtr	common = priv->common;
    int			loop;
    int			num = 0;
    
    for(loop=0; loop<common->srbNumDevices; loop++) {
	if (common->srbDevices[loop]->fd >= 0) {
	    num++;
	}
    }
    DBG(4, ErrorF("SpOrb number of open devices = %d\n", num));
    
    if (num == 1) {		    
	SYSCALL(close(local->fd));
    }
    
    local->fd = -1;
}

/*
 ***************************************************************************
 *
 * xf86srbProc --
 *      Handle the initialization, etc. of a SpOrb
 *
 ***************************************************************************
 */
static int
xf86srbProc(DeviceIntPtr       psrb,
	    int                what)
{
    CARD8                 map[(32 << 4) + 1];
    int                   nbaxes;
    int                   nbbuttons;
    KeySymsRec            keysyms;
    int                   loop;
    LocalDevicePtr        local = (LocalDevicePtr)psrb->public.devicePrivate;
    SpOrbDevicePtr        priv = (SpOrbDevicePtr)PRIVATE(psrb);
  
    DBG(2, ErrorF("BEGIN xf86srbProc dev=0x%x priv=0x%x flags=%d what=%d\n",
		  psrb, priv, priv->flags, what));
  
    switch (what)
	{
	case DEVICE_INIT: 
	    DBG(1, ErrorF("xf86srbProc psrb=0x%x what=INIT\n", psrb));
      
	    nbaxes = 6;			/* X, Y, Z, U, V, R */
	    nbbuttons = 7;
	    
	    for(loop=1; loop<=nbbuttons; loop++) map[loop] = loop;

	    if (InitButtonClassDeviceStruct(psrb,
					    nbbuttons,
					    map) == FALSE) {
		ErrorF("unable to allocate Button class device\n");
		return !Success;
	    }
      
	    if (InitFocusClassDeviceStruct(psrb) == FALSE) {
		ErrorF("unable to init Focus class device\n");
		return !Success;
	    }
          
	    if (InitPtrFeedbackClassDeviceStruct(psrb,
						 xf86srbControlProc) == FALSE) {
		ErrorF("unable to init ptr feedback\n");
		return !Success;
	    }
	    
	    if (InitProximityClassDeviceStruct(psrb) == FALSE) {
		ErrorF("unable to init proximity class device\n");
		return !Success;
	    }

	    if (InitKeyClassDeviceStruct(psrb, &SpOrb_keysyms, NULL) == FALSE) {
		ErrorF("unable to init key class device\n"); 
		return !Success;
	    }

	    if (InitValuatorClassDeviceStruct(psrb, 
					      nbaxes,
					      xf86GetMotionEvents, 
					      local->history_size,
					      (priv->flags & ABSOLUTE_FLAG) 
					      ? Absolute : Relative)
		== FALSE) {
		ErrorF("unable to allocate Valuator class device\n"); 
		return !Success;
	    }
	    else {
		/* allocate the motion history buffer if needed */
		xf86MotionHistoryAllocate(local);

		AssignTypeAndName(psrb, local->atom, local->name);
	    }

	    /* open the device to gather informations */
	    xf86srbOpenDevice(psrb);

	    break; 
      
	case DEVICE_ON:
	    DBG(1, ErrorF("xf86srbProc psrb=0x%x what=ON\n", psrb));

	    if ((local->fd < 0) && (!xf86srbOpenDevice(psrb))) {
		return !Success;
	    }      
	    AddEnabledDevice(local->fd);
	    psrb->public.on = TRUE;
	    break;
      
	case DEVICE_OFF:
	    DBG(1, ErrorF("xf86srbProc  psrb=0x%x what=%s\n", psrb,
			  (what == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
	    if (local->fd >= 0)
		RemoveEnabledDevice(local->fd);
	    psrb->public.on = FALSE;
	    break;
      
	case DEVICE_CLOSE:
	    DBG(1, ErrorF("xf86srbProc  psrb=0x%x what=%s\n", psrb,
			  (what == DEVICE_CLOSE) ? "CLOSE" : "OFF"));
	    xf86srbClose(local);
	    break;

	default:
	    ErrorF("unsupported mode=%d\n", what);
	    return !Success;
	    break;
	}
    DBG(2, ErrorF("END   xf86srbProc Success what=%d dev=0x%x priv=0x%x\n",
		  what, psrb, priv));
    return Success;
}

/*
 ***************************************************************************
 *
 * xf86srbChangeControl --
 *
 ***************************************************************************
 */
static int
xf86srbChangeControl(LocalDevicePtr	local,
		     xDeviceCtl		*control)
{
    return(Success);
}

/*
 ***************************************************************************
 *
 * xf86srbSwitchMode --
 *
 ***************************************************************************
 */
static int
xf86srbSwitchMode(ClientPtr	client,
		  DeviceIntPtr	dev,
		  int		mode)
{
    LocalDevicePtr        local = (LocalDevicePtr)dev->public.devicePrivate;
    SpOrbDevicePtr        priv = (SpOrbDevicePtr)local->private;

    DBG(3, ErrorF("xf86srbSwitchMode dev=0x%x mode=%d\n", dev, mode));
  
    if (mode == Absolute) {
	priv->flags = priv->flags | ABSOLUTE_FLAG;
    }
    else {
	if (mode == Relative) {
	    priv->flags = priv->flags & ~ABSOLUTE_FLAG; 
	}
	else {
	    DBG(1, ErrorF("xf86srbSwitchMode dev=0x%x invalid mode=%d\n", dev,
			  mode));
	    return BadMatch;
	}
    }
    return Success;
}

/*
 ***************************************************************************
 *
 * xf86srbAllocate --
 *
 ***************************************************************************
 */
static LocalDevicePtr
xf86srbAllocate(char *  name,
                int     flag)
{
    LocalDevicePtr        local = (LocalDevicePtr) xalloc(sizeof(LocalDeviceRec));
    SpOrbDevicePtr        priv = (SpOrbDevicePtr) xalloc(sizeof(SpOrbDeviceRec));
    SpOrbCommonPtr        common = (SpOrbCommonPtr) xalloc(sizeof(SpOrbCommonRec));
#if defined(sun) && !defined(i386)
    char			*dev_name = (char *) getenv("SpOrb_DEV");  
#endif

    local->name = name;
    local->flags = 0; /*XI86_NO_OPEN_ON_INIT;*/
#if !defined(sun) || defined(i386)
    local->device_config = xf86srbConfig;
#endif
    local->device_control = xf86srbProc;
    local->read_input = xf86srbReadInput;
    local->control_proc = xf86srbChangeControl;
    local->close_proc = xf86srbClose;
    local->switch_mode = xf86srbSwitchMode;
    local->fd = -1;
    local->atom = 0;
    local->dev = NULL;
    local->private = priv;
    local->private_flags = 0;
    local->history_size  = 0;

    priv->flags = flag;			/* various flags (device type, absolute, first touch...) */
    priv->oldx = -1;			/* previous X position */
    priv->oldy = -1;			/* previous Y position */
    priv->oldz = -1;			/* previous Z position */
    priv->oldu = -1;			/* previous U position */
    priv->oldv = -1;			/* previous V position */
    priv->oldr = -1;			/* previous R position */
    priv->oldButtons = 0;		/* previous buttons state */
    priv->factorX = 0.1;		/* X factor */
    priv->factorY = 0.1;		/* Y factor */
    priv->offsetX = 0;			/* X offset */
    priv->offsetY = 0;			/* Y offset */
    priv->common = common;		/* common info pointer */
    
    common->srbDevice = "";		/* device file name */
#if defined(sun) && !defined(i386)
    if (dev_name) {
	common->srbDevice = (char*) xalloc(strlen(dev_name)+1);
	strcpy(common->srbDevice, dev_name);
	ErrorF("xf86srbOpen port changed to '%s'\n", common->srbDevice);
    }
#endif
    common->srbFlags = 0;		/* various flags */
    common->srbDevices = (LocalDevicePtr*) xalloc(sizeof(LocalDevicePtr));
    common->srbDevices[0] = local;
    common->srbNumDevices = 1;		/* number of devices */
    common->srbIndex = 0;		/* number of bytes read */
    common->srbPktLength[0] = SORB_BUTTON_SIZE;		/* length of a packet */
    common->srbPktLength[1] = SORB_MOTION_SIZE;		/* length of a packet */
    common->srbPktLength[2] = SORB_GREETING_SIZE;		/* length of a packet */

    return local;
}

/*
 ***************************************************************************
 *
 * xf86srbAllocateSpBall --
 *
 ***************************************************************************
 */
static LocalDevicePtr
xf86srbAllocateSpBall()
{
    LocalDevicePtr        local = xf86srbAllocate(XI_SPORB, SPORB_ID);

    local->type_name = "SpaceTec SpaceBall";
    return local;
}

/*
 ***************************************************************************
 *
 * SpOrb Stylus device association --
 *
 ***************************************************************************
 */
DeviceAssocRec SpOrb_assoc =
{
    SPORB_SECTION_NAME,		/* config_section_name */
    xf86srbAllocateSpBall		/* device_allocate */
};

#ifdef DYNAMIC_MODULE
/*
 ***************************************************************************
 *
 * entry point of dynamic loading
 *
 ***************************************************************************
 */
int
#ifndef DLSYM_BUG
init_module(unsigned long	server_version)
#else
init_xf86SpOrb(unsigned long    server_version)
#endif
{
    xf86AddDeviceAssoc(&SpOrb_assoc);

    if (server_version != XF86_VERSION_CURRENT) {
	ErrorF("Warning: SpOrb module compiled for version%s\n", XF86_VERSION);
	return 0;
    } else {
	return 1;
    }
}
#endif

/* end of xf86SpOrb.c */

