/*
 * Copyright (c) 1987 by Sun Microsystems, Inc.
 */
#ident	"@(#)tblt.c	1.3	90/05/17 SMI"	/* SunOS4.0 1.52	*/
/*
 * Mouse streams module.
 */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/termios.h>
#include <sys/termio.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/tty.h>
#include <sys/strtty.h>
#include <sys/systm.h>
#include <sys/kmem.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/errno.h>
#include <sys/reboot.h>
#include <sys/vuid_event.h>
#include "tbltio.h"
#include "tbltreg.h"
#include <sys/modctl.h>
#include <sys/cmn_err.h>
/*   
 * This is the loadable module wrapper.    
 */    
extern struct streamtab tblt_info;
static struct fmodsw fsw = {
	"tblt",
	&tblt_info, 
	D_OLD
};
/*
 * Module linkage information for the kernel.
 */
extern struct mod_ops mod_strmodops;
static struct modlstrmod modlstrmod = {
	&mod_strmodops, "streams module for tablet", &fsw
};
static struct modlinkage modlinkage = {
	MODREV_1, &modlstrmod, NULL
};
static int module_keepcnt = 0;	/* ==0 means the module is unloadable */
_init()
{   
        return (mod_install(&modlinkage));    
}
_info(modinfop)
	struct modinfo *modinfop;
{
	return (mod_info(&modlinkage, modinfop));
}
/*
 * Note: We use spl5 for the tablet because it is functionally the
 * same as spl6 and the tty mechanism is using spl5.  The original
 * code that was doing its own select processing was using spl6.
 */
#define	spl_tblt	spl5
#define ABS(x)		((x) < 0 ? -(x) : (x))
#define BYTECLIP(x)	(char)((x) > 127 ? 127 : ((x) < -128 ? -128 : (x)))
struct tbltdata {
    struct tblt_softc tbltd_softc;
    queue_t        *tbltd_readq;		/* upstream read queue */
    mblk_t         *tbltd_iocpending;	/* "ioctl" awaiting buffer */
    int             tbltd_flags;		/* random flags */
    int             tbltd_iocid;		/* ID of "ioctl" being waited for */
    int             tbltd_iocerror;	/* error return from "ioctl" */
    char            tbltd_oldbutt;	/* button state at last sample */
    char            tbltd_oldpressure;	/* pressure level at last sample */
    char            tbltd_oldproximity;	/* pressure level at last sample */
    short           tbltd_state;		/* state counter for input routine */
    short           tbltd_jitter;
    int		    tbltd_timeout_id;	/* id returned by timeout() */
    short	    tbltd_pen_state;
    int		    tbltd_baud_rate;
};
#define TBLT_DEV_MSC	0
#define TBLT_DEV_XEROX	1
#define TBLT_DEV_PEN	2
#define TBLT_PEN_OUTOFPROX	0
#define TBLT_PEN_ENTERPROX	1
#define TBLT_PEN_INPROX		2
#define	TBLT_OPEN		0x00000001	/* tablet is open for business */
#define	TBLT_IOCWAIT	0x00000002	/* "open" waiting for ioctl to finish */
#define	TBLT_IOCTOSS	0x00000004	/* Toss ioct return */
/*
 * Input routine states. See tbltinput().
 */
#define	TBLT_WAIT_BUTN		0
#define	TBLT_WAIT_X		1
#define	TBLT_WAIT_Y		2
#define	TBLT_WAIT_X2		3
#define	TBLT_WAIT_Y2		4
#define TBLT_PWAIT_LEAD		5
#define TBLT_PWAIT_X1		6
#define TBLT_PWAIT_X2		7
#define TBLT_PWAIT_Y		8
#define TBLT_PWAIT_Y1		9
#define TBLT_PWAIT_Y2		10
#define TBLT_PWAIT_SWITCHES	11
int             tblt_overrun_msg;	/* Message when overrun circular buffer */
int             tblt_overrun_cnt;	/* Increment when overrun circular buffer */
/*
 * Max pixel delta of jitter controlled. As this number increases the jumpiness
 * of the tblt increases, i.e., the coarser the motion for medium speeds. 
 */
int             tlbt_jitter_thresh = 0;
/*
 * tlbt_jitter_thresh is the maximum number of jitters suppressed. Thus,
 * hz/tlbt_jitter_thresh is the maximum interval of jitters suppressed. As
 * tlbt_jitter_thresh increases, a wider range of jitter is suppressed. However,
 * the more inertia the tablet seems to have, i.e., the slower the tablet is to
 * react. 
 */
/*
 * Measure how many (tlbt_speed_count) tblt deltas exceed threshold
 * (tlbt_speedlimit). If tlbt_speedlaw then throw away deltas over tlbt_speedlimit.
 * This is to keep really bad mice that jump around from getting too far. 
 */
int             tlbt_speedlimit = 48;
int             tlbt_speedlaw = 0;
int             tlbt_speed_count;
int             tlbtjitterrate = 12;
#define	JITTER_TIMEOUT (hz/tlbtjitterrate)
int             tbltjittertimeout;/* Timeout used when tblttimeout in effect */
/*
 * Mouse buffer size in bytes.  Place here as variable so that one could
 * massage it using adb if it turns out to be too small. 
 */
int             TBLT_BUF_BYTES = 4096;
int             TBLT_DEBUG;
int             DARYL = 1;
/*
 * Pen/tablet initialization string
 */
char	tblt_pen_init_press[] = "SP\rPH1\rAS1\rSU2\rAL2\rIT2\rST\r";
char	tblt_pen_init_puck[] = "SP\rPH0\rAS1\rSU2\rAL2\rIT2\rST\r";
/*
 * Most of these should be "void", but the people who defined the "streams"
 * data structures for S5 didn't understand data types.
 */
static int tbltopen(queue_t *, int, int, int);
static int tbltclose(queue_t *);
static int tbltwput(queue_t *, mblk_t *);
static int tbltrput(queue_t *, mblk_t *);
static int tbltrserv(queue_t *);
static struct module_info tbltmiinfo = {
	0,
	"tblt",
	0,
	INFPSZ,
	2048,
	128
};
static struct qinit tbltrinit = {
	tbltrput,
	tbltrserv,
	tbltopen,
	tbltclose,
	NULL,
	&tbltmiinfo
};
static struct module_info tbltmoinfo = {
	0,
	"tblt",
	0,
	INFPSZ,
	2048,
	128
};
static struct qinit tbltwinit = {
	tbltwput,
	NULL,
	tbltopen,
	tbltclose,
	NULL,
	&tbltmoinfo
};
struct streamtab tblt_info = {
	&tbltrinit,
	&tbltwinit,
	NULL,
	NULL,
};
static void	tbltresched(long);
static void	tbltreioctl(long);
static void	tbltioctl(queue_t *, mblk_t *);
static int 	tblt_getparms(register Tblt_parms *, struct tbltdata *);
static int 	tblt_setparms(register Tblt_parms *, struct tbltdata *);
static void	tbltflush(struct tbltdata *);
static void 	tbltinput(register struct tbltdata *, char);
static void	tbltincr(struct tbltdata *);
static void 	tblt_abs_pos(register struct tbltdata *);
static void 	tblt_rel_pos(register struct tbltdata *);
static void 	tblt_switches(register struct tbltdata *, char);
static int 	getnextbaudrate(int);
static void 	tblt_pen_set_switch_mode(queue_t *, register struct tbltdata *, int);
static void 	copy(char *, char *, int);
extern void uniqtime();
/*
 * Open a tablet.
 */
