xref: /openbsd-src/sys/dev/usb/ukbd.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: ukbd.c,v 1.76 2016/01/12 19:16:21 jcs Exp $	*/
2 /*      $NetBSD: ukbd.c,v 1.85 2003/03/11 16:44:00 augustss Exp $        */
3 
4 /*
5  * Copyright (c) 2010 Miodrag Vallat.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 /*
20  * Copyright (c) 1998 The NetBSD Foundation, Inc.
21  * All rights reserved.
22  *
23  * This code is derived from software contributed to The NetBSD Foundation
24  * by Lennart Augustsson (lennart@augustsson.net) at
25  * Carlstedt Research & Technology.
26  *
27  * Redistribution and use in source and binary forms, with or without
28  * modification, are permitted provided that the following conditions
29  * are met:
30  * 1. Redistributions of source code must retain the above copyright
31  *    notice, this list of conditions and the following disclaimer.
32  * 2. Redistributions in binary form must reproduce the above copyright
33  *    notice, this list of conditions and the following disclaimer in the
34  *    documentation and/or other materials provided with the distribution.
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
37  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
38  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
41  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
42  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
43  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
44  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46  * POSSIBILITY OF SUCH DAMAGE.
47  */
48 
49 /*
50  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
51  */
52 
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/timeout.h>
56 #include <sys/kernel.h>
57 #include <sys/device.h>
58 #include <sys/ioctl.h>
59 
60 #include <dev/usb/usb.h>
61 #include <dev/usb/usbhid.h>
62 
63 #include <dev/usb/usbdi.h>
64 #include <dev/usb/usbdi_util.h>
65 #include <dev/usb/usbdevs.h>
66 #include <dev/usb/usb_quirks.h>
67 #include <dev/usb/uhidev.h>
68 #include <dev/usb/ukbdvar.h>
69 
70 #include <dev/wscons/wsconsio.h>
71 #include <dev/wscons/wskbdvar.h>
72 #include <dev/wscons/wsksymdef.h>
73 #include <dev/wscons/wsksymvar.h>
74 
75 #include <dev/hid/hidkbdsc.h>
76 
77 #ifdef UKBD_DEBUG
78 #define DPRINTF(x)	do { if (ukbddebug) printf x; } while (0)
79 #define DPRINTFN(n,x)	do { if (ukbddebug>(n)) printf x; } while (0)
80 int	ukbddebug = 0;
81 #else
82 #define DPRINTF(x)
83 #define DPRINTFN(n,x)
84 #endif
85 
86 const kbd_t ukbd_countrylayout[1 + HCC_MAX] = {
87 	(kbd_t)-1,
88 	(kbd_t)-1,	/* arabic */
89 	KB_BE,		/* belgian */
90 	(kbd_t)-1,	/* canadian bilingual */
91 	KB_CF,		/* canadian french */
92 	(kbd_t)-1,	/* czech */
93 	KB_DK,		/* danish */
94 	(kbd_t)-1,	/* finnish */
95 	KB_FR,		/* french */
96 	KB_DE,		/* german */
97 	(kbd_t)-1,	/* greek */
98 	(kbd_t)-1,	/* hebrew */
99 	KB_HU,		/* hungary */
100 	(kbd_t)-1,	/* international (iso) */
101 	KB_IT,		/* italian */
102 	KB_JP,		/* japanese (katakana) */
103 	(kbd_t)-1,	/* korean */
104 	KB_LA,		/* latin american */
105 	(kbd_t)-1,	/* netherlands/dutch */
106 	KB_NO,		/* norwegian */
107 	(kbd_t)-1,	/* persian (farsi) */
108 	KB_PL,		/* polish */
109 	KB_PT,		/* portuguese */
110 	KB_RU,		/* russian */
111 	(kbd_t)-1,	/* slovakia */
112 	KB_ES,		/* spanish */
113 	KB_SV,		/* swedish */
114 	KB_SF,		/* swiss french */
115 	KB_SG,		/* swiss german */
116 	(kbd_t)-1,	/* switzerland */
117 	(kbd_t)-1,	/* taiwan */
118 	KB_TR,		/* turkish Q */
119 	KB_UK,		/* uk */
120 	KB_US,		/* us */
121 	(kbd_t)-1,	/* yugoslavia */
122 	(kbd_t)-1	/* turkish F */
123 };
124 
125 struct ukbd_softc {
126 	struct uhidev		sc_hdev;
127 #define sc_ledsize		sc_hdev.sc_osize
128 
129 	struct hidkbd		sc_kbd;
130 
131 	int			sc_spl;
132 
133 	struct hid_location	sc_apple_fn;
134 
135 	void			(*sc_munge)(void *, uint8_t *, u_int);
136 };
137 
138 void	ukbd_cngetc(void *, u_int *, int *);
139 void	ukbd_cnpollc(void *, int);
140 void	ukbd_cnbell(void *, u_int, u_int, u_int);
141 
142 const struct wskbd_consops ukbd_consops = {
143 	ukbd_cngetc,
144 	ukbd_cnpollc,
145 	ukbd_cnbell,
146 };
147 
148 void	ukbd_intr(struct uhidev *addr, void *ibuf, u_int len);
149 
150 int	ukbd_enable(void *, int);
151 void	ukbd_set_leds(void *, int);
152 int	ukbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
153 
154 const struct wskbd_accessops ukbd_accessops = {
155 	ukbd_enable,
156 	ukbd_set_leds,
157 	ukbd_ioctl,
158 };
159 
160 int	ukbd_match(struct device *, void *, void *);
161 void	ukbd_attach(struct device *, struct device *, void *);
162 int	ukbd_detach(struct device *, int);
163 
164 struct cfdriver ukbd_cd = {
165 	NULL, "ukbd", DV_DULL
166 };
167 
168 const struct cfattach ukbd_ca = {
169 	sizeof(struct ukbd_softc), ukbd_match, ukbd_attach, ukbd_detach
170 };
171 
172 struct ukbd_translation {
173 	uint8_t original;
174 	uint8_t translation;
175 };
176 
177 #ifdef __loongson__
178 void	ukbd_gdium_munge(void *, uint8_t *, u_int);
179 #endif
180 void	ukbd_apple_munge(void *, uint8_t *, u_int);
181 void	ukbd_apple_mba_munge(void *, uint8_t *, u_int);
182 void	ukbd_apple_iso_munge(void *, uint8_t *, u_int);
183 void	ukbd_apple_iso_mba_munge(void *, uint8_t *, u_int);
184 void	ukbd_apple_translate(void *, uint8_t *, u_int,
185 	    const struct ukbd_translation *, u_int);
186 uint8_t	ukbd_translate(const struct ukbd_translation *, size_t, uint8_t);
187 
188 int
189 ukbd_match(struct device *parent, void *match, void *aux)
190 {
191 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
192 	int size;
193 	void *desc;
194 
195 	uhidev_get_report_desc(uha->parent, &desc, &size);
196 	if (!hid_is_collection(desc, size, uha->reportid,
197 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
198 		return (UMATCH_NONE);
199 
200 	return (UMATCH_IFACECLASS);
201 }
202 
203 void
204 ukbd_attach(struct device *parent, struct device *self, void *aux)
205 {
206 	struct ukbd_softc *sc = (struct ukbd_softc *)self;
207 	struct hidkbd *kbd = &sc->sc_kbd;
208 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
209 	struct usb_hid_descriptor *hid;
210 	u_int32_t quirks, qflags = 0;
211 	int dlen, repid;
212 	int console = 1;
213 	void *desc;
214 	kbd_t layout = (kbd_t)-1;
215 
216 	sc->sc_hdev.sc_intr = ukbd_intr;
217 	sc->sc_hdev.sc_parent = uha->parent;
218 	sc->sc_hdev.sc_udev = uha->uaa->device;
219 	sc->sc_hdev.sc_report_id = uha->reportid;
220 
221 	uhidev_get_report_desc(uha->parent, &desc, &dlen);
222 	repid = uha->reportid;
223 	sc->sc_hdev.sc_isize = hid_report_size(desc, dlen, hid_input, repid);
224 	sc->sc_hdev.sc_osize = hid_report_size(desc, dlen, hid_output, repid);
225 	sc->sc_hdev.sc_fsize = hid_report_size(desc, dlen, hid_feature, repid);
226 
227 	 /*
228 	  * Since the HID-Proxy is always detected before any
229 	  * real keyboard, do not let it grab the console.
230 	  */
231 	if (uha->uaa->vendor == USB_VENDOR_APPLE &&
232 	    uha->uaa->product == USB_PRODUCT_APPLE_BLUETOOTH_HCI)
233 		console = 0;
234 
235 	quirks = usbd_get_quirks(sc->sc_hdev.sc_udev)->uq_flags;
236 	if (quirks & UQ_SPUR_BUT_UP)
237 		qflags |= HIDKBD_SPUR_BUT_UP;
238 
239 	if (hidkbd_attach(self, kbd, console, qflags, repid, desc, dlen) != 0)
240 		return;
241 
242 	if (uha->uaa->vendor == USB_VENDOR_APPLE) {
243 		if (hid_locate(desc, dlen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY),
244 		    uha->reportid, hid_input, &sc->sc_apple_fn, &qflags)) {
245 			if (qflags & HIO_VARIABLE) {
246 				switch (uha->uaa->product) {
247 				case USB_PRODUCT_APPLE_FOUNTAIN_ISO:
248 				case USB_PRODUCT_APPLE_GEYSER_ISO:
249 				case USB_PRODUCT_APPLE_WELLSPRING6_ISO:
250 				case USB_PRODUCT_APPLE_WELLSPRING8_ISO:
251 					sc->sc_munge = ukbd_apple_iso_munge;
252 					break;
253 				case USB_PRODUCT_APPLE_WELLSPRING_ISO:
254 				case USB_PRODUCT_APPLE_WELLSPRING4_ISO:
255 				case USB_PRODUCT_APPLE_WELLSPRING4A_ISO:
256 					sc->sc_munge = ukbd_apple_iso_mba_munge;
257 					break;
258 				case USB_PRODUCT_APPLE_WELLSPRING_ANSI:
259 				case USB_PRODUCT_APPLE_WELLSPRING_JIS:
260 				case USB_PRODUCT_APPLE_WELLSPRING4_ANSI:
261 				case USB_PRODUCT_APPLE_WELLSPRING4_JIS:
262 				case USB_PRODUCT_APPLE_WELLSPRING4A_ANSI:
263 				case USB_PRODUCT_APPLE_WELLSPRING4A_JIS:
264 					sc->sc_munge = ukbd_apple_mba_munge;
265 					break;
266 				default:
267 					sc->sc_munge = ukbd_apple_munge;
268 					break;
269 				}
270 			}
271 		}
272 	}
273 
274 	if (uha->uaa->vendor == USB_VENDOR_TOPRE &&
275 	    uha->uaa->product == USB_PRODUCT_TOPRE_HHKB) {
276 		/* ignore country code on purpose */
277 	} else {
278 		usb_interface_descriptor_t *id;
279 
280 		id = usbd_get_interface_descriptor(uha->uaa->iface);
281 		hid = usbd_get_hid_descriptor(uha->uaa->device, id);
282 
283 		if (hid->bCountryCode <= HCC_MAX)
284 			layout = ukbd_countrylayout[hid->bCountryCode];
285 #ifdef DIAGNOSTIC
286 		if (hid->bCountryCode != 0)
287 			printf(", country code %d", hid->bCountryCode);
288 #endif
289 	}
290 	if (layout == (kbd_t)-1) {
291 #ifdef UKBD_LAYOUT
292 		layout = UKBD_LAYOUT;
293 #else
294 		layout = KB_US | KB_DEFAULT;
295 #endif
296 	}
297 
298 	printf("\n");
299 
300 #ifdef __loongson__
301 	if (uha->uaa->vendor == USB_VENDOR_CYPRESS &&
302 	    uha->uaa->product == USB_PRODUCT_CYPRESS_LPRDK)
303 		sc->sc_munge = ukbd_gdium_munge;
304 #endif
305 
306 	if (kbd->sc_console_keyboard) {
307 		extern struct wskbd_mapdata ukbd_keymapdata;
308 
309 		DPRINTF(("ukbd_attach: console keyboard sc=%p\n", sc));
310 		ukbd_keymapdata.layout = layout;
311 		wskbd_cnattach(&ukbd_consops, sc, &ukbd_keymapdata);
312 		ukbd_enable(sc, 1);
313 	}
314 
315 	/* Flash the leds; no real purpose, just shows we're alive. */
316 	ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM |
317 		          WSKBD_LED_CAPS | WSKBD_LED_COMPOSE);
318 	usbd_delay_ms(sc->sc_hdev.sc_udev, 400);
319 	ukbd_set_leds(sc, 0);
320 
321 	hidkbd_attach_wskbd(kbd, layout, &ukbd_accessops);
322 }
323 
324 int
325 ukbd_detach(struct device *self, int flags)
326 {
327 	struct ukbd_softc *sc = (struct ukbd_softc *)self;
328 	struct hidkbd *kbd = &sc->sc_kbd;
329 	int rv;
330 
331 	rv = hidkbd_detach(kbd, flags);
332 
333 	/* The console keyboard does not get a disable call, so check pipe. */
334 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
335 		uhidev_close(&sc->sc_hdev);
336 
337 	return (rv);
338 }
339 
340 void
341 ukbd_intr(struct uhidev *addr, void *ibuf, u_int len)
342 {
343 	struct ukbd_softc *sc = (struct ukbd_softc *)addr;
344 	struct hidkbd *kbd = &sc->sc_kbd;
345 
346 	if (kbd->sc_enabled != 0) {
347 		if (sc->sc_munge != NULL)
348 			(*sc->sc_munge)(sc, (uint8_t *)ibuf, len);
349 		hidkbd_input(kbd, (uint8_t *)ibuf, len);
350 	}
351 }
352 
353 int
354 ukbd_enable(void *v, int on)
355 {
356 	struct ukbd_softc *sc = v;
357 	struct hidkbd *kbd = &sc->sc_kbd;
358 	int rv;
359 
360 	if (on && usbd_is_dying(sc->sc_hdev.sc_udev))
361 		return EIO;
362 
363 	if ((rv = hidkbd_enable(kbd, on)) != 0)
364 		return rv;
365 
366 	if (on) {
367 		return uhidev_open(&sc->sc_hdev);
368 	} else {
369 		uhidev_close(&sc->sc_hdev);
370 		return 0;
371 	}
372 }
373 
374 void
375 ukbd_set_leds(void *v, int leds)
376 {
377 	struct ukbd_softc *sc = v;
378 	struct hidkbd *kbd = &sc->sc_kbd;
379 	u_int8_t res;
380 
381 	if (usbd_is_dying(sc->sc_hdev.sc_udev))
382 		return;
383 
384 	if (sc->sc_ledsize && hidkbd_set_leds(kbd, leds, &res) != 0)
385 		uhidev_set_report_async(sc->sc_hdev.sc_parent,
386 		    UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, &res, 1);
387 }
388 
389 int
390 ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
391 {
392 	struct ukbd_softc *sc = v;
393 	struct hidkbd *kbd = &sc->sc_kbd;
394 	int rc;
395 
396 	switch (cmd) {
397 	case WSKBDIO_GTYPE:
398 		*(int *)data = WSKBD_TYPE_USB;
399 		return (0);
400 	case WSKBDIO_SETLEDS:
401 		ukbd_set_leds(v, *(int *)data);
402 		return (0);
403 	default:
404 		rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p);
405 		if (rc != -1)
406 			return rc;
407 		else
408 			return hidkbd_ioctl(kbd, cmd, data, flag, p);
409 	}
410 }
411 
412 /* Console interface. */
413 void
414 ukbd_cngetc(void *v, u_int *type, int *data)
415 {
416 	struct ukbd_softc *sc = v;
417 	struct hidkbd *kbd = &sc->sc_kbd;
418 
419 	DPRINTFN(0,("ukbd_cngetc: enter\n"));
420 	kbd->sc_polling = 1;
421 	while (kbd->sc_npollchar <= 0)
422 		usbd_dopoll(sc->sc_hdev.sc_udev);
423 	kbd->sc_polling = 0;
424 	hidkbd_cngetc(kbd, type, data);
425 	DPRINTFN(0,("ukbd_cngetc: return 0x%02x\n", *data));
426 }
427 
428 void
429 ukbd_cnpollc(void *v, int on)
430 {
431 	struct ukbd_softc *sc = v;
432 
433 	DPRINTFN(2,("ukbd_cnpollc: sc=%p on=%d\n", v, on));
434 
435 	if (on)
436 		sc->sc_spl = splusb();
437 	else
438 		splx(sc->sc_spl);
439 	usbd_set_polling(sc->sc_hdev.sc_udev, on);
440 }
441 
442 void
443 ukbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
444 {
445 	hidkbd_bell(pitch, period, volume, 1);
446 }
447 
448 int
449 ukbd_cnattach(void)
450 {
451 	/*
452 	 * XXX USB requires too many parts of the kernel to be running
453 	 * XXX in order to work, so we can't do much for the console
454 	 * XXX keyboard until autconfiguration has run its course.
455 	 */
456 	hidkbd_is_console = 1;
457 	return (0);
458 }
459 
460 uint8_t
461 ukbd_translate(const struct ukbd_translation *table, size_t tsize,
462     uint8_t keycode)
463 {
464 	for (; tsize != 0; table++, tsize--)
465 		if (table->original == keycode)
466 			return table->translation;
467 	return 0;
468 }
469 
470 void
471 ukbd_apple_translate(void *vsc, uint8_t *ibuf, u_int ilen,
472     const struct ukbd_translation* trans, u_int tlen)
473 {
474 	struct ukbd_softc *sc = vsc;
475 	struct hidkbd *kbd = &sc->sc_kbd;
476 	uint8_t *pos, *spos, *epos, xlat;
477 
478 	spos = ibuf + kbd->sc_keycodeloc.pos / 8;
479 	epos = spos + kbd->sc_nkeycode;
480 
481 	for (pos = spos; pos != epos; pos++) {
482 		xlat = ukbd_translate(trans, tlen, *pos);
483 		if (xlat != 0)
484 			*pos = xlat;
485 	}
486 }
487 
488 void
489 ukbd_apple_munge(void *vsc, uint8_t *ibuf, u_int ilen)
490 {
491 	struct ukbd_softc *sc = vsc;
492 
493 	static const struct ukbd_translation apple_fn_trans[] = {
494 		{ 40, 73 },	/* return -> insert */
495 		{ 42, 76 },	/* backspace -> delete */
496 #ifdef notyet
497 		{ 58, 0 },	/* F1 -> screen brightness down */
498 		{ 59, 0 },	/* F2 -> screen brightness up */
499 		{ 60, 0 },	/* F3 */
500 		{ 61, 0 },	/* F4 */
501 		{ 62, 0 },	/* F5 -> keyboard backlight down */
502 		{ 63, 0 },	/* F6 -> keyboard backlight up */
503 		{ 64, 0 },	/* F7 -> audio back */
504 		{ 65, 0 },	/* F8 -> audio pause/play */
505 		{ 66, 0 },	/* F9 -> audio next */
506 #endif
507 #ifdef __macppc__
508 		{ 60, 127 },	/* F3 -> audio mute */
509 		{ 61, 129 },	/* F4 -> audio lower */
510 		{ 62, 128 },	/* F5 -> audio raise */
511 #else
512 		{ 67, 127 },	/* F10 -> audio mute */
513 		{ 68, 129 },	/* F11 -> audio lower */
514 		{ 69, 128 },	/* F12 -> audio raise */
515 #endif
516 		{ 79, 77 },	/* right -> end */
517 		{ 80, 74 },	/* left -> home */
518 		{ 81, 78 },	/* down -> page down */
519 		{ 82, 75 }	/* up -> page up */
520 	};
521 
522 	if (!hid_get_data(ibuf, ilen, &sc->sc_apple_fn))
523 		return;
524 
525 	ukbd_apple_translate(vsc, ibuf, ilen, apple_fn_trans,
526 	    nitems(apple_fn_trans));
527 }
528 
529 void
530 ukbd_apple_mba_munge(void *vsc, uint8_t *ibuf, u_int ilen)
531 {
532 	struct ukbd_softc *sc = vsc;
533 
534 	static const struct ukbd_translation apple_fn_trans[] = {
535 		{ 40, 73 },	/* return -> insert */
536 		{ 42, 76 },	/* backspace -> delete */
537 #ifdef notyet
538 		{ 58, 0 },	/* F1 -> screen brightness down */
539 		{ 59, 0 },	/* F2 -> screen brightness up */
540 		{ 60, 0 },	/* F3 */
541 		{ 61, 0 },	/* F4 */
542 		{ 62, 0 },	/* F5 */
543 		{ 63, 0 },	/* F6 -> audio back */
544 		{ 64, 0 },	/* F7 -> audio pause/play */
545 		{ 65, 0 },	/* F8 -> audio next */
546 #endif
547 		{ 66, 127 },	/* F9 -> audio mute */
548 		{ 67, 129 },	/* F10 -> audio lower */
549 		{ 68, 128 },	/* F11 -> audio raise */
550 #ifdef notyet
551 		{ 69, 0 },	/* F12 -> eject */
552 #endif
553 		{ 79, 77 },	/* right -> end */
554 		{ 80, 74 },	/* left -> home */
555 		{ 81, 78 },	/* down -> page down */
556 		{ 82, 75 }	/* up -> page up */
557 	};
558 
559 	if (!hid_get_data(ibuf, ilen, &sc->sc_apple_fn))
560 		return;
561 
562 	ukbd_apple_translate(vsc, ibuf, ilen, apple_fn_trans,
563 	    nitems(apple_fn_trans));
564 }
565 
566 void
567 ukbd_apple_iso_munge(void *vsc, uint8_t *ibuf, u_int ilen)
568 {
569 	static const struct ukbd_translation apple_iso_trans[] = {
570 		{ 53, 100 },	/* less -> grave */
571 		{ 100, 53 },
572 	};
573 
574 	ukbd_apple_translate(vsc, ibuf, ilen, apple_iso_trans,
575 	    nitems(apple_iso_trans));
576 	ukbd_apple_munge(vsc, ibuf, ilen);
577 }
578 
579 void
580 ukbd_apple_iso_mba_munge(void *vsc, uint8_t *ibuf, u_int ilen)
581 {
582 	static const struct ukbd_translation apple_iso_trans[] = {
583 		{ 53, 100 },	/* less -> grave */
584 		{ 100, 53 },
585 	};
586 
587 	ukbd_apple_translate(vsc, ibuf, ilen, apple_iso_trans,
588 	    nitems(apple_iso_trans));
589 	ukbd_apple_mba_munge(vsc, ibuf, ilen);
590 }
591 
592 #ifdef __loongson__
593 /*
594  * Software Fn- translation for Gdium Liberty keyboard.
595  */
596 #define	GDIUM_FN_CODE	0x82
597 void
598 ukbd_gdium_munge(void *vsc, uint8_t *ibuf, u_int ilen)
599 {
600 	struct ukbd_softc *sc = vsc;
601 	struct hidkbd *kbd = &sc->sc_kbd;
602 	uint8_t *pos, *spos, *epos, xlat;
603 	int fn;
604 
605 	static const struct ukbd_translation gdium_fn_trans[] = {
606 #ifdef notyet
607 		{ 58, 0 },	/* F1 -> toggle camera */
608 		{ 59, 0 },	/* F2 -> toggle wireless */
609 #endif
610 		{ 60, 127 },	/* F3 -> audio mute */
611 		{ 61, 128 },	/* F4 -> audio raise */
612 		{ 62, 129 },	/* F5 -> audio lower */
613 #ifdef notyet
614 		{ 63, 0 },	/* F6 -> toggle ext. video */
615 		{ 64, 0 },	/* F7 -> toggle mouse */
616 		{ 65, 0 },	/* F8 -> brightness up */
617 		{ 66, 0 },	/* F9 -> brightness down */
618 		{ 67, 0 },	/* F10 -> suspend */
619 		{ 68, 0 },	/* F11 -> user1 */
620 		{ 69, 0 },	/* F12 -> user2 */
621 		{ 70, 0 },	/* print screen -> sysrq */
622 #endif
623 		{ 76, 71 },	/* delete -> scroll lock */
624 		{ 81, 78 },	/* down -> page down */
625 		{ 82, 75 }	/* up -> page up */
626 	};
627 
628 	spos = ibuf + kbd->sc_keycodeloc.pos / 8;
629 	epos = spos + kbd->sc_nkeycode;
630 
631 	/*
632 	 * Check for Fn key being down and remove it from the report.
633 	 */
634 
635 	fn = 0;
636 	for (pos = spos; pos != epos; pos++)
637 		if (*pos == GDIUM_FN_CODE) {
638 			fn = 1;
639 			*pos = 0;
640 			break;
641 		}
642 
643 	/*
644 	 * Rewrite keycodes on the fly to perform Fn-key translation.
645 	 * Keycodes without a translation are passed unaffected.
646 	 */
647 
648 	if (fn != 0)
649 		for (pos = spos; pos != epos; pos++) {
650 			xlat = ukbd_translate(gdium_fn_trans,
651 			    nitems(gdium_fn_trans), *pos);
652 			if (xlat != 0)
653 				*pos = xlat;
654 		}
655 
656 }
657 #endif
658