1/*	$NetBSD: opms.c,v 1.26 2021/09/26 16:36:18 thorpej Exp $	*/
2/*	$OpenBSD: pccons.c,v 1.22 1999/01/30 22:39:37 imp Exp $	*/
3/*	NetBSD: pms.c,v 1.21 1995/04/18 02:25:18 mycroft Exp	*/
4
5/*-
6 * Copyright (c) 1990 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * William Jolitz and Don Ahn.
11 *
12 * Copyright (c) 1994 Charles M. Hannum.
13 * Copyright (c) 1992, 1993 Erik Forsberg.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in the
22 *    documentation and/or other materials provided with the distribution.
23 * 3. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *	@(#)pccons.c	5.11 (Berkeley) 5/21/91
40 */
41
42/*-
43 * Copyright (c) 1993, 1994, 1995 Charles M. Hannum.  All rights reserved.
44 *
45 * This code is derived from software contributed to Berkeley by
46 * William Jolitz and Don Ahn.
47 *
48 * Copyright (c) 1994 Charles M. Hannum.
49 * Copyright (c) 1992, 1993 Erik Forsberg.
50 *
51 * Redistribution and use in source and binary forms, with or without
52 * modification, are permitted provided that the following conditions
53 * are met:
54 * 1. Redistributions of source code must retain the above copyright
55 *    notice, this list of conditions and the following disclaimer.
56 * 2. Redistributions in binary form must reproduce the above copyright
57 *    notice, this list of conditions and the following disclaimer in the
58 *    documentation and/or other materials provided with the distribution.
59 * 3. All advertising materials mentioning features or use of this software
60 *    must display the following acknowledgement:
61 *	This product includes software developed by the University of
62 *	California, Berkeley and its contributors.
63 * 4. Neither the name of the University nor the names of its contributors
64 *    may be used to endorse or promote products derived from this software
65 *    without specific prior written permission.
66 *
67 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
68 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
69 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
70 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
71 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
72 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
73 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
74 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
75 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
76 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
77 * SUCH DAMAGE.
78 *
79 *	@(#)pccons.c	5.11 (Berkeley) 5/21/91
80 */
81
82#include <sys/cdefs.h>
83__KERNEL_RCSID(0, "$NetBSD: opms.c,v 1.26 2021/09/26 16:36:18 thorpej Exp $");
84
85#include <sys/param.h>
86#include <sys/systm.h>
87#include <sys/vnode.h>
88#include <sys/poll.h>
89#include <sys/tty.h>
90#include <sys/device.h>
91#include <sys/proc.h>
92#include <sys/conf.h>
93
94#include <sys/bus.h>
95#include <machine/kbdreg.h>
96#include <machine/mouse.h>
97
98#include <arc/dev/pcconsvar.h>
99#include <arc/dev/opmsvar.h>
100
101#include "ioconf.h"
102
103#define	PMSUNIT(dev)	(minor(dev))
104
105/* status bits */
106#define	PMS_OBUF_FULL	0x01
107#define	PMS_IBUF_FULL	0x02
108
109/* controller commands */
110#define	PMS_INT_ENABLE	0x47	/* enable controller interrupts */
111#define	PMS_INT_DISABLE	0x65	/* disable controller interrupts */
112#define	PMS_AUX_ENABLE	0xa7	/* enable auxiliary port */
113#define	PMS_AUX_DISABLE	0xa8	/* disable auxiliary port */
114#define	PMS_MAGIC_1	0xa9	/* XXX */
115
116#define	PMS_8042_CMD	0x65
117
118/* mouse commands */
119#define	PMS_SET_SCALE11	0xe6	/* set scaling 1:1 */
120#define	PMS_SET_SCALE21 0xe7	/* set scaling 2:1 */
121#define	PMS_SET_RES	0xe8	/* set resolution */
122#define	PMS_GET_SCALE	0xe9	/* get scaling factor */
123#define	PMS_SET_STREAM	0xea	/* set streaming mode */
124#define	PMS_SET_SAMPLE	0xf3	/* set sampling rate */
125#define	PMS_DEV_ENABLE	0xf4	/* mouse on */
126#define	PMS_DEV_DISABLE	0xf5	/* mouse off */
127#define	PMS_RESET	0xff	/* reset */
128
129#define	PMS_CHUNK	128	/* chunk size for read */
130#define	PMS_BSIZE	1020	/* buffer size */
131
132#define	FLUSHQ(q) { if((q)->c_cc) ndflush(q, (q)->c_cc); }
133
134dev_type_open(opmsopen);
135dev_type_close(opmsclose);
136dev_type_read(opmsread);
137dev_type_ioctl(opmsioctl);
138dev_type_poll(opmspoll);
139dev_type_kqfilter(opmskqfilter);
140
141const struct cdevsw opms_cdevsw = {
142	.d_open = opmsopen,
143	.d_close = opmsclose,
144	.d_read = opmsread,
145	.d_write = nowrite,
146	.d_ioctl = opmsioctl,
147	.d_stop = nostop,
148	.d_tty = notty,
149	.d_poll = opmspoll,
150	.d_mmap = nommap,
151	.d_kqfilter = opmskqfilter,
152	.d_discard = nodiscard,
153	.d_flag = 0
154};
155
156static inline void pms_dev_cmd(uint8_t);
157static inline void pms_aux_cmd(uint8_t);
158static inline void pms_pit_cmd(uint8_t);
159
160static inline void
161pms_dev_cmd(uint8_t value)
162{
163
164	kbd_flush_input();
165	kbd_cmd_write_1(0xd4);
166	kbd_flush_input();
167	kbd_data_write_1(value);
168}
169
170static inline void
171pms_aux_cmd(uint8_t value)
172{
173
174	kbd_flush_input();
175	kbd_cmd_write_1(value);
176}
177
178static inline void
179pms_pit_cmd(uint8_t value)
180{
181
182	kbd_flush_input();
183	kbd_cmd_write_1(0x60);
184	kbd_flush_input();
185	kbd_data_write_1(value);
186}
187
188int opms_common_match(bus_space_tag_t kbd_iot, struct pccons_config *config)
189{
190	uint8_t x;
191
192	kbd_context_init(kbd_iot, config);
193
194	pms_dev_cmd(KBC_RESET);
195	pms_aux_cmd(PMS_MAGIC_1);
196	delay(10000);
197	x = kbd_data_read_1();
198	pms_pit_cmd(PMS_INT_DISABLE);
199	if (x & 0x04)
200		return 0;
201
202	return 1;
203}
204
205void
206opms_common_attach(struct opms_softc *sc, bus_space_tag_t opms_iot,
207    struct pccons_config *config)
208{
209
210	kbd_context_init(opms_iot, config);
211	selinit(&sc->sc_rsel);
212
213	/* Other initialization was done by opmsprobe. */
214	sc->sc_state = 0;
215}
216
217int
218opmsopen(dev_t dev, int flag, int mode, struct lwp *l)
219{
220	struct opms_softc *sc;
221
222	sc = device_lookup_private(&opms_cd, PMSUNIT(dev));
223	if (!sc)
224		return ENXIO;
225
226	if (sc->sc_state & PMS_OPEN)
227		return EBUSY;
228
229	if (clalloc(&sc->sc_q, PMS_BSIZE, 0) == -1)
230		return ENOMEM;
231
232	sc->sc_state |= PMS_OPEN;
233	sc->sc_status = 0;
234	sc->sc_x = sc->sc_y = 0;
235
236	/* Enable interrupts. */
237	pms_dev_cmd(PMS_DEV_ENABLE);
238	pms_aux_cmd(PMS_AUX_ENABLE);
239	pms_dev_cmd(PMS_SET_RES);
240	pms_dev_cmd(3);		/* 8 counts/mm */
241	pms_dev_cmd(PMS_SET_SCALE21);
242#if 0
243	pms_dev_cmd(PMS_SET_SAMPLE);
244	pms_dev_cmd(100);	/* 100 samples/sec */
245	pms_dev_cmd(PMS_SET_STREAM);
246#endif
247	pms_pit_cmd(PMS_INT_ENABLE);
248
249	return 0;
250}
251
252int
253opmsclose(dev_t dev, int flag, int mode, struct lwp *l)
254{
255	struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev));
256
257	/* Disable interrupts. */
258	pms_dev_cmd(PMS_DEV_DISABLE);
259	pms_pit_cmd(PMS_INT_DISABLE);
260	pms_aux_cmd(PMS_AUX_DISABLE);
261
262	sc->sc_state &= ~PMS_OPEN;
263
264	clfree(&sc->sc_q);
265
266	return 0;
267}
268
269int
270opmsread(dev_t dev, struct uio *uio, int flag)
271{
272	struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev));
273	int s;
274	int error = 0;
275	size_t length;
276	u_char buffer[PMS_CHUNK];
277
278	/* Block until mouse activity occurred. */
279
280	s = spltty();
281	while (sc->sc_q.c_cc == 0) {
282		if (flag & IO_NDELAY) {
283			splx(s);
284			return EWOULDBLOCK;
285		}
286		sc->sc_state |= PMS_ASLP;
287		error = tsleep((void *)sc, PZERO | PCATCH, "pmsrea", 0);
288		if (error) {
289			sc->sc_state &= ~PMS_ASLP;
290			splx(s);
291			return error;
292		}
293	}
294	splx(s);
295
296	/* Transfer as many chunks as possible. */
297
298	while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
299		length = uimin(sc->sc_q.c_cc, uio->uio_resid);
300		if (length > sizeof(buffer))
301			length = sizeof(buffer);
302
303		/* Remove a small chunk from the input queue. */
304		(void) q_to_b(&sc->sc_q, buffer, length);
305
306		/* Copy the data to the user process. */
307		error = uiomove(buffer, length, uio);
308		if (error)
309			break;
310	}
311
312	return error;
313}
314
315int
316opmsioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
317{
318	struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev));
319	struct mouseinfo info;
320	int s;
321	int error;
322
323	switch (cmd) {
324	case MOUSEIOCREAD:
325		s = spltty();
326
327		info.status = sc->sc_status;
328		if (sc->sc_x || sc->sc_y)
329			info.status |= MOVEMENT;
330
331		if (sc->sc_x > 127)
332			info.xmotion = 127;
333		else if (sc->sc_x < -127)
334			/* Bounding at -127 avoids a bug in XFree86. */
335			info.xmotion = -127;
336		else
337			info.xmotion = sc->sc_x;
338
339		if (sc->sc_y > 127)
340			info.ymotion = 127;
341		else if (sc->sc_y < -127)
342			info.ymotion = -127;
343		else
344			info.ymotion = sc->sc_y;
345
346		/* Reset historical information. */
347		sc->sc_x = sc->sc_y = 0;
348		sc->sc_status &= ~BUTCHNGMASK;
349		ndflush(&sc->sc_q, sc->sc_q.c_cc);
350
351		splx(s);
352		error = copyout(&info, addr, sizeof(struct mouseinfo));
353		break;
354	default:
355		error = EINVAL;
356		break;
357	}
358
359	return error;
360}
361
362/* Masks for the first byte of a packet */
363#define PS2LBUTMASK 0x01
364#define PS2RBUTMASK 0x02
365#define PS2MBUTMASK 0x04
366
367int
368opmsintr(void *arg)
369{
370	struct opms_softc *sc = arg;
371	static int state = 0;
372	static u_char buttons;
373	u_char changed;
374	static char dx, dy;
375	u_char buffer[5];
376
377	if ((sc->sc_state & PMS_OPEN) == 0) {
378		/* Interrupts are not expected.  Discard the byte. */
379		kbd_flush_input();
380		return 0;
381	}
382
383	switch (state) {
384
385	case 0:
386		buttons = kbd_data_read_1();
387		if ((buttons & 0xc0) == 0)
388			++state;
389		break;
390
391	case 1:
392		dx = kbd_data_read_1();
393		/* Bounding at -127 avoids a bug in XFree86. */
394		dx = (dx == -128) ? -127 : dx;
395		++state;
396		break;
397
398	case 2:
399		dy = kbd_data_read_1();
400		dy = (dy == -128) ? -127 : dy;
401		state = 0;
402
403		buttons = ((buttons & PS2LBUTMASK) << 2) |
404		    ((buttons & (PS2RBUTMASK | PS2MBUTMASK)) >> 1);
405		changed = ((buttons ^ sc->sc_status) & BUTSTATMASK) << 3;
406		sc->sc_status = buttons | (sc->sc_status & ~BUTSTATMASK) |
407		    changed;
408
409		if (dx || dy || changed) {
410			/* Update accumulated movements. */
411			sc->sc_x += dx;
412			sc->sc_y += dy;
413
414			/* Add this event to the queue. */
415			buffer[0] = 0x80 | (buttons & BUTSTATMASK);
416			if(dx < 0)
417				buffer[0] |= 0x10;
418			buffer[1] = dx & 0x7f;
419			if(dy < 0)
420				buffer[0] |= 0x20;
421			buffer[2] = dy & 0x7f;
422			buffer[3] = buffer[4] = 0;
423			(void) b_to_q(buffer, sizeof buffer, &sc->sc_q);
424
425			if (sc->sc_state & PMS_ASLP) {
426				sc->sc_state &= ~PMS_ASLP;
427				wakeup((void *)sc);
428			}
429			selnotify(&sc->sc_rsel, 0, 0);
430		}
431
432		break;
433	}
434	return -1;
435}
436
437int
438opmspoll(dev_t dev, int events, struct lwp *l)
439{
440	struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev));
441	int revents = 0;
442	int s = spltty();
443
444	if (events & (POLLIN | POLLRDNORM)) {
445		if (sc->sc_q.c_cc > 0)
446			revents |= events & (POLLIN | POLLRDNORM);
447		else
448			selrecord(l, &sc->sc_rsel);
449	}
450
451	splx(s);
452	return revents;
453}
454
455static void
456filt_opmsrdetach(struct knote *kn)
457{
458	struct opms_softc *sc = kn->kn_hook;
459	int s;
460
461	s = spltty();
462	selremove_knote(&sc->sc_rsel, kn);
463	splx(s);
464}
465
466static int
467filt_opmsread(struct knote *kn, long hint)
468{
469	struct opms_softc *sc = kn->kn_hook;
470
471	kn->kn_data = sc->sc_q.c_cc;
472	return kn->kn_data > 0;
473}
474
475static const struct filterops opmsread_filtops = {
476	.f_flags = FILTEROP_ISFD,
477	.f_attach = NULL,
478	.f_detach = filt_opmsrdetach,
479	.f_event = filt_opmsread,
480};
481
482int
483opmskqfilter(dev_t dev, struct knote *kn)
484{
485	struct opms_softc *sc = device_lookup_private(&opms_cd, PMSUNIT(dev));
486	int s;
487
488	switch (kn->kn_filter) {
489	case EVFILT_READ:
490		kn->kn_fop = &opmsread_filtops;
491		break;
492
493	default:
494		return EINVAL;
495	}
496
497	kn->kn_hook = sc;
498
499	s = spltty();
500	selrecord_knote(&sc->sc_rsel, kn);
501	splx(s);
502
503	return 0;
504}
505