/*ARGSUSED*/
static int
tbltopen(queue_t *q,int dev, int oflag, int sflag)
{
    register struct tabletbuf *b;
    register struct tblt_softc *tblt;
    register struct tbltdata *tbltd;
    mblk_t         *mp;
    mblk_t         *set_msg;
    mblk_t         *datap;
    register struct iocblk *iocb;
    register struct termios *cb;
#ifndef _STYPES
    cred_t *crp;
#endif
    if (q->q_ptr != NULL)
	return (0);		/* already attached */
    if (sflag != MODOPEN)
	return (OPENFAIL);
    /*
     * Allocate an tbltdata structure.
     */
    tbltd = (struct tbltdata *)kmem_zalloc(sizeof (struct tbltdata), 0);
    /*
     * Set up queue pointers, so that the "put" procedure will accept
     * the reply to the "ioctl" message we send down.
     */
    q->q_ptr = (caddr_t)tbltd;
    WR(q)->q_ptr = (caddr_t)tbltd;
    /*
     * Setup tty modes.
     */
    if ((mp = allocb(sizeof (struct iocblk), BPRI_HI)) == NULL) {
	if (strwaitbuf(sizeof (struct iocblk), BPRI_HI))
	    return(OPENFAIL);
    }
    if ((datap = allocb(sizeof (struct termios), BPRI_HI)) == NULL) {
	if (strwaitbuf(sizeof (struct termios), BPRI_HI)) {
	    freemsg(mp);
	    return(OPENFAIL);
	}
    }
    iocb = (struct iocblk *)mp->b_wptr;
#ifndef _STYPES
    iocb->ioc_cr = crp = crget();
#endif
    iocb->ioc_cmd = TCSETSF;
    iocb->ioc_uid = 0;
    iocb->ioc_gid = 0;
    iocb->ioc_id = getiocseqno();
    iocb->ioc_count = sizeof (struct iocblk);
    iocb->ioc_error = 0;
    iocb->ioc_rval = 0;
    mp->b_wptr += (sizeof *iocb)/(sizeof *datap->b_wptr);
    mp->b_datap->db_type = M_IOCTL;
    cb = (struct termios *)datap->b_wptr;
    cb->c_iflag = 0;
    cb->c_oflag = 0;
    cb->c_cflag = CREAD|CS8|B9600;
    cb->c_lflag = 0;
    bzero((caddr_t)cb->c_cc, NCCS);
    datap->b_wptr += (sizeof *cb)/(sizeof *datap->b_wptr);
    datap->b_datap->db_type = M_DATA;
    mp->b_cont = datap;
    tbltd->tbltd_flags |= TBLT_IOCWAIT;	/* indicate that we're waiting for */
    tbltd->tbltd_iocid = iocb->ioc_id;	/* this response */
    putnext(WR(q), mp);
    /*
     * Now wait for it.  Let our read queue put routine wake us up
     * when it arrives.
     */
    while (tbltd->tbltd_flags & TBLT_IOCWAIT) {
	if (sleep((caddr_t)&tbltd->tbltd_iocerror, STOPRI|PCATCH)) {
	    ttolwp(curthread)->lwp_error = EINTR;
	    goto error;
	}
    }
    if (ttolwp(curthread)->lwp_error = tbltd->tbltd_iocerror)
	goto error;
    /*
     * Set up private data. 
     */
    tbltd->tbltd_state = TBLT_WAIT_BUTN;
    tbltd->tbltd_pen_state = TBLT_PEN_OUTOFPROX;
    tbltd->tbltd_readq = q;
    tbltd->tbltd_iocpending = NULL;
    tbltd->tbltd_baud_rate = B9600;
    tblt = &tbltd->tbltd_softc;
    /*
     * Allocate buffer and initialize data. 
     */
    if (tblt->tblt_buf == 0) {
	tblt->tblt_bufbytes = TBLT_BUF_BYTES;
	b = (struct tabletbuf *) kmem_zalloc((u_int)tblt->tblt_bufbytes, 0);
	if (b == 0) {
	    ttolwp(curthread)->lwp_error = EINVAL;
	    goto error;
	}
	b->mb_size = 1 + (tblt->tblt_bufbytes - sizeof (struct tabletbuf))
	    / sizeof (struct tabletinfo);
	tblt->tblt_buf = b;
	tblt->tblt_vuidaddr = vuid_first(TABLET_DEVID);
	tblt->pen_prev_x = 0;
	tblt->pen_prev_y = 0;
	tblt->pen_flags = 0;
	tblt->pen_nabs_scale = TBLT_PEN_NLABS_SCALE;
	tblt->pen_dabs_scale = TBLT_PEN_DLABS_SCALE;
	tblt->pen_nrel_scale = TBLT_PEN_NREL_SCALE;
	tblt->pen_drel_scale = TBLT_PEN_DREL_SCALE;
	tblt->pen_press_off_level = 10;
	tblt->pen_press_on_level = 10;
	tblt->pen_dev_setting = TBLT_PEN_DEV_TOGGLE;
	tblt->pen_dev_type= TBLT_PEN_DEV_PRESS;
	if (set_msg=allocb(sizeof(tblt_pen_init_puck),BPRI_MED)){
		copy((char *) (set_msg->b_rptr),
		    tblt_pen_init_puck,
		    sizeof(tblt_pen_init_puck));
		set_msg->b_wptr += sizeof(tblt_pen_init_puck);
		putnext(WR(q),set_msg);
	} else {
		return(OPENFAIL);
	}
	tbltjittertimeout = JITTER_TIMEOUT;
	tbltflush(tbltd);
    }
    tbltd->tbltd_flags = TBLT_OPEN;
#ifdef PEN_PROTOTYPE
#ifdef AUDIBLE_TIP
    tblt->pen_audible_tip = 0;	
#endif
#endif
    /*
     * Tell the module below us that it should return input immediately.
     */
    (void) putnextctl1(WR(q), M_CTL, MC_SERVICEIMM);
#ifndef _STYPES
    crfree(crp);
#endif
    module_keepcnt++;	/* not unloadable */
    return (0);
error:
#ifndef _STYPES
    crfree(crp);
#endif
    bzero((caddr_t) tbltd, sizeof (*tbltd));
    bzero((caddr_t) tblt, sizeof (*tblt));
    return (OPENFAIL);
}
/*
 * Close the tablet 
 */
