1 /* $OpenBSD: psm.c,v 1.4 2002/03/14 01:27:00 millert Exp $ */
2 /* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */
3 
4 /*-
5  * Copyright (c) 1994 Charles M. Hannum.
6  * Copyright (c) 1992, 1993 Erik Forsberg.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
16  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
18  * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/device.h>
30 #include <sys/ioctl.h>
31 
32 #include <machine/bus.h>
33 
34 #include <dev/ic/pckbcvar.h>
35 
36 #include <dev/pckbc/psmreg.h>
37 
38 #include <dev/wscons/wsconsio.h>
39 #include <dev/wscons/wsmousevar.h>
40 
41 struct pms_softc {		/* driver status information */
42 	struct device sc_dev;
43 
44 	pckbc_tag_t sc_kbctag;
45 	int sc_kbcslot;
46 
47 	int sc_enabled;		/* input enabled? */
48 	int inputstate;
49 	u_int buttons, oldbuttons;	/* mouse button status */
50 	signed char dx;
51 
52 	struct device *sc_wsmousedev;
53 };
54 
55 int pmsprobe(struct device *, void *, void *);
56 void pmsattach(struct device *, struct device *, void *);
57 void pmsinput(void *, int);
58 
59 struct cfattach pms_ca = {
60 	sizeof(struct pms_softc), pmsprobe, pmsattach,
61 };
62 
63 int	pms_enable(void *);
64 int	pms_ioctl(void *, u_long, caddr_t, int, struct proc *);
65 void	pms_disable(void *);
66 
67 const struct wsmouse_accessops pms_accessops = {
68 	pms_enable,
69 	pms_ioctl,
70 	pms_disable,
71 };
72 
73 int
pmsprobe(parent,match,aux)74 pmsprobe(parent, match, aux)
75 	struct device *parent;
76 	void *match;
77 	void *aux;
78 {
79 	struct pckbc_attach_args *pa = aux;
80 	u_char cmd[1], resp[2];
81 	int res;
82 
83 	if (pa->pa_slot != PCKBC_AUX_SLOT)
84 		return (0);
85 
86 	/* Flush any garbage. */
87 	pckbc_flush(pa->pa_tag, pa->pa_slot);
88 
89 	/* reset the device */
90 	cmd[0] = PMS_RESET;
91 	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
92 	if (res) {
93 #ifdef DEBUG
94 		printf("pmsprobe: reset error %d\n", res);
95 #endif
96 		return (0);
97 	}
98 	if (resp[0] != PMS_RSTDONE) {
99 		printf("pmsprobe: reset response 0x%x\n", resp[0]);
100 		return (0);
101 	}
102 
103 	/* get type number (0 = mouse) */
104 	if (resp[1] != 0) {
105 #ifdef DEBUG
106 		printf("pmsprobe: type 0x%x\n", resp[1]);
107 #endif
108 		return (0);
109 	}
110 
111 	return (10);
112 }
113 
114 void
pmsattach(parent,self,aux)115 pmsattach(parent, self, aux)
116 	struct device *parent, *self;
117 	void *aux;
118 {
119 	struct pms_softc *sc = (void *)self;
120 	struct pckbc_attach_args *pa = aux;
121 	struct wsmousedev_attach_args a;
122 	u_char cmd[1], resp[2];
123 	int res;
124 
125 	sc->sc_kbctag = pa->pa_tag;
126 	sc->sc_kbcslot = pa->pa_slot;
127 
128 	printf("\n");
129 
130 	/* Flush any garbage. */
131 	pckbc_flush(pa->pa_tag, pa->pa_slot);
132 
133 	/* reset the device */
134 	cmd[0] = PMS_RESET;
135 	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
136 #ifdef DEBUG
137 	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
138 		printf("pmsattach: reset error\n");
139 		return;
140 	}
141 #endif
142 
143 	sc->inputstate = 0;
144 	sc->oldbuttons = 0;
145 
146 	pckbc_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
147 			       pmsinput, sc, sc->sc_dev.dv_xname);
148 
149 	a.accessops = &pms_accessops;
150 	a.accesscookie = sc;
151 
152 	/*
153 	 * Attach the wsmouse, saving a handle to it.
154 	 * Note that we don't need to check this pointer against NULL
155 	 * here or in pmsintr, because if this fails pms_enable() will
156 	 * never be called, so pmsinput() will never be called.
157 	 */
158 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
159 
160 	/* no interrupts until enabled */
161 	cmd[0] = PMS_DEV_DISABLE;
162 	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 0, 0, 0);
163 	if (res)
164 		printf("pmsattach: disable error\n");
165 	pckbc_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
166 }
167 
168 int
pms_enable(v)169 pms_enable(v)
170 	void *v;
171 {
172 	struct pms_softc *sc = v;
173 	u_char cmd[1];
174 	int res;
175 
176 	if (sc->sc_enabled)
177 		return EBUSY;
178 
179 	sc->sc_enabled = 1;
180 	sc->inputstate = 0;
181 	sc->oldbuttons = 0;
182 
183 	pckbc_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1);
184 
185 	cmd[0] = PMS_DEV_ENABLE;
186 	res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, 1, 0, 1, 0);
187 	if (res)
188 		printf("pms_enable: command error\n");
189 #if 0
190 	{
191 		u_char scmd[2];
192 
193 		scmd[0] = PMS_SET_RES;
194 		scmd[1] = 3; /* 8 counts/mm */
195 		res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
196 					2, 0, 1, 0);
197 		if (res)
198 			printf("pms_enable: setup error1 (%d)\n", res);
199 
200 		scmd[0] = PMS_SET_SCALE21;
201 		res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
202 					1, 0, 1, 0);
203 		if (res)
204 			printf("pms_enable: setup error2 (%d)\n", res);
205 
206 		scmd[0] = PMS_SET_SAMPLE;
207 		scmd[1] = 100; /* 100 samples/sec */
208 		res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
209 					2, 0, 1, 0);
210 		if (res)
211 			printf("pms_enable: setup error3 (%d)\n", res);
212 	}
213 #endif
214 
215 	return 0;
216 }
217 
218 void
pms_disable(v)219 pms_disable(v)
220 	void *v;
221 {
222 	struct pms_softc *sc = v;
223 	u_char cmd[1];
224 	int res;
225 
226 	cmd[0] = PMS_DEV_DISABLE;
227 	res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, 1, 0, 1, 0);
228 	if (res)
229 		printf("pms_disable: command error\n");
230 
231 	pckbc_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
232 
233 	sc->sc_enabled = 0;
234 }
235 
236 int
pms_ioctl(v,cmd,data,flag,p)237 pms_ioctl(v, cmd, data, flag, p)
238 	void *v;
239 	u_long cmd;
240 	caddr_t data;
241 	int flag;
242 	struct proc *p;
243 {
244 	struct pms_softc *sc = v;
245 	u_char kbcmd[2];
246 	int i;
247 
248 	switch (cmd) {
249 	case WSMOUSEIO_GTYPE:
250 		*(u_int *)data = WSMOUSE_TYPE_PS2;
251 		break;
252 
253 	case WSMOUSEIO_SRES:
254 		i = ((int) *(u_int *)data - 12) / 25;
255 		/* valid values are {0,1,2,3} */
256 		if (i < 0)
257 			i = 0;
258 		if (i > 3)
259 			i = 3;
260 
261 		kbcmd[0] = PMS_SET_RES;
262 		kbcmd[1] = (unsigned char) i;
263 		i = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, kbcmd,
264 		    2, 0, 1, 0);
265 
266 		if (i)
267 			printf("pms_ioctl: SET_RES command error\n");
268 		break;
269 
270 	default:
271 		return (-1);
272 	}
273 	return (0);
274 }
275 
276 /* Masks for the first byte of a packet */
277 #define PS2LBUTMASK 0x01
278 #define PS2RBUTMASK 0x02
279 #define PS2MBUTMASK 0x04
280 
pmsinput(vsc,data)281 void pmsinput(vsc, data)
282 void *vsc;
283 int data;
284 {
285 	struct pms_softc *sc = vsc;
286 	signed char dy;
287 	u_int changed;
288 
289 	if (!sc->sc_enabled) {
290 		/* Interrupts are not expected.  Discard the byte. */
291 		return;
292 	}
293 
294 	switch (sc->inputstate) {
295 
296 	case 0:
297 		if ((data & 0xc0) == 0) { /* no ovfl, bit 3 == 1 too? */
298 			sc->buttons = ((data & PS2LBUTMASK) ? 0x1 : 0) |
299 			    ((data & PS2MBUTMASK) ? 0x2 : 0) |
300 			    ((data & PS2RBUTMASK) ? 0x4 : 0);
301 			++sc->inputstate;
302 		}
303 		break;
304 
305 	case 1:
306 		sc->dx = data;
307 		/* Bounding at -127 avoids a bug in XFree86. */
308 		sc->dx = (sc->dx == -128) ? -127 : sc->dx;
309 		++sc->inputstate;
310 		break;
311 
312 	case 2:
313 		dy = data;
314 		dy = (dy == -128) ? -127 : dy;
315 		sc->inputstate = 0;
316 
317 		changed = (sc->buttons ^ sc->oldbuttons);
318 		sc->oldbuttons = sc->buttons;
319 
320 		if (sc->dx || dy || changed)
321 			wsmouse_input(sc->sc_wsmousedev,
322 				      sc->buttons, sc->dx, dy, 0,
323 				      WSMOUSE_INPUT_DELTA);
324 		break;
325 	}
326 
327 	return;
328 }
329 
330 struct cfdriver pms_cd = {
331 	NULL, "pms", DV_DULL
332 };
333