Jak mogę uzyskać dodatkowe przyciski klawiatury USB?

5

Niedawno kupiłem klawiaturę USB. Ma 12 dodatkowych przycisków, ale działa tylko 5 z nich. W dziennikach nie ma komunikatów „nieznany scancode”. Evtest nie może ich wykryć, nie może nawet wykryć działającej 5 (tylko zwykłe klucze). Xev wykrywa działające 5, ale nie pozostałe. „cat /dev/input/by-path/pci-0000:00:02.0-usb-0:4:1.0-event-kbd” jest taki sam jak evtest z brzydszymi danymi wyjściowymi. W rzeczywistości jedynym sposobem, w jaki udało mi się wykryć pozostałe 7 kluczy, jest wąchanie USB za pomocą Wiresharka. Więc moja klawiatura nie jest uszkodzona.

Używam Gentoo Linux z jądrem gentoo-sources-2.6.30-r4, xorg-server-1.6.2-r1 i sterownikiem xf86-input-evdev w wersji 1.6.2-r1. Oto odpowiednia sekcja 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

Próbowałem dowiedzieć się więcej o opcjach takich jak XkbModel, ale strony podręcznika nie są zbyt pomocne. Szukałem tutaj każdego pytania z [klawiaturą], ale znalazłem tylko coś podobnego w systemie Windows.

Co mogę zrobić, aby klucze działały? Jeśli to błąd, gdzie powinienem to zgłosić?

Aktualizacja: Oto wynik działania klawiszy showkeys. Serwer X nie działał, kiedy to robiłem.

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

Nacisnąłem dodatkowe klawisze od lewej do prawej. Każdy klawisz ma 2 linie (chyba naciśnij i puść) i wykrywane są tylko działające 5.

Aktualizacja: Wymyśliłem naprawdę zły sposób na zrobienie tego. Mogę uruchomić tshark (interfejs wiersza poleceń wireshark) w tle, analizując dane wyjściowe i uruchamiając dowolne programy na odpowiednich pakietach USB. Istnieje poważny problem z bezpieczeństwem: każdy użytkownik, który może korzystać z dodatkowych kluczy, będzie mógł zobaczyć każdy ruch USB i sieciowy. Jedyną zaletą tego podejścia jest to, że działa. Po oczyszczeniu opublikuję pełny program do tego.

stribika
źródło
Nie wiem, czy to ten sam problem, ale odkryłem, że moja klawiatura wysyła określone klawisze raczej jako kliknięcia myszą niż jako naciśnięcie klawisza!
jes5199

Odpowiedzi:

4

Ok, więc mój program działał przez noc i nadal działa, więc wysyłam kod. Jest trochę niezręcznie, ale działa. Napiszę też o tym, jak to zrobiłem, ponieważ będzie to przydatne dla osób, które nie mają mojej klawiatury. Program potrzebuje najnowszej wersji libpcap i wireshark. Debugfs musi zostać zamontowany (mount -t debugfs none_debugs / sys / kernel / debug) i załadowany moduł usbmon (modprobe -v usbmon).

To jest program działający w tle:

#!/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

Jak widać, istnieje duża tablica poleceń zindeksowana danymi aplikacji z pakietów USB. Wartościami są wydane polecenia. Używam DBus do robienia tego, co należy zrobić, ale możesz użyć xvkbd do wygenerowania prawdziwych zdarzeń naciśnięcia klawisza (znalazłem xvkbd bardzo wolno, aby wysłać prostą kombinację klawiszy). tshark-wrapper to proste opakowanie dookoła tshark, które wykonuje tshark jako root i wyłącza stderr.

#!/bin/sh

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

Jest problem. Użytkownik potrzebuje uprawnień do wykonania tshark jako root bez hasła. To naprawdę bardzo zła rzecz. Ryzyko można zmniejszyć, umieszczając więcej argumentów w opakowaniu, a mniej w skrypcie Pythona i umożliwiając użytkownikom wykonanie opakowania jako root.

Teraz o procesie robienia tego z innymi klawiaturami. Nie wiem prawie nic o USB i wciąż nie było to takie trudne. Większość mojego czasu spędziłem na zastanawianiu się, jak zrobić odczyt niebuforowany z potoku. Z wyjścia lsusb wiedziałem, że moja klawiatura jest na drugim interfejsie USB. Zacząłem więc przechwytywać za pomocą wireshark na usbmon2. Mysz i inne oprogramowanie sprzętowe generują dużo hałasu, więc odłącz je od zasilania lub przynajmniej nie poruszaj myszą.

Pierwszą rzeczą, którą zauważyłem, było to, że dodatkowe klucze mają identyfikator punktu końcowego 0x82, a normalne klucze mają identyfikator punktu końcowego 0x81. Na początku było kilka pakietów z 0x80. To dobrze, że można go łatwo przefiltrować:

usb.endpoint_number == 0x82

Normalne naciśnięcie klawisza:

Normalne naciśnięcie klawisza

Dodatkowe naciśnięcie klawisza:

Dodatkowe naciśnięcie klawisza

Łatwo było zauważyć, że naciśnięcie klawisza generuje 4 pakiety USB: 2 dla prasy, 2 dla wydania. W każdej parze pierwszy pakiet był wysyłany przez klawiaturę do komputera, a drugi był na odwrót. Wydawało się, że ACK-y z TCP. „ACK” to URB-SUBMIT, a normalny pakiet to URB-COMPLETE. Postanowiłem więc odfiltrować „ACK” i wyświetlać tylko normalne pakiety:

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

„ACK” USB:

alternatywny tekst

Teraz były tylko 2 pakiety na naciśnięcie klawisza. Każda sekunda miała zerowe pole wartości aplikacji, a wszystko inne miało inne wartości. Więc odfiltrowałem zera i użyłem innych wartości do identyfikacji kluczy.

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

Dodatkowe wydanie klucza:

alternatywny tekst

Moja klawiatura to Slimstar 220 (mam nadzieję, że nie kwalifikuje się to jako spam. Jeśli to zrobię, usunę ją.) Jeśli masz takie same szanse, to niezmodyfikowany program będzie działał. W przeciwnym razie myślę, że przynajmniej wartość aplikacji będzie inna.

Jeśli ktoś ma ochotę napisać prawdziwy sterownik na podstawie tych danych, proszę dać mi znać. Nie podoba mi się mój brzydki hack.

Aktualizacja: Mam nadzieję, że kod jest teraz odporny na ponowne uruchomienie.

stribika
źródło
0

Zwykle jeśli nie otrzymujesz żadnych wiadomości z jądra LUB danych wejściowych do X, oznacza to, że jądro odrzuca klucze. Nie jestem pewien, co mogę polecić oprócz debugowania printk w jądrze (co może być nieco przesadzone).

Mike McQuaid
źródło
0

Używam lineakd do mapowania dowolnego klucza, który generuje kod klucza xev (1). Na obecnej klawiaturze mam około połowy przycisków generujących kody, inne nie. Nie znalazłem rozwiązania dla kluczy, których X nie rozpoznaje jako generujących kod klucza.

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

I napisał o tym jakiś czas temu , ale nie zaktualizowane stanowisko ze szczegółami korzystania xev uzupełnienie dostarczonych keycodes.

inne
źródło