static int
tbltclose(queue_t *q)
{
    register struct tbltdata *tbltd = (struct tbltdata *)q->q_ptr;
    register struct tblt_softc *tblt;
    /*
     * Tell the module below us that it need not return input immediately.
     */
    (void) putnextctl1(q, M_CTL, MC_SERVICEDEF);
    /*
     * Since we're about to destroy our private data, turn off
     * our open flag first, so we don't accept any more input
     * and try to use that data.
     */
    tbltd->tbltd_flags = 0;
    if (tbltd->tbltd_iocpending != NULL) {
	/*
	 * We were holding an "ioctl" response pending the
	 * availability of an "mblk" to hold data to be passed up;
	 * another "ioctl" came through, which means that "ioctl"
	 * must have timed out or been aborted.
	 */
	freemsg(tbltd->tbltd_iocpending);
	tbltd->tbltd_iocpending = NULL;
    }
    tblt = &tbltd->tbltd_softc;
    /* Free tablet buffer */
    if (tblt->tblt_buf != NULL)
	kmem_free((void *)tblt->tblt_buf, (u_int)tblt->tblt_bufbytes);
    /* Free tbltdata structure */
    kmem_free((void *)tbltd, sizeof (*tbltd));
    module_keepcnt--;	/* might be unloadable */
}
/*
 * Read queue service routine.
 * Turn buffered tablet events into stream messages.
 */
