xref: /openbsd/sys/dev/sun/sunkbd.c (revision a6445c1d)
1 /*	$OpenBSD: sunkbd.c,v 1.26 2011/11/09 14:22:37 shadchin 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_setleds(void *, int);
65 
66 struct wskbd_accessops sunkbd_accessops = {
67 	sunkbd_enable,
68 	sunkbd_setleds,
69 	sunkbd_ioctl
70 };
71 
72 void
73 sunkbd_attach(struct sunkbd_softc *sc, struct wskbddev_attach_args *waa)
74 {
75 	if (ISTYPE5(sc->sc_layout))
76 		sc->sc_decode = sunkbd_decode5;
77 	else
78 		sc->sc_decode = sunkbd_decode;
79 
80 	sc->sc_wskbddev = config_found((struct device *)sc, waa,
81 	    wskbddevprint);
82 }
83 
84 void
85 sunkbd_bell(struct sunkbd_softc *sc, u_int period, u_int pitch, u_int volume)
86 {
87 	int ticks, s;
88 	u_int8_t c = SKBD_CMD_BELLON;
89 
90 #if NTCTRL > 0
91 	if (tadpole_bell(period / 10, pitch, volume) != 0)
92 		return;
93 #endif
94 
95 	s = spltty();
96 	if (sc->sc_bellactive) {
97 		if (sc->sc_belltimeout == 0)
98 			timeout_del(&sc->sc_bellto);
99 	}
100 	if (pitch == 0 || period == 0) {
101 		sunkbd_bellstop(sc);
102 		splx(s);
103 		return;
104 	}
105 	if (sc->sc_bellactive == 0) {
106 		ticks = (period * hz) / 1000;
107 		if (ticks <= 0)
108 			ticks = 1;
109 
110 		sc->sc_bellactive = 1;
111 		sc->sc_belltimeout = 1;
112 		(*sc->sc_sendcmd)(sc, &c, 1);
113 		timeout_add(&sc->sc_bellto, ticks);
114 	}
115 	splx(s);
116 }
117 
118 void
119 sunkbd_bellstop(void *v)
120 {
121 	struct sunkbd_softc *sc = v;
122 	int s;
123 	u_int8_t c;
124 
125 	s = spltty();
126 	sc->sc_belltimeout = 0;
127 	c = SKBD_CMD_BELLOFF;
128 	(*sc->sc_sendcmd)(v, &c, 1);
129 	sc->sc_bellactive = 0;
130 	splx(s);
131 }
132 
133 void
134 sunkbd_decode(u_int8_t c, u_int *type, int *value)
135 {
136 	switch (c) {
137 	case SKBD_RSP_IDLE:
138 		*type = WSCONS_EVENT_ALL_KEYS_UP;
139 		*value = 0;
140 		break;
141 	default:
142 		*type = (c & 0x80) ?
143 		    WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
144 		*value = c & 0x7f;
145 		break;
146 	}
147 }
148 
149 void
150 sunkbd_decode5(u_int8_t c, u_int *type, int *value)
151 {
152 	sunkbd_decode(c, type, value);
153 	/*
154 	 * Scancode 0x2d is KS_KP_Equal on type 4, and KS_AudioMute on
155 	 * type 5. Rather than provide two distinct maps, we remap the
156 	 * scancode here.
157 	 */
158 	if (*value == 0x2d)
159 		*value = 0x7f;
160 }
161 
162 int
163 sunkbd_enable(void *v, int on)
164 {
165 	return (0);
166 }
167 
168 int
169 sunkbd_getleds(struct sunkbd_softc *sc)
170 {
171 	return (sc->sc_leds);
172 }
173 
174 void
175 sunkbd_input(struct sunkbd_softc *sc, u_int8_t *buf, u_int buflen)
176 {
177 	u_int type;
178 	int value;
179 	int s;
180 
181 	if (sc->sc_wskbddev == NULL)
182 		return;	/* why bother */
183 
184 #ifdef WSDISPLAY_COMPAT_RAWKBD
185 	if (sc->sc_rawkbd) {
186 		u_char rbuf[SUNKBD_MAX_INPUT_SIZE * 2];
187 		int c, rlen = 0;
188 
189 		while (buflen-- != 0) {
190 			(*sc->sc_decode)(*buf++, &type, &value);
191 			c = sunkbd_rawmap[value];
192 			if (c == RAWKEY_Null)
193 				continue;
194 			/* fake extended scancode if necessary */
195 			if (c & 0x80)
196 				rbuf[rlen++] = 0xe0;
197 			rbuf[rlen] = c & 0x7f;
198 			if (type == WSCONS_EVENT_KEY_UP)
199 				rbuf[rlen] |= 0x80;
200 			rlen++;
201 		}
202 
203 		s = spltty();
204 		wskbd_rawinput(sc->sc_wskbddev, rbuf, rlen);
205 		splx(s);
206 	} else
207 #endif
208 	{
209 		s = spltty();
210 		while (buflen-- != 0) {
211 			(*sc->sc_decode)(*buf++, &type, &value);
212 			wskbd_input(sc->sc_wskbddev, type, value);
213 		}
214 		splx(s);
215 	}
216 }
217 
218 int
219 sunkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
220 {
221 	struct sunkbd_softc *sc = v;
222 	int *d_int = (int *)data;
223 	struct wskbd_bell_data *d_bell = (struct wskbd_bell_data *)data;
224 
225 	switch (cmd) {
226 	case WSKBDIO_GTYPE:
227 		if (ISTYPE5(sc->sc_layout)) {
228 			*d_int = WSKBD_TYPE_SUN5;
229 		} else {
230 			*d_int = WSKBD_TYPE_SUN;
231 		}
232 		return (0);
233 	case WSKBDIO_SETLEDS:
234 		sunkbd_setleds(sc, *d_int);
235 		return (0);
236 	case WSKBDIO_GETLEDS:
237 		*d_int = sunkbd_getleds(sc);
238 		return (0);
239 	case WSKBDIO_COMPLEXBELL:
240 		sunkbd_bell(sc, d_bell->period, d_bell->pitch, d_bell->volume);
241 		return (0);
242 #ifdef WSDISPLAY_COMPAT_RAWKBD
243 	case WSKBDIO_SETMODE:
244 		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
245 		return (0);
246 #endif
247 	}
248 
249 	return (-1);
250 }
251 
252 void
253 sunkbd_raw(struct sunkbd_softc *sc, u_int8_t c)
254 {
255 	int claimed = 0;
256 
257 	if (sc->sc_kbdstate == SKBD_STATE_LAYOUT) {
258 		sc->sc_kbdstate = SKBD_STATE_GETKEY;
259 		sc->sc_layout = c;
260 		return;
261 	}
262 
263 	switch (c) {
264 	case SKBD_RSP_RESET:
265 		sc->sc_kbdstate = SKBD_STATE_RESET;
266 		claimed = 1;
267 		break;
268 	case SKBD_RSP_LAYOUT:
269 		sc->sc_kbdstate = SKBD_STATE_LAYOUT;
270 		claimed = 1;
271 		break;
272 	case SKBD_RSP_IDLE:
273 		sc->sc_kbdstate = SKBD_STATE_GETKEY;
274 		claimed = 1;
275 	}
276 
277 	if (claimed)
278 		return;
279 
280 	switch (sc->sc_kbdstate) {
281 	case SKBD_STATE_RESET:
282 		sc->sc_kbdstate = SKBD_STATE_GETKEY;
283 		if (c < KB_SUN2 || c > KB_SUN4)
284 			printf("%s: reset: invalid keyboard type 0x%02x\n",
285 			    sc->sc_dev.dv_xname, c);
286 		else
287 			sc->sc_id = c;
288 		break;
289 	case SKBD_STATE_GETKEY:
290 		break;
291 	}
292 }
293 
294 int
295 sunkbd_setclick(struct sunkbd_softc *sc, int click)
296 {
297 	u_int8_t c;
298 
299 	/* Type 2 keyboards do not support keyclick */
300 	if (sc->sc_id == KB_SUN2)
301 		return (ENXIO);
302 
303 	c = click ? SKBD_CMD_CLICKON : SKBD_CMD_CLICKOFF;
304 	(*sc->sc_sendcmd)(sc, &c, 1);
305 	return (0);
306 }
307 
308 void
309 sunkbd_setleds(void *v, int wled)
310 {
311 	struct sunkbd_softc *sc = v;
312 	u_int8_t sled = 0;
313 	u_int8_t cmd[2];
314 
315 	sc->sc_leds = wled;
316 
317 	if (wled & WSKBD_LED_CAPS)
318 		sled |= SKBD_LED_CAPSLOCK;
319 	if (wled & WSKBD_LED_NUM)
320 		sled |= SKBD_LED_NUMLOCK;
321 	if (wled & WSKBD_LED_SCROLL)
322 		sled |= SKBD_LED_SCROLLLOCK;
323 	if (wled & WSKBD_LED_COMPOSE)
324 		sled |= SKBD_LED_COMPOSE;
325 
326 	cmd[0] = SKBD_CMD_SETLED;
327 	cmd[1] = sled;
328 	(*sc->sc_sendcmd)(sc, cmd, sizeof(cmd));
329 }
330