1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Generic keyboard support: translation
31 *
32 * This module is project private.  Please see PSARC/1998/176 and
33 * PSARC/1998/026 for references to the kbtrans module.
34 *
35 * It is believed that it is safe to call these functions within debugger mode
36 * except kbtrans_dprintf.  Debugger mode is a single threaded mode where most
37 * kernel services are not available, including memory allocation.  Debugger
38 * mode is for kmdb and OBP debugging, where the debugger calls back into the
39 * kernel to obtain console input.
40 *
41 * Please be _very_ careful about what external functions you call.
42 */
43
44#define	KEYMAP_SIZE_VARIABLE
45
46#include <sys/types.h>
47#include <sys/cred.h>
48#include <sys/ddi.h>
49#include <sys/sunddi.h>
50#include <sys/kmem.h>
51#include <sys/kbd.h>
52#include <sys/cmn_err.h>
53#include <sys/modctl.h>
54#include <sys/kbio.h>
55#include <sys/vuid_event.h>
56#include <sys/consdev.h>
57#include <sys/kbtrans.h>
58#include <sys/errno.h>
59#include <sys/promif.h>
60#include <sys/varargs.h>
61#include "kbtrans_lower.h"
62
63/*
64 * Internal Function Prototypes
65 */
66static boolean_t	kbtrans_do_compose(struct kbtrans_lower *, ushort_t,
67			    ushort_t, ushort_t *);
68static void		kbtrans_translate(struct kbtrans_lower *,
69				struct keyboard_callback *, kbtrans_key_t,
70				enum keystate);
71/*
72 * kbtrans_processkey:
73 *
74 * 	lower	- state information used by the calling driver
75 *		  this parameter is passed back to the callback routines.
76 * 	key	- scancode
77 * 	state	- KEY_PRESSED / KEY_RELEASED
78 *
79 * This routine checks to see if there is a raw callback, and calls it
80 * if it exists.  If there is no raw callback, the key is translated.
81 * The raw callback allows the driver that called the translation module
82 * to be passed untranslated scancodes.
83 */
84void
85kbtrans_processkey(struct kbtrans_lower *lower,
86	struct keyboard_callback	*cb,
87	kbtrans_key_t 			key,
88	enum keystate 			state)
89{
90	DPRINTF(PRINT_L0, PRINT_MASK_ALL, (lower, "kbtrans_processkey: "
91		"newstate=%d key=%d", state, key));
92
93	/*
94	 * If there is a raw routine, then call it and return.
95	 */
96	if (cb->kc_keypressed_raw != NULL) {
97
98		if (state == KEY_PRESSED) {
99
100			cb->kc_keypressed_raw(lower->kbtrans_upper, key);
101		} else {
102
103			cb->kc_keyreleased_raw(lower->kbtrans_upper, key);
104		}
105
106		return;
107	}
108
109	/*
110	 * translate the scancode into a key.
111	 */
112	kbtrans_translate(lower, cb, key, state);
113}
114
115
116/*
117 * kbtrans_translate:
118 *
119 * 	lower	- state information used by the calling driver
120 *		  this parameter is passed back to the callback routines.
121 * 	key		- scan code
122 * 	state	- KEY_PRESSED / KEY_RELEASED
123 *
124 * Called to process key events if we are in TR_ASCII or TR_EVENT
125 * (sunview) mode.  This routine will call the appropriate translation_callback
126 * for the character when it is done translating it.
127 */
128static void
129kbtrans_translate(struct kbtrans_lower	*lower,
130	struct keyboard_callback	*cb,
131	kbtrans_key_t 			key,
132	enum keystate 			newstate)
133{
134	unsigned		shiftmask;
135	register ushort_t	entry;
136	register ushort_t	entrytype;
137	ushort_t		result_iso;
138	unsigned short		*ke;
139	int			i;
140	boolean_t		good_compose;
141
142	DPRINTF(PRINT_L0, PRINT_MASK_ALL, (lower, "KEY TRANSLATE "
143		"newstate=0x%x key=0x%x\n", newstate, key));
144
145	if (lower->kbtrans_keyboard == NULL) {
146		/*
147		 * Nobody has told us about this keyboard yet.
148		 */
149		return;
150	}
151
152	/*
153	 * Get the current state of the shiftmask
154	 */
155	shiftmask = lower->kbtrans_shiftmask;
156
157	/*
158	 * If the key has been released, then or in the UPMASK flag.
159	 */
160	if (newstate == KEY_RELEASED)
161		shiftmask |= UPMASK;
162
163	/*
164	 * Based on the shiftmask, lookup the keymap entry that we should
165	 * be using for this scancode.
166	 */
167	ke = kbtrans_find_entry(lower, shiftmask, key);
168
169	if (ke == NULL) {
170		/*
171		 * This is a gross error.  Cancel the repeat key and exit,
172		 * we can not translate this scancode.
173		 */
174		cb->kc_cancel_repeat(lower->kbtrans_upper);
175
176		return;
177	}
178
179	/*
180	 * Get the key for this scancode.
181	 */
182	entry = *ke;
183
184	if (entry == NONL) {
185		/*
186		 * NONL appears only in the Num Lock table, and indicates that
187		 * this key is not affected by Num Lock.  This means we should
188		 * ask for the table we would have gotten had Num Lock not been
189		 * down, and translate using that table.
190		 */
191		ke = kbtrans_find_entry(lower, shiftmask & ~NUMLOCKMASK,
192			key);
193
194		if (ke == NULL) {
195			/*
196			 * This is a gross error.  Cancel the repeat key and
197			 * exit, we can not translate this scancode.
198			 */
199			cb->kc_cancel_repeat(lower->kbtrans_upper);
200
201			return;
202		}
203
204		/*
205		 * Get the new key for this scancode.
206		 */
207		entry = *ke;
208	}
209
210	/*
211	 * The entrytype indicates what category of key we are processing.
212	 * Categories include shift keys, function keys, and numeric keypad
213	 * keys.
214	 */
215	entrytype = (ushort_t)(entry & 0xFF00);
216
217	if (entrytype == SHIFTKEYS) {
218		/*
219		 * Handle the state of toggle shifts specially.
220		 * Ups should be ignored, and downs should be mapped to ups if
221		 * that shift is currently on.
222		 */
223		if ((1 << (entry & 0x0F)) &
224		    lower->kbtrans_keyboard->k_toggleshifts) {
225			if ((1 << (entry & 0x0F)) &
226				lower->kbtrans_togglemask) {
227				newstate = KEY_RELEASED; /* toggling off */
228			} else {
229				newstate = KEY_PRESSED;	/* toggling on */
230			}
231		}
232	} else {
233		/*
234		 * Handle Compose and floating accent key sequences
235		 */
236		switch (lower->kbtrans_state) {
237		case COMPOSE1:
238			if (newstate == KEY_RELEASED)
239
240				return;
241
242			if (entry < ASCII_SET_SIZE) {
243				if (lower->kbtrans_compose_map[entry] >= 0) {
244					lower->kbtrans_compose_key = entry;
245					lower->kbtrans_state = COMPOSE2;
246
247					return;
248				}
249			}
250			lower->kbtrans_state = NORMAL;
251			lower->kbtrans_led_state &= ~LED_COMPOSE;
252
253			cb->kc_setled(lower->kbtrans_upper);
254
255			return;
256
257		case COMPOSE2:
258			if (newstate == KEY_RELEASED)
259				return;
260
261			/* next state is "normal" */
262			lower->kbtrans_state = NORMAL;
263			lower->kbtrans_led_state &= ~LED_COMPOSE;
264
265			cb->kc_setled(lower->kbtrans_upper);
266
267			good_compose = kbtrans_do_compose(lower,
268				lower->kbtrans_compose_key, entry,
269				&result_iso);
270			if (good_compose) {
271				if (lower->kbtrans_compat)
272					result_iso += ISO_FIRST;
273				else
274					result_iso += EUC_FIRST;
275				cb->kc_keypressed(lower->kbtrans_upper,
276				    entrytype, key, result_iso);
277			}
278			return;
279
280		case FLTACCENT:
281			if (newstate == KEY_RELEASED)
282
283				return;
284
285			/* next state is "normal" */
286			lower->kbtrans_state = NORMAL;
287			for (i = 0;
288			    (lower->kbtrans_fltaccent_table[i].fa_entry
289				!= lower->kbtrans_fltaccent_entry) ||
290			    (lower->kbtrans_fltaccent_table[i].ascii != entry);
291			    i++) {
292				if (lower->kbtrans_fltaccent_table[i].fa_entry
293				    == 0) {
294					/* Invalid second key: ignore key */
295
296					return;
297				}
298			}
299
300			cb->kc_keypressed(lower->kbtrans_upper, entrytype,
301					key, (lower->kbtrans_compat ?
302						ISO_FIRST : EUC_FIRST) +
303					lower->kbtrans_fltaccent_table[i].iso);
304
305			return;
306		}
307	}
308
309	/*
310	 * If the key is going down, and it's not one of the keys that doesn't
311	 * auto-repeat, set up the auto-repeat timeout.
312	 *
313	 * The keys that don't auto-repeat are the Compose key,
314	 * the shift keys, the "bucky bit" keys, the "floating accent" keys,
315	 * and the function keys when in TR_EVENT mode.
316	 */
317	if (newstate == KEY_PRESSED && entrytype != SHIFTKEYS &&
318	    entrytype != BUCKYBITS && entrytype != FUNNY &&
319	    entrytype != FA_CLASS) {
320
321		if (lower->kbtrans_repeatkey != key) {
322			cb->kc_cancel_repeat(lower->kbtrans_upper);
323			cb->kc_setup_repeat(lower->kbtrans_upper, entrytype,
324				key);
325		}
326		/* key going up */
327	} else if (key == lower->kbtrans_repeatkey) {
328
329		cb->kc_cancel_repeat(lower->kbtrans_upper);
330	}
331
332	if (newstate == KEY_RELEASED) {
333		cb->kc_keyreleased(lower->kbtrans_upper, key);
334	}
335
336	/*
337	 * We assume here that keys other than shift keys and bucky keys have
338	 * entries in the "up" table that cause nothing to be done, and thus we
339	 * don't have to check for newstate == KEY_RELEASED.
340	 */
341	switch (entrytype) {
342
343	case 0x0:		/* regular key */
344		cb->kc_keypressed(lower->kbtrans_upper, entrytype, key,
345			entry | lower->kbtrans_buckybits);
346		break;
347
348	case SHIFTKEYS: {
349		uint_t shiftbit = 1 << (entry & 0x0F);
350
351		/* Modify toggle state (see toggle processing above) */
352		if (shiftbit & lower->kbtrans_keyboard->k_toggleshifts) {
353			if (newstate == KEY_RELEASED) {
354				if (shiftbit == CAPSMASK) {
355					lower->kbtrans_led_state &=
356						~LED_CAPS_LOCK;
357
358					cb->kc_setled(lower->kbtrans_upper);
359
360				} else if (shiftbit == NUMLOCKMASK) {
361					lower->kbtrans_led_state &=
362						    ~LED_NUM_LOCK;
363
364					cb->kc_setled(lower->kbtrans_upper);
365				}
366				lower->kbtrans_togglemask &= ~shiftbit;
367			} else {
368				if (shiftbit == CAPSMASK) {
369					lower->kbtrans_led_state |=
370						LED_CAPS_LOCK;
371
372					cb->kc_setled(lower->kbtrans_upper);
373				} else if (shiftbit == NUMLOCKMASK) {
374					lower->kbtrans_led_state |=
375						LED_NUM_LOCK;
376
377					cb->kc_setled(lower->kbtrans_upper);
378				}
379				lower->kbtrans_togglemask |= shiftbit;
380			}
381		}
382
383		if (newstate == KEY_RELEASED)
384			lower->kbtrans_shiftmask &= ~shiftbit;
385		else
386			lower->kbtrans_shiftmask |= shiftbit;
387
388		if (newstate == KEY_PRESSED) {
389			cb->kc_keypressed(lower->kbtrans_upper, entrytype, key,
390				entry);
391		}
392
393		break;
394		}
395
396	case BUCKYBITS:
397		lower->kbtrans_buckybits ^= 1 << (7 + (entry & 0x0F));
398
399		if (newstate == KEY_PRESSED) {
400			cb->kc_keypressed(lower->kbtrans_upper, entrytype, key,
401				entry);
402		}
403
404		break;
405
406	case FUNNY:
407		switch (entry) {
408		case NOP:
409			break;
410
411		case IDLE:
412			/* Fall thru into RESET code */
413			/* FALLTHRU */
414		case RESET:
415		case ERROR:
416			lower->kbtrans_shiftmask &=
417				lower->kbtrans_keyboard->k_idleshifts;
418
419			lower->kbtrans_shiftmask |=
420					lower->kbtrans_togglemask;
421
422			lower->kbtrans_buckybits &=
423				lower->kbtrans_keyboard->k_idlebuckys;
424
425			cb->kc_cancel_repeat(lower->kbtrans_upper);
426
427			cb->kc_keypressed(lower->kbtrans_upper, entrytype, key,
428				entry);
429
430			break;
431
432
433		case COMPOSE:
434			lower->kbtrans_state = COMPOSE1;
435			lower->kbtrans_led_state |= LED_COMPOSE;
436			cb->kc_setled(lower->kbtrans_upper);
437			break;
438		/*
439		 * Remember when adding new entries that,
440		 * if they should NOT auto-repeat,
441		 * they should be put into the IF statement
442		 * just above this switch block.
443		 */
444		default:
445			/* Ignore it */
446			break;
447		}
448		break;
449
450	case FA_CLASS:
451		if (lower->kbtrans_state == NORMAL) {
452			lower->kbtrans_fltaccent_entry = entry;
453			lower->kbtrans_state = FLTACCENT;
454		}
455		break;
456
457	case STRING:
458		cb->kc_keypressed(lower->kbtrans_upper, entrytype, key, entry);
459
460		break;
461
462	case FUNCKEYS:
463		cb->kc_keypressed(lower->kbtrans_upper, entrytype, key, entry);
464
465		break;
466
467	/*
468	 * Remember when adding new entries that,
469	 * if they should NOT auto-repeat,
470	 * they should be put into the IF statement
471	 * just above this switch block.
472	 */
473	case PADKEYS:
474		cb->kc_keypressed(lower->kbtrans_upper, entrytype, key, entry);
475
476		break;
477	}
478}
479
480/*
481 * kbtrans_do_compose:
482 *	Given a two key compose sequence, lookup the iso equivalent and put
483 * 	the result in the result_iso_ptr.
484 */
485static boolean_t
486kbtrans_do_compose(struct kbtrans_lower *lower,
487		ushort_t	first_entry,
488		ushort_t	second_entry,
489		ushort_t	*result_iso_ptr)
490{
491	struct compose_sequence_t *ptr;
492	ushort_t	tmp;
493
494	/*
495	 * Validate the second keystroke.
496	 */
497	if (second_entry >= ASCII_SET_SIZE)
498		return (B_FALSE);
499
500	if (lower->kbtrans_compose_map[second_entry] < 0)
501		return (B_FALSE);
502
503	/*
504	 * Get them in code order, rather than press order.
505	 */
506	if (first_entry > second_entry) {
507		tmp = first_entry;
508		first_entry = second_entry;
509		second_entry = tmp;
510	}
511
512	ptr = lower->kbtrans_compose_table +
513		    lower->kbtrans_compose_map[first_entry];
514
515	while (ptr->first == first_entry) {
516		if (ptr->second == second_entry) {
517			*result_iso_ptr = ptr->iso;
518
519			return (B_TRUE);
520		}
521		ptr++;
522	}
523	return (B_FALSE);
524}
525
526
527/*
528 * kbtrans_find_entry:
529 * 	This routine finds the entry corresponding to the current shift
530 * 	state and keycode.
531 */
532unsigned short *
533kbtrans_find_entry(struct kbtrans_lower *lower,
534	register uint_t			mask,
535	kbtrans_key_t			key_station)
536{
537	register struct keyboard *kp;
538	keymap_entry_t *km;
539	struct exception_map *ex;
540
541	kp = lower->kbtrans_keyboard;
542
543	if (kp == NULL)
544		return (NULL);
545
546	if (key_station < 0 || key_station >= kp->k_keymap_size)
547		return (NULL);
548
549	ex = kp->k_except;
550	if (ex != NULL) {
551		for (; ex->exc_care != 0; ex++) {
552			if ((mask & ex->exc_care) == ex->exc_mask &&
553			    key_station == ex->exc_key)
554				return (&ex->exc_entry);
555		}
556	}
557
558	if (mask & UPMASK)
559		km = kp->k_up;
560	else if (mask & NUMLOCKMASK)
561		km = kp->k_numlock;
562	else if (mask & CTRLMASK)
563		km = kp->k_control;
564	else if (mask & ALTGRAPHMASK)
565		km = kp->k_altgraph;
566	else if (mask & SHIFTMASK)
567		km = kp->k_shifted;
568	else if (mask & CAPSMASK)
569		km = kp->k_caps;
570	else km = kp->k_normal;
571
572	return (&km[key_station]);
573}
574
575#ifdef DEBUG
576/*ARGSUSED*/
577void
578kbtrans_dprintf(void *un, const char *fmt, ...)
579{
580	char buf[256];
581	va_list ap;
582
583	va_start(ap, fmt);
584	(void) vsprintf(buf, fmt, ap);
585	va_end(ap);
586
587	cmn_err(CE_CONT, "kbtrans: %s", buf);
588}
589#endif
590