1/*	$NetBSD: qv.c,v 1.27 2009/11/21 04:45:39 rmind Exp $	*/
2
3/*-
4 * Copyright (c) 1988
5 *	The Regents of the University of California.  All rights reserved.
6 * (c) UNIX System Laboratories, Inc.
7 * All or some portions of this file are derived from material licensed
8 * to the University of California by American Telephone and Telegraph
9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10 * the permission of UNIX System Laboratories, Inc.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	@(#)qv.c	7.2 (Berkeley) 1/21/94
37 */
38
39/*
40 *	derived from: @(#)qv.c	1.8 (ULTRIX) 8/21/85
41 */
42
43/************************************************************************
44 *									*
45 *			Copyright (c) 1985 by				*
46 *		Digital Equipment Corporation, Maynard, MA		*
47 *			All rights reserved.				*
48 *									*
49 *   This software is furnished under a license and may be used and	*
50 *   copied  only  in accordance with the terms of such license and	*
51 *   with the  inclusion  of  the  above  copyright  notice.   This	*
52 *   software  or  any  other copies thereof may not be provided or	*
53 *   otherwise made available to any other person.  No title to and	*
54 *   ownership of the software is hereby transferred.			*
55 *									*
56 *   This software is  derived  from  software  received  from  the	*
57 *   University    of   California,   Berkeley,   and   from   Bell	*
58 *   Laboratories.  Use, duplication, or disclosure is  subject  to	*
59 *   restrictions  under  license  agreements  with  University  of	*
60 *   California and with AT&T.						*
61 *									*
62 *   The information in this software is subject to change  without	*
63 *   notice  and should not be construed as a commitment by Digital	*
64 *   Equipment Corporation.						*
65 *									*
66 *   Digital assumes no responsibility for the use  or  reliability	*
67 *   of its software on equipment which is not supplied by Digital.	*
68 *									*
69 ************************************************************************
70 *
71 * This driver provides glass tty functionality to the qvss. It is a strange
72 * device in that it supports three subchannels. The first being the asr,
73 * the second being a channel that intercepts the chars headed for the screen
74 * ( like a pseudo tty ) and the third being a source of mouse state changes.
75 * NOTE: the second is conditional on #ifdef CONS_HACK in this version
76 * of the driver, as it's a total crock.
77 *
78 * There may be one and only one qvss in the system.  This restriction is based
79 * on the inability to map more than one at a time.  This restriction will
80 * exist until the kernel has shared memory services. This driver therefore
81 * support a single unit. No attempt was made to have it service more.
82 *
83 * (this belongs in sccs - not here)
84 *
85 * 02 Aug 85 -- rjl
86 *	Changed the names of the special setup routines so that the system
87 *	can have a qvss or a qdss system console.
88 *
89 * 03 Jul 85 -- rjl
90 *	Added a check for virtual mode in qvputc so that the driver
91 *	doesn't crash while in a dump which is done in physical mode.
92 *
93 * 10 Apr 85 -- jg
94 *	Well, our theory about keyboard handling was wrong; most of the
95 *	keyboard is in autorepeat, down mode.  These changes are to make
96 *	the qvss work the same as the Vs100, which is not necessarily
97 *	completely correct, as some chord usage may fail.  But since we
98 *	can't easily change the Vs100, we might as well propagate the
99 *	problem to another device.  There are also changes for screen and
100 *	mouse accellaration.
101 *
102 * 27 Mar 85 -- rjl
103 *	MicroVAX-II systems have interval timers that interrupt at ipl4.
104 *	Everything else is higher and thus causes us to miss clock ticks. The
105 *	problem isn't severe except in the case of a device like this one that
106 *	generates lots of interrupts. We aren't willing to make this change to
107 *	all device drivers but it seems acceptable in this case.
108 *
109 *  3 Dec 84 -- jg
110 *	To continue the tradition of building a better mouse trap,  this
111 * 	driver has been extended to form Vs100 style event queues.  If the
112 *	mouse device is open, the keyboard events are intercepted and put
113 *	into the shared memory queue.  Unfortunately, we are ending up with
114 *	one of the longest Unix device drivers.  Sigh....
115 *
116 * 20 Nov 84 -- rjl
117 *      As a further complication this driver is required to function as the
118 *      virtual system console. This code runs before and during auto-
119 *      configuration and therefore is require to have a second path for setup.
120 *      It is futher constrained to have a character output routine that
121 *      is not dependent on the interrupt system.
122 *
123 */
124
125#include <sys/cdefs.h>
126__KERNEL_RCSID(0, "$NetBSD: qv.c,v 1.27 2009/11/21 04:45:39 rmind Exp $");
127
128#include "qv.h"
129#if NQV > 0
130
131#include "../include/pte.h"
132
133#include "sys/param.h"
134#include "sys/conf.h"
135#include "qvioctl.h"
136#include "sys/tty.h"
137#include "sys/buf.h"
138#include "sys/vm.h"
139#include "sys/file.h"
140#include "sys/uio.h"
141#include "sys/kernel.h"
142#include "sys/syslog.h"
143#include "../include/cpu.h"
144#include "../include/mtpr.h"
145#include "ubareg.h"
146#include "ubavar.h"
147
148#define CONS_HACK
149
150struct	uba_device *qvinfo[NQV];
151
152struct	tty qv_tty[NQV*4];
153
154#define	nNQV  NQV
155int	nqv = NQV*4;
156
157/*
158 * Definition of the driver for the auto-configuration program.
159 */
160int	qvprobe(), qvattach(), qvkint(), qvvint();
161u_short	qvstd[] = { 0 };
162struct	uba_driver qvdriver =
163	{ qvprobe, 0, qvattach, 0, qvstd, "qv", qvinfo };
164
165extern	char qvmem[][512*VAX_NBPG];
166extern	struct pte QVmap[][512];
167
168/*
169 * Local variables for the driver. Initialized for 15' screen
170 * so that it can be used during the boot process.
171 */
172
173#define QVWAITPRI 	(PZERO+1)
174
175#define QVKEYBOARD 	0	/* minor 0, keyboard/glass tty */
176#define QVPCONS 	1	/* minor 1, console interceptor XXX */
177#define QVMOUSECHAN 	2	/* minor 2, mouse */
178#define	QVSPARE		3	/* unused */
179#define QVCHAN(unit)	((unit) & 03)
180/*
181 * v_putc is the switch that is used to redirect the console cnputc to the
182 * virtual console vputc.  consops is used to redirect the console
183 * device to the qvss console.
184 */
185extern int (*v_putc)();
186extern const struct cdevsw *consops;
187/*
188 * qv_def_scrn is used to select the appropriate tables. 0=15 inch 1=19 inch,
189 * 2 = uVAXII.
190 */
191int qv_def_scrn = 2;
192
193#define QVMAXEVQ	64	/* must be power of 2 */
194#define EVROUND(x)	((x) & (QVMAXEVQ - 1))
195
196/*
197 * Screen parameters 15 & 19 inch monitors. These determine the max size in
198 * pixel and character units for the display and cursor positions.
199 * Notice that the mouse defaults to original square algorithm, but X
200 * will change to its defaults once implemented.
201 */
202struct qv_info *qv_scn;
203struct qv_info qv_scn_defaults[] = {
204	{0, {0, 0}, 0, {0, 0}, 0, 0, 30, 80, 768, 480, 768-16, 480-16,
205	 0, 0, 0, 0, 0, QVMAXEVQ, 0, 0, {0, 0}, {0, 0, 0, 0}, 2, 4},
206	{0, {0, 0}, 0, {0, 0}, 0, 0, 55, 120, 960, 864, 960-16, 864-16,
207	 0, 0, 0, 0, 0, QVMAXEVQ, 0, 0, {0, 0}, {0, 0, 0, 0}, 2, 4},
208	{0, {0, 0}, 0, {0, 0}, 0, 0, 56, 120,1024, 864,1024-16, 864-16,
209	 0, 0, 0, 0, 0, QVMAXEVQ, 0, 0, {0, 0}, {0, 0, 0, 0}, 2, 4}
210};
211
212/*
213 * Screen controller initialization parameters. The definations and use
214 * of these parameters can be found in the Motorola 68045 crtc specs. In
215 * essence they set the display parameters for the chip. The first set is
216 * for the 15" screen and the second is for the 19" separate sync. There
217 * is also a third set for a 19" composite sync monitor which we have not
218 * tested and which is not supported.
219 */
220static short qv_crt_parms[][16] = {
221           { 31, 25, 27, 0142, 31, 13, 30, 31, 4, 15, 040, 0, 0, 0, 0, 0 },
222/* VR100*/ { 39, 30, 32, 0262, 55, 5, 54, 54, 4, 15, 040, 0, 0, 0, 0, 0 },
223/* VR260*/ { 39, 32, 33, 0264, 56, 5, 54, 54, 4, 15, 040, 0, 0, 0, 0, 0},
224};
225
226/*
227 * Screen parameters
228 */
229struct qv_info  *qv_scn;
230int maxqvmem = 254*1024 - sizeof(struct qv_info) - QVMAXEVQ*sizeof(vsEvent);
231
232/*
233 * Keyboard state
234 */
235struct qv_keyboard {
236	int shift;			/* state variables	*/
237	int cntrl;
238	int lock;
239	char last;			/* last character	*/
240} qv_keyboard;
241
242short divdefaults[15] = { LK_DOWN,	/* 0 doesn't exist */
243	LK_AUTODOWN, LK_AUTODOWN, LK_AUTODOWN, LK_DOWN,
244	LK_UPDOWN,   LK_UPDOWN,   LK_AUTODOWN, LK_AUTODOWN,
245	LK_AUTODOWN, LK_AUTODOWN, LK_AUTODOWN, LK_AUTODOWN,
246	LK_DOWN, LK_AUTODOWN };
247
248short kbdinitstring[] = {		/* reset any random keyboard stuff */
249	LK_AR_ENABLE,			/* we want autorepeat by default */
250	LK_CL_ENABLE,			/* keyclick */
251	0x84,				/* keyclick volume */
252	LK_KBD_ENABLE,			/* the keyboard itself */
253	LK_BELL_ENABLE,			/* keyboard bell */
254	0x84,				/* bell volume */
255	LK_LED_DISABLE,			/* keyboard leds */
256	LED_ALL };
257#define KBD_INIT_LENGTH	sizeof(kbdinitstring)/sizeof(short)
258
259#define TOY ((time.tv_sec * 100) + (time.tv_usec / 10000))
260
261int	qv_ipl_lo = 1;			/* IPL low flag			*/
262int	mouseon = 0;			/* mouse channel is enabled when 1*/
263struct proc *qvrsel;			/* process waiting for select */
264
265int	qvstart(), qvputc(),  ttrstrt();
266
267/*
268 * Keyboard translation and font tables
269 */
270extern u_short q_key[], q_shift_key[], q_cursor[];
271extern char *q_special[], q_font[];
272
273dev_type_open(qvopen);
274dev_type_close(qvclose);
275dev_type_read(qvread);
276dev_type_write(qvwrite);
277dev_type_ioctl(qvioctl);
278dev_type_stop(qvstop);
279dev_type_poll(qvpoll);
280dev_type_kqfilter(qvkqfilter);
281
282const struct cdevsw qv_cdevsw = {
283	qvopen, qvclose, qvread, qvwrite, qvioctl,
284	qvstop, notty, qvpoll, nommap, qvkqfilter,
285};
286
287/*
288 * See if the qvss will interrupt.
289 */
290
291/*ARGSUSED*/
292qvprobe(void *reg, int ctlr)
293{
294	register int br, cvec;		/* these are ``value-result'' */
295	register struct qvdevice *qvaddr = (struct qvdevice *)reg;
296	static int tvec, ovec;
297
298#ifdef lint
299	br = 0; cvec = br; br = cvec;
300	qvkint(0); qvvint(0);
301#endif
302	/*
303	 * Allocate the next two vectors
304	 */
305	tvec = 0360;
306	ovec = cvec;
307	/*
308	 * Turn on the keyboard and vertical interrupt vectors.
309	 */
310	qvaddr->qv_intcsr = 0;		/* init the interrupt controller */
311	qvaddr->qv_intcsr = 0x40;	/* reset irr			*/
312	qvaddr->qv_intcsr = 0x80;	/* specify individual vectors	*/
313	qvaddr->qv_intcsr = 0xc0;	/* preset autoclear data	*/
314	qvaddr->qv_intdata = 0xff;	/* all setup as autoclear	*/
315
316	qvaddr->qv_intcsr = 0xe0;	/* preset vector address 1	*/
317	qvaddr->qv_intdata = tvec;	/* give it the keyboard vector	*/
318	qvaddr->qv_intcsr = 0x28;	/* enable tx/rx interrupt	*/
319
320	qvaddr->qv_intcsr = 0xe1;	/* preset vector address 2	*/
321	qvaddr->qv_intdata = tvec+4;	/* give it the vertical sysnc	*/
322	qvaddr->qv_intcsr = 0x29;	/* enable 			*/
323
324	qvaddr->qv_intcsr = 0xa1;	/* arm the interrupt ctrl	*/
325
326	qvaddr->qv_uartcmd = 0x15;	/* set mode pntr/enable rx/tx	*/
327	qvaddr->qv_uartmode = 0x17;	/* noparity, 8-bit		*/
328	qvaddr->qv_uartmode = 0x07;	/* 1 stop bit			*/
329	qvaddr->qv_uartstatus = 0x99;	/* 4800 baud xmit/recv 		*/
330	qvaddr->qv_uartintstatus = 2;	/* enable recv interrupts	*/
331
332	qvaddr->qv_csr |= QV_INT_ENABLE | QV_CUR_MODE;
333
334	DELAY(10000);
335
336	qvaddr->qv_csr &= ~QV_INT_ENABLE;
337
338	/*
339	 * If the qvss did interrupt it was the second vector not
340	 * the first so we have to return the first so that they
341	 * will be setup properly
342	 */
343	if( ovec == cvec ) {
344		return 0;
345	} else
346		cvec -= 4;
347	return (sizeof (struct qvdevice));
348}
349
350/*
351 * Routine called to attach a qv.
352 */
353qvattach(struct uba_device *ui)
354{
355
356        /*
357         * If not the console then we have to setup the screen
358         */
359        if (v_putc != qvputc || ui->ui_unit != 0)
360                (void)qv_setup((struct qvdevice *)ui->ui_addr, ui->ui_unit, 1);
361	else
362		qv_scn->qvaddr = (struct qvdevice *)ui->ui_addr;
363}
364
365
366/*ARGSUSED*/
367int
368qvopen(dev_t dev, int flag, int mode, struct proc *p)
369{
370	register struct tty *tp;
371	register int unit, qv;
372	register struct qvdevice *qvaddr;
373	register struct uba_device *ui;
374	register struct qv_info *qp = qv_scn;
375
376	unit = minor(dev);
377	qv = unit >> 2;
378	if (unit >= nqv || (ui = qvinfo[qv])== 0 || ui->ui_alive == 0)
379		return (ENXIO);
380	if (QVCHAN(unit) == QVSPARE
381#ifndef CONS_HACK
382	   || QVCHAN(unit) == QVPCONS
383#endif
384	   )
385		return (ENODEV);
386	tp = &qv_tty[unit];
387	if (tp->t_state&TS_XCLUDE && u.u_uid!=0)
388		return (EBUSY);
389	qvaddr = (struct qvdevice *)ui->ui_addr;
390        qv_scn->qvaddr = qvaddr;
391	tp->t_addr = (void *)qvaddr;
392	tp->t_oproc = qvstart;
393
394	if ((tp->t_state&TS_ISOPEN) == 0) {
395		ttychars(tp);
396		tp->t_state = TS_ISOPEN|TS_CARR_ON;
397		tp->t_ispeed = B9600;
398		tp->t_ospeed = B9600;
399		if( QVCHAN(unit) == QVKEYBOARD ) {
400			/* make sure keyboard is always back to default */
401			qvkbdreset();
402			qvaddr->qv_csr |= QV_INT_ENABLE;
403			tp->t_iflag = TTYDEF_IFLAG;
404			tp->t_oflag = TTYDEF_OFLAG;
405			tp->t_lflag = TTYDEF_LFLAG;
406			tp->t_cflag = TTYDEF_CFLAG;
407		}
408		/* XXX ?why?  else
409			tp->t_flags = RAW;
410		*/
411	}
412	/*
413	 * Process line discipline specific open if its not the
414	 * mouse channel. For the mouse we init the ring ptr's.
415	 */
416	if( QVCHAN(unit) != QVMOUSECHAN )
417		return ((*tp->t_linesw->l_open)(dev, tp));
418	else {
419		mouseon = 1;
420		/* set up event queue for later */
421		qp->ibuff = (vsEvent *)qp - QVMAXEVQ;
422		qp->iqsize = QVMAXEVQ;
423		qp->ihead = qp->itail = 0;
424		return 0;
425	}
426
427	return (0);
428}
429
430/*
431 * Close a QVSS line.
432 */
433/*ARGSUSED*/
434int
435qvclose(dev_t dev, int flag, int mode, struct proc *p)
436{
437	register struct tty *tp;
438	register unit;
439	register struct qvdevice *qvaddr;
440	int error;
441
442	unit = minor(dev);
443	tp = &qv_tty[unit];
444
445	/*
446	 * If this is the keyboard unit (0) shutdown the
447	 * interface.
448	 */
449	qvaddr = (struct qvdevice *)tp->t_addr;
450	if (QVCHAN(unit) == QVKEYBOARD )
451		qvaddr->qv_csr &= ~QV_INT_ENABLE;
452
453	/*
454	 * If unit is not the mouse channel call the line disc.
455	 * otherwise clear the state flag, and put the keyboard into down/up.
456	 */
457	if (QVCHAN(unit) != QVMOUSECHAN) {
458		(*tp->t_linesw->l_close)(tp, flag);
459		error = ttyclose(tp);
460	} else {
461		mouseon = 0;
462		qv_init( qvaddr );
463		error = 0;
464	}
465	tp->t_state = 0;
466	return (error);
467}
468
469int
470qvread(dev_t dev, struct uio *uio, int flag)
471{
472	register struct tty *tp;
473	int unit = minor( dev );
474
475	if (QVCHAN(unit) != QVMOUSECHAN) {
476		tp = &qv_tty[unit];
477		return ((*tp->t_linesw->l_read)(tp, uio));
478	}
479	return (ENXIO);
480}
481
482int
483qvwrite(dev_t dev, struct uio *uio, int flag)
484{
485	register struct tty *tp;
486	int unit = minor( dev );
487
488	/*
489	 * If this is the mouse we simply fake the i/o, otherwise
490	 * we let the line disp. handle it.
491	 */
492	if (QVCHAN(unit) == QVMOUSECHAN) {
493		uio->uio_offset = uio->uio_resid;
494		uio->uio_resid = 0;
495		return 0;
496	}
497	tp = &qv_tty[unit];
498	return ((*tp->t_linesw->l_write)(tp, uio));
499}
500
501int
502qvpoll(dev_t dev, int events, struct proc *p)
503{
504	register struct tty *tp;
505	int unit = minor( dev );
506
507	/*
508	 * XXX Should perform similar checks to deprecated `qvselect()'
509	 */
510	tp = &qv_tty[unit];
511	return ((*tp->t_linesw->l_poll)(tp, events, p));
512}
513
514/*
515 * XXX Is qvselect() even useful now?
516 * This driver looks to have suffered some serious bit-rot...
517 */
518
519/*
520 * Mouse activity select routine
521 */
522qvselect(dev_t dev, rw)
523{
524	register int s = spl5();
525	register struct qv_info *qp = qv_scn;
526
527	if( QVCHAN(minor(dev)) == QVMOUSECHAN )
528		switch(rw) {
529		case FREAD:			/* if events okay */
530			if(qp->ihead != qp->itail) {
531				splx(s);
532				return(1);
533			}
534			qvrsel = u.u_procp;
535			splx(s);
536			return(0);
537		default:			/* can never write */
538			splx(s);
539			return(0);
540		}
541	else {
542		splx(s);
543		return( ttselect(dev, rw) );
544	}
545	/*NOTREACHED*/
546}
547
548/*
549 * QVSS keyboard interrupt.
550 */
551qvkint(int qv)
552{
553	struct tty *tp;
554	register c;
555	struct uba_device *ui;
556	register int key;
557	register int i;
558
559	ui = qvinfo[qv];
560	if (ui == 0 || ui->ui_alive == 0)
561		return;
562	tp = &qv_tty[qv<<2];
563	/*
564	 * Get a character from the keyboard.
565	 */
566	key = ((struct qvdevice *)ui->ui_addr)->qv_uartdata & 0xff;
567	if( mouseon == 0) {
568		/*
569		 * Check for various keyboard errors
570		 */
571		if( key == LK_POWER_ERROR || key == LK_KDOWN_ERROR ||
572		    key == LK_INPUT_ERROR || key == LK_OUTPUT_ERROR) {
573			log(LOG_ERR,
574			    "qv%d: Keyboard error, code = %x\n",qv,key);
575			return;
576		}
577		if( key < LK_LOWEST ) return;
578		/*
579		 * See if its a state change key
580		 */
581		switch ( key ) {
582		case LOCK:
583			qv_keyboard.lock ^= 0xffff;	/* toggle */
584			if( qv_keyboard.lock )
585				qv_key_out( LK_LED_ENABLE );
586			else
587				qv_key_out( LK_LED_DISABLE );
588			qv_key_out( LED_3 );
589			return;
590		case SHIFT:
591			qv_keyboard.shift ^= 0xffff;
592			return;
593		case CNTRL:
594			qv_keyboard.cntrl ^= 0xffff;
595			return;
596		case ALLUP:
597			qv_keyboard.cntrl = qv_keyboard.shift = 0;
598			return;
599		case REPEAT:
600			c = qv_keyboard.last;
601			break;
602		default:
603		/*
604		 * Test for control characters. If set, see if the character
605		 * is elligible to become a control character.
606		 */
607			if( qv_keyboard.cntrl ) {
608				c = q_key[ key ];
609				if( c >= ' ' && c <= '~' )
610					c &= 0x1f;
611			} else if( qv_keyboard.lock || qv_keyboard.shift )
612				c = q_shift_key[ key ];
613				else
614				c = q_key[ key ];
615			break;
616		}
617
618		qv_keyboard.last = c;
619
620		/*
621		 * Check for special function keys
622		 */
623		if( c & 0x80 ) {
624			register char *string;
625			string = q_special[ c & 0x7f ];
626			while( *string )
627			(*tp->t_linesw->l_rint)(*string++, tp);
628		} else
629			(*tp->t_linesw->l_rint)(c, tp);
630	} else {
631		/*
632		 * Mouse channel is open put it into the event queue
633		 * instead.
634		 */
635		register struct qv_info *qp = qv_scn;
636		register vsEvent *vep;
637
638		if ((i = EVROUND(qp->itail+1)) == qp->ihead)
639			return;
640		vep = &qp->ibuff[qp->itail];
641		vep->vse_direction = VSE_KBTRAW;
642		vep->vse_type = VSE_BUTTON;
643		vep->vse_device = VSE_DKB;
644		vep->vse_x = qp->mouse.x;
645		vep->vse_y = qp->mouse.y;
646		vep->vse_time = TOY;
647		vep->vse_key = key;
648		qp->itail = i;
649		if(qvrsel) {
650			selnotify(qvrsel, 0, 0);
651			qvrsel = 0;
652		}
653	}
654}
655
656/*
657 * Ioctl for QVSS.
658 */
659/*ARGSUSED*/
660int
661qvioctl(dev_t dev, u_long cmd, register void *data, int flag, struct proc *p)
662{
663	register struct tty *tp;
664	register int unit = minor(dev);
665	register struct qv_info *qp = qv_scn;
666	register struct qv_kpcmd *qk;
667	register unsigned char *cp;
668	int error;
669
670	/*
671	 * Check for and process qvss specific ioctl's
672	 */
673	switch( cmd ) {
674	case QIOCGINFO:					/* return screen info */
675		memcpy(data, (void *)qp, sizeof (struct qv_info));
676		break;
677
678	case QIOCSMSTATE:				/* set mouse state */
679		qp->mouse = *((vsCursor *)data);
680		qv_pos_cur( qp->mouse.x, qp->mouse.y );
681		break;
682
683	case QIOCINIT:					/* init screen	*/
684		qv_init( qp->qvaddr );
685		break;
686
687	case QIOCKPCMD:
688		qk = (struct qv_kpcmd *)data;
689		if(qk->nbytes == 0) qk->cmd |= 0200;
690		if(mouseon == 0) qk->cmd |= 1;	/* no mode changes */
691		qv_key_out(qk->cmd);
692		cp = &qk->par[0];
693		while(qk->nbytes-- > 0) {	/* terminate parameters */
694			if(qk->nbytes <= 0) *cp |= 0200;
695			qv_key_out(*cp++);
696		}
697		break;
698	case QIOCADDR:					/* get struct addr */
699		*(struct qv_info **) data = qp;
700		break;
701	default:					/* not ours ??  */
702		tp = &qv_tty[unit];
703		error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag);
704		if (error != EPASSTHROUGH)
705			return (error);
706		return ttioctl(tp, cmd, data, flag);
707		break;
708	}
709	return (0);
710}
711/*
712 * Initialize the screen and the scanmap
713 */
714qv_init(struct qvdevice *qvaddr)
715{
716	register short *scanline;
717	register int i;
718	register short scan;
719	register char *ptr;
720	register struct qv_info *qp = qv_scn;
721
722	/*
723	 * Clear the bit map
724	 */
725	for( i=0 , ptr = qp->bitmap ; i<240 ; i += 2 , ptr += 2048)
726		memset( ptr, 0, 2048 );
727	/*
728	 * Reinitialize the scanmap
729	 */
730        scan = qvaddr->qv_csr & QV_MEM_BANK;
731        scanline = qp->scanmap;
732        for(i = 0 ; i < qp->max_y ; i++ )
733                *scanline++ = scan++;
734
735	/*
736	 * Home the cursor
737	 */
738	qp->row = qp->col = 0;
739
740	/*
741	 * Reset the cursor to the default type.
742	 */
743	for( i=0 ; i<16 ; i++ )
744		qp->cursorbits[i] = q_cursor[i];
745	qvaddr->qv_csr |= QV_CUR_MODE;
746	/*
747	 * Reset keyboard to default state.
748	 */
749	qvkbdreset();
750}
751
752qvreset(void)
753{
754}
755qvkbdreset(void)
756{
757	register int i;
758	qv_key_out(LK_DEFAULTS);
759	for( i=1 ; i < 15 ; i++ )
760		qv_key_out( divdefaults[i] | (i<<3));
761	for (i = 0; i < KBD_INIT_LENGTH; i++)
762		qv_key_out(kbdinitstring[i]);
763}
764
765#define abs(x) (((x) > 0) ? (x) : (-(x)))
766/*
767 * QVSS vertical sync interrupt
768 */
769qvvint(int qv)
770{
771	extern int selwait;
772	register struct qvdevice *qvaddr;
773	struct uba_device *ui;
774	register struct qv_info *qp = qv_scn;
775	int unit;
776	struct tty *tp0;
777	int i;
778	register int j;
779	/*
780	 * Mouse state info
781	 */
782	static ushort omouse = 0, nmouse = 0;
783	static char omx=0, omy=0, mx=0, my=0, om_switch=0, m_switch=0;
784	register int dx, dy;
785
786	/*
787	 * Test and set the qv_ipl_lo flag. If the result is not zero then
788	 * someone else must have already gotten here.
789	 */
790	if( --qv_ipl_lo )
791		return;
792	(void)spl4();
793	ui = qvinfo[qv];
794	unit = qv<<2;
795	qvaddr = (struct qvdevice *)ui->ui_addr;
796	tp0 = &qv_tty[QVCHAN(unit) + QVMOUSECHAN];
797	/*
798	 * See if the mouse has moved.
799	 */
800	if( omouse != (nmouse = qvaddr->qv_mouse) ) {
801		omouse = nmouse;
802		mx = nmouse & 0xff;
803		my = nmouse >> 8;
804		dy = my - omy; omy = my;
805		dx = mx - omx; omx = mx;
806		if( dy < 50 && dy > -50 && dx < 50 && dx > -50 ) {
807			register vsEvent *vep;
808			if( qp->mscale < 0 ) {	/* Ray Lanza's original */
809				if( dy < 0 )
810					dy = -( dy * dy );
811				else
812					dy *= dy;
813				if( dx < 0 )
814					dx = -( dx * dx );
815				else
816					dx *= dx;
817			}
818			else {			/* Vs100 style, see WGA spec */
819			    int thresh = qp->mthreshold;
820			    int scale  = qp->mscale;
821			    if( abs(dx) > thresh ) {
822				if ( dx < 0 )
823				    dx = (dx + thresh)*scale - thresh;
824				else
825				    dx = (dx - thresh)*scale + thresh;
826			    }
827			    if( abs(dy) > thresh ) {
828				if ( dy < 0 )
829				    dy = (dy + thresh)*scale - thresh;
830				else
831				    dy = (dy - thresh)*scale + thresh;
832			    }
833			}
834			qp->mouse.x += dx;
835			qp->mouse.y -= dy;
836			if( qp->mouse.x < 0 )
837				qp->mouse.x = 0;
838			if( qp->mouse.y < 0 )
839				qp->mouse.y = 0;
840			if( qp->mouse.x > qp->max_cur_x )
841				qp->mouse.x = qp->max_cur_x;
842			if( qp->mouse.y > qp->max_cur_y )
843				qp->mouse.y = qp->max_cur_y;
844			if( tp0->t_state & TS_ISOPEN )
845				qv_pos_cur( qp->mouse.x, qp->mouse.y );
846			if (qp->mouse.y < qp->mbox.bottom &&
847			    qp->mouse.y >=  qp->mbox.top &&
848			    qp->mouse.x < qp->mbox.right &&
849			    qp->mouse.x >=  qp->mbox.left) goto switches;
850			qp->mbox.bottom = 0;	/* trash box */
851			if (EVROUND(qp->itail+1) == qp->ihead)
852				goto switches;
853			i = EVROUND(qp->itail - 1);
854			if ((qp->itail != qp->ihead) &&	(i != qp->ihead)) {
855				vep = & qp->ibuff[i];
856				if(vep->vse_type == VSE_MMOTION) {
857					vep->vse_x = qp->mouse.x;
858					vep->vse_y = qp->mouse.y;
859					goto switches;
860				}
861			}
862			/* put event into queue and do select */
863			vep = & qp->ibuff[qp->itail];
864			vep->vse_type = VSE_MMOTION;
865			vep->vse_time = TOY;
866			vep->vse_x = qp->mouse.x;
867			vep->vse_y = qp->mouse.y;
868			qp->itail = EVROUND(qp->itail+1);
869		}
870	}
871	/*
872	 * See if mouse switches have changed.
873	 */
874switches:if( om_switch != ( m_switch = (qvaddr->qv_csr & QV_MOUSE_ANY) >> 8 ) ) {
875		qp->mswitches = ~m_switch & 0x7;
876		for (j = 0; j < 3; j++) {	/* check each switch */
877			register vsEvent *vep;
878			if ( ((om_switch>>j) & 1) == ((m_switch>>j) & 1) )
879				continue;
880			/* check for room in the queue */
881			if ((i = EVROUND(qp->itail+1)) == qp->ihead) return;
882			/* put event into queue and do select */
883			vep = &qp->ibuff[qp->itail];
884			vep->vse_type = VSE_BUTTON;
885			vep->vse_key = 2 - j;
886			vep->vse_direction = VSE_KBTDOWN;
887			if ( (m_switch >> j) & 1)
888				vep->vse_direction = VSE_KBTUP;
889			vep->vse_device = VSE_MOUSE;
890			vep->vse_time = TOY;
891			vep->vse_x = qp->mouse.x;
892			vep->vse_y = qp->mouse.y;
893		}
894		qp->itail =  i;
895		om_switch = m_switch;
896		qp->mswitches = m_switch;
897	}
898	/* if we have proc waiting, and event has happened, wake him up */
899	if(qvrsel && (qp->ihead != qp->itail)) {
900		selnotify(qvrsel, 0, 0);
901		qvrsel = 0;
902	}
903	/*
904	 * Okay we can take another hit now
905	 */
906	qv_ipl_lo = 1;
907}
908
909/*
910 * Start  transmission
911 */
912qvstart(register struct tty *tp)
913{
914	register int unit, c;
915	register struct tty *tp0;
916	int s;
917
918	unit = minor(tp->t_dev);
919#ifdef CONS_HACK
920	tp0 = &qv_tty[(unit&0xfc)+QVPCONS];
921#endif
922	unit = QVCHAN(unit);
923
924	s = spl5();
925	/*
926	 * If it's currently active, or delaying, no need to do anything.
927	 */
928	if (tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
929		goto out;
930	/*
931	 * Display chars until the queue is empty, if the second subchannel
932	 * is open direct them there. Drop characters from subchannels other
933	 * than 0 on the floor.
934	 */
935
936	while( tp->t_outq.c_cc ) {
937		c = getc(&tp->t_outq);
938		if (unit == QVKEYBOARD)
939#ifdef CONS_HACK
940			if( tp0->t_state & TS_ISOPEN ){
941				(*tp0->t_linesw->l_rint)(c, tp0);
942			} else
943#endif
944				qvputchar( c & 0xff );
945	}
946	/*
947	 * Position the cursor to the next character location.
948	 */
949	qv_pos_cur( qv_scn->col*8, qv_scn->row*15 );
950
951	/*
952	 * If there are sleepers, and output has drained below low
953	 * water mark, wake up the sleepers.
954	 */
955	ttypull(tp);
956	tp->t_state &= ~TS_BUSY;
957out:
958	splx(s);
959}
960
961/*
962 * Stop output on a line, e.g. for ^S/^Q or output flush.
963 */
964/*ARGSUSED*/
965void
966qvstop(register struct tty *tp, int flag)
967{
968	register int s;
969
970	/*
971	 * Block input/output interrupts while messing with state.
972	 */
973	s = spl5();
974	if (tp->t_state & TS_BUSY) {
975		if ((tp->t_state&TS_TTSTOP)==0) {
976			tp->t_state |= TS_FLUSH;
977		} else
978			tp->t_state &= ~TS_BUSY;
979	}
980	splx(s);
981}
982
983qvputc(char c)
984{
985	qvputchar(c);
986	if (c == '\n')
987		qvputchar('\r');
988}
989
990/*
991 * Routine to display a character on the screen.  The model used is a
992 * glass tty.  It is assummed that the user will only use this emulation
993 * during system boot and that the screen will be eventually controlled
994 * by a window manager.
995 *
996 */
997qvputchar( c )
998register char c;
999{
1000
1001	register char *b_row, *f_row;
1002	register int i;
1003	register short *scanline;
1004	register int ote = 128;
1005	register struct qv_info *qp = qv_scn;
1006
1007	/*
1008	 * This routine may be called in physical mode by the dump code
1009	 * so we check and punt if that's the case.
1010	 */
1011	if( (mfpr(MAPEN) & 1) == 0 )
1012		return;
1013
1014	c &= 0x7f;
1015
1016	switch ( c ) {
1017	case '\t':				/* tab		*/
1018		for( i = 8 - (qp->col & 0x7) ; i > 0 ; i-- )
1019			qvputchar( ' ' );
1020		break;
1021
1022	case '\r':				/* return	*/
1023		qp->col = 0;
1024		break;
1025
1026	case '\010':				/* backspace	*/
1027		if( --qp->col < 0 )
1028			qp->col = 0;
1029		break;
1030
1031	case '\n':				/* linefeed	*/
1032		if( qp->row+1 >= qp->max_row )
1033			qvscroll();
1034		else
1035			qp->row++;
1036		/*
1037		* Position the cursor to the next character location.
1038		*/
1039		qv_pos_cur( qp->col*8, qp->row*15 );
1040		break;
1041
1042	case '\007':				/* bell		*/
1043                /*
1044                 * We don't do anything to the keyboard until after
1045                 * autoconfigure.
1046                 */
1047		if( qp->qvaddr )
1048			qv_key_out( LK_RING_BELL );
1049		return;
1050
1051	default:
1052		if( c >= ' ' && c <= '~' ) {
1053                        scanline = qp->scanmap;
1054                        b_row = qp->bitmap+(scanline[qp->row*15]&0x3ff)*128+qp->col;
1055			i = c - ' ';
1056			if( i < 0 || i > 95 )
1057				i = 0;
1058			else
1059				i *= 15;
1060			f_row = (char *)((int)q_font + i);
1061
1062/*			for( i=0 ; i<15 ; i++ , b_row += 128, f_row++ )
1063				*b_row = *f_row;*/
1064			/* inline expansion for speed */
1065			*b_row = *f_row++; b_row += ote;
1066			*b_row = *f_row++; b_row += ote;
1067			*b_row = *f_row++; b_row += ote;
1068			*b_row = *f_row++; b_row += ote;
1069			*b_row = *f_row++; b_row += ote;
1070			*b_row = *f_row++; b_row += ote;
1071			*b_row = *f_row++; b_row += ote;
1072			*b_row = *f_row++; b_row += ote;
1073			*b_row = *f_row++; b_row += ote;
1074			*b_row = *f_row++; b_row += ote;
1075			*b_row = *f_row++; b_row += ote;
1076			*b_row = *f_row++; b_row += ote;
1077			*b_row = *f_row++; b_row += ote;
1078			*b_row = *f_row++; b_row += ote;
1079			*b_row = *f_row++; b_row += ote;
1080
1081			if( ++qp->col >= qp->max_col ) {
1082				qp->col = 0 ;
1083				if( qp->row+1 >= qp->max_row )
1084					qvscroll();
1085				else
1086					qp->row++;
1087			}
1088		}
1089		break;
1090	}
1091}
1092
1093/*
1094 * Position the cursor to a particular spot.
1095 */
1096qv_pos_cur( x, y)
1097register int x,y;
1098{
1099	register struct qvdevice *qvaddr;
1100	register struct qv_info *qp = qv_scn;
1101	register index;
1102
1103	if( qvaddr = qp->qvaddr ) {
1104		if( y < 0 || y > qp->max_cur_y )
1105			y = qp->max_cur_y;
1106		if( x < 0 || x > qp->max_cur_x )
1107			x = qp->max_cur_x;
1108		qp->cursor.x = x;		/* keep track of real cursor*/
1109		qp->cursor.y = y;		/* position, indep. of mouse*/
1110
1111		qvaddr->qv_crtaddr = 10;	/* select cursor start reg */
1112		qvaddr->qv_crtdata = y & 0xf;
1113		qvaddr->qv_crtaddr = 11;	/* select cursor end reg */
1114		qvaddr->qv_crtdata = y & 0xf;
1115		qvaddr->qv_crtaddr = 14;	/* select cursor y pos. */
1116		qvaddr->qv_crtdata = y >> 4;
1117		qvaddr->qv_xcur = x;		/* pos x axis	*/
1118		/*
1119		 * If the mouse is being used then we change the mode of
1120		 * cursor display based on the pixels under the cursor
1121		 */
1122		if( mouseon ) {
1123			index = y*128 + x/8;
1124			if( qp->bitmap[ index ] && qp->bitmap[ index+128 ] )
1125				qvaddr->qv_csr &= ~QV_CUR_MODE;
1126			else
1127				qvaddr->qv_csr |=  QV_CUR_MODE;
1128		}
1129	}
1130}
1131/*
1132 * Scroll the bitmap by moving the scanline map words. This could
1133 * be done by moving the bitmap but it's much too slow for a full screen.
1134 * The only drawback is that the scanline map must be reset when the user
1135 * wants to do graphics.
1136 */
1137qvscroll(void)
1138{
1139	short tmpscanlines[15];
1140	register char *b_row;
1141	register short *scanline;
1142	register struct qv_info *qp = qv_scn;
1143
1144	/*
1145	 * If the mouse is on we don't scroll so that the bit map
1146	 * remains sane.
1147	 */
1148	if( mouseon ) {
1149		qp->row = 0;
1150		return;
1151	}
1152	/*
1153	 * Save the first 15 scanlines so that we can put them at
1154	 * the bottom when done.
1155	 */
1156	memcpy((void *)tmpscanlines, (void *)qp->scanmap, sizeof tmpscanlines);
1157
1158	/*
1159	 * Clear the wrapping line so that it won't flash on the bottom
1160	 * of the screen.
1161	 */
1162        scanline = qp->scanmap;
1163        b_row = qp->bitmap+(*scanline&0x3ff)*128;
1164	memset( b_row, 0, 1920 );
1165
1166	/*
1167	 * Now move the scanlines down
1168	 */
1169	memcpy((void *)qp->scanmap, (void *)(qp->scanmap+15),
1170	      (qp->row * 15) * sizeof (short) );
1171
1172	/*
1173	 * Now put the other lines back
1174	 */
1175	memcpy((void *)(qp->scanmap+(qp->row * 15)), (void *)tmpscanlines,
1176	      sizeof (tmpscanlines) );
1177
1178}
1179
1180/*
1181 * Output to the keyboard. This routine status polls the transmitter on the
1182 * keyboard to output a code. The timer is to avoid hanging on a bad device.
1183 */
1184qv_key_out(u_short c)
1185{
1186	int timer = 30000;
1187	register struct qv_info *qp = qv_scn;
1188
1189	if (qp->qvaddr) {
1190		while ((qp->qvaddr->qv_uartstatus & 0x4) == 0  && timer--)
1191			;
1192		qp->qvaddr->qv_uartdata = c;
1193	}
1194}
1195/*
1196 * Virtual console initialization. This routine sets up the qvss so that it can
1197 * be used as the system console. It is invoked before autoconfig and has to do
1198 * everything necessary to allow the device to serve as the system console.
1199 * In this case it must map the q-bus and device areas and initialize the qvss
1200 * screen.
1201 */
1202qvcons_init(void)
1203{
1204        struct percpu *pcpu;            /* pointer to percpu structure  */
1205	register struct qbus *qb;
1206        struct qvdevice *qvaddr;        /* device pointer               */
1207        short *devptr;                  /* virtual device space         */
1208	extern cnputc();		/* standard serial console putc */
1209#define QVSSCSR 017200
1210
1211	/*
1212	 * If secondary console already configured,
1213	 * don't override the previous one.
1214	 */
1215	if (v_putc != cnputc)
1216		return 0;
1217        /*
1218         * find the percpu entry that matches this machine.
1219         */
1220        for( pcpu = percpu ; pcpu && pcpu->pc_cputype != cpu ; pcpu++ )
1221                ;
1222        if( pcpu == NULL )
1223                return 0;
1224	if (pcpu->pc_io->io_type != IO_QBUS)
1225		return 0;
1226
1227        /*
1228         * Found an entry for this CPU. Because this device is Microvax specific
1229         * we assume that there is a single q-bus and don't have to worry about
1230         * multiple adapters.
1231         *
1232         * Map the device registers.
1233         */
1234	qb = (struct qbus *)pcpu->pc_io->io_details;
1235	ioaccess(qb->qb_iopage, UMEMmap[0] + qb->qb_memsize, UBAIOPAGES * VAX_NBPG);
1236
1237        /*
1238         * See if the qvss is there.
1239         */
1240        devptr = (short *)((char *)umem[0] + (qb->qb_memsize * VAX_NBPG));
1241        qvaddr = (struct qvdevice *)((u_int)devptr + ubdevreg(QVSSCSR));
1242        if (badaddr((void *)qvaddr, sizeof(short)))
1243                return 0;
1244        /*
1245         * Okay the device is there lets set it up
1246         */
1247        if (!qv_setup(qvaddr, 0, 0))
1248		return 0;
1249	v_putc = qvputc;
1250        consops = &qv_cdevsw;
1251	return 1;
1252}
1253/*
1254 * Do the board specific setup
1255 */
1256qv_setup(struct qvdevice *qvaddr, int unit, int probed)
1257{
1258        void *qvssmem;		/* pointer to the display mem   */
1259        register i;			/* simple index                 */
1260	register struct qv_info *qp;
1261        register int *pte;
1262        struct percpu *pcpu;            /* pointer to percpu structure  */
1263	register struct qbus *qb;
1264
1265        /*
1266         * find the percpu entry that matches this machine.
1267         */
1268        for( pcpu = percpu ; pcpu && pcpu->pc_cputype != cpu ; pcpu++ )
1269                ;
1270        if( pcpu == NULL )
1271                return(0);
1272
1273        /*
1274         * Found an entry for this CPU. Because this device is Microvax specific
1275         * we assume that there is a single q-bus and don't have to worry about
1276         * multiple adapters.
1277         *
1278         * Map the device memory.
1279         */
1280	qb = (struct qbus *)pcpu->pc_io->io_details;
1281
1282        i = (u_int)(qvaddr->qv_csr & QV_MEM_BANK) << 7;
1283	ioaccess(qb->qb_maddr + i, QVmap[unit], 512 * VAX_NBPG);
1284	qvssmem = qvmem[unit];
1285        pte = (int *)(QVmap[unit]);
1286        for (i=0; i < 512; i++, pte++)
1287                *pte = (*pte & ~PG_PROT) | PG_UW | PG_V;
1288
1289        qv_scn = (struct qv_info *)((u_int)qvssmem + 251*1024);
1290	qp = qv_scn;
1291        if( (qvaddr->qv_csr & QV_19INCH) && qv_def_scrn == 0)
1292                qv_def_scrn = 1;
1293        *qv_scn = qv_scn_defaults[ qv_def_scrn ];
1294	if (probed)
1295		qp->qvaddr = qvaddr;
1296 	qp->bitmap = qvssmem;
1297        qp->scanmap = (short *)((u_int)qvssmem + 254*1024);
1298        qp->cursorbits = (short *)((u_int)qvssmem + 256*1024-32);
1299	/* set up event queue for later */
1300	qp->ibuff = (vsEvent *)qp - QVMAXEVQ;
1301	qp->iqsize = QVMAXEVQ;
1302	qp->ihead = qp->itail = 0;
1303
1304        /*
1305         * Setup the crt controller chip.
1306         */
1307        for( i=0 ; i<16 ; i++ ) {
1308                qvaddr->qv_crtaddr = i;
1309                qvaddr->qv_crtdata = qv_crt_parms[ qv_def_scrn ][ i ];
1310        }
1311        /*
1312         * Setup the display.
1313         */
1314        qv_init( qvaddr );
1315
1316        /*
1317         * Turn on the video
1318         */
1319        qvaddr->qv_csr |= QV_VIDEO_ENA ;
1320	return 1;
1321}
1322#endif
1323