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