1 /* $NetBSD: kbd.c,v 1.43 2022/06/25 00:58:36 tsutsui Exp $ */
2
3 /*
4 * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
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 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: kbd.c,v 1.43 2022/06/25 00:58:36 tsutsui Exp $");
34
35 #include "ite.h"
36 #include "bell.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/device.h>
41 #include <sys/ioctl.h>
42 #include <sys/tty.h>
43 #include <sys/proc.h>
44 #include <sys/conf.h>
45 #include <sys/file.h>
46 #include <sys/uio.h>
47 #include <sys/kernel.h>
48 #include <sys/syslog.h>
49 #include <sys/signalvar.h>
50 #include <sys/cpu.h>
51 #include <sys/bus.h>
52 #include <sys/intr.h>
53 #include <sys/mutex.h>
54 #include <sys/rndsource.h>
55
56 #include <arch/x68k/dev/intiovar.h>
57 #include <arch/x68k/dev/mfp.h>
58 #include <arch/x68k/dev/itevar.h>
59
60 /* for sun-like event mode, if you go thru /dev/kbd. */
61 #include <arch/x68k/dev/event_var.h>
62
63 #include <machine/kbio.h>
64 #include <machine/kbd.h>
65 #include <machine/vuid_event.h>
66
67 #include "ioconf.h"
68
69 struct kbd_softc {
70 device_t sc_dev;
71 int sc_event_mode; /* if true, collect events, else pass to ite */
72 struct evvar sc_events; /* event queue state */
73 void *sc_softintr_cookie;
74 kmutex_t sc_lock;
75 krndsource_t sc_rndsource;
76 };
77
78 void kbdenable(int);
79 void kbd_setLED(void);
80 #if NITE > 0
81 int kbdcngetc(void);
82 #endif
83
84 static int kbdintr(void *);
85 static void kbdsoftint(void *);
86 static void kbd_bell(int);
87 static int kbd_send_command(int);
88
89 static int kbdmatch(device_t, cfdata_t, void *);
90 static void kbdattach(device_t, device_t, void *);
91
92 CFATTACH_DECL_NEW(kbd, sizeof(struct kbd_softc),
93 kbdmatch, kbdattach, NULL, NULL);
94
95 static int kbd_attached;
96
97 static dev_type_open(kbdopen);
98 static dev_type_close(kbdclose);
99 static dev_type_read(kbdread);
100 static dev_type_ioctl(kbdioctl);
101 static dev_type_poll(kbdpoll);
102 static dev_type_kqfilter(kbdkqfilter);
103
104 const struct cdevsw kbd_cdevsw = {
105 .d_open = kbdopen,
106 .d_close = kbdclose,
107 .d_read = kbdread,
108 .d_write = nowrite,
109 .d_ioctl = kbdioctl,
110 .d_stop = nostop,
111 .d_tty = notty,
112 .d_poll = kbdpoll,
113 .d_mmap = nommap,
114 .d_kqfilter = kbdkqfilter,
115 .d_discard = nodiscard,
116 .d_flag = 0
117 };
118
119 static int
kbdmatch(device_t parent,cfdata_t cf,void * aux)120 kbdmatch(device_t parent, cfdata_t cf, void *aux)
121 {
122
123 if (strcmp(aux, "kbd") != 0)
124 return (0);
125 if (kbd_attached)
126 return (0);
127
128 return (1);
129 }
130
131 static void
kbdattach(device_t parent,device_t self,void * aux)132 kbdattach(device_t parent, device_t self, void *aux)
133 {
134 struct kbd_softc *sc = device_private(self);
135 struct mfp_softc *mfp = device_private(parent);
136
137 kbd_attached = 1;
138 sc->sc_dev = self;
139 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
140
141 /* MFP interrupt #12 is for USART receive buffer full */
142 intio_intr_establish(mfp->sc_intr + 12, "kbd", kbdintr, sc);
143 sc->sc_softintr_cookie = softint_establish(SOFTINT_SERIAL,
144 kbdsoftint, sc);
145
146 rnd_attach_source(&sc->sc_rndsource, device_xname(self),
147 RND_TYPE_TTY, RND_FLAG_DEFAULT);
148
149 kbdenable(1);
150 sc->sc_event_mode = 0;
151 sc->sc_events.ev_io = 0;
152
153 aprint_normal("\n");
154 }
155
156
157 /* definitions for x68k keyboard encoding. */
158 #define KEY_CODE(c) ((c) & 0x7f)
159 #define KEY_UP(c) ((c) & 0x80)
160
161 void
kbdenable(int mode)162 kbdenable(int mode) /* 1: interrupt, 0: poll */
163 {
164
165 intio_set_sysport_keyctrl(8);
166 mfp_bit_clear_iera(MFP_INTR_RCV_FULL | MFP_INTR_TIMER_B);
167 mfp_set_tbcr(MFP_TIMERB_RESET | MFP_TIMERB_STOP);
168 mfp_set_tbdr(13); /* Timer B interrupt interval */
169 mfp_set_tbcr(1); /* 1/4 delay mode */
170 mfp_set_ucr(MFP_UCR_CLKX16 | MFP_UCR_RW_8 | MFP_UCR_ONESB);
171 mfp_set_rsr(MFP_RSR_RE); /* USART receive enable */
172 mfp_set_tsr(MFP_TSR_TE); /* USART transmit enable */
173
174 if (mode) {
175 mfp_bit_set_iera(MFP_INTR_RCV_FULL);
176 /*
177 * Perform null read in case that an input byte is in the
178 * receiver buffer, which prevents further interrupts.
179 * We could save the input, but probably not so valuable.
180 */
181 (void) mfp_get_udr();
182 }
183
184 kbdled = 0; /* all keyboard LED turn off. */
185 kbd_setLED();
186
187 if (!(intio_get_sysport_keyctrl() & 8))
188 aprint_normal(" (no connected keyboard)");
189 }
190
191 static int
kbdopen(dev_t dev,int flags,int mode,struct lwp * l)192 kbdopen(dev_t dev, int flags, int mode, struct lwp *l)
193 {
194 struct kbd_softc *k;
195
196 k = device_lookup_private(&kbd_cd, minor(dev));
197 if (k == NULL)
198 return (ENXIO);
199
200 if (k->sc_events.ev_io)
201 return (EBUSY);
202 k->sc_events.ev_io = l->l_proc;
203 ev_init(&k->sc_events, device_xname(k->sc_dev), &k->sc_lock);
204
205 return (0);
206 }
207
208 static int
kbdclose(dev_t dev,int flags,int mode,struct lwp * l)209 kbdclose(dev_t dev, int flags, int mode, struct lwp *l)
210 {
211 struct kbd_softc *k = device_lookup_private(&kbd_cd, minor(dev));
212
213 /* Turn off event mode, dump the queue */
214 k->sc_event_mode = 0;
215 ev_fini(&k->sc_events);
216 k->sc_events.ev_io = NULL;
217
218 return (0);
219 }
220
221
222 static int
kbdread(dev_t dev,struct uio * uio,int flags)223 kbdread(dev_t dev, struct uio *uio, int flags)
224 {
225 struct kbd_softc *k = device_lookup_private(&kbd_cd, minor(dev));
226
227 return ev_read(&k->sc_events, uio, flags);
228 }
229
230 #if NBELL > 0
231 struct bell_info;
232 int opm_bell_setup(struct bell_info *);
233 void opm_bell_on(void);
234 void opm_bell_off(void);
235 #endif
236
237 static int
kbdioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)238 kbdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
239 {
240 struct kbd_softc *k = device_lookup_private(&kbd_cd, minor(dev));
241 int cmd_data;
242
243 switch (cmd) {
244 case KIOCTRANS:
245 if (*(int *)data == TR_UNTRANS_EVENT)
246 return (0);
247 break;
248
249 case KIOCGTRANS:
250 /*
251 * Get translation mode
252 */
253 *(int *)data = TR_UNTRANS_EVENT;
254 return (0);
255
256 case KIOCSDIRECT:
257 k->sc_event_mode = *(int *)data;
258 return (0);
259
260 case KIOCCMD:
261 cmd_data = *(int *)data;
262 return kbd_send_command(cmd_data);
263
264 case KIOCSLED:
265 kbdled = *(char *)data;
266 kbd_setLED();
267 return (0);
268
269 case KIOCGLED:
270 *(char *)data = kbdled;
271 return (0);
272
273 case KIOCSBELL:
274 #if NBELL > 0
275 return opm_bell_setup((struct bell_info *)data);
276 #else
277 return (0); /* always success */
278 #endif
279
280 case FIONBIO: /* we will remove this someday (soon???) */
281 return (0);
282
283 case FIOASYNC:
284 k->sc_events.ev_async = *(int *)data != 0;
285 return (0);
286
287 case FIOSETOWN:
288 if (-*(int *)data != k->sc_events.ev_io->p_pgid
289 && *(int *)data != k->sc_events.ev_io->p_pid)
290 return (EPERM);
291 return 0;
292
293 case TIOCSPGRP:
294 if (*(int *)data != k->sc_events.ev_io->p_pgid)
295 return (EPERM);
296 return (0);
297
298 default:
299 return (ENOTTY);
300 }
301
302 /*
303 * We identified the ioctl, but we do not handle it.
304 */
305 return (EOPNOTSUPP); /* misuse, but what the heck */
306 }
307
308
309 static int
kbdpoll(dev_t dev,int events,struct lwp * l)310 kbdpoll(dev_t dev, int events, struct lwp *l)
311 {
312 struct kbd_softc *k;
313
314 k = device_lookup_private(&kbd_cd, minor(dev));
315 return (ev_poll(&k->sc_events, events, l));
316 }
317
318 static int
kbdkqfilter(dev_t dev,struct knote * kn)319 kbdkqfilter(dev_t dev, struct knote *kn)
320 {
321 struct kbd_softc *k;
322
323 k = device_lookup_private(&kbd_cd, minor(dev));
324 return (ev_kqfilter(&k->sc_events, kn));
325 }
326
327 #define KBDBUFMASK 63
328 #define KBDBUFSIZ 64
329 static u_char kbdbuf[KBDBUFSIZ];
330 static int kbdputoff = 0;
331 static int kbdgetoff = 0;
332
333 static int
kbdintr(void * arg)334 kbdintr(void *arg)
335 {
336 uint8_t c, st;
337 struct kbd_softc *sc = arg;
338 struct firm_event *fe;
339 int put;
340
341 /* clear receiver error if any */
342 st = mfp_get_rsr();
343
344 c = mfp_get_udr();
345
346 rnd_add_uint32(&sc->sc_rndsource, (st << 8) | c);
347
348 if ((st & MFP_RSR_BF) == 0)
349 return 0; /* intr caused by an err -- no char received */
350
351 /* if not in event mode, deliver straight to ite to process key stroke */
352 if (!sc->sc_event_mode) {
353 kbdbuf[kbdputoff++ & KBDBUFMASK] = c;
354 softint_schedule(sc->sc_softintr_cookie);
355 return 0;
356 }
357
358 /* Keyboard is generating events. Turn this keystroke into an
359 event and put it in the queue. If the queue is full, the
360 keystroke is lost (sorry!). */
361
362 put = sc->sc_events.ev_put;
363 fe = &sc->sc_events.ev_q[put];
364 put = (put + 1) % EV_QSIZE;
365 if (put == sc->sc_events.ev_get) {
366 log(LOG_WARNING, "keyboard event queue overflow\n"); /* ??? */
367 return 0;
368 }
369 fe->id = KEY_CODE(c);
370 fe->value = KEY_UP(c) ? VKEY_UP : VKEY_DOWN;
371 firm_gettime(fe);
372 sc->sc_events.ev_put = put;
373 softint_schedule(sc->sc_softintr_cookie);
374
375 return 0;
376 }
377
378 static void
kbdsoftint(void * arg)379 kbdsoftint(void *arg) /* what if ite is not configured? */
380 {
381 struct kbd_softc *sc = arg;
382
383 if (sc->sc_event_mode)
384 ev_wakeup(&sc->sc_events);
385
386 mutex_enter(&sc->sc_lock);
387 while (kbdgetoff < kbdputoff) {
388 mutex_exit(&sc->sc_lock);
389 ite_filter(kbdbuf[kbdgetoff++ & KBDBUFMASK]);
390 mutex_enter(&sc->sc_lock);
391 }
392 kbdgetoff = kbdputoff = 0;
393
394 mutex_exit(&sc->sc_lock);
395 }
396
397 static void
kbd_bell(int mode)398 kbd_bell(int mode)
399 {
400 #if NBELL > 0
401 if (mode)
402 opm_bell_on();
403 else
404 opm_bell_off();
405 #endif
406 }
407
408 unsigned char kbdled;
409
410 void
kbd_setLED(void)411 kbd_setLED(void)
412 {
413 mfp_send_usart(~kbdled | 0x80);
414 }
415
416 static int
kbd_send_command(int cmd)417 kbd_send_command(int cmd)
418 {
419 switch (cmd) {
420 case KBD_CMD_RESET:
421 /* XXX */
422 return 0;
423
424 case KBD_CMD_BELL:
425 kbd_bell(1);
426 return 0;
427
428 case KBD_CMD_NOBELL:
429 kbd_bell(0);
430 return 0;
431
432 default:
433 return ENOTTY;
434 }
435 }
436
437 /*
438 * for console
439 */
440 #if NITE > 0
441 int
kbdcngetc(void)442 kbdcngetc(void)
443 {
444 int s;
445 u_char ints, c;
446
447 s = splhigh();
448 ints = mfp_get_iera();
449
450 mfp_bit_clear_iera(MFP_INTR_RCV_FULL);
451 mfp_set_rsr(mfp_get_rsr() | MFP_RSR_RE);
452 c = mfp_receive_usart();
453
454 mfp_set_iera(ints);
455 splx(s);
456
457 return c;
458 }
459 #endif
460