text: /* $NetBSD: ms.c,v 1.7 1997/09/02 13:20:57 thorpej Exp $ */
  
  /*
   * Copyright (c) 1992, 1993
   *	The Regents of the University of California.  All rights reserved.
   *
   * This software was developed by the Computer Systems Engineering group
   * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
   * contributed to Berkeley.
   *
   * All advertising materials mentioning features or use of this software
   * must display the following acknowledgement:
   *	This product includes software developed by the University of
   *	California, Lawrence Berkeley Laboratory.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   * 3. All advertising materials mentioning features or use of this software
   *    must display the following acknowledgement:
   *	This product includes software developed by the University of
   *	California, Berkeley and its contributors.
   * 4. Neither the name of the University nor the names of its contributors
   *    may be used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   *
   *	@(#)ms.c	8.1 (Berkeley) 6/11/93
   */
  
  /*
   * Mouse driver.
   */
  
  #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
  
  __KERNEL_RCSID(0, "$NetBSD: ms.c,v 1.7 1997/09/02 13:20:57 thorpej Exp $");
  
  #include <sys/param.h>
  #include <sys/ioctl.h>
  #include <sys/kernel.h>
  #include <sys/proc.h>
  #include <sys/syslog.h>
  #include <sys/systm.h>
  #include <sys/tty.h>
  #include <sys/signalvar.h>
  
  #include <machine/conf.h>
  
  #include <machine/vuid_event.h>
  #include <alpha/wscons/event_var.h>
  #include <alpha/wscons/wsconsvar.h>
  
  cdev_decl(ms);
  
  struct ms_softc {
  	struct device *ms_dev;
  	struct wscons_mdev_spec ms_spec;
  	char	ms_mb;			/* mouse button state */
  	char	ms_ub;			/* user button state */
  	int	ms_dx;			/* delta-x */
  	int	ms_dy;			/* delta-y */
  	volatile int ms_ready;		/* event queue is ready */
  	struct	evvar ms_events;	/* event queue state */
  } ms_softc;
  
  void
  msattach(mdev, mspec)
          struct device *mdev;
          struct wscons_mdev_spec *mspec;
  {
          register struct ms_softc *m = &ms_softc;
  
  	m->ms_dev = mdev;
  	m->ms_spec = *mspec;
  }
  
  void
  ms_event(buttons, dx, dy)
  	char buttons;			/* 0 is up */
  	int dx, dy;
  {
  	register struct firm_event *fe;
  	register struct ms_softc *ms = &ms_softc;
  	register int mb, ub, d, get, put, any;
  	static const char to_one[] = { 1, 2, 2, 4, 4, 4, 4 };
  	static const int to_id[] = { MS_RIGHT, MS_MIDDLE, 0, MS_LEFT };
  
          /*
           * Discard input if not ready.
           */
  	if (ms->ms_ready == 0)
  		return;
  
  	ms->ms_mb = (buttons) & 0x7;
  	ms->ms_dx += dx;
  	ms->ms_dy += dy;
  
  	/*
  	 * We have at least one event (mouse button, delta-X, or
  	 * delta-Y; possibly all three, and possibly three separate
  	 * button events).  Deliver these events until we are out
  	 * of changes or out of room.  As events get delivered,
  	 * mark them `unchanged'.
  	 */
  	any = 0;
  	get = ms->ms_events.ev_get;
  	put = ms->ms_events.ev_put;
  	fe = &ms->ms_events.ev_q[put];
  
  	/* NEXT prepares to put the next event, backing off if necessary */
  #define	NEXT \
  	if ((++put) % EV_QSIZE == get) { \
  		put--; \
  		goto out; \
  	}
  	/* ADVANCE completes the `put' of the event */
  #define	ADVANCE \
  	fe++; \
  	if (put >= EV_QSIZE) { \
  		put = 0; \
  		fe = &ms->ms_events.ev_q[0]; \
  	} \
  	any = 1
  
  	mb = ms->ms_mb;
  	ub = ms->ms_ub;
  	while ((d = mb ^ ub) != 0) {
  		/*
  		 * Mouse button change.  Convert up to three changes
  		 * to the `first' change, and drop it into the event queue.
  		 */
  		NEXT;
  		d = to_one[d - 1];		/* from 1..7 to {1,2,4} */
  		fe->id = to_id[d - 1];		/* from {1,2,4} to ID */
  		fe->value = mb & d ? VKEY_DOWN : VKEY_UP;
  		fe->time = time;
  		ADVANCE;
  		ub ^= d;
  	}
  	if (ms->ms_dx) {
  		NEXT;
  		fe->id = LOC_X_DELTA;
  		fe->value = ms->ms_dx;
  		fe->time = time;
  		ADVANCE;
  		ms->ms_dx = 0;
  	}
  	if (ms->ms_dy) {
  		NEXT;
  		fe->id = LOC_Y_DELTA;
  		fe->value = ms->ms_dy;
  		fe->time = time;
  		ADVANCE;
  		ms->ms_dy = 0;
  	}
  out:
  	if (any) {
  		ms->ms_ub = ub;
  		ms->ms_events.ev_put = put;
  		EV_WAKEUP(&ms->ms_events);
  	}
  }
  
  int
  msopen(dev, flags, mode, p)
  	dev_t dev;
  	int flags, mode;
  	struct proc *p;
  {
  	int error;
  
  	if (ms_softc.ms_dev == NULL)	/* never attached! */
  		return (ENXIO);
  
  	if (ms_softc.ms_events.ev_io)
  		return (EBUSY);
  
  	ms_softc.ms_events.ev_io = p;
  	ev_init(&ms_softc.ms_events);	/* may cause sleep */
  
  	ms_softc.ms_ready = 1;		/* start accepting events */
  
  	error = (*ms_softc.ms_spec.wm_enable)(ms_softc.ms_dev);
  
  	if (error) {
  		ms_softc.ms_ready = 0;		/* stop accepting events */
  		ev_fini(&ms_softc.ms_events);
  		ms_softc.ms_events.ev_io = NULL;
  		return (error);
  	}
  
  	return (0);
  }
  
  int
  msclose(dev, flags, mode, p)
  	dev_t dev;
  	int flags, mode;
  	struct proc *p;
  {
  
  	(*ms_softc.ms_spec.wm_disable)(ms_softc.ms_dev);
  
  	ms_softc.ms_ready = 0;		/* stop accepting events */
  	ev_fini(&ms_softc.ms_events);
  	ms_softc.ms_events.ev_io = NULL;
  	return (0);
  }
  
  int
  msread(dev, uio, flags)
  	dev_t dev;
  	struct uio *uio;
  	int flags;
  {
  
  	return (ev_read(&ms_softc.ms_events, uio, flags));
  }
  
  /* this routine should not exist, but is convenient to write here for now */
  int
  mswrite(dev, uio, flags)
  	dev_t dev;
  	struct uio *uio;
  	int flags;
  {
  
  	return (EOPNOTSUPP);
  }
  
  int
  msioctl(dev, cmd, data, flag, p)
  	dev_t dev;
  	u_long cmd;
  	register caddr_t data;
  	int flag;
  	struct proc *p;
  {
  
  	switch (cmd) {
  
  	case FIONBIO:		/* we will remove this someday (soon???) */
  		return (0);
  
  	case FIOASYNC:
  		ms_softc.ms_events.ev_async = *(int *)data != 0;
  		return (0);
  
  	case TIOCSPGRP:
  		if (*(int *)data != ms_softc.ms_events.ev_io->p_pgid)
  			return (EPERM);
  		return (0);
  
  	case VUIDGFORMAT:
  		/* we only do firm_events */
  		*(int *)data = VUID_FIRM_EVENT;
  		return (0);
  
  	case VUIDSFORMAT:
  		if (*(int *)data != VUID_FIRM_EVENT)
  			return (EINVAL);
  		return (0);
  	}
  	return (ENOTTY);
  }
  
  int
  mspoll(dev, events, p)
  	dev_t dev;
  	int events;
  	struct proc *p;
  {
  
  	return (ev_poll(&ms_softc.ms_events, events, p));
  }
  