Como posso fazer com que os botões extras do teclado USB funcionem?

5

Comprei recentemente um teclado USB. Tem 12 botões extras, mas apenas 5 deles estão funcionando. Não há mensagens de "scancode desconhecido" nos logs. Evtest não pode detectá-los, não pode sequer detectar o trabalho 5 (somente chaves regulares). Xev detecta o trabalho 5, mas não os outros. "cat /dev/input/by-path/pci-0000:00:02.0-usb-0:4:1.0-event-kbd" é o mesmo que evtest com saída mais feia. Na verdade, a única maneira que eu era capaz de detectar as outras 7 chaves é o USB sniffing com wireshark. Então meu teclado não está com defeito.

Estou usando o Gentoo Linux com kernel gentoo-sources-2.6.30-r4, xorg-server-1.6.2-r1 e o driver xf86-input-evdev versão 1.6.2-r1. Aqui está a seção relevante do xorg.conf:

Section "InputDevice"
    Identifier  "Keyboard0"
    Driver      "evdev"    
    Option      "Device"        "/dev/input/by-path/pci-0000:00:02.0-usb-0:4:1.0-event-kbd"
    Option      "XkbLayout"     "hu"                                                       
EndSection

Eu tentei descobrir mais sobre opções como o XkbModel, mas as páginas man não são muito úteis. Eu procurei por todas as perguntas do [teclado] aqui, mas encontrei apenas algo semelhante no Windows.

O que posso fazer para que as teclas funcionem? Se é um bug onde devo denunciá-lo?

Atualização: Aqui está a saída do showkeys -s. O servidor X não estava em execução quando fiz isso.

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)...
0xe0 0x22
0xe0 0xa2
0xe0 0x24
0xe0 0xa4
0xe0 0x20
0xe0 0xa0
0xe0 0x32
0xe0 0xb2
0xe0 0x6c
0xe0 0xec

Eu pressionei as teclas extras da esquerda para a direita. Cada tecla tem 2 linhas (pressione e solte eu acho) e somente o trabalho 5 é detectado.

Atualização: eu criei uma maneira muito ruim de fazer isso. Eu posso rodar o tshark (interface de linha de comando do wireshark) em segundo plano analisando a saída e executando programas arbitrários nos pacotes USB apropriados. Há um sério problema de segurança: qualquer usuário que tenha permissão para usar as chaves extras terá a capacidade de ver qualquer tráfego USB e de rede. A única vantagem dessa abordagem é que ela funciona. Vou postar o programa completo para isso depois de alguma limpeza.

    
por stribika 26.07.2009 / 12:23

3 respostas

4

Ok, meu programa estava funcionando por uma noite e ainda funciona, então eu postei o código. É um pouco fugly mas trabalha. Eu também vou escrever sobre como eu fiz isso, porque será útil para pessoas que não têm exatamente o meu teclado. O programa precisa de uma libpcap e wireshark recentes. O debugfs precisa ser montado (montar -t debugfs none_debugs / sys / kernel / debug) e o módulo usbmon carregado (modprobe -v usbmon).

Este é o programa que é executado em segundo plano:

#!/usr/bin/python                            
# This program should be run as the logged in user. The user must have
# permissions to execute tshark as root.                              

from pexpect import spawn
from pexpect import TIMEOUT
from subprocess import PIPE
from subprocess import Popen

# Configuration variables
## Device ID from lsusb output
deviceID = "0458:0708"        
## Output filter for tshark   
filter = "usb.endpoint_number == 0x82 && usb.data != 00:00:00:00"
## Tshark command to execute                                     
tsharkCmd = "/home/stribika/bin/tshark-wrapper"                  
## Keypress - command mapping                                    
### Key: USB Application data in hex ":" between bytes "\r\n" at the end.
### Value: The command to execute. See subprocess.Popen.                 
commands = {                                                             
  "00:00:20:00\r\n":[                                                    
    "qdbus", "org.freedesktop.ScreenSaver", "/ScreenSaver",              
    "org.freedesktop.ScreenSaver.Lock"                                   
  ],                                                                     
  "00:00:40:00\r\n":[                                                    
    "qdbus", "org.kde.amarok", "/Player", "org.freedesktop.MediaPlayer.Prev"
  ],                                                                        
  "00:00:10:00\r\n":[                                                       
    "qdbus", "org.kde.amarok", "/Player", "org.freedesktop.MediaPlayer.Next"
  ],                                                                        
  "02:00:00:00\r\n":[                                                       
    "qdbus", "org.kde.amarok", "/Player", "org.freedesktop.MediaPlayer.Pause"
  ],                                                                         
  "04:00:00:00\r\n":[                                                        
    "qdbus", "org.kde.amarok", "/Player", "org.freedesktop.MediaPlayer.Stop" 
  ],
  "00:04:00:00\r\n":[
    "qdbus", "org.kde.amarok", "/Player", "org.freedesktop.MediaPlayer.Mute"
  ],
  "20:00:00:00\r\n":[
    "qdbus", "org.kde.kwin", "/KWin", "org.kde.KWin.setCurrentDesktop", "1"
  ],
  "40:00:00:00\r\n":[
    "qdbus", "org.kde.kwin", "/KWin", "org.kde.KWin.setCurrentDesktop", "2"
  ],
  "00:00:80:00\r\n":[
    "qdbus", "org.kde.kwin", "/KWin", "org.kde.KWin.setCurrentDesktop", "3"
  ],
  "00:00:00:08\r\n":[
    "qdbus", "org.kde.kwin", "/KWin", "org.kde.KWin.setCurrentDesktop", "4"
  ],
  "00:00:00:20\r\n":[
    "qdbus", "org.kde.kwin", "/KWin", "org.kde.KWin.setCurrentDesktop", "5"
  ],
  "00:00:00:10\r\n":[
    "qdbus", "org.kde.kwin", "/KWin", "org.kde.KWin.setCurrentDesktop", "6"
  ],
}