static int
tbltrserv(register queue_t *q)
{
    struct tbltdata *tbltd = (struct tbltdata *)q->q_ptr;
    register struct tblt_softc *tblt;
    register struct tabletbuf *b;
    register struct tabletinfo *mi;
    register int    button_number;
    register int    hwbit, pri;
    mblk_t         *bp;
    hwbit = 0;
    tblt = &tbltd->tbltd_softc;
    b = tblt->tblt_buf;
    pri = spl_tblt();
    while (canputnext(q) && tblt->tblt_oldoff != b->mb_off) {
	mi = &b->mb_info[tblt->tblt_oldoff];
	switch (tblt->tblt_readformat) {
	case TBLT_3BYTE_FORMAT: {
	    register char *cp;
	    if ((bp = allocb(3, BPRI_HI)) != NULL) {
		cp = (char *)bp->b_wptr;
		*cp++ = 0x80 | mi->mi_buttons;
		/* Update read buttons */
		tblt->tblt_prevbuttons = mi->mi_buttons;
		*cp++ = mi->mi_x;
		*cp++ = -mi->mi_y;
		/* lower pri to avoid tablet droppings */
		(void) splx(pri);
		bp->b_wptr = (u_char *)cp;
		putnext(q, bp);
		pri = spl_tblt();
	    } else {
		if (bufcall(3, BPRI_HI, tbltresched, (long)tbltd))
		    return;	/* try again later */
		/* bufcall failed; just pitch this event */
		/* or maybe flush queue? */
	    }
	    tblt->tblt_oldoff++;	/* next event */
	    if (tblt->tblt_oldoff >= b->mb_size)
		tblt->tblt_oldoff = 0;	/* circular buffer wraparound */
		break;
	}
	case TBLT_VUID_FORMAT: {
	    register Firm_event *fep;
	    bp = NULL;
	    switch (tblt->tblt_eventstate) {
	    case EVENT_X:
		/*
		 * Send x if changed.
		 */
		if ((mi->mi_x != 0) || (tblt->pen_flags & TBLT_PEN_ABSOLUTE)) {
		    if ((bp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
			fep = (Firm_event *)bp->b_wptr;
		        if (tblt->pen_flags & TBLT_PEN_ABSOLUTE) {
				fep->id = vuid_id_addr(tblt->tblt_vuidaddr) |
				  vuid_id_offset(TBLT_X_ABSOLUTE);
				fep->pair_type = FE_PAIR_DELTA;
				fep->pair = TBLT_X_DELTA;
				fep->value = mi->mi_pen_x;
			} else {
				fep->id = vuid_id_addr(tblt->tblt_vuidaddr) |
				  vuid_id_offset(TBLT_X_DELTA);
				fep->pair_type = FE_PAIR_ABSOLUTE;
				fep->pair = TBLT_X_ABSOLUTE;
				fep->value = mi->mi_x;
			}
			fep->time = mi->mi_time;
		    } else {
			if (bufcall(sizeof (Firm_event), BPRI_HI, tbltresched,
			  (long)tbltd))
			    return;	/* try again later */
			/* bufcall failed; just pitch this event */
			/* or maybe flush queue? */
			tblt->tblt_eventstate = EVENT_BUT3;
		    }
		}
		break;
	    case EVENT_Y:
		/*
		 * Send y if changed.
		 */
		if ((mi->mi_y != 0) || (tblt->pen_flags & TBLT_PEN_ABSOLUTE)) {
		    if ((bp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
			fep = (Firm_event *)bp->b_wptr;
		        if (tblt->pen_flags & TBLT_PEN_ABSOLUTE) {
				fep->id = vuid_id_addr(tblt->tblt_vuidaddr) |
				  vuid_id_offset(TBLT_Y_ABSOLUTE);
				fep->pair_type = FE_PAIR_DELTA;
				fep->pair = TBLT_Y_DELTA;
				fep->value = mi->mi_pen_y;
			} else {
				fep->id = vuid_id_addr(tblt->tblt_vuidaddr) |
				  vuid_id_offset(TBLT_Y_DELTA);
				fep->pair_type = FE_PAIR_ABSOLUTE;
				fep->pair = TBLT_Y_ABSOLUTE;
				fep->value = -mi->mi_y;
			}
			fep->time = mi->mi_time;
		    } else {
			if (bufcall(sizeof (Firm_event), BPRI_HI, tbltresched,
			  (long)tbltd))
			    return;	/* try again later */
			/* bufcall failed; just pitch this event */
			/* or maybe flush queue? */
			tblt->tblt_eventstate = EVENT_BUT3;
		    }
		}
		break;
	    case EVENT_PRESSURE:
		/*
		 * Send pressure if changed.
		 */
		if (mi->mi_pressure != tblt->tblt_prevpressure) {
		    if ((bp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
			fep = (Firm_event *)bp->b_wptr;
			fep->id = vuid_id_addr(tblt->tblt_vuidaddr) |
				  vuid_id_offset(TBLT_PRESSURE);
			fep->pair_type = FE_PAIR_NONE;
			fep->pair = 0;
			fep->value = mi->mi_pressure;
			tblt->tblt_prevpressure = mi->mi_pressure;
			fep->time = mi->mi_time;
		    } else {
			if (bufcall(sizeof (Firm_event), BPRI_HI, tbltresched,
			  (long)tbltd))
			    return;	/* try again later */
			/* bufcall failed; just pitch this event */
			/* or maybe flush queue? */
			tblt->tblt_eventstate = EVENT_BUT3;
		    }
		}
		break;
	    case EVENT_PROXIMITY:
		/*
		 * Send proximity if changed.
		 */
		if (mi->mi_proximity != tblt->tblt_prevproximity) {
		    if ((bp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
			fep = (Firm_event *)bp->b_wptr;
			fep->id = vuid_id_addr(tblt->tblt_vuidaddr) |
				  vuid_id_offset(TBLT_PROXIMITY);
			fep->pair_type = FE_PAIR_NONE;
			fep->pair = 0;
			fep->value = mi->mi_proximity;
			tblt->tblt_prevproximity = mi->mi_proximity;
			fep->time = mi->mi_time;
		    } else {
			if (bufcall(sizeof (Firm_event), BPRI_HI, tbltresched,
			  (long)tbltd))
			    return;	/* try again later */
			/* bufcall failed; just pitch this event */
			/* or maybe flush queue? */
			tblt->tblt_eventstate = EVENT_BUT3;
		    }
		}
		break;
	    case EVENT_BUT1:
	    case EVENT_BUT2:
	    case EVENT_BUT3:
		/*
		 * Test the button, and send an event for it if it changed.
		 */
		button_number = tblt->tblt_eventstate - EVENT_BUT1;
		hwbit = TBLT_HW_BUT1 >> button_number;
		if ((tblt->tblt_prevbuttons & hwbit) != (mi->mi_buttons & hwbit)) {
		    if ((bp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
			fep = (Firm_event *)bp->b_wptr;
			fep->id = vuid_id_addr(tblt->tblt_vuidaddr) |
			  vuid_id_offset(TBLT_BUTTON_1 + button_number);
			fep->pair_type = FE_PAIR_NONE;
			fep->pair = 0;
			/* Update read buttons and set value */
			if (mi->mi_buttons & hwbit) {
			    fep->value = 0;
			    tblt->tblt_prevbuttons |= hwbit;
			} else {
			    fep->value = 1;
			    tblt->tblt_prevbuttons &= ~hwbit;
			}
			fep->time = mi->mi_time;
		    } else {
			if (bufcall(sizeof (Firm_event), BPRI_HI, tbltresched,
			  (long)tbltd))
			    return;	/* try again later */
			/* bufcall failed; just pitch this event */
			/* or maybe flush queue? */
			tblt->tblt_eventstate = EVENT_BUT3;
		    }
		}
		break;
	    }
	    if (bp != NULL) {
		/* lower pri to avoid tablet droppings */
		(void) splx(pri);
		bp->b_wptr += sizeof (Firm_event);
		putnext(q, bp);
		pri = spl_tblt();
	    }
	    if (tblt->tblt_eventstate == EVENT_BUT3) {
		tblt->tblt_eventstate = EVENT_X;
		tblt->tblt_oldoff++;	/* next event */
		if (tblt->tblt_oldoff >= b->mb_size)
		    tblt->tblt_oldoff = 0;	/* circular buffer wraparound */
	    } else
		tblt->tblt_eventstate++;
	}
	}
    }
}
static void
tbltresched(long tbltdptr)
{
    register queue_t *q;
    register struct tbltdata *tbltd = (struct tbltdata *)tbltdptr;
    if ((q = tbltd->tbltd_readq) != 0)
	qenable(q);	/* run the service procedure */
}
/*
 * Line discipline output queue put procedure: handles M_IOCTL
 * messages.
 */
static int
tbltwput(register queue_t *q, register mblk_t *mp)
{
    /*
     * Process M_FLUSH, and some M_IOCTL, messages here; pass
     * everything else down.
     */
    switch (mp->b_datap->db_type) {
    case M_FLUSH:
	if (*mp->b_rptr & FLUSHW)
	    flushq(q, FLUSHDATA);
	if (*mp->b_rptr & FLUSHR)
	    flushq(RD(q), FLUSHDATA);
    default:
	putnext(q, mp);	/* pass it down the line */
	break;
    case M_IOCTL:
	tbltioctl(q, mp);
	break;
    }
}
static void
tbltreioctl(long tbltdptr)
{
    struct tbltdata *tbltd = (struct tbltdata *)tbltdptr;
    register queue_t *q;
    register mblk_t *mp;
    if ((q = tbltd->tbltd_readq) == 0)
	return;
    if ((mp = tbltd->tbltd_iocpending) != NULL) {
	tbltd->tbltd_iocpending = NULL;	/* not pending any more */
	tbltioctl(WR(q), mp);
    }
}
static void
tbltioctl(register queue_t *q, register mblk_t *mp)
{
    struct tbltdata *tbltd;
    register struct tblt_softc *tblt;
    register struct iocblk *iocp;
    caddr_t         data;
    Vuid_addr_probe *addr_probe;
    u_int           ioctlrespsize;
    int             pri;
    int             err = 0;
    tbltd = (struct tbltdata *)q->q_ptr;
    if (tbltd == 0) {
	err = EINVAL;
	goto out;
    }
    tblt = &tbltd->tbltd_softc;
    iocp = (struct iocblk *)mp->b_rptr;
    if (mp->b_cont != NULL)
	data = (caddr_t)mp->b_cont->b_rptr;
    if (TBLT_DEBUG)
	printf("tbltwput(M_IOCTL,%X,%X)\n", iocp->ioc_cmd, data);
    switch (iocp->ioc_cmd) {
      case VUIDSFORMAT:
	if (*(int *) data == tblt->tblt_readformat)
	    break;
	if ((*(int *)data == TBLT_3BYTE_FORMAT) && 
	    (tblt->pen_flags & TBLT_PEN_ABSOLUTE)) {
		cmn_err(CE_NOTE,
		    "Absolute mode and 3 byte format not support\n");
		err = 1;
		break;
	}
	tblt->tblt_readformat = *(int *) data;
	/*
	 * Flush tablet buffer because the messages upstream of us are in
	 * the old format.
	 */
	tbltflush(tbltd);
	break;
      case VUIDGFORMAT: {
	register mblk_t *datap;
	if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
	    ioctlrespsize = sizeof (int);
	    goto allocfailure;
	}
	*(int *)datap->b_wptr = tblt->tblt_readformat;
	datap->b_wptr += sizeof (int);
	mp->b_cont = datap;
	iocp->ioc_count = sizeof (int);
	break;
      }
      case VUIDSADDR:
	addr_probe = (Vuid_addr_probe *) data;
	if (addr_probe->base != VKEY_FIRST) {
	    err = ENODEV;
	    break;
	}
	tblt->tblt_vuidaddr = addr_probe->data.next;
	break;
      case VUIDGADDR:
	addr_probe = (Vuid_addr_probe *) data;
	if (addr_probe->base != VKEY_FIRST) {
	    err = ENODEV;
	    break;
	}
	addr_probe->data.current = tblt->tblt_vuidaddr;
	break;
      case TBLTIOGETPARMS: {
	register mblk_t *datap;
	if (TBLT_DEBUG)
	    printf("tblt_getparms\n");
	if ((datap = allocb(sizeof (Tblt_parms), BPRI_HI)) == NULL) {
	    ioctlrespsize = sizeof (Tblt_parms);
	    goto allocfailure;
	}
	err = tblt_getparms((Tblt_parms *)datap->b_wptr, tbltd);
	datap->b_wptr += sizeof (Tblt_parms);
	mp->b_cont = datap;
	iocp->ioc_count = sizeof (Tblt_parms);
	break;
      }
      case TBLTIOSETPARMS:
	if (TBLT_DEBUG)
	    printf("tblt_setparms\n");
	err = tblt_setparms((Tblt_parms *)data, tbltd);
	break;
      default:
	putnext(q, mp);	/* pass it down the line */
	return;
    }
out:
    if (err != 0) {
	iocp->ioc_rval = 0;
	iocp->ioc_error = err;
	mp->b_datap->db_type = M_IOCNAK;
    } else {
	iocp->ioc_rval = 0;
	iocp->ioc_error = 0;	/* brain rot */
	mp->b_datap->db_type = M_IOCACK;
    }
    qreply(q, mp);
    return;
allocfailure:
    /*
     * We needed to allocate something to handle this "ioctl", but
     * couldn't; save this "ioctl" and arrange to get called back when
     * it's more likely that we can get what we need.
     * If there's already one being saved, throw it out, since it
     * must have timed out.
     */
    pri = splstr();
    if (tbltd->tbltd_iocpending != NULL)
	freemsg(tbltd->tbltd_iocpending);
    tbltd->tbltd_iocpending = mp;
    (void) splx(pri);
    (void) bufcall(ioctlrespsize, BPRI_HI, tbltreioctl, (long)tbltd);
    return;
}
static int
tblt_getparms(register Tblt_parms *data, struct tbltdata *tbltd)
{
    register struct tblt_softc *tblt = &tbltd->tbltd_softc;
    data->mode = ((tblt->pen_flags & TBLT_PEN_ABSOLUTE) ? 
	    TBLT_ABS_MODE : TBLT_REL_MODE);
    data->pen = tblt->pen_dev_setting;
    data->press_on = tblt->pen_press_on_level;
    data->press_off = tblt->pen_press_off_level;
#ifdef AUDIBLE_TIP
    data->audible_tip = tblt->pen_audible_tip;
#endif
    data->jitter_thresh = tlbt_jitter_thresh;
    data->speed_law = tlbt_speedlaw;
    data->speed_limit = tlbt_speedlimit;
    return 0;
}
static int
tblt_setparms(register Tblt_parms *data, struct tbltdata *tbltd)
{
    register struct tblt_softc *tblt = &tbltd->tbltd_softc;
    int err;
    err = 0;
    if (data->mode == TBLT_ABS_MODE) {
	if (tblt->tblt_readformat == TBLT_3BYTE_FORMAT) {
	    cmn_err(CE_NOTE,"Absolute mode and 3 byte format not support\n");
	    err = 1;
	} else {
	    tblt->pen_flags |= TBLT_PEN_ABSOLUTE;
	}
    } else {
	tblt->pen_flags &= ~TBLT_PEN_ABSOLUTE;
    }
    tblt->pen_dev_setting = data->pen;
    tblt->pen_press_on_level=((data->press_on >= 0) ? data->press_on : 0);
    tblt->pen_press_off_level=((data->press_off >= 0) ? data->press_off : 0);
#ifdef AUDIBLE_TIP
    tblt->pen_audible_tip = data->audible_tip;
#endif
    tlbt_jitter_thresh = data->jitter_thresh;
    tlbt_speedlaw = data->speed_law;
    tlbt_speedlimit = data->speed_limit;
    return (err);
}
static void
tbltflush(register struct tbltdata *tbltd)
{
    register struct tblt_softc *tblt = &tbltd->tbltd_softc;
    int             s = spl_tblt();
    register queue_t *q;
    tblt->tblt_oldoff = 0;
    tblt->tblt_eventstate = EVENT_X;
    tblt->tblt_buf->mb_off = 0;
    tblt->tblt_prevbuttons = TBLT_HW_BUT1 | TBLT_HW_BUT2 | TBLT_HW_BUT3;
    tblt->tblt_prevpressure = 0;
    tblt->tblt_prevproximity = 0;
    tbltd->tbltd_oldbutt = tblt->tblt_prevbuttons;
    if ((q = tbltd->tbltd_readq) != NULL && q->q_next != NULL)
	(void) putnextctl1(q, M_FLUSH, FLUSHR);
    (void) splx(s);
}
/*
 * Mouse read queue put procedure.
 */
static int
tbltrput(queue_t *q, mblk_t *mp)
{
    register struct tbltdata *tbltd = (struct tbltdata *)q->q_ptr;
    register mblk_t *bp;
    register mblk_t *imp;
    register mblk_t *datap;
    register struct iocblk *iocb;
    register struct termios *cb;
    register char *readp;
    struct iocblk *iocp;
#ifndef _STYPES
    cred_t *crp;
#endif
    if (tbltd == 0)
	return;
    switch (mp->b_datap->db_type) {
    case M_FLUSH:
	if (*mp->b_rptr & FLUSHW)
	    flushq(WR(q), FLUSHDATA);
	if (*mp->b_rptr & FLUSHR)
	    flushq(q, FLUSHDATA);
    default:
	putnext(q, mp);
	return;
    case M_BREAK:
        if (tbltd->tbltd_flags & TBLT_IOCTOSS) {
                freemsg(mp);
                return;
        }
        /*
         * Change baud rate.
         */
        if ((imp = allocb(sizeof (struct iocblk), BPRI_HI)) == NULL) {
		return;
        }
        if ((datap = allocb(sizeof (struct termios), BPRI_HI)) == NULL) {
		freemsg(imp);
		return;
        }
        tbltd->tbltd_baud_rate = getnextbaudrate(tbltd->tbltd_baud_rate);
        iocb = (struct iocblk *)imp->b_wptr;
#ifndef _STYPES
	    iocb->ioc_cr = crp = crget();
#endif
        iocb->ioc_cmd = TCSETSF;
        iocb->ioc_uid = 0;
        iocb->ioc_gid = 0;
        iocb->ioc_id = getiocseqno();
        iocb->ioc_count = sizeof (struct iocblk);
        iocb->ioc_error = 0;
        iocb->ioc_rval = 0;
        imp->b_wptr += (sizeof *iocb)/(sizeof *datap->b_wptr);
        imp->b_datap->db_type = M_IOCTL;
        cb = (struct termios *)datap->b_wptr;
        cb->c_iflag = 0;
        cb->c_oflag = 0;
        cb->c_cflag = CREAD|CS8|tbltd->tbltd_baud_rate;
        cb->c_lflag = 0;
        bzero((caddr_t)cb->c_cc, NCCS);
        datap->b_wptr += (sizeof *cb)/(sizeof *datap->b_wptr);
        datap->b_datap->db_type = M_DATA;
        imp->b_cont = datap;
        tbltd->tbltd_flags |= TBLT_IOCTOSS|TBLT_IOCWAIT;
        tbltd->tbltd_iocid = iocb->ioc_id;  
	tbltflush(tbltd);
	flushq(q, FLUSHALL);
        putnext(WR(q), imp);
        freemsg(mp);
#ifndef _STYPES
        crfree(crp);
#endif
        return;
    case M_IOCACK:
    case M_IOCNAK:
	/*
	 * If we are doing an "ioctl" ourselves, check if this
	 * is the reply to that code.  If so, wake up the
	 * "open" routine, and toss the reply, otherwise just
	 * pass it up.
	 */
	iocp = (struct iocblk *)mp->b_rptr;
	if (!(tbltd->tbltd_flags & TBLT_IOCWAIT)
	  || iocp->ioc_id != tbltd->tbltd_iocid) {
	    /*
	     * This isn't the reply we're looking for.  Move along.
	     */
	    putnext(q, mp);
	} else {
	    tbltd->tbltd_flags &= ~TBLT_IOCWAIT;
	    tbltd->tbltd_iocerror = iocp->ioc_error;
	    /*
	     * If we sent down a request to change the baud rate.  This 
	     * is the reply.  Just ignore it.
	     */
	    if (tbltd->tbltd_flags & TBLT_IOCTOSS) {
		tbltd->tbltd_flags &= ~TBLT_IOCTOSS;
		tbltflush(tbltd);
		flushq(q, FLUSHALL);
	    } else {
		    wakeup((caddr_t)&tbltd->tbltd_iocerror);
	    }
	    freemsg(mp);
	}
	return;
     case M_DATA:
	    if (tbltd->tbltd_flags & TBLT_IOCTOSS) {
		freemsg(mp);
		return;
	    } 
	    break;
    }
    /*
     * A data message, consisting of bytes from the tablet.
     * Hand each byte to our input routine.
     */
    bp = mp;
    do {
	readp = (char *)bp->b_rptr;
	while (readp < (char *)bp->b_wptr)
	    tbltinput(tbltd, *readp++);
	bp->b_rptr = (unsigned char *)readp;
    } while ((bp = bp->b_cont) != NULL);	/* next block, if any */
    freemsg(mp);
   }
/*
 * Increment the tablet sample pointer.
 * Called either immediately after a sample or after a jitter timeout.
 */
static void
tbltincr(struct tbltdata  *tbltd)
{
    register struct tblt_softc *tblt = &tbltd->tbltd_softc;
    register struct tabletbuf *b;
    register struct tabletinfo *mi;
    char            oldbutt;
    register short  xc, yc;
    register int    wake;
    register int    speedlimit = tlbt_speedlimit;
    register int    xabs, yabs;
    int             s;
    b = tblt->tblt_buf;
    if (b == NULL)
	return;
    s = spl_tblt();
    mi = &b->mb_info[b->mb_off];
    if (tlbt_speedlaw) {
	xabs = ABS((int) mi->mi_x);
	yabs = ABS((int) mi->mi_y);
	if (xabs > speedlimit || yabs > speedlimit)
	    tlbt_speed_count++;
	if (xabs > speedlimit)
	    mi->mi_x = 0;
	if (yabs > speedlimit)
	    mi->mi_y = 0;
    }
    oldbutt = mi->mi_buttons;
    xc = yc = 0;
    /* See if we need to wake up anyone waiting for input */
    wake = b->mb_off == tblt->tblt_oldoff;
    /* Adjust circular buffer pointer */
    if (++b->mb_off >= b->mb_size) {
	b->mb_off = 0;
	mi = b->mb_info;
    } else {
	mi++;
    }
    /*
     * If over-took read index then flush buffer so that tablet state
     * is consistent. 
     */
    if (b->mb_off == tblt->tblt_oldoff) {
	if (tblt_overrun_msg)
	    printf("Mouse buffer flushed when overrun.\n");
	tbltflush(tbltd);
	tblt_overrun_cnt++;
	mi = b->mb_info;
    }
    /* Remember current buttons and fractional part of x & y */
    mi->mi_buttons = oldbutt;
    mi->mi_x = (char) xc;
    mi->mi_y = (char) yc;
    if (wake)
	qenable(tbltd->tbltd_readq);	/* run the service procedure */
    (void) splx(s);
}
/*
 * The input data coming from the tablet consists of a 7-byte
 * data block:
 *      Byte 1: 1, In Prox, Stylus, Pressure mode, NA, Sign-X, X15-X14
 *      Byte 2: 0, X13-X7
 *      Byte 3: 0, X6-X0
 *      Byte 4: 0, NA, NA, NA, NA, Sign-Y, Y15-Y14
 *      Byte 5: 0, Y13-Y7
 *      Byte 6: 0, Y6-Y0
 *      Byte 7: If pressure mode, then 0, Sign-pressure, P5-P0
 *                      else if Stylus, then 0, NA, NA, NA, NA, NA, Side switch, Tip Switch
 *                      else 0, NA, NA, NA, NA, S2-S0
 *                                      S2 S1 S0        Switch
 *                                      0  0  0         None
 *                                      0  0  1         Top
 *                                      0  1  0         Left
 *                                      0  1  1         Bottom
 *                                      1  0  0         Right
 */
static void
tbltinput(register struct tbltdata *tbltd, char c)
{
    register struct tblt_softc *tblt;
    register struct tabletbuf *b;
    register struct tabletinfo *mi;
    register int    jitter_radius;
    register int    temp;
    mblk_t	 *set_msg;
    tblt = &tbltd->tbltd_softc;
    b = tblt->tblt_buf;
    if (b == NULL)
	return;
    mi = &b->mb_info[b->mb_off];
    /*
     * This will force the first packet to be thrown away.  It is garbage
     * anyway.  Bug in hardware.
     */
    if (tbltd->tbltd_pen_state == TBLT_PEN_OUTOFPROX) {
	tbltd->tbltd_pen_state = TBLT_PEN_ENTERPROX;
	tbltd->tbltd_state = TBLT_PWAIT_LEAD;
	return;
    }
    switch (tbltd->tbltd_state) {
    case TBLT_PWAIT_LEAD:
	/*
	 * Wait for a packet haeder byte.
	 */
	if (!(c & TBLT_PEN_LEADBYTE)) {
		return;
	}
	/*
	 * Check that we are still in proximity.  If not go back to OUTOFPROX
	 */
	 /*********
	if (!(c & TBLT_PEN_PROX)) {
		tbltd->tbltd_pen_state = TBLT_PEN_OUTOFPROX;
	}
	**********/
	/*
	 * Save control information for later use
	 */
	if (c & TBLT_PEN_DEVICE) {
		tblt->pen_flags |= TBLT_PEN_DEVICE;
	} else {
		tblt->pen_flags &= ~TBLT_PEN_DEVICE;
	}
	if (c & TBLT_PEN_TIP_PRESS) {
		tblt->pen_flags |= TBLT_PEN_TIP_PRESS;
	} else {
		tblt->pen_flags &= ~TBLT_PEN_TIP_PRESS;
	}
	if ((tblt->pen_dev_setting == TBLT_PEN_DEV_PRESS_N_SIDE) ||
	    (tblt->pen_dev_setting == TBLT_PEN_DEV_PRESS)) {
		if (tblt->pen_flags & TBLT_PEN_DEVICE) {
			/*
			 * We have one of the two pressure pens 
			 */
			if (tblt->pen_dev_type != TBLT_PEN_DEV_PRESS) {
				tblt_pen_set_switch_mode(WR(tbltd->tbltd_readq), tbltd,
				    TBLT_PEN_DEV_PRESS);
				/*
				 * Just flushed queues.  
				 * Wait for next lead byte 
				 */
				return; 
			}
		} else { /* We have the puck */
			if (tblt->pen_dev_type != TBLT_PEN_DEV_TOGGLE) {
				tblt_pen_set_switch_mode(WR(tbltd->tbltd_readq), tbltd,
				    TBLT_PEN_DEV_TOGGLE);
				/*
				 * Just flushed queues.  
				 * Wait for next lead byte 
				 */
				return; 
			}
		}
	} else if ((tblt->pen_dev_setting == TBLT_PEN_DEV_TOGGLE_N_SIDE) ||
	           (tblt->pen_dev_setting == TBLT_PEN_DEV_TOGGLE)) {
		if (tblt->pen_dev_type != TBLT_PEN_DEV_TOGGLE) {
			/*
			 * We have a puck and or toggle tip pen.
			 */
			tblt_pen_set_switch_mode(WR(tbltd->tbltd_readq), tbltd,
			    TBLT_PEN_DEV_TOGGLE);
			/*
			 * Just flushed queues.  
			 * Wait for next lead byte 
			 */
			return; 
		}
	}
	if (c & TBLT_PEN_PROX)
	    mi->mi_proximity = 1;
        else
	    mi->mi_proximity = 0;
	mi->mi_pen_x = (unsigned int)(c & TBLT_PEN_X) << X_SHIFT;
	uniqtime(&mi->mi_time);	/* record time when sample arrived */
	tbltd->tbltd_state = TBLT_PWAIT_X1; 
	break; 
    case TBLT_PWAIT_X1:
	mi->mi_pen_x |= (unsigned int)c << X1_SHIFT;
	tbltd->tbltd_state = TBLT_PWAIT_X2;
	break;
    case TBLT_PWAIT_X2:
	mi->mi_pen_x |= (unsigned int)c;
	tbltd->tbltd_state = TBLT_PWAIT_Y;
	break;
    case TBLT_PWAIT_Y:
	mi->mi_pen_y = (unsigned int)(c & TBLT_PEN_Y) << Y_SHIFT;
	tbltd->tbltd_state = TBLT_PWAIT_Y1;
	break;
    case TBLT_PWAIT_Y1:
	mi->mi_pen_y |= (unsigned int)c << Y1_SHIFT;
	tbltd->tbltd_state = TBLT_PWAIT_Y2;
	break;
    case TBLT_PWAIT_Y2:
	mi->mi_pen_y |= (unsigned int)c;
	if (tblt->pen_flags & TBLT_PEN_ABSOLUTE) {
		tblt_abs_pos(tbltd);
	} else {
		tblt_rel_pos(tbltd);
	}
	tblt->pen_prev_x = mi->mi_pen_x;
	tblt->pen_prev_y = mi->mi_pen_y;
	tbltd->tbltd_state = TBLT_PWAIT_SWITCHES;
	break;
    case TBLT_PWAIT_SWITCHES:
	tbltd->tbltd_state = TBLT_PWAIT_LEAD;
	if (!(mi->mi_proximity))
	    tbltd->tbltd_pen_state = TBLT_PEN_OUTOFPROX;
	else
	    tbltd->tbltd_pen_state = TBLT_PEN_INPROX;
	tblt_switches(tbltd, c);
	if (tbltd->tbltd_jitter) {
		untimeout(tbltd->tbltd_timeout_id);
		tbltd->tbltd_jitter = 0;
	}
	/*
	 * If we are not in absolute mode and the button did not change,
	 * see if we moved less than jitter amount.
	 */
	if (!(tblt->pen_flags & TBLT_PEN_ABSOLUTE) && 
	    (mi->mi_buttons == tbltd->tbltd_oldbutt) &&
	    (mi->mi_proximity == tbltd->tbltd_oldproximity) &&
	    (mi->mi_pressure == tbltd->tbltd_oldpressure)) {
		if (mi->mi_x == 0 && mi->mi_y == 0) {
			return;	/* no, position did not change - boring event */
		}
		/*
		* Did the tablet move more than the jitter threshhold?
		*/
		jitter_radius = tlbt_jitter_thresh;
		if (ABS((int) mi->mi_x) <= jitter_radius &&
		    ABS((int) mi->mi_y) <= jitter_radius) {
			/*
			* Mouse moved less than the jitter threshhold. Don't 
			* indicate an event; keep accumulating motions. 
			* After "tbltjittertimeout" ticks expire, treat the 
			* accumulated delta as the real delta.
			*/
			tbltd->tbltd_jitter = 1;
			tbltd->tbltd_timeout_id =
			timeout((void (*)())tbltincr, (caddr_t) tbltd, 
			    tbltjittertimeout);
			return;
		}
	}
	tbltd->tbltd_oldbutt = mi->mi_buttons;
	tbltd->tbltd_oldpressure = mi->mi_pressure;
	tbltd->tbltd_oldproximity = mi->mi_proximity;
	tbltincr(tbltd);
	break;
    default:
	/* 
	 * Bad state, lets wait for a lead byte
	 */
	tbltd->tbltd_state = TBLT_PWAIT_LEAD;
    }
}
static void
tblt_abs_pos(register struct tbltdata *tbltd)
{
	register struct tblt_softc *tblt;
	register struct tabletbuf *b;
	register struct tabletinfo *mi;
	tblt = &tbltd->tbltd_softc;
	b = tblt->tblt_buf;
	if (b == NULL)
		return;
	mi = &b->mb_info[b->mb_off];
	/*
	 * Scale tablet
	 */
	mi->mi_pen_x = mi->mi_pen_x * tblt->pen_nabs_scale / tblt->pen_dabs_scale;
	mi->mi_pen_y = mi->mi_pen_y * tblt->pen_nabs_scale / tblt->pen_dabs_scale;
	mi->mi_pen_x -= TBLT_PEN_XSTART;
	if (mi->mi_pen_x > TBLT_PEN_XMAX) mi->mi_pen_x = TBLT_PEN_XMAX;
	else if (mi->mi_pen_x < 0) mi->mi_pen_x = 0;
	mi->mi_pen_y -= TBLT_PEN_YSTART;
	if (mi->mi_pen_y > TBLT_PEN_YMAX) mi->mi_pen_y = TBLT_PEN_YMAX;
	else if (mi->mi_pen_y < 0) mi->mi_pen_y = 0;
}
static void
tblt_rel_pos(register struct tbltdata *tbltd)
{
	register struct tblt_softc *tblt;
	register struct tabletbuf *b;
	register struct tabletinfo *mi;
	tblt = &tbltd->tbltd_softc;
	b = tblt->tblt_buf;
	if (b == NULL)
		return;
	mi = &b->mb_info[b->mb_off];
	/*
	 * Scale tablet
	 */
	mi->mi_pen_x = mi->mi_pen_x * tblt->pen_nrel_scale / tblt->pen_drel_scale;
	mi->mi_pen_y = mi->mi_pen_y * tblt->pen_nrel_scale / tblt->pen_drel_scale;
	if (tbltd->tbltd_pen_state == TBLT_PEN_ENTERPROX) {
		mi->mi_x = 0;
		mi->mi_y = 0;
		tblt->pen_prev_x = mi->mi_pen_x;
		tblt->pen_prev_y = mi->mi_pen_y;
		return;
	}
	mi->mi_x = mi->mi_pen_x - tblt->pen_prev_x;
	mi->mi_y = mi->mi_pen_y - tblt->pen_prev_y;
}
static void
tblt_switches(register struct tbltdata *tbltd, char button)
{
    register struct tblt_softc *tblt;
    register struct tabletbuf *b;
    register struct tabletinfo *mi;
    mblk_t	 *bp;
    
    tblt = &tbltd->tbltd_softc;
    b = tblt->tblt_buf;
    if (b == NULL)
	return;
    mi = &b->mb_info[b->mb_off];
    if (tblt->pen_flags & TBLT_PEN_DEVICE) {
	if (tblt->pen_flags & TBLT_PEN_TIP_PRESS) {
			/*
			 * Invert and shift so range is between 0 and 0x40
			 */
      	    if (button & TBLT_PEN_SIGN)
	        button = (button + 0x20) & 0x3f;
            else
	        button += 0x20;
			/*
			 * Determine if side switch is pressed for menu
			 * if we have the pressure pen with side switch.
			 */
	    if ((tblt->pen_dev_setting == TBLT_PEN_DEV_PRESS_N_SIDE) &&
			    (button > 0x2a)) {
		mi->mi_buttons = (TBLT_HW_BUT1|TBLT_HW_BUT2);
	    } else {
                if (mi->mi_proximity) 
                    mi->mi_pressure = button;
                else                     
                    mi->mi_pressure = 0;
/**************************
		if (tblt->pen_flags & TBLT_PEN_TIP_ACTIVE) {
		    if (button > tblt->pen_press_off_level) {
			mi->mi_buttons = (TBLT_HW_BUT2|TBLT_HW_BUT3);
		    } else {
		        mi->mi_buttons = (TBLT_HW_BUT1|
					  TBLT_HW_BUT2|TBLT_HW_BUT3);
			tblt->pen_flags &= ~TBLT_PEN_TIP_ACTIVE;
		    }
	        } else {
		    if (button >= tblt->pen_press_on_level) {
			mi->mi_buttons = (TBLT_HW_BUT2|TBLT_HW_BUT3);
			tblt->pen_flags |= TBLT_PEN_TIP_ACTIVE;
		    } else {
			mi->mi_buttons = (TBLT_HW_BUT1|
					  TBLT_HW_BUT2|TBLT_HW_BUT3);
		    }
		}
**************************/
	    } /* TBLT_PEN_DEV_PRESS_N_SIDE */
	} /* TBLT_PEN_TIP_PRESS */
	else {
	    switch (button & 0x07) {
	    case 1:
		mi->mi_buttons = (TBLT_HW_BUT2 | TBLT_HW_BUT3);
		break;
	    case 2:
		mi->mi_buttons = (TBLT_HW_BUT1 | TBLT_HW_BUT2);
		break;
	    default:
		mi->mi_buttons = (TBLT_HW_BUT1|TBLT_HW_BUT2|TBLT_HW_BUT3);
		break;
	    }
	}
    }  /* TBLT_PEN_DEVICE */
    else {
	switch (button & 0x07) {
	case 1:
	case 3:
	    mi->mi_buttons = (TBLT_HW_BUT1 | TBLT_HW_BUT3);
	    break;
	case 2:
	    mi->mi_buttons = (TBLT_HW_BUT2 | TBLT_HW_BUT3);
	    break;
	case 4:
	    mi->mi_buttons = (TBLT_HW_BUT1 | TBLT_HW_BUT2);
	    break;
	default:
	    mi->mi_buttons = (TBLT_HW_BUT1|TBLT_HW_BUT2|TBLT_HW_BUT3);
	}
    }
}
static int
getnextbaudrate(int b)
{
        switch (b) {
        case B1200:
                return(B2400);
        case B9600:
                return(B19200);
        case B19200:
	default:
                return(B1200);
        }
}
static void
tblt_pen_set_switch_mode(queue_t *q, register struct tbltdata *tbltd, int mode)
{
	mblk_t	 *set_msg;
	register struct tblt_softc *tblt;
	tblt = &tbltd->tbltd_softc;
	switch (mode) {
	case TBLT_PEN_DEV_TOGGLE:
		if (set_msg=allocb(sizeof(tblt_pen_init_puck),BPRI_MED)){
			copy((char *) (set_msg->b_rptr),
			    tblt_pen_init_puck,
			    sizeof(tblt_pen_init_puck));
			set_msg->b_wptr += sizeof(tblt_pen_init_puck);
			tblt->pen_dev_type= TBLT_PEN_DEV_TOGGLE;
		} else {
			return;
		}
		break;
	case TBLT_PEN_DEV_PRESS:
		if (set_msg=allocb(sizeof(tblt_pen_init_press),BPRI_MED)){
			copy((char *) (set_msg->b_rptr),
			    tblt_pen_init_press,
			    sizeof(tblt_pen_init_press));
			set_msg->b_wptr += sizeof(tblt_pen_init_press);
			tblt->pen_dev_type = TBLT_PEN_DEV_PRESS;
		} else {
			return;
		}
		break;
 
	case TBLT_PEN_DEV_PRESS_N_SIDE:
		return;
 
	}
	putnext(q,set_msg);
	tbltflush(tbltd);
	flushq(q, FLUSHALL);
	flushq(RD(q), FLUSHALL);
}
static void
copy(char *dest, char *src, int size)
{
        while (size--)
                *dest++ = *src++;
}
