ukbd.c revision 1.3 1 /* $NetBSD: ukbd.c,v 1.3 1998/07/26 17:42:49 augustss Exp $ */
2
3 /*
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Author: Lennart Augustsson <augustss (at) carlstedt.se>
8 * Carlstedt Research & Technology
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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/device.h>
45 #include <sys/ioctl.h>
46 #include <sys/tty.h>
47 #include <sys/file.h>
48 #include <sys/select.h>
49 #include <sys/proc.h>
50 #include <sys/vnode.h>
51 #include <sys/device.h>
52 #include <sys/poll.h>
53
54 #include <dev/usb/usb.h>
55 #include <dev/usb/usbhid.h>
56 #include <dev/usb/usbdi.h>
57 #include <dev/usb/usbdi_util.h>
58 #include <dev/usb/usbdevs.h>
59 #include <dev/usb/usb_quirks.h>
60 #include <dev/usb/hid.h>
61
62 #include <dev/wscons/wsconsio.h>
63 #include <dev/wscons/wskbdvar.h>
64 #include <dev/wscons/wsksymdef.h>
65 #include <dev/wscons/wsksymvar.h>
66 #include <dev/wscons/wskbdmap_mfii.h>
67
68 #include "opt_pckbd_layout.h"
69 #include "opt_wsdisplay_compat.h"
70
71 #ifdef USB_DEBUG
72 #define DPRINTF(x) if (ukbddebug) printf x
73 #define DPRINTFN(n,x) if (ukbddebug>(n)) printf x
74 int ukbddebug = 0;
75 #else
76 #define DPRINTF(x)
77 #define DPRINTFN(n,x)
78 #endif
79
80 #define UPROTO_BOOT_KEYBOARD 1
81
82 #define NKEYCODE 6
83
84 #define NUM_LOCK 0x01
85 #define CAPS_LOCK 0x02
86 #define SCROLL_LOCK 0x04
87
88 struct ukbd_data {
89 u_int8_t modifiers;
90 #define MOD_CONTROL_L 0x01
91 #define MOD_CONTROL_R 0x10
92 #define MOD_SHIFT_L 0x02
93 #define MOD_SHIFT_R 0x20
94 #define MOD_ALT_L 0x04
95 #define MOD_ALT_R 0x40
96 #define MOD_WIN_L 0x08
97 #define MOD_WIN_R 0x80
98 u_int8_t reserved;
99 u_int8_t keycode[NKEYCODE];
100 };
101
102 #define PRESS 0
103 #define RELEASE 0x80
104
105 #define NMOD 6
106 static struct {
107 int mask, key;
108 } ukbd_mods[NMOD] = {
109 { MOD_CONTROL_L, 29 },
110 { MOD_CONTROL_R, 58 },
111 { MOD_SHIFT_L, 42 },
112 { MOD_SHIFT_R, 54 },
113 { MOD_ALT_L, 56 },
114 { MOD_ALT_R, 56 },
115 };
116
117 #define XX 0
118 /* Translate USB keycodes to US keyboard scancodes. */
119 /* XXX very incomplete */
120 static char ukbd_trtab[256] = {
121 0, 0, 0, 0, 30, 48, 46, 32,
122 18, 33, 34, 35, 23, 36, 37, 38,
123 50, 49, 24, 25, 16, 19, 31, 20,
124 22, 47, 17, 45, 21, 44, 2, 3,
125 4, 5, 6, 7, 8, 9, 10, 11,
126 28, 1, 14, 15, 57, 12, 13, 26,
127 27, 43, XX, 39, 40, 41, 51, 52,
128 53, 58, 59, 60, 61, 62, 63, 64,
129 65, 66, 67, 68, 87, 88, XX, 70,
130 XX, XX, XX, XX, XX,
131 };
132
133 #define KEY_ERROR 0x01
134
135 struct ukbd_softc {
136 struct device sc_dev; /* base device */
137 usbd_interface_handle sc_iface; /* interface */
138 usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
139 int sc_ep_addr;
140
141 struct ukbd_data sc_ndata;
142 struct ukbd_data sc_odata;
143
144 int sc_state;
145 #define UKBD_NEEDCLEAR 0x01 /* needs clearing endpoint stall */
146 int sc_disconnected; /* device is gone */
147
148 int sc_leds;
149 struct device *sc_wskbddev;
150 #ifdef WSDISPLAY_COMPAT_RAWKBD
151 int sc_rawkbd;
152 #endif
153
154 int sc_polling;
155 int sc_pollchar;
156 };
157
158 #define UKBDUNIT(dev) (minor(dev))
159 #define UKBD_CHUNK 128 /* chunk size for read */
160 #define UKBD_BSIZE 1020 /* buffer size */
161
162 int ukbd_match __P((struct device *, struct cfdata *, void *));
163 void ukbd_attach __P((struct device *, struct device *, void *));
164
165 void ukbd_cngetc __P((void *, u_int *, int *));
166 void ukbd_cnpollc __P((void *, int));
167
168 void ukbd_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
169 void ukbd_disco __P((void *));
170
171 void ukbd_set_leds __P((void *, int));
172 int ukbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
173
174 extern struct cfdriver ukbd_cd;
175
176 struct cfattach ukbd_ca = {
177 sizeof(struct ukbd_softc), ukbd_match, ukbd_attach
178 };
179
180 int
181 ukbd_match(parent, match, aux)
182 struct device *parent;
183 struct cfdata *match;
184 void *aux;
185 {
186 struct usb_attach_arg *uaa = (struct usb_attach_arg *)aux;
187 usb_interface_descriptor_t *id;
188
189 /* Check that this is a keyboard that speaks the boot protocol. */
190 if (!uaa->iface)
191 return (UMATCH_NONE);
192 id = usbd_get_interface_descriptor(uaa->iface);
193 if (id->bInterfaceClass != UCLASS_HID ||
194 id->bInterfaceSubClass != USUBCLASS_BOOT ||
195 id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD)
196 return (UMATCH_NONE);
197 return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
198 }
199
200 void
201 ukbd_attach(parent, self, aux)
202 struct device *parent;
203 struct device *self;
204 void *aux;
205 {
206 struct ukbd_softc *sc = (struct ukbd_softc *)self;
207 struct usb_attach_arg *uaa = aux;
208 usbd_interface_handle iface = uaa->iface;
209 usb_interface_descriptor_t *id;
210 usb_endpoint_descriptor_t *ed;
211 usbd_status r;
212 char devinfo[1024];
213 struct wskbddev_attach_args a;
214
215 sc->sc_disconnected = 1;
216 sc->sc_iface = iface;
217 id = usbd_get_interface_descriptor(iface);
218 usbd_devinfo(uaa->device, 0, devinfo);
219 printf(": %s (interface class %d/%d)\n", devinfo,
220 id->bInterfaceClass, id->bInterfaceSubClass);
221 ed = usbd_interface2endpoint_descriptor(iface, 0);
222 if (!ed) {
223 printf("%s: could not read endpoint descriptor\n",
224 sc->sc_dev.dv_xname);
225 return;
226 }
227
228 DPRINTFN(10,("ukbd_attach: \
229 bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
230 ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
231 ed->bEndpointAddress & UE_IN ? "in" : "out",
232 ed->bmAttributes & UE_XFERTYPE,
233 UGETW(ed->wMaxPacketSize), ed->bInterval));
234
235 if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
236 (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
237 printf("%s: unexpected endpoint\n",
238 sc->sc_dev.dv_xname);
239 return;
240 }
241
242 if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) {
243 r = usbd_set_protocol(iface, 0);
244 DPRINTFN(5, ("ukbd_attach: protocol set\n"));
245 if (r != USBD_NORMAL_COMPLETION) {
246 printf("%s: set protocol failed\n",
247 sc->sc_dev.dv_xname);
248 return;
249 }
250 }
251
252 sc->sc_ep_addr = ed->bEndpointAddress;
253 sc->sc_disconnected = 0;
254
255 a.console = 0; /* XXX */
256 #ifdef PCKBD_LAYOUT
257 a.layout = PCKBD_LAYOUT;
258 #else
259 a.layout = KB_US;
260 #endif
261 a.keydesc = pckbd_keydesctab;
262 a.num_keydescs = sizeof(pckbd_keydesctab)/sizeof(pckbd_keydesctab[0]);
263 a.getc = ukbd_cngetc;
264 a.pollc = ukbd_cnpollc;
265 a.set_leds = ukbd_set_leds;
266 a.ioctl = ukbd_ioctl;
267 a.accesscookie = sc;
268 sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
269
270 /* Set up interrupt pipe. */
271 r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
272 USBD_SHORT_XFER_OK,
273 &sc->sc_intrpipe, sc, &sc->sc_ndata,
274 sizeof(sc->sc_ndata), ukbd_intr);
275 if (r != USBD_NORMAL_COMPLETION) {
276 printf("%s: usbd_open_pipe_intr failed, error=%d\n",
277 sc->sc_dev.dv_xname, r);
278 return;
279 }
280 usbd_set_disco(sc->sc_intrpipe, ukbd_disco, sc);
281 }
282
283 void
284 ukbd_disco(p)
285 void *p;
286 {
287 struct ukbd_softc *sc = p;
288 sc->sc_disconnected = 1;
289 }
290
291 void
292 ukbd_intr(reqh, addr, status)
293 usbd_request_handle reqh;
294 usbd_private_handle addr;
295 usbd_status status;
296 {
297 struct ukbd_softc *sc = addr;
298 struct ukbd_data *ud = &sc->sc_ndata;
299 int mod, omod;
300 char ibuf[NMOD+2*NKEYCODE]; /* chars events */
301 int nkeys, i, j;
302 int key, c;
303 #define ADDKEY(c) ibuf[nkeys++] = (c)
304
305 DPRINTFN(5, ("ukbd_intr: status=%d\n", status));
306 if (status == USBD_CANCELLED)
307 return;
308
309 if (status != USBD_NORMAL_COMPLETION) {
310 DPRINTF(("ukbd_intr: status=%d\n", status));
311 sc->sc_state |= UKBD_NEEDCLEAR;
312 return;
313 }
314
315 DPRINTFN(5, (" mod=0x%02x key0=0x%02x key1=0x%02x\n",
316 ud->modifiers, ud->keycode[0], ud->keycode[1]));
317
318 if (ud->keycode[0] == KEY_ERROR)
319 return; /* ignore */
320 nkeys = 0;
321 mod = ud->modifiers;
322 omod = sc->sc_odata.modifiers;
323 if (mod != omod)
324 for (i = 0; i < NMOD; i++)
325 if (( mod & ukbd_mods[i].mask) !=
326 (omod & ukbd_mods[i].mask))
327 ADDKEY(ukbd_mods[i].key |
328 (mod & ukbd_mods[i].mask
329 ? PRESS : RELEASE));
330 if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) {
331 /* Check for released keys. */
332 for (i = 0; i < NKEYCODE; i++) {
333 key = sc->sc_odata.keycode[i];
334 if (key == 0)
335 continue;
336 for (j = 0; j < NKEYCODE; j++)
337 if (key == ud->keycode[j])
338 goto rfound;
339 c = ukbd_trtab[key];
340 if (c)
341 ADDKEY(c | RELEASE);
342 rfound:
343 ;
344 }
345
346 /* Check for pressed keys. */
347 for (i = 0; i < NKEYCODE; i++) {
348 key = ud->keycode[i];
349 if (key == 0)
350 continue;
351 for (j = 0; j < NKEYCODE; j++)
352 if (key == sc->sc_odata.keycode[j])
353 goto pfound;
354 c = ukbd_trtab[key];
355 if (c)
356 ADDKEY(c | PRESS);
357 pfound:
358 ;
359 }
360 }
361 sc->sc_odata = *ud;
362
363 if (sc->sc_polling) {
364 if (nkeys > 0)
365 sc->sc_pollchar = ibuf[0];
366 return;
367 }
368 for (i = 0; i < nkeys; i++) {
369 c = ibuf[i];
370 wskbd_input(sc->sc_wskbddev,
371 c&0x80 ? WSCONS_EVENT_KEY_UP:WSCONS_EVENT_KEY_DOWN,
372 c & 0x7f);
373 }
374 }
375
376 void
377 ukbd_set_leds(v, leds)
378 void *v;
379 int leds;
380 {
381 struct ukbd_softc *sc = v;
382 u_int8_t res;
383
384 DPRINTF(("ukbd_set_leds: sc=%p leds=%d\n", sc, leds));
385
386 sc->sc_leds = leds;
387 res = 0;
388 if (leds & WSKBD_LED_SCROLL)
389 res |= SCROLL_LOCK;
390 if (leds & WSKBD_LED_NUM)
391 res |= NUM_LOCK;
392 if (leds & WSKBD_LED_CAPS)
393 res |= CAPS_LOCK;
394 usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, 0, &res, 1);
395 }
396
397 int
398 ukbd_ioctl(v, cmd, data, flag, p)
399 void *v;
400 u_long cmd;
401 caddr_t data;
402 int flag;
403 struct proc *p;
404 {
405 struct ukbd_softc *sc = v;
406
407 switch (cmd) {
408 case WSKBDIO_GTYPE:
409 *(int *)data = WSKBD_TYPE_PC_XT;
410 return 0;
411 case WSKBDIO_SETLEDS:
412 ukbd_set_leds(v, *(int *)data);
413 return 0;
414 case WSKBDIO_GETLEDS:
415 *(int *)data = sc->sc_leds;
416 return (0);
417 #ifdef WSDISPLAY_COMPAT_RAWKBD
418 case WSKBDIO_SETMODE:
419 sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
420 return (0);
421 #endif
422 }
423 return -1;
424 }
425
426 void
427 ukbd_cngetc(v, type, data)
428 void *v;
429 u_int *type;
430 int *data;
431 {
432 struct ukbd_softc *sc = v;
433 usbd_lock_token s;
434 extern int usbd_use_polling;
435
436 s = usbd_lock();
437 usbd_use_polling = 1;
438 sc->sc_polling = 1;
439 sc->sc_pollchar = -1;
440 while(sc->sc_pollchar == -1)
441 usbd_dopoll(sc->sc_iface);
442 sc->sc_polling = 0;
443 usbd_use_polling = 0;
444 *type = sc->sc_pollchar&0x80?WSCONS_EVENT_KEY_UP:WSCONS_EVENT_KEY_DOWN;
445 *data = sc->sc_pollchar & 0x7f;
446 usbd_unlock(s);
447 }
448
449 void
450 ukbd_cnpollc(v, on)
451 void *v;
452 int on;
453 {
454 DPRINTF(("ukbd_cnpollc: sc=%p on=%d\n", v, on));
455 }
456