1 /*        $NetBSD: dtkbd.c,v 1.13 2021/08/07 16:19:02 thorpej Exp $   */
2 
3 /*-
4  * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Doran.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: dtkbd.c,v 1.13 2021/08/07 16:19:02 thorpej Exp $");
34 
35 #include "locators.h"
36 
37 #include <sys/param.h>
38 #include <sys/bus.h>
39 #include <sys/callout.h>
40 #include <sys/device.h>
41 #include <sys/ioctl.h>
42 #include <sys/systm.h>
43 
44 #include <dev/wscons/wsconsio.h>
45 #include <dev/wscons/wskbdvar.h>
46 #include <dev/wscons/wsksymdef.h>
47 #include <dev/wscons/wsksymvar.h>
48 #include <dev/dec/wskbdmap_lk201.h>
49 #include <dev/tc/tcvar.h>
50 
51 #include <pmax/tc/dtreg.h>
52 #include <pmax/tc/dtvar.h>
53 
54 #include <pmax/pmax/cons.h>
55 
56 struct dtkbd_softc {
57           device_t  sc_dev;
58           device_t  sc_wskbddev;
59           int                 sc_enabled;
60 };
61 
62 int       dtkbd_match(device_t, cfdata_t, void *);
63 void      dtkbd_attach(device_t, device_t, void *);
64 int       dtkbd_enable(void *, int);
65 int       dtkbd_ioctl(void *, u_long, void *, int, struct lwp *);
66 void      dtkbd_cngetc(void *, u_int *, int *);
67 void      dtkbd_cnpollc(void *, int);
68 int       dtkbd_process_msg(struct dt_msg *, u_int *, int *);
69 void      dtkbd_handler(void *, struct dt_msg *);
70 void      dtkbd_set_leds(void *, int);
71 
72 const struct wskbd_accessops dtkbd_accessops = {
73           dtkbd_enable,
74           dtkbd_set_leds,
75           dtkbd_ioctl,
76 };
77 
78 const struct wskbd_consops dtkbd_consops = {
79           dtkbd_cngetc,
80           dtkbd_cnpollc,
81 };
82 
83 CFATTACH_DECL_NEW(dtkbd, sizeof(struct dtkbd_softc),
84     dtkbd_match, dtkbd_attach, NULL, NULL);
85 
86 const struct wskbd_mapdata dtkbd_keymapdata = {
87           lkkbd_keydesctab,
88 #ifdef DTKBD_LAYOUT
89           DTKBD_LAYOUT,
90 #else
91           KB_US | KB_LK401,
92 #endif
93 };
94 
95 int       dtkbd_isconsole;
96 uint8_t   dtkbd_map[10];
97 int       dtkbd_maplen;
98 
99 int
dtkbd_match(device_t parent,cfdata_t cf,void * aux)100 dtkbd_match(device_t parent, cfdata_t cf, void *aux)
101 {
102           struct dt_attach_args *dta;
103 
104           dta = aux;
105           return (dta->dta_addr == DT_ADDR_KBD);
106 }
107 
108 void
dtkbd_attach(device_t parent,device_t self,void * aux)109 dtkbd_attach(device_t parent, device_t self, void *aux)
110 {
111           struct dt_softc *dt;
112           struct dtkbd_softc *sc;
113           struct wskbddev_attach_args a;
114 
115           dt = device_private(parent);
116           sc = device_private(self);
117           sc->sc_dev = self;
118 
119           printf("\n");
120 
121           if (dt_establish_handler(dt, &dt_kbd_dv, sc, dtkbd_handler)) {
122                     printf("%s: unable to establish handler\n", device_xname(self));
123                     return;
124           }
125 
126           sc->sc_enabled = 1;
127 
128           a.console = dtkbd_isconsole;
129           a.keymap = &dtkbd_keymapdata;
130           a.accessops = &dtkbd_accessops;
131           a.accesscookie = sc;
132           sc->sc_wskbddev = config_found(self, &a, wskbddevprint, CFARGS_NONE);
133 }
134 
135 void
dtkbd_cnattach(void)136 dtkbd_cnattach(void)
137 {
138 
139           dtkbd_isconsole = 1;
140           dt_cninit();
141 
142           wskbd_cnattach(&dtkbd_consops, &dtkbd_map, &dtkbd_keymapdata);
143 }
144 
145 int
dtkbd_enable(void * v,int on)146 dtkbd_enable(void *v, int on)
147 {
148           struct dtkbd_softc *sc;
149 
150           sc = v;
151           sc->sc_enabled = on;
152 
153           return (0);
154 }
155 
156 void
dtkbd_cngetc(void * v,u_int * type,int * data)157 dtkbd_cngetc(void *v, u_int *type, int *data)
158 {
159           struct dt_msg msg;
160           static u_int types[20];
161           static int cnt, i, vals[20];
162 
163           while (i >= cnt) {
164                     for (;;) {
165                               if (dt_msg_get(&msg, 0) == DT_GET_DONE)
166                                         if (msg.src == dt_kbd_addr &&
167                                             !DT_CTL_P(msg.ctl))
168                                                   break;
169                               DELAY(1000);
170                     }
171 
172                     cnt = dtkbd_process_msg(&msg, types, vals);
173                     i = 0;
174           }
175 
176           *type = types[i++];
177           *data = vals[i++];
178 }
179 
180 void
dtkbd_cnpollc(void * v,int on)181 dtkbd_cnpollc(void *v, int on)
182 {
183 
184           /* XXX */
185 }
186 
187 int
dtkbd_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)188 dtkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
189 {
190 
191           switch (cmd) {
192           case WSKBDIO_GTYPE:
193                     *(int *)data = WSKBD_TYPE_LK201;
194                     return 0;
195           default:
196                     /* XXX */
197                     return (EPASSTHROUGH);
198           }
199 }
200 
201 void
dtkbd_set_leds(void * v,int state)202 dtkbd_set_leds(void *v, int state)
203 {
204 
205           /* XXX */
206 }
207 
208 void
dtkbd_handler(void * cookie,struct dt_msg * msg)209 dtkbd_handler(void *cookie, struct dt_msg *msg)
210 {
211           struct dtkbd_softc *sc;
212           u_int types[20];
213           int i, cnt, vals[20];
214 
215           sc = cookie;
216 
217           if (!sc->sc_enabled)
218                     return;
219 
220           cnt = dtkbd_process_msg(msg, types, vals);
221           for (i = 0; i < cnt; i++)
222                     wskbd_input(sc->sc_wskbddev, types[i], vals[i]);
223 }
224 
225 int
dtkbd_process_msg(struct dt_msg * msg,u_int * types,int * vals)226 dtkbd_process_msg(struct dt_msg *msg, u_int *types, int *vals)
227 {
228           u_int len, c, count;
229           int i, j;
230 
231           len = DT_CTL_LEN(msg->ctl);
232 
233           if ((msg->body[0] < DT_KBD_KEY_MIN && msg->body[0] != DT_KBD_EMPTY) ||
234               len > 10) {
235                     printf("dtkbd0: error: %x %x %x\n", len, msg->body[0],
236                         msg->body[1]);
237 
238                     /*
239                      * Fake an "all ups" to avoid the stuck key syndrome.
240                      */
241                     msg->body[0] = DT_KBD_EMPTY;
242                     len = 1;
243           }
244 
245           if (msg->body[0] == DT_KBD_EMPTY) {
246                     types[0] = WSCONS_EVENT_ALL_KEYS_UP;
247                     vals[0] = 0;
248                     dtkbd_maplen = 0;
249                     return (1);
250           }
251 
252           count = 0;
253 
254           for (i = 0; i < len; i++) {
255                     c = msg->body[i];
256 
257                     for (j = 0; j < dtkbd_maplen; j++)
258                               if (dtkbd_map[j] == c)
259                                         break;
260 
261                     if (j == dtkbd_maplen) {
262                               types[count] = WSCONS_EVENT_KEY_DOWN;
263                               vals[count] = c - MIN_LK201_KEY;
264                               count++;
265                     }
266           }
267 
268           for (j = 0; j < dtkbd_maplen; j++) {
269                     c = dtkbd_map[j];
270 
271                     for (i = 0; i < len; i++)
272                               if (msg->body[i] == c)
273                                         break;
274 
275                     if (i == len) {
276                               types[count] = WSCONS_EVENT_KEY_UP;
277                               vals[count] = c - MIN_LK201_KEY;
278                               count++;
279                     }
280           }
281 
282           memcpy(dtkbd_map, msg->body, len);
283           dtkbd_maplen = len;
284 
285           return (count);
286 }
287