# USB interface names change across reboots. This determines what is the correct
# interface called this week. If this turns out to be the case with endpoint
# numbers lsusb can tell that too.
lsusbCmd = [ "lsusb", "-d", deviceID ]
sedCmd = [
  "sed", "-r",
  "s/^Bus ([0-9]{3}) Device [0-9]{3}: ID " + deviceID + ".*$/\1/;s/^0+/usbmon/"
]

lsusb = Popen(lsusbCmd, stdin = PIPE, stdout = PIPE, stderr = PIPE)
sed = Popen(sedCmd, stdin = lsusb.stdout, stdout = PIPE, stderr = PIPE)
usbIface = sed.stdout.readline().rstrip()

# Arguments for Tshark
## -i is the interface (usbmon[0-9]+)
## -R is the output filter
tsharkArgs = [
  "-T", "fields", "-e", "usb.data",
  "-i", usbIface,
  "-R", filter
]

# Start capturing
## pexpect is needed to disable buffering. (Nothing else actally disables it
## don't belive the lies about Popen's bufsize=0)
tshark = spawn(tsharkCmd, tsharkArgs, timeout = 3600)
line = "----"

# Read keypresses while tshark is running and execute the proper command.
while line != "":
    try:
        line = tshark.readline()
        Popen(commands[line], stdin = PIPE, stdout = PIPE, stderr = PIPE)
    # We do not care about timeout.
    except TIMEOUT:
        pass

Como você pode ver, há uma grande matriz de comandos indexada com os dados do aplicativo dos pacotes USB. Os valores são os comandos emitidos. Eu estou usando o DBus para fazer o que precisa ser feito, mas você pode usar o xvkbd para gerar eventos de pressionamento de teclas reais (eu achei o xvkbd muito lento em segundos para enviar uma simples combinação de teclas). O tshark-wrapper é um simples wrapper em volta do tshark, ele executa o tshark como root e desativa o stderr.

#!/bin/sh

sudo tshark "$@" 2> /dev/null

Existe o problema. O usuário precisa de permissão para executar tshark como root sem senha. Isso é realmente muito ruim. O risco poderia ser reduzido colocando mais argumentos no wrapper e menos no script python e permitindo que os usuários executem o wrapper como root.

Agora, sobre o processo de fazer isso com outros teclados. Eu não sei quase nada sobre USB e ainda não foi tão difícil. A maior parte do meu tempo foi gasto descobrindo como fazer a leitura sem buffer de um cano. Eu sabia da saída do lsusb que meu teclado está na segunda interface USB. Então comecei a capturar com wireshark no usbmon2. O mouse e outros hardwares geram muito ruído, então desconecte-os ou, pelo menos, não mova o mouse.

A primeira coisa que notei foi que as teclas extras têm ID de ponto de extremidade 0x82 e as chaves normais têm ID de ponto de extremidade 0x81. Havia alguns pacotes no começo com 0x80. Isso é bom, pode ser facilmente filtrado:

usb.endpoint_number == 0x82

Tecla normal:

Imprensaextra:

Foi fácil ver que uma tecla pressionada gera 4 pacotes USB: 2 para a impressora, 2 para a liberação. Em cada par o primeiro pacote foi enviado pelo teclado para o PC e o segundo foi ao contrário. Parecia ACK-s com TCP. O "ACK" era URB-SUBMIT e o pacote normal era do tipo URB-COMPLETE. Então eu decidi filtrar os "ACK" e mostrar apenas pacotes normais:

usb.urb_type == "C\x01\x82\x03\x02"

USB "ACK":

Agora,haviaapenasdoispacotesporpressionamentodetecla.Cadasegundotinhacampodevalordeaplicaçãozeroetodoorestotinhavaloresdiferentes.Então,eufiltravaoszeroseusavaosoutrosvaloresparaidentificarchaves.

usb.data!=00:00:00:00

Lançamentodechaveextra:

Meu teclado é um Slimstar 220 (espero que isso não se qualifique como spam. Se eu o fizer, removê-lo.) Se você tiver o mesmo tipo de chances, o programa não modificado funcionará. Caso contrário, acho que pelo menos o material do valor do aplicativo será diferente.

Se alguém quiser escrever um driver real com base nesses dados, entre em contato. Eu não gosto do meu hack feio.

Atualização: O código agora é à prova de reinicialização.

    
por 27.07.2009 / 13:45
0

Normalmente, se você não está recebendo nenhuma mensagem do kernel OU inserindo em X, significa que o kernel está descartando as chaves. Não tenho certeza do que posso recomendar além de alguma depuração do printk no kernel (o que pode ser um pouco exagerado).

    
por 26.07.2009 / 18:28
0

Eu uso o lineakd para mapear qualquer chave que gere um código de acesso em xev (1) . No teclado atual eu tenho cerca de metade dos botões gerando códigos de teclas, os outros não. Não encontrei uma solução para chaves que o X não reconhece como gerador de um código de acesso.

KeyPress event, serial 28, synthetic NO, window 0x4400001,
    root 0xfd, subw 0x0, time 9475187, (382,534), root:(417,620),
    state 0x0, **keycode 69** (keysym 0xffc0, F3), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

Eu escrevi sobre isso há algum tempo , mas não atualizou a postagem com os detalhes do uso do xev para complementar os códigos de teclas fornecidos.

    
por 27.07.2009 / 03:15