4

I recently acquired MK-85 mechanical USB keyboard from QPAD. The keyboard works perfect on Windows. It works perfect in Syslinux. It works almost perfect on Linux. The only issue on Linux is that a single key is misbehaving (Gentoo (3.6.11), Arch Linux and Linux Mint (2.6.38) are all affected).

The keyboard is a 105-key German layout keyboard and the key in question is the one between Ä and ENTER. On US layout this corresponds to the key \, on German layout this corresponds to # and on Scandinavian layout it's '.

When this key is pressed with other keys, it produces an extra keypress for each other key that is simultaneously pressed. For example, under Scandinavian layout if I want to type the word "don't" really fast I end up with: don'''t'

The behaviour can be observed with the program showkeys:

kb mode was UNICODE
[ if you are trying this under X, it might not work
since the X server is also reading /dev/console ]

press any key (program terminates 10s after last keypress)...
keycode  28 release
keycode  32 press    // d pressed
keycode  24 press    // o pressed
keycode  49 press    // n pressed
keycode  32 release  // d released
keycode  43 press    // ' pressed
keycode  24 release  // o released
keycode  43 release  // ' released
keycode  43 press    // ' pressed, extra ' produced
keycode  49 release  // n released
keycode  43 release  // ' released
keycode  43 press    // ' pressed, extra ' produced
keycode  20 press    // t pressed
keycode  43 release  // ' released
keycode  43 press    // ' pressed, extra ' produced
keycode  20 release  // t released
keycode  43 release  // ' released
keycode  43 press    // ' pressed, extra ' produced
keycode  43 release  // ' released (REAL)

It only happens with this single key, regardless of keyboard layout. Another way it manifests itself is if I press and hold a key, it repeats, and I press and hold another key which should also start repeating:

aaaaaaaaaakkkkkkkkkkkkk (works as intended)
¨¨¨¨¨¨¨¨¨¨fffffffffffff (works as intended)
''''''''''a'''''''''''' (a is not repeated, instead ' continues)

On Windows this issue does not exist:

OnKeyDown, Key code=68, Control keys=, Key name d
OnKeyPress d
OnKeyDown, Key code=79, Control keys=, Key name o
OnKeyPress o
OnKeyDown, Key code=78, Control keys=, Key name n
OnKeyPress n
OnKeyup, Key code=68, Control keys=, Key name d
OnKeyDown, Key code=191, Control keys=, Key name ........OEM specific
OnKeyPress '
OnKeyup, Key code=79, Control keys=, Key name o
OnKeyup, Key code=78, Control keys=, Key name n
OnKeyDown, Key code=84, Control keys=, Key name t
OnKeyPress t
OnKeyup, Key code=191, Control keys=, Key name ........OEM specific
OnKeyup, Key code=84, Control keys=, Key name t

What do you think SE? Hardware issue? It works fine in Syslinux which makes me feel like there'''s' something wrong on Linux side. Any pointers, ideas or better ways to debug? If getting this to work right requires patching the kernel I'm up for it.

  • Investigation reveals that the issue goes all the way down to kernel hid-core.c and hid-input.c. Working on a dirty hack to fix it. – snaipperi Aug 27 '13 at 16:38
  • Dirty hack successful through editing drivers/hid/hid-input.c. Basically it immediately forces the Key 43 to depress and doesn't allow it to press again unless no other key is pressed or 4 polling rounds have passed. Will post code tomorrow after testing it a bit more.. – snaipperi Aug 27 '13 at 18:06
4

I have tried to make a proper patch for this bug. It is a problem in the kernel rather than with the keyboard, although it could be argued that the keyboard behave in a strange way. Anyway, the patch is submitted to the linux-input list for review but there are no comments on it yet.

This should fix the problem mentioned here with the QPAD MK-85, but the same problem exists with Corsair K70, Gigabyte Osmium and other similar keyboards. If you have a keyboard that has the bug it would be great it you can test the patch. If you test it let me know if it works and also what keyboard you have, it is also important what language version you are using, US and non-US keyboards will behave differently. Note that the backslash key on US keyboards will have other labels on other versions of the keyboard.

Here is the mail from linux-input with the patch:

