xref: /openbsd/sys/dev/sun/sunkbd.c (revision 17df1aa7)
1 /*	$OpenBSD: sunkbd.c,v 1.25 2009/01/12 21:11:58 miod Exp $	*/
2 
3 /*
4  * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *
28  * Effort sponsored in part by the Defense Advanced Research Projects
29  * Agency (DARPA) and Air Force Research Laboratory, Air Force
30  * Materiel Command, USAF, under agreement number F30602-01-2-0537.
31  *
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/device.h>
37 #include <sys/kernel.h>
38 #include <sys/timeout.h>
39 
40 #include <dev/wscons/wsconsio.h>
41 #include <dev/wscons/wskbdvar.h>
42 #ifdef WSDISPLAY_COMPAT_RAWKBD
43 #include <dev/wscons/wskbdraw.h>
44 #endif
45 
46 #include <dev/sun/sunkbdreg.h>
47 #include <dev/sun/sunkbdvar.h>
48 
49 #ifdef __sparc64__
50 #define	NTCTRL 0
51 #else
52 #include "tctrl.h"
53 #endif
54 
55 #if NTCTRL > 0
56 #include <sparc/dev/tctrlvar.h>		/* XXX for tadpole_bell() */
57 #endif
58 
59 void	sunkbd_bell(struct sunkbd_softc *, u_int, u_int, u_int);
60 void	sunkbd_decode5(u_int8_t, u_int *, int *);
61 int	sunkbd_enable(void *, int);
62 int	sunkbd_getleds(struct sunkbd_softc *);
63 int	sunkbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
64 void	sunkbd_rawrepeat(void *);
65 void	sunkbd_setleds(void *, int);
66 
67 struct wskbd_accessops sunkbd_accessops = {
68 	sunkbd_enable,
69 	sunkbd_setleds,
70 	sunkbd_ioctl
71 };
72 
73 void
74 sunkbd_attach(struct sunkbd_softc *sc, struct wskbddev_attach_args *waa)
75 {
76 #ifdef WSDISPLAY_COMPAT_RAWKBD
77 	timeout_set(&sc->sc_rawrepeat_tmo, sunkbd_rawrepeat, sc);
78 #endif
79 
80 	if (ISTYPE5(sc->sc_layout))
81 		sc->sc_decode = sunkbd_decode5;
82 	else
83 		sc->sc_decode = sunkbd_decode;
84 
85 	sc->sc_wskbddev = config_found((struct device *)sc, waa,
86 	    wskbddevprint);
87 }
88 
89 void
90 sunkbd_bell(struct sunkbd_softc *sc, u_int period, u_int pitch, u_int volume)
91 {
92 	int ticks, s;
93 	u_int8_t c = SKBD_CMD_BELLON;
94 
95 #if NTCTRL > 0
96 	if (tadpole_bell(period / 10, pitch, volume) != 0)
97 		return;
98 #endif
99 
100 	s = spltty();
101 	if (sc->sc_bellactive) {
102 		if (sc->sc_belltimeout == 0)
103 			timeout_del(&sc->sc_bellto);
104 	}
105 	if (pitch == 0 || period == 0) {
106 		sunkbd_bellstop(sc);
107 		splx(s);
108 		return;
109 	}
110 	if (sc->sc_bellactive == 0) {
111 		ticks = (period * hz) / 1000;
112 		if (ticks <= 0)
113 			ticks = 1;
114 
115 		sc->sc_bellactive = 1;
116 		sc->sc_belltimeout = 1;
117 		(*sc->sc_sendcmd)(sc, &c, 1);
118 		timeout_add(&sc->sc_bellto, ticks);
119 	}
120 	splx(s);
121 }
122 
123 void
124 sunkbd_bellstop(void *v)
125 {
126 	struct sunkbd_softc *sc = v;
127 	int s;
128 	u_int8_t c;
129 
130 	s = spltty();
131 	sc->sc_belltimeout = 0;
132 	c = SKBD_CMD_BELLOFF;
133 	(*sc->sc_sendcmd)(v, &c, 1);
134 	sc->sc_bellactive = 0;
135 	splx(s);
136 }
137 
138 void
139 sunkbd_decode(u_int8_t c, u_int *type, int *value)
140 {
141 	switch (c) {
142 	case SKBD_RSP_IDLE:
143 		*type = WSCONS_EVENT_ALL_KEYS_UP;
144 		*value = 0;
145 		break;
146 	default:
147 		*type = (c & 0x80) ?
148 		    WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
149 		*value = c & 0x7f;
150 		break;
151 	}
152 }
153 
154 void
155 sunkbd_decode5(u_int8_t c, u_int *type, int *value)
156 {
157 	sunkbd_decode(c, type, value);
158 	/*
159 	 * Scancode 0x2d is KS_KP_Equal on type 4, and KS_AudioMute on
160 	 * type 5. Rather than provide two distinct maps, we remap the
161 	 * scancode here.
162 	 */
163 	if (*value == 0x2d)
164 		*value = 0x7f;
165 }
166 
167 int
168 sunkbd_enable(void *v, int on)
169 {
170 	return (0);
171 }
172 
173 int
174 sunkbd_getleds(struct sunkbd_softc *sc)
175 {
176 	return (sc->sc_leds);
177 }
178 
179 void
180 sunkbd_input(struct sunkbd_softc *sc, u_int8_t *buf, u_int buflen)
181 {
182 	u_int type;
183 	int value;
184 	int s;
185 
186 	if (sc->sc_wskbddev == NULL)
187 		return;	/* why bother */
188 
189 #ifdef WSDISPLAY_COMPAT_RAWKBD
190 	if (sc->sc_rawkbd) {
191 		u_char rbuf[SUNKBD_MAX_INPUT_SIZE * 2];
192 		int c, rlen, npress;
193 
194 		timeout_del(&sc->sc_rawrepeat_tmo);
195 
196 		npress = rlen = 0;
197 		while (buflen-- != 0) {
198 			(*sc->sc_decode)(*buf++, &type, &value);
199 			c = sunkbd_rawmap[value];
200 			if (c == RAWKEY_Null)
201 				continue;
202 			/* fake extended scancode if necessary */
203 			if (c & 0x80)
204 				rbuf[rlen++] = 0xe0;
205 			rbuf[rlen] = c & 0x7f;
206 			if (type == WSCONS_EVENT_KEY_UP)
207 				rbuf[rlen] |= 0x80;
208 			else {
209 				/* remember down keys for autorepeat */
210 				if (c & 0x80)
211 					sc->sc_rep[npress++] = 0xe0;
212 				sc->sc_rep[npress++] = c & 0x7f;
213 			}
214 			rlen++;
215 		}
216 
217 		s = spltty();
218 		wskbd_rawinput(sc->sc_wskbddev, rbuf, rlen);
219 		splx(s);
220 		sc->sc_nrep = npress;
221 		if (npress != 0)
222 			timeout_add_msec(&sc->sc_rawrepeat_tmo, REP_DELAY1);
223 	} else
224 #endif
225 	{
226 		s = spltty();
227 		while (buflen-- != 0) {
228 			(*sc->sc_decode)(*buf++, &type, &value);
229 			wskbd_input(sc->sc_wskbddev, type, value);
230 		}
231 		splx(s);
232 	}
233 }
234 
235 int
236 sunkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
237 {
238 	struct sunkbd_softc *sc = v;
239 	int *d_int = (int *)data;
240 	struct wskbd_bell_data *d_bell = (struct wskbd_bell_data *)data;
241 
242 	switch (cmd) {
243 	case WSKBDIO_GTYPE:
244 		if (ISTYPE5(sc->sc_layout)) {
245 			*d_int = WSKBD_TYPE_SUN5;
246 		} else {
247 			*d_int = WSKBD_TYPE_SUN;
248 		}
249 		return (0);
250 	case WSKBDIO_SETLEDS:
251 		sunkbd_setleds(sc, *d_int);
252 		return (0);
253 	case WSKBDIO_GETLEDS:
254 		*d_int = sunkbd_getleds(sc);
255 		return (0);
256 	case WSKBDIO_COMPLEXBELL:
257 		sunkbd_bell(sc, d_bell->period, d_bell->pitch, d_bell->volume);
258 		return (0);
259 #ifdef WSDISPLAY_COMPAT_RAWKBD
260 	case WSKBDIO_SETMODE:
261 		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
262 		timeout_del(&sc->sc_rawrepeat_tmo);
263 		return (0);
264 #endif
265 	}
266 
267 	return (-1);
268 }
269 
270 void
271 sunkbd_raw(struct sunkbd_softc *sc, u_int8_t c)
272 {
273 	int claimed = 0;
274 
275 	if (sc->sc_kbdstate == SKBD_STATE_LAYOUT) {
276 		sc->sc_kbdstate = SKBD_STATE_GETKEY;
277 		sc->sc_layout = c;
278 		return;
279 	}
280 
281 	switch (c) {
282 	case SKBD_RSP_RESET:
283 		sc->sc_kbdstate = SKBD_STATE_RESET;
284 		claimed = 1;
285 		break;
286 	case SKBD_RSP_LAYOUT:
287 		sc->sc_kbdstate = SKBD_STATE_LAYOUT;
288 		claimed = 1;
289 		break;
290 	case SKBD_RSP_IDLE:
291 		sc->sc_kbdstate = SKBD_STATE_GETKEY;
292 		claimed = 1;
293 	}
294 
295 	if (claimed)
296 		return;
297 
298 	switch (sc->sc_kbdstate) {
299 	case SKBD_STATE_RESET:
300 		sc->sc_kbdstate = SKBD_STATE_GETKEY;
301 		if (c < KB_SUN2 || c > KB_SUN4)
302 			printf("%s: reset: invalid keyboard type 0x%02x\n",
303 			    sc->sc_dev.dv_xname, c);
304 		else
305 			sc->sc_id = c;
306 		break;
307 	case SKBD_STATE_GETKEY:
308 		break;
309 	}
310 }
311 
312 #ifdef WSDISPLAY_COMPAT_RAWKBD
313 void
314 sunkbd_rawrepeat(void *v)
315 {
316 	struct sunkbd_softc *sc = v;
317 	int s;
318 
319 	s = spltty();
320 	wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep);
321 	splx(s);
322 	timeout_add_msec(&sc->sc_rawrepeat_tmo, REP_DELAYN);
323 }
324 #endif
325 
326 int
327 sunkbd_setclick(struct sunkbd_softc *sc, int click)
328 {
329 	u_int8_t c;
330 
331 	/* Type 2 keyboards do not support keyclick */
332 	if (sc->sc_id == KB_SUN2)
333 		return (ENXIO);
334 
335 	c = click ? SKBD_CMD_CLICKON : SKBD_CMD_CLICKOFF;
336 	(*sc->sc_sendcmd)(sc, &c, 1);
337 	return (0);
338 }
339 
340 void
341 sunkbd_setleds(void *v, int wled)
342 {
343 	struct sunkbd_softc *sc = v;
344 	u_int8_t sled = 0;
345 	u_int8_t cmd[2];
346 
347 	sc->sc_leds = wled;
348 
349 	if (wled & WSKBD_LED_CAPS)
350 		sled |= SKBD_LED_CAPSLOCK;
351 	if (wled & WSKBD_LED_NUM)
352 		sled |= SKBD_LED_NUMLOCK;
353 	if (wled & WSKBD_LED_SCROLL)
354 		sled |= SKBD_LED_SCROLLLOCK;
355 	if (wled & WSKBD_LED_COMPOSE)
356 		sled |= SKBD_LED_COMPOSE;
357 
358 	cmd[0] = SKBD_CMD_SETLED;
359 	cmd[1] = sled;
360 	(*sc->sc_sendcmd)(sc, cmd, sizeof(cmd));
361 }
362