Uma única tecla no teclado produz pressionamentos de tecla extras para cada tecla pressionada simultaneamente

4

Adquiri recentemente o teclado USB mecânico MK-85 da QPAD. O teclado funciona perfeitamente no Windows. Funciona perfeitamente no Syslinux. Funciona quase perfeito no Linux. O único problema no Linux é que uma única chave está se comportando mal (Gentoo (3.6.11), Arch Linux e Linux Mint (2.6.38) são todos afetados).

O teclado é um teclado com layout alemão de 105 teclas e a chave em questão é aquela entre Ä e ENTER. No layout norte-americano isso corresponde à chave \ , no layout alemão isso corresponde a # e no layout escandinavo é '.

Quando esta tecla é pressionada com outras teclas, produz um pressionamento de tecla extra para cada tecla que é pressionada simultaneamente. Por exemplo, no layout escandinavo, se eu quiser digitar a palavra " não " muito rápido, acabo com: don '' 't'

O comportamento pode ser observado com os programas 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)

Isso só acontece com essa chave única, independentemente do layout do teclado. Outra maneira de se manifestar é se eu mantiver pressionada uma tecla, ela se repete e pressiono outra tecla que também deve começar a se repetir:

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

No Windows, esse problema não existe:

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

O que você acha da SE? Problema de hardware? Ele funciona bem no Syslinux, o que me faz sentir como se houvesse algo errado no lado do Linux. Qualquer ponteiros, idéias ou melhores maneiras de depurar? Se conseguir que isso funcione corretamente, é necessário corrigir o kernel. Eu estou pronto para isso.

    
por snaipperi 26.08.2013 / 20:54

2 respostas

4

Eu tentei criar um patch adequado para esse bug. É um problema no kernel, e não no teclado, embora se possa argumentar que o teclado se comporta de uma forma estranha. De qualquer forma, o patch é submetido à lista de entrada do linux para revisão, mas ainda não há comentários sobre isso.

Isso deve corrigir o problema mencionado aqui com o QPAD MK-85, mas o mesmo problema existe com o Corsair K70, o Gigabyte Osmium e outros teclados similares. Se você tem um teclado que tem o bug, seria ótimo, você pode testar o patch. Se você testar, me avise se funciona e também o teclado que você tem, também é importante qual versão de idioma você está usando, teclados americanos e não americanos se comportarão de maneira diferente. Observe que a tecla de barra invertida nos teclados dos EUA terá outros rótulos em outras versões do teclado.

Aqui está o e-mail da entrada do Linux com o patch:

link

    
por 04.09.2014 / 10:45
2

Tudo bem, consegui montar um hack que resolve o problema. Vou escrever aqui caso alguém tropeça com o mesmo problema.

Primeiro, se você não está interessado em mexer no código fonte do kernel, você pode ter outra opção: link - I não testei, mas a descrição parece bastante promissora. Permite ajustar a entrada antes de ser passada para o sistema.

Minha solução foi editar os drivers de arquivo / hid / hid-input.c. No início do arquivo, adicionei três novas definições de variáveis;

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

Encontre a função

void hidinput_hid_event

Na parte inferior desta função está

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

A entrada é o controlador, o tipo refere-se ao tipo de evento (1 é o pressionamento da tecla .. 2 é o movimento do mouse?), o código é o código e o valor é 0 para pressionado e 1 para pressionado.

Em qualquer tecla, o sistema HID faz um loop 4 vezes em todas as teclas do teclado. Por que ele faz isso 4 vezes eu não sei, mas 4 corresponde à quantidade extra de pressionamentos de teclas que eu obtive com a minha chave problemática. No primeiro loop, a tecla pressionada tem valor 0, no segundo valor 1, e no terceiro e quarto novamente, valor 0.

A solução foi modificar esta função para que não permitisse que a tecla problemática fosse pressionada novamente quando outras teclas fossem pressionadas OU dentro de 4 voltas do pressionamento de tecla original. Isto foi conseguido com o seguinte código (eu mencionei que não codifiquei C por pelo menos uma década? Desculpe)

/* 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);

Se você estiver implementando isso, talvez queira remover as instruções do printk depois de verificar se está funcionando conforme o esperado. Eles estão lá apenas para ajudar na depuração.

    
por 27.08.2013 / 21:46