http://article.gmane.org/gmane.linux.kernel.input/37583

  • Thanks! Amazing to see a reply one year afterwards. I just compiled your patch into my kernel and it seems to work as intended. I wasn't able to apply the patch directly though because the mailing list seems to have corrupted the patch more or less (or I dun goofed it). I put my working patch here: pastebin.com/ZBaKaXxx So the patch works, keyboard is QPAD MK-85 with DE hardware layout but FI software layout. On US software layout the button is the backslash. On DE layout it's # and FI layout it's ' Do you want me to drop an email to the mailing list? – snaipperi Sep 5 '14 at 19:25
  • Yes it would be great if you can post a reply on the original mail so it gets bumped and perhaps noticed by the maintainer. – megahallon Sep 8 '14 at 7:59
  • 2
    A fix for this bug is now queued for linux 3.20 – megahallon Jan 11 '15 at 17:00
  • I'm having this bug for Windows 7. :( – Zolomon Aug 28 '15 at 15:29
2

Alright, I managed to put together a hack that solves the issue. I'll write it here in case someone stumbles with the same issue.

First off, if you're not interested in tweaking the kernel source, you might have another option: http://kbd-mangler.sourceforge.net/ - I did not test it, but the description looks rather promising. It allows you to tweak input before it's passed to the system.

My solution was to edit the file drivers/hid/hid-input.c. In the beginning of the file I added three new variable definitions;

static bool CODE43TRUE = 0; // If true, code43 has been pressed
static bool CODEXXTRUE = 0; // If true, any other key has been pressed
static int  CODESKIP = 0;   // Counter for skipping extra code43 events

Find the function

void hidinput_hid_event

At the bottom of this function is

input_event(input, usage->type, usage->code, value);

Input is the controller, type refers to event type (1 is keypress.. 2 is mouse motion?), code is the keycode and value is either 0 for depressed and 1 for pressed.

At any keypress the HID system loops 4 times through all keyboard keys. Why does it do it 4 times I do not know, but 4 corresponds with the amount of extra keypresses I got with my problematic key. On the first loop the pressed key has value 0, on second value 1, and on third and fourth again value 0.

The solution was to modify this function so that it would not allow the problematic key to be pressed again when other keys are pressed OR within 4 loops of the original keypress. This was achieved by the following code (Did I mention I haven't coded C for at least a decade? Sorry)

/* report the usage code as scancode if the key status has changed */
if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
    input_event(input, EV_MSC, MSC_SCAN, usage->hid);

// NEW CODE STARTS HERE
if (usage->type == 1 && value == 1) // Keypress ahead
{
    if (usage->code == 43) { // Keypress is code 43
        if (CODE43TRUE == 0) {  // Key not yet pressed 
            CODE43TRUE = 1;
            printk(KERN_INFO "CODE43 SET TRUE\n");
        }
    else { // Key already pressed, so force value 1
        printk(KERN_INFO "CODE43 ALREADY TRUE SET VALUE 1\n");
        value = 0;
    }
}
else { // Some other key pressed, set XX true
    CODEXXTRUE = 1;
    printk(KERN_INFO "CODEXX SET TRUE\n");  
}
printk(KERN_INFO "Keypress type:%u code:%u value%d\n", (unsigned int) usage->type, (unsigned int) usage->code, (int) value);
}

if (usage->type == 1 && value == 0) { // Non-pressed key ahead
    if (usage->code == 43) { // If its a 43
        printk(KERN_INFO "43 call..\n");
        if (CODE43TRUE == 1) { // And 43 is fake pressed still
            if (CODEXXTRUE == 1 || CODESKIP < 4) { // If other buttons are pressed OR we are less than 5 ticks into the press..
                printk(KERN_INFO "FAKE PRESS 43. CODESKIP %d\n",CODESKIP);
                value = 0;
                CODESKIP ++;
            }
            else { // No other buttons pressed and over five ticks have passed
                printk(KERN_INFO "43 RELEASED\n");
                CODE43TRUE = 0;
                CODESKIP = 0;   
            }
        }
        // Reset the CODEXXTRUE (next time we get info about 43, we have looped through all the other keys so we know if something is pressed)
        CODEXXTRUE = 0;
    }   
}

// NEW CODE ENDS HERE
input_event(input, usage->type, usage->code, value);

If you're implementing this you might want to remove the printk statements once you've verified it's working as intended. They're just there to help debugging.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.