Annotation of sys/dev/pckbc/pckbd.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: pckbd.c,v 1.9 2007/01/30 20:45:05 jcs Exp $ */
2: /* $NetBSD: pckbd.c,v 1.24 2000/06/05 22:20:57 sommerfeld Exp $ */
3:
4: /*-
5: * Copyright (c) 1998 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Charles M. Hannum.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the NetBSD
22: * Foundation, Inc. and its contributors.
23: * 4. Neither the name of The NetBSD Foundation nor the names of its
24: * contributors may be used to endorse or promote products derived
25: * from this software without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37: * POSSIBILITY OF SUCH DAMAGE.
38: */
39:
40: /*-
41: * Copyright (c) 1990 The Regents of the University of California.
42: * All rights reserved.
43: *
44: * This code is derived from software contributed to Berkeley by
45: * William Jolitz and Don Ahn.
46: *
47: * Redistribution and use in source and binary forms, with or without
48: * modification, are permitted provided that the following conditions
49: * are met:
50: * 1. Redistributions of source code must retain the above copyright
51: * notice, this list of conditions and the following disclaimer.
52: * 2. Redistributions in binary form must reproduce the above copyright
53: * notice, this list of conditions and the following disclaimer in the
54: * documentation and/or other materials provided with the distribution.
55: * 3. Neither the name of the University nor the names of its contributors
56: * may be used to endorse or promote products derived from this software
57: * without specific prior written permission.
58: *
59: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69: * SUCH DAMAGE.
70: *
71: * @(#)pccons.c 5.11 (Berkeley) 5/21/91
72: */
73:
74: /*
75: * code to work keyboard for PC-style console
76: */
77:
78: #include <sys/param.h>
79: #include <sys/systm.h>
80: #include <sys/device.h>
81: #include <sys/malloc.h>
82: #include <sys/ioctl.h>
83:
84: #include <machine/bus.h>
85:
86: #ifndef __sparc64__
87: #include <dev/isa/isavar.h> /* XXX XXX XXX */
88: #endif
89:
90: #include <dev/ic/pckbcvar.h>
91:
92: #include <dev/pckbc/pckbdreg.h>
93: #include <dev/pckbc/pckbdvar.h>
94: #include <dev/pckbc/wskbdmap_mfii.h>
95:
96: #include <dev/wscons/wsconsio.h>
97: #include <dev/wscons/wskbdvar.h>
98: #include <dev/wscons/wsksymdef.h>
99: #include <dev/wscons/wsksymvar.h>
100:
101: #if defined(__i386__) || defined(__alpha__)
102: #include <sys/kernel.h> /* XXX for hz */
103: #endif
104:
105: struct pckbd_internal {
106: int t_isconsole;
107: pckbc_tag_t t_kbctag;
108: pckbc_slot_t t_kbcslot;
109:
110: int t_lastchar;
111: int t_extended;
112: int t_extended1;
113:
114: struct pckbd_softc *t_sc; /* back pointer */
115: };
116:
117: struct pckbd_softc {
118: struct device sc_dev;
119:
120: struct pckbd_internal *id;
121: int sc_enabled;
122:
123: int sc_ledstate;
124:
125: struct device *sc_wskbddev;
126: #ifdef WSDISPLAY_COMPAT_RAWKBD
127: int rawkbd;
128: #endif
129: };
130:
131: static int pckbd_is_console(pckbc_tag_t, pckbc_slot_t);
132:
133: int pckbdprobe(struct device *, void *, void *);
134: void pckbdattach(struct device *, struct device *, void *);
135:
136: struct cfattach pckbd_ca = {
137: sizeof(struct pckbd_softc), pckbdprobe, pckbdattach,
138: };
139:
140: int pckbd_enable(void *, int);
141: void pckbd_set_leds(void *, int);
142: int pckbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
143:
144: const struct wskbd_accessops pckbd_accessops = {
145: pckbd_enable,
146: pckbd_set_leds,
147: pckbd_ioctl,
148: };
149:
150: void pckbd_cngetc(void *, u_int *, int *);
151: void pckbd_cnpollc(void *, int);
152: void pckbd_cnbell(void *, u_int, u_int, u_int);
153:
154: const struct wskbd_consops pckbd_consops = {
155: pckbd_cngetc,
156: pckbd_cnpollc,
157: pckbd_cnbell,
158: };
159:
160: const struct wskbd_mapdata pckbd_keymapdata = {
161: pckbd_keydesctab,
162: #ifdef PCKBD_LAYOUT
163: PCKBD_LAYOUT,
164: #else
165: KB_US,
166: #endif
167: };
168:
169: /*
170: * Hackish support for a bell on the PC Keyboard; when a suitable feeper
171: * is found, it attaches itself into the pckbd driver here.
172: */
173: void (*pckbd_bell_fn)(void *, u_int, u_int, u_int, int);
174: void *pckbd_bell_fn_arg;
175:
176: void pckbd_bell(u_int, u_int, u_int, int);
177:
178: int pckbd_set_xtscancode(pckbc_tag_t, pckbc_slot_t);
179: int pckbd_init(struct pckbd_internal *, pckbc_tag_t, pckbc_slot_t,
180: int);
181: void pckbd_input(void *, int);
182:
183: static int pckbd_decode(struct pckbd_internal *, int,
184: u_int *, int *);
185: static int pckbd_led_encode(int);
186: static int pckbd_led_decode(int);
187:
188: struct pckbd_internal pckbd_consdata;
189:
190: int
191: pckbd_set_xtscancode(kbctag, kbcslot)
192: pckbc_tag_t kbctag;
193: pckbc_slot_t kbcslot;
194: {
195: /* default to have the 8042 translate the keyboard with table 3. */
196: int table = 3;
197:
198: if (!pckbc_xt_translation(kbctag, kbcslot, 1)) {
199: #ifdef DEBUG
200: printf("pckbd: enabling of translation failed\n");
201: #endif
202: /* just set the basic XT table and hope it works. */
203: table = 1;
204: }
205:
206: /* keep falling back until we hit a table that looks usable. */
207: for (; table >= 1; table--) {
208: u_char cmd[2];
209: #ifdef DEBUG
210: printf("pckbd: trying table %d\n", table);
211: #endif
212: cmd[0] = KBC_SETTABLE;
213: cmd[1] = table;
214: if (pckbc_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0)) {
215: u_char cmd[1];
216: #ifdef DEBUG
217: printf("pckbd: table set of %d failed\n", table);
218: #endif
219: if (table > 1) {
220: cmd[0] = KBC_RESET;
221: (void)pckbc_poll_cmd(kbctag, kbcslot, cmd,
222: 1, 1, 0, 1);
223: pckbc_flush(kbctag, kbcslot);
224:
225: continue;
226: }
227: }
228:
229: /*
230: * the 8042 took the table set request, however, not all that
231: * report they can work with table 3 actually work, so ask what
232: * table it reports it's in.
233: */
234: if (table == 3) {
235: u_char cmd[1], resp[0];
236:
237: cmd[0] = KBC_SETTABLE;
238: cmd[1] = 0;
239: if (pckbc_poll_cmd(kbctag, kbcslot, cmd, 2, 1, resp, 0)) {
240: /*
241: * query failed, step down to table 2 to be
242: * safe.
243: */
244: #ifdef DEBUG
245: printf("pckbd: table 3 verification failed\n");
246: #endif
247: continue;
248: } else if (resp[0] == 3) {
249: #ifdef DEBUG
250: printf("pckbd: settling on table 3\n");
251: #endif
252: return (0);
253: }
254: #ifdef DEBUG
255: else
256: printf("pckbd: table \"%x\" != 3, trying 2\n",
257: resp[0]);
258: #endif
259: } else {
260: #ifdef DEBUG
261: printf("pckbd: settling on table %d\n", table);
262: #endif
263: return (0);
264: }
265: }
266:
267: return (1);
268: }
269:
270: static int
271: pckbd_is_console(tag, slot)
272: pckbc_tag_t tag;
273: pckbc_slot_t slot;
274: {
275: return (pckbd_consdata.t_isconsole &&
276: (tag == pckbd_consdata.t_kbctag) &&
277: (slot == pckbd_consdata.t_kbcslot));
278: }
279:
280: /*
281: * these are both bad jokes
282: */
283: int
284: pckbdprobe(parent, match, aux)
285: struct device *parent;
286: void *match;
287: void *aux;
288: {
289: struct cfdata *cf = match;
290: struct pckbc_attach_args *pa = aux;
291: u_char cmd[1], resp[1];
292: int res;
293:
294: /*
295: * XXX There are rumours that a keyboard can be connected
296: * to the aux port as well. For me, this didn't work.
297: * For further experiments, allow it if explicitly
298: * wired in the config file.
299: */
300: if ((pa->pa_slot != PCKBC_KBD_SLOT) &&
301: (cf->cf_loc[PCKBCCF_SLOT] == PCKBCCF_SLOT_DEFAULT))
302: return (0);
303:
304: /* Flush any garbage. */
305: pckbc_flush(pa->pa_tag, pa->pa_slot);
306:
307: /* Reset the keyboard. */
308: cmd[0] = KBC_RESET;
309: res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 1, resp, 1);
310: if (res) {
311: #ifdef DEBUG
312: printf("pckbdprobe: reset error %d\n", res);
313: #endif
314: /*
315: * There is probably no keyboard connected.
316: * Let the probe succeed if the keyboard is used
317: * as console input - it can be connected later.
318: */
319: return (pckbd_is_console(pa->pa_tag, pa->pa_slot) ? 1 : 0);
320: }
321: if (resp[0] != KBR_RSTDONE) {
322: printf("pckbdprobe: reset response 0x%x\n", resp[0]);
323: return (0);
324: }
325:
326: /*
327: * Some keyboards seem to leave a second ack byte after the reset.
328: * This is kind of stupid, but we account for them anyway by just
329: * flushing the buffer.
330: */
331: pckbc_flush(pa->pa_tag, pa->pa_slot);
332:
333: if (pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot))
334: return (0);
335:
336: return (2);
337: }
338:
339: void
340: pckbdattach(parent, self, aux)
341: struct device *parent, *self;
342: void *aux;
343: {
344: struct pckbd_softc *sc = (void *)self;
345: struct pckbc_attach_args *pa = aux;
346: int isconsole;
347: struct wskbddev_attach_args a;
348: u_char cmd[1];
349:
350: printf("\n");
351:
352: isconsole = pckbd_is_console(pa->pa_tag, pa->pa_slot);
353:
354: if (isconsole) {
355: sc->id = &pckbd_consdata;
356: /*
357: * Some keyboards are not enabled after a reset,
358: * so make sure it is enabled now.
359: */
360: cmd[0] = KBC_ENABLE;
361: (void) pckbc_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
362: cmd, 1, 0, 0, 0);
363: sc->sc_enabled = 1;
364: } else {
365: sc->id = malloc(sizeof(struct pckbd_internal),
366: M_DEVBUF, M_WAITOK);
367: (void) pckbd_init(sc->id, pa->pa_tag, pa->pa_slot, 0);
368:
369: /* no interrupts until enabled */
370: cmd[0] = KBC_DISABLE;
371: (void) pckbc_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
372: cmd, 1, 0, 0, 0);
373: sc->sc_enabled = 0;
374: }
375:
376: sc->id->t_sc = sc;
377:
378: pckbc_set_inputhandler(sc->id->t_kbctag, sc->id->t_kbcslot,
379: pckbd_input, sc, sc->sc_dev.dv_xname);
380:
381: a.console = isconsole;
382:
383: a.keymap = &pckbd_keymapdata;
384:
385: a.accessops = &pckbd_accessops;
386: a.accesscookie = sc;
387:
388: /*
389: * Attach the wskbd, saving a handle to it.
390: * XXX XXX XXX
391: */
392: sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
393: }
394:
395: int
396: pckbd_enable(v, on)
397: void *v;
398: int on;
399: {
400: struct pckbd_softc *sc = v;
401: u_char cmd[1];
402: int res;
403:
404: if (on) {
405: if (sc->sc_enabled)
406: return (EBUSY);
407:
408: pckbc_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 1);
409:
410: cmd[0] = KBC_ENABLE;
411: res = pckbc_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
412: cmd, 1, 0, NULL, 0);
413: if (res) {
414: printf("pckbd_enable: command error\n");
415: return (res);
416: }
417:
418: res = pckbd_set_xtscancode(sc->id->t_kbctag,
419: sc->id->t_kbcslot);
420: if (res)
421: return (res);
422:
423: sc->sc_enabled = 1;
424: } else {
425: if (sc->id->t_isconsole)
426: return (EBUSY);
427:
428: cmd[0] = KBC_DISABLE;
429: res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
430: cmd, 1, 0, 1, 0);
431: if (res) {
432: printf("pckbd_disable: command error\n");
433: return (res);
434: }
435:
436: pckbc_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 0);
437:
438: sc->sc_enabled = 0;
439: }
440:
441: return (0);
442: }
443:
444: static int
445: pckbd_decode(id, datain, type, dataout)
446: struct pckbd_internal *id;
447: int datain;
448: u_int *type;
449: int *dataout;
450: {
451: int key;
452:
453: if (datain == KBR_EXTENDED0) {
454: id->t_extended = 1;
455: return(0);
456: } else if (datain == KBR_EXTENDED1) {
457: id->t_extended1 = 2;
458: return(0);
459: }
460:
461: /* map extended keys to (unused) codes 128-254 */
462: key = (datain & 0x7f) | (id->t_extended ? 0x80 : 0);
463: id->t_extended = 0;
464:
465: /*
466: * process BREAK key (EXT1 1D 45 EXT1 9D C5):
467: * map to (unused) code 7F
468: */
469: if (id->t_extended1 == 2 && (datain == 0x1d || datain == 0x9d)) {
470: id->t_extended1 = 1;
471: return(0);
472: } else if (id->t_extended1 == 1 &&
473: (datain == 0x45 || datain == 0xc5)) {
474: id->t_extended1 = 0;
475: key = 0x7f;
476: } else if (id->t_extended1 > 0) {
477: id->t_extended1 = 0;
478: }
479:
480: if (datain & 0x80) {
481: id->t_lastchar = 0;
482: *type = WSCONS_EVENT_KEY_UP;
483: } else {
484: /* Always ignore typematic keys */
485: if (key == id->t_lastchar)
486: return(0);
487: id->t_lastchar = key;
488: *type = WSCONS_EVENT_KEY_DOWN;
489: }
490:
491: *dataout = key;
492: return(1);
493: }
494:
495: int
496: pckbd_init(t, kbctag, kbcslot, console)
497: struct pckbd_internal *t;
498: pckbc_tag_t kbctag;
499: pckbc_slot_t kbcslot;
500: int console;
501: {
502: bzero(t, sizeof(struct pckbd_internal));
503:
504: t->t_isconsole = console;
505: t->t_kbctag = kbctag;
506: t->t_kbcslot = kbcslot;
507:
508: return (pckbd_set_xtscancode(kbctag, kbcslot));
509: }
510:
511: static int
512: pckbd_led_encode(led)
513: int led;
514: {
515: int res;
516:
517: res = 0;
518:
519: if (led & WSKBD_LED_SCROLL)
520: res |= 0x01;
521: if (led & WSKBD_LED_NUM)
522: res |= 0x02;
523: if (led & WSKBD_LED_CAPS)
524: res |= 0x04;
525: return(res);
526: }
527:
528: static int
529: pckbd_led_decode(led)
530: int led;
531: {
532: int res;
533:
534: res = 0;
535: if (led & 0x01)
536: res |= WSKBD_LED_SCROLL;
537: if (led & 0x02)
538: res |= WSKBD_LED_NUM;
539: if (led & 0x04)
540: res |= WSKBD_LED_CAPS;
541: return(res);
542: }
543:
544: void
545: pckbd_set_leds(v, leds)
546: void *v;
547: int leds;
548: {
549: struct pckbd_softc *sc = v;
550: u_char cmd[2];
551:
552: cmd[0] = KBC_MODEIND;
553: cmd[1] = pckbd_led_encode(leds);
554: sc->sc_ledstate = cmd[1];
555:
556: (void) pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
557: cmd, 2, 0, 0, 0);
558: }
559:
560: /*
561: * Got a console receive interrupt -
562: * the console processor wants to give us a character.
563: */
564: void
565: pckbd_input(vsc, data)
566: void *vsc;
567: int data;
568: {
569: struct pckbd_softc *sc = vsc;
570: int type, key;
571:
572: #ifdef WSDISPLAY_COMPAT_RAWKBD
573: if (sc->rawkbd) {
574: char d = data;
575: wskbd_rawinput(sc->sc_wskbddev, &d, 1);
576: return;
577: }
578: #endif
579: if (pckbd_decode(sc->id, data, &type, &key))
580: wskbd_input(sc->sc_wskbddev, type, key);
581: }
582:
583: int
584: pckbd_ioctl(v, cmd, data, flag, p)
585: void *v;
586: u_long cmd;
587: caddr_t data;
588: int flag;
589: struct proc *p;
590: {
591: struct pckbd_softc *sc = v;
592:
593: switch (cmd) {
594: case WSKBDIO_GTYPE:
595: *(int *)data = WSKBD_TYPE_PC_XT;
596: return 0;
597: case WSKBDIO_SETLEDS: {
598: char cmd[2];
599: int res;
600: cmd[0] = KBC_MODEIND;
601: cmd[1] = pckbd_led_encode(*(int *)data);
602: sc->sc_ledstate = cmd[1];
603: res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot,
604: cmd, 2, 0, 1, 0);
605: return (res);
606: }
607: case WSKBDIO_GETLEDS:
608: *(int *)data = pckbd_led_decode(sc->sc_ledstate);
609: return (0);
610: case WSKBDIO_COMPLEXBELL:
611: #define d ((struct wskbd_bell_data *)data)
612: /*
613: * Keyboard can't beep directly; we have an
614: * externally-provided global hook to do this.
615: */
616: pckbd_bell(d->pitch, d->period, d->volume, 0);
617: #undef d
618: return (0);
619: #ifdef WSDISPLAY_COMPAT_RAWKBD
620: case WSKBDIO_SETMODE:
621: sc->rawkbd = (*(int *)data == WSKBD_RAW);
622: return (0);
623: #endif
624: }
625: return -1;
626: }
627:
628: void
629: pckbd_bell(pitch, period, volume, poll)
630: u_int pitch, period, volume;
631: int poll;
632: {
633:
634: if (pckbd_bell_fn != NULL)
635: (*pckbd_bell_fn)(pckbd_bell_fn_arg, pitch, period,
636: volume, poll);
637: }
638:
639: void
640: pckbd_hookup_bell(fn, arg)
641: void (*fn)(void *, u_int, u_int, u_int, int);
642: void *arg;
643: {
644:
645: if (pckbd_bell_fn == NULL) {
646: pckbd_bell_fn = fn;
647: pckbd_bell_fn_arg = arg;
648: }
649: }
650:
651: int
652: pckbd_cnattach(kbctag, kbcslot)
653: pckbc_tag_t kbctag;
654: int kbcslot;
655: {
656: char cmd[1];
657: int res;
658:
659: res = pckbd_init(&pckbd_consdata, kbctag, kbcslot, 1);
660: #if 0 /* we allow the console to be attached if no keyboard is present */
661: if (res)
662: return (res);
663: #endif
664:
665: /* Just to be sure. */
666: cmd[0] = KBC_ENABLE;
667: res = pckbc_poll_cmd(kbctag, kbcslot, cmd, 1, 0, 0, 0);
668: #if 0
669: if (res)
670: return (res);
671: #endif
672:
673: wskbd_cnattach(&pckbd_consops, &pckbd_consdata, &pckbd_keymapdata);
674:
675: return (0);
676: }
677:
678: /* ARGSUSED */
679: void
680: pckbd_cngetc(v, type, data)
681: void *v;
682: u_int *type;
683: int *data;
684: {
685: struct pckbd_internal *t = v;
686: int val;
687:
688: for (;;) {
689: val = pckbc_poll_data(t->t_kbctag, t->t_kbcslot);
690: if ((val != -1) && pckbd_decode(t, val, type, data))
691: return;
692: }
693: }
694:
695: void
696: pckbd_cnpollc(v, on)
697: void *v;
698: int on;
699: {
700: struct pckbd_internal *t = v;
701:
702: pckbc_set_poll(t->t_kbctag, t->t_kbcslot, on);
703: }
704:
705: void
706: pckbd_cnbell(v, pitch, period, volume)
707: void *v;
708: u_int pitch, period, volume;
709: {
710:
711: pckbd_bell(pitch, period, volume, 1);
712: }
713:
714: struct cfdriver pckbd_cd = {
715: NULL, "pckbd", DV_DULL
716: };
CVSweb