xref: /openbsd/sys/dev/pckbc/pms.c (revision 3d8817e4)
1 /* $OpenBSD: pms.c,v 1.18 2011/01/03 19:46:34 shadchin 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/pmsreg.h>
37 
38 #include <dev/wscons/wsconsio.h>
39 #include <dev/wscons/wsmousevar.h>
40 
41 #define DEVNAME(sc)	((sc)->sc_dev.dv_xname)
42 
43 struct pms_softc;
44 
45 struct pms_protocol {
46 	int type;
47 #define PMS_STANDARD	0
48 #define PMS_INTELLI	1
49 	u_int packetsize;
50 	int (*enable)(struct pms_softc *);
51 	int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *);
52 	int (*sync)(struct pms_softc *, int);
53 	void (*proc)(struct pms_softc *);
54 	void (*disable)(struct pms_softc *);
55 };
56 
57 struct pms_softc {		/* driver status information */
58 	struct device sc_dev;
59 
60 	pckbc_tag_t sc_kbctag;
61 
62 	int sc_state;
63 #define PMS_STATE_DISABLED	0
64 #define PMS_STATE_ENABLED	1
65 #define PMS_STATE_SUSPENDED	2
66 
67 	int poll;
68 	int inputstate;
69 
70 	const struct pms_protocol *protocol;
71 
72 	u_char packet[8];
73 
74 	struct device *sc_wsmousedev;
75 };
76 
77 #define PMS_BUTTON1DOWN		0x0001	/* left */
78 #define PMS_BUTTON2DOWN		0x0002	/* middle */
79 #define PMS_BUTTON3DOWN		0x0004	/* right */
80 
81 static const u_int butmap[8] = {
82 	0,
83 	PMS_BUTTON1DOWN,
84 	PMS_BUTTON3DOWN,
85 	PMS_BUTTON1DOWN | PMS_BUTTON3DOWN,
86 	PMS_BUTTON2DOWN,
87 	PMS_BUTTON1DOWN | PMS_BUTTON2DOWN,
88 	PMS_BUTTON2DOWN | PMS_BUTTON3DOWN,
89 	PMS_BUTTON1DOWN | PMS_BUTTON2DOWN | PMS_BUTTON3DOWN
90 };
91 
92 /* PS/2 mouse data packet */
93 #define PMS_PS2_BUTTONSMASK	0x07
94 #define PMS_PS2_BUTTON1		0x01	/* left */
95 #define PMS_PS2_BUTTON2		0x04	/* middle */
96 #define PMS_PS2_BUTTON3		0x02	/* right */
97 #define PMS_PS2_XNEG		0x10
98 #define PMS_PS2_YNEG		0x20
99 
100 #define PMS_INTELLI_MAGIC1	200
101 #define PMS_INTELLI_MAGIC2	100
102 #define PMS_INTELLI_MAGIC3	80
103 #define PMS_INTELLI_ID		0x03
104 
105 int	pmsprobe(struct device *, void *, void *);
106 void	pmsattach(struct device *, struct device *, void *);
107 int	pmsactivate(struct device *, int);
108 
109 void	pmsinput(void *, int);
110 
111 int	pms_change_state(struct pms_softc *, int);
112 int	pms_ioctl(void *, u_long, caddr_t, int, struct proc *);
113 int	pms_enable(void *);
114 void	pms_disable(void *);
115 
116 int	pms_cmd(struct pms_softc *, u_char *, int, u_char *, int);
117 int	pms_get_devid(struct pms_softc *, u_char *);
118 int	pms_get_status(struct pms_softc *, u_char *);
119 int	pms_set_rate(struct pms_softc *, int);
120 int	pms_set_resolution(struct pms_softc *, int);
121 int	pms_set_scaling(struct pms_softc *, int);
122 int	pms_reset(struct pms_softc *);
123 int	pms_dev_enable(struct pms_softc *);
124 int	pms_dev_disable(struct pms_softc *);
125 
126 int	pms_enable_intelli(struct pms_softc *);
127 
128 int	pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *);
129 int	pms_sync_mouse(struct pms_softc *, int);
130 void	pms_proc_mouse(struct pms_softc *);
131 
132 struct cfattach pms_ca = {
133 	sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
134 	pmsactivate
135 };
136 
137 struct cfdriver pms_cd = {
138 	NULL, "pms", DV_DULL
139 };
140 
141 const struct wsmouse_accessops pms_accessops = {
142 	pms_enable,
143 	pms_ioctl,
144 	pms_disable,
145 };
146 
147 const struct pms_protocol pms_mouse[] = {
148 	/* Generic PS/2 mouse */
149 	{
150 		PMS_STANDARD, 3,
151 		NULL,
152 		pms_ioctl_mouse,
153 		pms_sync_mouse,
154 		pms_proc_mouse,
155 		NULL
156 	},
157 	/* Microsoft IntelliMouse */
158 	{
159 		PMS_INTELLI, 4,
160 		pms_enable_intelli,
161 		pms_ioctl_mouse,
162 		pms_sync_mouse,
163 		pms_proc_mouse,
164 		NULL
165 	}
166 };
167 
168 int
169 pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen)
170 {
171 	if (sc->poll) {
172 		return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
173 		    cmd, len, resplen, resp, 1);
174 	} else {
175 		return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
176 		    cmd, len, resplen, 1, resp);
177 	}
178 }
179 
180 int
181 pms_get_devid(struct pms_softc *sc, u_char *resp)
182 {
183 	u_char cmd[1];
184 
185 	cmd[0] = PMS_SEND_DEV_ID;
186 	return (pms_cmd(sc, cmd, 1, resp, 1));
187 }
188 
189 int
190 pms_get_status(struct pms_softc *sc, u_char *resp)
191 {
192 	u_char cmd[1];
193 
194 	cmd[0] = PMS_SEND_DEV_STATUS;
195 	return (pms_cmd(sc, cmd, 1, resp, 3));
196 }
197 
198 int
199 pms_set_rate(struct pms_softc *sc, int value)
200 {
201 	u_char cmd[2];
202 
203 	cmd[0] = PMS_SET_SAMPLE;
204 	cmd[1] = value;
205 	return (pms_cmd(sc, cmd, 2, NULL, 0));
206 }
207 
208 int
209 pms_set_resolution(struct pms_softc *sc, int value)
210 {
211 	u_char cmd[2];
212 
213 	cmd[0] = PMS_SET_RES;
214 	cmd[1] = value;
215 	return (pms_cmd(sc, cmd, 2, NULL, 0));
216 }
217 
218 int
219 pms_set_scaling(struct pms_softc *sc, int scale)
220 {
221 	u_char cmd[1];
222 
223 	switch (scale) {
224 	case 1:
225 	default:
226 		cmd[0] = PMS_SET_SCALE11;
227 		break;
228 	case 2:
229 		cmd[0] = PMS_SET_SCALE21;
230 		break;
231 	}
232 	return (pms_cmd(sc, cmd, 1, NULL, 0));
233 }
234 
235 int
236 pms_reset(struct pms_softc *sc)
237 {
238 	u_char cmd[1], resp[2];
239 	int res;
240 
241 	cmd[0] = PMS_RESET;
242 	res = pms_cmd(sc, cmd, 1, resp, 2);
243 #ifdef DEBUG
244 	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0)
245 		printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n",
246 		    DEVNAME(sc), res, resp[0], resp[1]);
247 #endif
248 	return (res);
249 }
250 
251 int
252 pms_dev_enable(struct pms_softc *sc)
253 {
254 	u_char cmd[1];
255 	int res;
256 
257 	cmd[0] = PMS_DEV_ENABLE;
258 	res = pms_cmd(sc, cmd, 1, NULL, 0);
259 	if (res)
260 		printf("%s: enable error\n", DEVNAME(sc));
261 	return (res);
262 }
263 
264 int
265 pms_dev_disable(struct pms_softc *sc)
266 {
267 	u_char cmd[1];
268 	int res;
269 
270 	cmd[0] = PMS_DEV_DISABLE;
271 	res = pms_cmd(sc, cmd, 1, NULL, 0);
272 	if (res)
273 		printf("%s: disable error\n", DEVNAME(sc));
274 	return (res);
275 }
276 
277 int
278 pms_enable_intelli(struct pms_softc *sc)
279 {
280 	u_char resp;
281 
282 	/* the special sequence to enable the third button and the roller */
283 	if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) ||
284 	    pms_set_rate(sc, PMS_INTELLI_MAGIC2) ||
285 	    pms_set_rate(sc, PMS_INTELLI_MAGIC3) ||
286 	    pms_get_devid(sc, &resp) ||
287 	    resp != PMS_INTELLI_ID)
288 		return (0);
289 
290 	return (1);
291 }
292 
293 int
294 pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
295     struct proc *p)
296 {
297 	int i;
298 
299 	switch (cmd) {
300 	case WSMOUSEIO_GTYPE:
301 		*(u_int *)data = WSMOUSE_TYPE_PS2;
302 		break;
303 	case WSMOUSEIO_SRES:
304 		i = ((int) *(u_int *)data - 12) / 25;
305 		/* valid values are {0,1,2,3} */
306 		if (i < 0)
307 			i = 0;
308 		if (i > 3)
309 			i = 3;
310 
311 		if (pms_set_resolution(sc, i))
312 			printf("%s: SET_RES command error\n", DEVNAME(sc));
313 		break;
314 	default:
315 		return (-1);
316 	}
317 	return (0);
318 }
319 
320 int
321 pms_sync_mouse(struct pms_softc *sc, int data)
322 {
323 	if (sc->inputstate != 0)
324 		return (0);
325 
326 	switch (sc->protocol->type) {
327 	case PMS_STANDARD:
328 		if ((data & 0xc0) != 0)
329 			return (-1);
330 		break;
331 	case PMS_INTELLI:
332 		if ((data & 0x08) != 0x08)
333 			return (-1);
334 		break;
335 	}
336 
337 	return (0);
338 }
339 
340 void
341 pms_proc_mouse(struct pms_softc *sc)
342 {
343 	u_int buttons;
344 	int  dx, dy, dz;
345 
346 	buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
347 	dx = (sc->packet[0] & PMS_PS2_XNEG) ?
348 	    (int)sc->packet[1] - 256 : sc->packet[1];
349 	dy = (sc->packet[0] & PMS_PS2_YNEG) ?
350 	    (int)sc->packet[2] - 256 : sc->packet[2];
351 
352 	switch (sc->protocol->type) {
353 	case PMS_STANDARD:
354 		dz = 0;
355 		break;
356 	case PMS_INTELLI:
357 		dz = (signed char)sc->packet[3];
358 		break;
359 	}
360 
361 	wsmouse_input(sc->sc_wsmousedev,
362 	    buttons, dx, dy, dz, 0, WSMOUSE_INPUT_DELTA);
363 }
364 
365 int
366 pmsprobe(struct device *parent, void *match, void *aux)
367 {
368 	struct pckbc_attach_args *pa = aux;
369 	u_char cmd[1], resp[2];
370 	int res;
371 
372 	if (pa->pa_slot != PCKBC_AUX_SLOT)
373 		return (0);
374 
375 	/* Flush any garbage. */
376 	pckbc_flush(pa->pa_tag, pa->pa_slot);
377 
378 	/* reset the device */
379 	cmd[0] = PMS_RESET;
380 	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
381 	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
382 #ifdef DEBUG
383 		printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n",
384 		    res, resp[0], resp[1]);
385 #endif
386 		return (0);
387 	}
388 
389 	return (1);
390 }
391 
392 void
393 pmsattach(struct device *parent, struct device *self, void *aux)
394 {
395 	struct pms_softc *sc = (void *)self;
396 	struct pckbc_attach_args *pa = aux;
397 	struct wsmousedev_attach_args a;
398 
399 	sc->sc_kbctag = pa->pa_tag;
400 
401 	printf("\n");
402 
403 	pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT,
404 	    pmsinput, sc, DEVNAME(sc));
405 
406 	a.accessops = &pms_accessops;
407 	a.accesscookie = sc;
408 
409 	/*
410 	 * Attach the wsmouse, saving a handle to it.
411 	 * Note that we don't need to check this pointer against NULL
412 	 * here or in pmsintr, because if this fails pms_enable() will
413 	 * never be called, so pmsinput() will never be called.
414 	 */
415 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
416 
417 	/* no interrupts until enabled */
418 	sc->poll = 1;
419 	pms_change_state(sc, PMS_STATE_ENABLED);
420 	sc->poll = 1; /* XXX */
421 	pms_change_state(sc, PMS_STATE_DISABLED);
422 }
423 
424 int
425 pmsactivate(struct device *self, int act)
426 {
427 	struct pms_softc *sc = (struct pms_softc *)self;
428 
429 	switch (act) {
430 	case DVACT_SUSPEND:
431 		if (sc->sc_state == PMS_STATE_ENABLED)
432 			pms_change_state(sc, PMS_STATE_SUSPENDED);
433 		break;
434 	case DVACT_RESUME:
435 		if (sc->sc_state == PMS_STATE_SUSPENDED)
436 			pms_change_state(sc, PMS_STATE_ENABLED);
437 		break;
438 	}
439 	return (0);
440 }
441 
442 int
443 pms_change_state(struct pms_softc *sc, int newstate)
444 {
445 	int i;
446 
447 	switch (newstate) {
448 	case PMS_STATE_ENABLED:
449 		if (sc->sc_state == PMS_STATE_ENABLED)
450 			return (EBUSY);
451 
452 		sc->inputstate = 0;
453 
454 		pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1);
455 
456 		if (sc->poll)
457 			pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT);
458 
459 		pms_reset(sc);
460 
461 		sc->protocol = &pms_mouse[0];
462 		for (i = 1; i < nitems(pms_mouse); i++)
463 			if (pms_mouse[i].enable(sc))
464 				sc->protocol = &pms_mouse[i];
465 
466 #ifdef DEBUG
467 		printf("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
468 #endif
469 
470 		pms_dev_enable(sc);
471 		break;
472 	case PMS_STATE_DISABLED:
473 	case PMS_STATE_SUSPENDED:
474 		pms_dev_disable(sc);
475 
476 		if (sc->protocol && sc->protocol->disable)
477 			sc->protocol->disable(sc);
478 
479 		pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0);
480 		break;
481 	}
482 
483 	sc->sc_state = newstate;
484 	sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0;
485 
486 	return (0);
487 }
488 
489 int
490 pms_enable(void *v)
491 {
492 	struct pms_softc *sc = v;
493 
494 	return pms_change_state(sc, PMS_STATE_ENABLED);
495 }
496 
497 void
498 pms_disable(void *v)
499 {
500 	struct pms_softc *sc = v;
501 
502 	pms_change_state(sc, PMS_STATE_DISABLED);
503 }
504 
505 int
506 pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
507 {
508 	struct pms_softc *sc = v;
509 
510 	if (sc->protocol && sc->protocol->ioctl)
511 		return (sc->protocol->ioctl(sc, cmd, data, flag, p));
512 	else
513 		return (-1);
514 }
515 
516 void
517 pmsinput(void *vsc, int data)
518 {
519 	struct pms_softc *sc = vsc;
520 
521 	if (sc->sc_state != PMS_STATE_ENABLED) {
522 		/* Interrupts are not expected.  Discard the byte. */
523 		return;
524 	}
525 
526 	if (sc->protocol->sync(sc, data)) {
527 #ifdef DEBUG
528 		printf("%s: not in sync yet, discard input\n", DEVNAME(sc));
529 #endif
530 		sc->inputstate = 0;
531 		return;
532 	}
533 
534 	sc->packet[sc->inputstate++] = data;
535 	if (sc->inputstate != sc->protocol->packetsize)
536 		return;
537 
538 	sc->protocol->proc(sc);
539 	sc->inputstate = 0;
540 }
541