1 /* $OpenBSD: psm_intelli.c,v 1.4 2002/03/14 01:27:00 millert Exp $ */
2 /* $NetBSD: psm_intelli.c,v 1.8 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 pmsi_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, dy;
51 
52 	struct device *sc_wsmousedev;
53 };
54 
55 int pmsiprobe(struct device *, void *, void *);
56 void pmsiattach(struct device *, struct device *, void *);
57 void pmsiinput(void *, int);
58 
59 struct cfattach pmsi_ca = {
60 	sizeof(struct pmsi_softc), pmsiprobe, pmsiattach,
61 };
62 
63 int	pmsi_enable(void *);
64 int	pmsi_ioctl(void *, u_long, caddr_t, int, struct proc *);
65 void	pmsi_disable(void *);
66 
67 const struct wsmouse_accessops pmsi_accessops = {
68 	pmsi_enable,
69 	pmsi_ioctl,
70 	pmsi_disable,
71 };
72 
73 static int pmsi_setintellimode(pckbc_tag_t, pckbc_slot_t);
74 
75 static int
pmsi_setintellimode(tag,slot)76 pmsi_setintellimode(tag, slot)
77 	pckbc_tag_t tag;
78 	pckbc_slot_t slot;
79 {
80 	u_char cmd[2], resp[1];
81 	int i, res;
82 	static u_char rates[] = {200, 100, 80};
83 
84 	cmd[0] = PMS_SET_SAMPLE;
85 	for (i = 0; i < 3; i++) {
86 		cmd[1] = rates[i];
87 		res = pckbc_poll_cmd(tag, slot, cmd, 2, 0, 0, 0);
88 		if (res)
89 			return (res);
90 	}
91 
92 	cmd[0] = PMS_SEND_DEV_ID;
93 	res = pckbc_poll_cmd(tag, slot, cmd, 1, 1, resp, 0);
94 	if (res)
95 		return (res);
96 	if (resp[0] != 3)
97 		return (ENXIO);
98 
99 	return (0);
100 }
101 
102 int
pmsiprobe(parent,match,aux)103 pmsiprobe(parent, match, aux)
104 	struct device *parent;
105 	void *match;
106 	void *aux;
107 {
108 	struct pckbc_attach_args *pa = aux;
109 	u_char cmd[1], resp[2];
110 	int res;
111 
112 	if (pa->pa_slot != PCKBC_AUX_SLOT)
113 		return (0);
114 
115 	/* Flush any garbage. */
116 	pckbc_flush(pa->pa_tag, pa->pa_slot);
117 
118 	/* reset the device */
119 	cmd[0] = PMS_RESET;
120 	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
121 	if (res) {
122 #ifdef DEBUG
123 		printf("pmsiprobe: reset error %d\n", res);
124 #endif
125 		return (0);
126 	}
127 	if (resp[0] != PMS_RSTDONE) {
128 		printf("pmsiprobe: reset response 0x%x\n", resp[0]);
129 		return (0);
130 	}
131 
132 	/* get type number (0 = mouse) */
133 	if (resp[1] != 0) {
134 #ifdef DEBUG
135 		printf("pmsiprobe: type 0x%x\n", resp[1]);
136 #endif
137 		return (0);
138 	}
139 
140 	if ((res = pmsi_setintellimode(pa->pa_tag, pa->pa_slot))) {
141 #ifdef DEBUG
142 		printf("pmsiprobe: intellimode -> %d\n", res);
143 #endif
144 		return (0);
145 	}
146 
147 	return (20);
148 }
149 
150 void
pmsiattach(parent,self,aux)151 pmsiattach(parent, self, aux)
152 	struct device *parent, *self;
153 	void *aux;
154 {
155 	struct pmsi_softc *sc = (void *)self;
156 	struct pckbc_attach_args *pa = aux;
157 	struct wsmousedev_attach_args a;
158 	u_char cmd[1], resp[2];
159 	int res;
160 
161 	sc->sc_kbctag = pa->pa_tag;
162 	sc->sc_kbcslot = pa->pa_slot;
163 
164 	printf("\n");
165 
166 	/* Flush any garbage. */
167 	pckbc_flush(pa->pa_tag, pa->pa_slot);
168 
169 	/* reset the device */
170 	cmd[0] = PMS_RESET;
171 	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
172 #ifdef DEBUG
173 	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
174 		printf("pmsiattach: reset error\n");
175 		return;
176 	}
177 #endif
178 	res = pmsi_setintellimode(pa->pa_tag, pa->pa_slot);
179 #ifdef DEBUG
180 	if (res) {
181 		printf("pmsiattach: error setting intelli mode\n");
182 		return;
183 	}
184 #endif
185 
186 	/* Other initialization was done by pmsiprobe. */
187 	sc->inputstate = 0;
188 	sc->oldbuttons = 0;
189 
190 	pckbc_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
191 			       pmsiinput, sc, sc->sc_dev.dv_xname);
192 
193 	a.accessops = &pmsi_accessops;
194 	a.accesscookie = sc;
195 
196 	/*
197 	 * Attach the wsmouse, saving a handle to it.
198 	 * Note that we don't need to check this pointer against NULL
199 	 * here or in pmsintr, because if this fails pms_enable() will
200 	 * never be called, so pmsiinput() will never be called.
201 	 */
202 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
203 
204 	/* no interrupts until enabled */
205 	cmd[0] = PMS_DEV_DISABLE;
206 	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 0, 0, 0);
207 	if (res)
208 		printf("pmsiattach: disable error\n");
209 	pckbc_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
210 }
211 
212 int
pmsi_enable(v)213 pmsi_enable(v)
214 	void *v;
215 {
216 	struct pmsi_softc *sc = v;
217 	u_char cmd[1];
218 	int res;
219 
220 	if (sc->sc_enabled)
221 		return EBUSY;
222 
223 	sc->sc_enabled = 1;
224 	sc->inputstate = 0;
225 	sc->oldbuttons = 0;
226 
227 	pckbc_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1);
228 
229 	cmd[0] = PMS_DEV_ENABLE;
230 	res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, 1, 0, 1, 0);
231 	if (res)
232 		printf("pmsi_enable: command error\n");
233 
234 	return 0;
235 }
236 
237 void
pmsi_disable(v)238 pmsi_disable(v)
239 	void *v;
240 {
241 	struct pmsi_softc *sc = v;
242 	u_char cmd[1];
243 	int res;
244 
245 	cmd[0] = PMS_DEV_DISABLE;
246 	res = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, 1, 0, 1, 0);
247 	if (res)
248 		printf("pmsi_disable: command error\n");
249 
250 	pckbc_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
251 
252 	sc->sc_enabled = 0;
253 }
254 
255 int
pmsi_ioctl(v,cmd,data,flag,p)256 pmsi_ioctl(v, cmd, data, flag, p)
257 	void *v;
258 	u_long cmd;
259 	caddr_t data;
260 	int flag;
261 	struct proc *p;
262 {
263 	struct pmsi_softc *sc = v;
264 	u_char kbcmd[2];
265 	int i;
266 
267 	switch (cmd) {
268 	case WSMOUSEIO_GTYPE:
269 		*(u_int *)data = WSMOUSE_TYPE_PS2;
270 		break;
271 
272 	case WSMOUSEIO_SRES:
273 		i = ((int) *(u_int *)data - 12) / 25;
274 		/* valid values are {0,1,2,3} */
275 		if (i < 0)
276 			i = 0;
277 		if (i > 3)
278 			i = 3;
279 
280 		kbcmd[0] = PMS_SET_RES;
281 		kbcmd[1] = (unsigned char) i;
282 		i = pckbc_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, kbcmd,
283 		    2, 0, 1, 0);
284 
285 		if (i)
286 			printf("pms_ioctl: SET_RES command error\n");
287 		break;
288 
289 	default:
290 		return (-1);
291 	}
292 	return (0);
293 }
294 
295 /* Masks for the first byte of a packet */
296 #define PS2LBUTMASK 0x01
297 #define PS2RBUTMASK 0x02
298 #define PS2MBUTMASK 0x04
299 
pmsiinput(vsc,data)300 void pmsiinput(vsc, data)
301 void *vsc;
302 int data;
303 {
304 	struct pmsi_softc *sc = vsc;
305 	signed char dz;
306 	u_int changed;
307 
308 	if (!sc->sc_enabled) {
309 		/* Interrupts are not expected.  Discard the byte. */
310 		return;
311 	}
312 
313 	switch (sc->inputstate) {
314 
315 	case 0:
316 		if ((data & 0xc0) == 0) { /* no ovfl, bit 3 == 1 too? */
317 			sc->buttons = ((data & PS2LBUTMASK) ? 0x1 : 0) |
318 			    ((data & PS2MBUTMASK) ? 0x2 : 0) |
319 			    ((data & PS2RBUTMASK) ? 0x4 : 0);
320 			++sc->inputstate;
321 		}
322 		break;
323 
324 	case 1:
325 		sc->dx = data;
326 		/* Bounding at -127 avoids a bug in XFree86. */
327 		sc->dx = (sc->dx == -128) ? -127 : sc->dx;
328 		++sc->inputstate;
329 		break;
330 
331 	case 2:
332 		sc->dy = data;
333 		sc->dy = (sc->dy == -128) ? -127 : sc->dy;
334 		++sc->inputstate;
335 		break;
336 
337 	case 3:
338 		dz = data;
339 		dz = (dz == -128) ? -127 : dz;
340 		sc->inputstate = 0;
341 
342 		changed = (sc->buttons ^ sc->oldbuttons);
343 		sc->oldbuttons = sc->buttons;
344 
345 		if (sc->dx || sc->dy || dz || changed)
346 			wsmouse_input(sc->sc_wsmousedev,
347 				      sc->buttons, sc->dx, sc->dy, dz,
348 				      WSMOUSE_INPUT_DELTA);
349 		break;
350 	}
351 
352 	return;
353 }
354 
355 struct cfdriver pmsi_cd = {
356 	NULL, "pmsi", DV_DULL
357 };
358