|
Description
|
Category
kernel
Sub-Category
virtualkm
Description
As soon as the "kbtrans" kernel module is loaded and the german keyboard
layout is installed, the "#" / "'" / "`" (backtick, AltGr-#) key does not
work any more inside kmdb.
Without the '#' key, it's not possible to set delayed breakpoints
(::bp module_name`function_name) because there's no way to type the "`"
character (AltGr-#) in kmdb.
Outside of kmdb (on the text console), the "#" / "'" / "`" key works as
expected.
The interrupt driven code path in kbtrans already contains a workaround for
this problem [*], so that the problem does not occur outside of kmdb. The
same workaround is needed for the polled code path (see the suggested fix).
------------------------------------------------------------------------
What happens inside the Solaris kernel when the '#' key is pressed on a
german layout PS/2 keyboard?
When '#' is typed, the kb8042 module reads scancode 0x2b
usr/src/uts/common/io/kb8042/at_keyprocess.c translates the 0x2b scancode to
a keycode 29 in function KeyboardConvertScan() using the keytab_base[] array.
Because the kb8042 is used in "simulated_kbd_type == USB" mode, the keycode 29
is translated to an USB keycode of 49 using the keytab_pc2usb[] array in
usr/src/uts/common/io/kb8042/kb8042.c
Now the kbtrans module translates the usb keycode 49 in function
kbtrans_translate() which calls kbtrans_find_entry(). kbtrans_find_entry()
uses the kbtrans`keytab_usb_lc array and finds an entry 0x302 (HOLE)
==> the '#' key is ignored.
Note that the contents of the keytab_usb_lc[] array is changed from the
version compiled into the kbtrans module. The compiled version contains
the entry '\\' at element 49. During boot a german layout version is
installed using the function kbtrans`kbtrans_skey() and this changes the
keytab_usb_lc[49] entry to HOLE. The process that triggers this is
"/usr/bin/loadkeys", started from /lib/svc/method/keymap.
/usr/share/lib/keytables/type_6/layout_09:
key 50 base # shift '\'' caps # ctrl # altg '`'
key 49 all hole
According to the USB "HID Usage Tables (Version 1.11)" specification (Section
10, Keyboard/Keypad Page (0x07), on page 54), there is:
Usage ID 49: Keyboard \ and |
Usage ID 50: Keyboard Non-US # and ~
A german layout *USB* keyboard does in fact send the keycode 50 when the "#"
key is pressed.
When using a PS/2 keyboard and kmdb is not active, something inside the Solaris
kernel translates 49 -> 50 [*]; when kbtrans debug messages are enabled, the
following is listed:
kbtrans: kbtrans_processkey: newstate=0 key=49
kbtrans: kbtrans_queuepress: key=49
kbtrans: kbtrans_processkey: newstate=0 key=50
... and kbtrans_findentry is called with keycode 50. Note how a firmevent
with key=49 is queued, but the next call to kbtrans_processkey translates
key=50
Root cause: a german layout USB keyboard has no key 49 but a key 50.
A US layout USB keyboard appears to use key 49.
When kb8042 emulates a USB keyboard, it generates USB key 49.
For a german layout USB keyboard emulation this appears incorrect, it should
generate key 50.
------------------------------------------------------------------------
Probably the same problem exists for all keyboard layouts that have
key 49 defined as "hole" in the usb keyboard layout tables:
% grep 'key *49.*hole' /usr/share/lib/keytables/type_6/*
/usr/share/lib/keytables/type_6/denmark:key 49 all hole
/usr/share/lib/keytables/type_6/finnish:key 49 all hole
/usr/share/lib/keytables/type_6/france:key 49 all hole
/usr/share/lib/keytables/type_6/germany:key 49 all hole
/usr/share/lib/keytables/type_6/italy:key 49 all hole
/usr/share/lib/keytables/type_6/japan:key 49 all hole
/usr/share/lib/keytables/type_6/layout_06:key 49 all hole
/usr/share/lib/keytables/type_6/layout_07:key 49 all hole
/usr/share/lib/keytables/type_6/layout_08:key 49 all hole
/usr/share/lib/keytables/type_6/layout_09:key 49 all hole
/usr/share/lib/keytables/type_6/layout_0e:key 49 all hole
/usr/share/lib/keytables/type_6/layout_0f:key 49 all hole
/usr/share/lib/keytables/type_6/layout_12:key 49 all hole
/usr/share/lib/keytables/type_6/layout_13:key 49 all hole
/usr/share/lib/keytables/type_6/layout_16:key 49 all hole
/usr/share/lib/keytables/type_6/layout_19:key 49 all hole
/usr/share/lib/keytables/type_6/layout_1a:key 49 all hole
/usr/share/lib/keytables/type_6/layout_1b:key 49 all hole
/usr/share/lib/keytables/type_6/layout_1c:key 49 all hole
/usr/share/lib/keytables/type_6/layout_20:key 49 all hole
/usr/share/lib/keytables/type_6/netherlands:key 49 all hole
/usr/share/lib/keytables/type_6/norway:key 49 all hole
/usr/share/lib/keytables/type_6/portugal:key 49 all hole
/usr/share/lib/keytables/type_6/spain:key 49 all hole
/usr/share/lib/keytables/type_6/sweden:key 49 all hole
/usr/share/lib/keytables/type_6/swiss_french:key 49 all hole
/usr/share/lib/keytables/type_6/swiss_german:key 49 all hole
/usr/share/lib/keytables/type_6/uk:key 49 all hole
---
[*] YES: usr/src/uts/common/io/conskbd.c, conskbdlrput():
1054 case M_DATA:
1055 if (conskbd.conskbd_bypassed == B_FALSE) {
1056
1057 fe = (Firm_event *)mp->b_rptr;
1058
1059 /*
1060 * This is a workaround.
1061 *
1062 * According to HID specification, there are the
1063 * following keycode mapping between PS2 and USB,
1064 *
1065 * PS2 AT-101 keycode(29) ---> USB(49)
1066 * PS2 AT-102 keycode(42) ---> USB(50)
1067 *
1068 * However, the two keys, AT-101(29) and AT-102(42),
1069 * have the same scancode,0x2B, in PS2 scancode SET1
1070 * which we are using. The Kb8042 driver always
1071 * recognizes the two keys as PS2(29) so that we could
1072 * not know which is being pressed or released when we
1073 * receive scancode 0x2B. Fortunately, the two keys can
1074 * not co-exist in a specific layout. In other words,
1075 * in the table of keycode-to-symbol mapping, either
1076 * entry 49 or 50 is a hole. So, if we're processing a
1077 * keycode 49, we look at the entry for 49. If it's
1078 * HOLE, remap the key to 50; If we're processing a 50,
1079 * look at the entry for 50. If it's HOLE, we remap
1080 * the key to 49.
1081 */
1082 if (fe->id == 49 || fe->id == 50) {
1083 if (conskbd_keyindex->k_normal[50] == HOLE)
1084 fe->id = 49;
1085 else
1086 fe->id = 50;
1087 }
1088
Frequency
Always
Regression
Solaris 10
Steps to Reproduce
On an x86 system with a german layout ps/2 keyboard and "eeprom kbd-type=German"
boot into single user mode and enter kmdb using "F1-A" or "mdb -K".
Try to set a delayed breakpoint
::bp module`function
Try to get any keyboard input from the # key
Expected Result
The user can type the ` character using the sequence AltGr-#
Actual Result
The # key is completely ignored inside kmdb.
You cannot type # or "AltGr-#" (for the ` character).
Error Message(s)
Test Case
Workaround
Workaround #1:
==============
Use a USB keyboard. With a german layout USB keyboard the
"#" key works both inside kmdb and on the text console.
Workaround #2:
==============
Change keytab_pc2usb[29] from 49 to 50:
echo 'keytab_pc2usb+4*0t29?W 0t50' | adb -wk
Workaround #3:
==============
Changing /usr/share/lib/keytables/type_6/layout_09 like this fixes the kmdb
problem:
key 49 base # shift '\'' caps # ctrl # altg '`'
key 50 all hole
Suggested fix:
==============
The conskbd module already contains a workaround for this problem for
interrupt driven code.
The polled mode codepath needs the same fix:
*** usr/src/uts/common/io/conskbd.c~ Thu Aug 18 18:51:54 2005
--- usr/src/uts/common/io/conskbd.c Sun Aug 28 11:19:52 2005
***************
*** 550,556 ****
conskbd_consqueue = q;
/*
! * initialzie kbtrans module for conskbd
*/
err = kbtrans_streams_init(q, sflag, crp, (struct kbtrans_hardware *)
&conskbd, &conskbd_callbacks, &conskbd.conskbd_kbtrans, 0, 0);
--- 550,556 ----
conskbd_consqueue = q;
/*
! * initialize kbtrans module for conskbd
*/
err = kbtrans_streams_init(q, sflag, crp, (struct kbtrans_hardware *)
&conskbd, &conskbd_callbacks, &conskbd.conskbd_kbtrans, 0, 0);
***************
*** 2003,2013 ****
(cb->cons_polledio_keycheck != NULL)) {
ret = cb->cons_polledio_keycheck(
cb->cons_polledio_argument, keycode, state);
- }
! /* Get a char from lower queue(hardware) ? */
! if (ret == B_TRUE) {
! break;
}
}
--- 2003,2037 ----
(cb->cons_polledio_keycheck != NULL)) {
ret = cb->cons_polledio_keycheck(
cb->cons_polledio_argument, keycode, state);
! /* Get a char from lower queue(hardware) ? */
! if (ret == B_TRUE) {
!
! /*
! * This is the PS2 scancode 0x2B -> USB(49) /
! * USB(50) keycode mapping workaround, for
! * polled mode.
! *
! * There are two possible USB keycode mappings
! * for PS2 scancode 0x2B and this workaround
! * makes sure that we use the USB keycode that
! * does not end up being mapped to a HOLE key
! * using the current keyboard translation
! * tables.
! *
! * See conskbdlrput() for a detailed
! * explanation of the problem.
! */
! if (*keycode == 49 || *keycode == 50) {
! if (conskbd_keyindex->k_normal[50] ==
! HOLE)
! *keycode = 49;
! else
! *keycode = 50;
! }
!
! break;
! }
}
}
Submitter wants to work on bug
Yes
Additional configuration information
PS/2 keyboard, german keyboard layout
The same problem can also be reproduced on S10-GA with patch 118860-01 installed.
|
|
Work Around
|
Workaround #1:
==============
Use a USB keyboard. With a german layout USB keyboard the
"#" key works both inside kmdb and on the text console.
Workaround #2:
==============
Change keytab_pc2usb[29] from 49 to 50:
echo 'keytab_pc2usb+4*0t29?W 0t50' | adb -wk
Workaround #3:
==============
Changing /usr/share/lib/keytables/type_6/layout_09 like this fixes the kmdb
problem:
key 49 base # shift '\'' caps # ctrl # altg '`'
key 50 all hole
Suggested fix:
==============
The conskbd module already contains a workaround for this problem for
interrupt driven code.
The polled mode codepath needs the same fix:
*** usr/src/uts/common/io/conskbd.c~ Thu Aug 18 18:51:54 2005
--- usr/src/uts/common/io/conskbd.c Sun Aug 28 11:19:52 2005
***************
*** 550,556 ****
conskbd_consqueue = q;
/*
! * initialzie kbtrans module for conskbd
*/
err = kbtrans_streams_init(q, sflag, crp, (struct kbtrans_hardware *)
&conskbd, &conskbd_callbacks, &conskbd.conskbd_kbtrans, 0, 0);
--- 550,556 ----
conskbd_consqueue = q;
/*
! * initialize kbtrans module for conskbd
*/
err = kbtrans_streams_init(q, sflag, crp, (struct kbtrans_hardware *)
&conskbd, &conskbd_callbacks, &conskbd.conskbd_kbtrans, 0, 0);
***************
*** 2003,2013 ****
(cb->cons_polledio_keycheck != NULL)) {
ret = cb->cons_polledio_keycheck(
cb->cons_polledio_argument, keycode, state);
- }
! /* Get a char from lower queue(hardware) ? */
! if (ret == B_TRUE) {
! break;
}
}
--- 2003,2037 ----
(cb->cons_polledio_keycheck != NULL)) {
ret = cb->cons_polledio_keycheck(
cb->cons_polledio_argument, keycode, state);
! /* Get a char from lower queue(hardware) ? */
! if (ret == B_TRUE) {
!
! /*
! * This is the PS2 scancode 0x2B -> USB(49) /
! * USB(50) keycode mapping workaround, for
! * polled mode.
! *
! * There are two possible USB keycode mappings
! * for PS2 scancode 0x2B and this workaround
! * makes sure that we use the USB keycode that
! * does not end up being mapped to a HOLE key
! * using the current keyboard translation
! * tables.
! *
! * See conskbdlrput() for a detailed
! * explanation of the problem.
! */
! if (*keycode == 49 || *keycode == 50) {
! if (conskbd_keyindex->k_normal[50] ==
! HOLE)
! *keycode = 49;
! else
! *keycode = 50;
! }
!
! break;
! }
}
}
|