5

Я недавно купил USB-клавиатуру. Он имеет 12 дополнительных кнопок, но только 5 из них работают. В журналах нет сообщений «неизвестный скан-код». Evtest не может их обнаружить, он даже не может обнаружить работающую 5 (только обычные ключи). Ксев обнаруживает рабочую 5, но не другие. "cat /dev/input/by-path/pci-0000:00:02.0-usb-0:4:1.0-event-kbd" аналогичен evtest с более уродливым выводом. Фактически единственный способ, которым я смог обнаружить другие 7 ключей, - это USB-слежка с помощью wireshark. Так что моя клавиатура не неисправна.

Я использую Gentoo Linux с ядром gentoo-sources-2.6.30-r4, xorg-server-1.6.2-r1 и драйвером xf86-input-evdev версии 1.6.2-r1. Вот соответствующий раздел 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

Я попытался узнать больше о таких опциях, как XkbModel, но страницы руководства не очень полезны. Я искал каждый вопрос [клавиатуры] здесь, но нашел только что-то похожее на окнах.

Что я могу сделать, чтобы ключи работали? Если это ошибка, где я должен сообщить об этом?

Обновление: вот вывод showkeys -s. Когда я это сделал, X-сервер не работал.

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

Я нажал дополнительные клавиши слева направо. Каждая клавиша имеет 2 строки (думаю, нажмите и отпустите), и только 5 рабочих обнаружены.

Обновление: я придумал действительно плохой способ сделать это. Я могу запустить tshark (интерфейс командной строки wireshark) в фоновом режиме, анализируя вывод и выполняя произвольные программы на соответствующих USB-пакетах. Существует серьезная проблема безопасности: любой пользователь, которому разрешено использовать дополнительные ключи, будет иметь возможность видеть любой USB и сетевой трафик. Единственное преимущество этого подхода в том, что он работает. Я выложу полную программу для этого после некоторой очистки.

3 ответа3

4

Итак, моя программа работала в течение ночи, и она все еще работает, поэтому я публикую код. Это немного неопрятно, но работает. Я также напишу о том, как я это сделал, потому что это будет полезно для людей с не совсем моей клавиатурой. Программа нуждается в достаточно недавних libpcap и wireshark. Необходимо отладить debugfs (mount -t debugfs none_debugs /sys /kernel /debug) и загрузить модуль usbmon (modprobe -v usbmon).

Это программа, которая работает в фоновом режиме:

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

Как вы можете видеть, существует большой массив команд, индексируемый данными приложения из пакетов USB. Значения являются выданными командами. Я использую DBus, чтобы делать то, что должно быть сделано, но вы можете использовать xvkbd для генерации реальных событий нажатия клавиш (я обнаружил, что xvkbd очень медленный, для отправки простой комбинации клавиш требуются секунды). tshark-wrapper - это простая оболочка вокруг tshark, она запускает tshark от имени пользователя root и отключает stderr.

#!/bin/sh

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

Есть проблема. Пользователю необходимо разрешение для запуска tshark от имени пользователя root без пароля. Это действительно очень плохо. Риск может быть уменьшен, если поместить больше оболочек в оболочку и меньше в скрипт Python и позволить пользователям запускать оболочку от имени пользователя root.

Теперь о том, как это сделать с другими клавиатурами. Я почти ничего не знаю о USB, и все же это было не так сложно. Большая часть моего времени была потрачена на выяснение того, как выполнять небуферизованное чтение из канала. Из вывода lsusb я знал, что моя клавиатура находится на втором USB-интерфейсе. Поэтому я начал захватывать с помощью wireshark на usbmon2. Мышь и другое оборудование создают много шума, поэтому отключите их или, по крайней мере, не двигайте мышь.

Первое, что я заметил, было то, что дополнительные ключи имеют идентификатор конечной точки 0x82, а обычные ключи имеют идентификатор конечной точки 0x81. В начале было несколько пакетов с 0x80. Это хорошо, это может быть легко отфильтровано:

usb.endpoint_number == 0x82

Обычное нажатие клавиши:

Нормальное нажатие клавиши

Дополнительное нажатие клавиши:

Дополнительное нажатие клавиши

Было легко увидеть, что нажатие клавиши генерирует 4 USB-пакета: 2 для нажатия, 2 для отпускания. В каждой паре первый пакет отправлялся с клавиатуры на ПК, а второй - наоборот. Казалось, ACK-ы с TCP. "ACK" был URB-SUBMIT, а обычный пакет был URB-COMPLETE. Поэтому я решил отфильтровать "ACK" и показывать только нормальные пакеты:

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

USB "ACK":

альтернативный текст

Теперь было только 2 пакета на нажатие клавиши. Каждая секунда имела нулевое поле значения приложения, а все остальные имели разные значения. Поэтому я отфильтровал нули и использовал другие значения для идентификации ключей.

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

Выпуск дополнительного ключа:

альтернативный текст

Моя клавиатура - Slimstar 220 (надеюсь, это не квалифицируется как спам, если она удаляется). Если у вас есть шансы того же типа, то немодифицированная программа будет работать. В противном случае, я думаю, что, по крайней мере, ценность приложения будет другой.

Если кому-то захочется написать настоящий драйвер на основе этих данных, пожалуйста, дайте мне знать. Мне не нравится мой уродливый хак.

Обновление: код теперь, надеюсь, защищен от перезагрузки.

0

Я использую lineakd для сопоставления любого ключа, который генерирует код ключа в xev (1) . На текущей клавиатуре у меня примерно половина кнопок генерирует коды клавиш, остальные нет. Я не нашел решения для ключей, которые X не распознает как генерирующие код ключа.

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

Я писал об этом некоторое время назад, но не обновил пост подробностями использования xev для дополнения предоставленных кодов клавиш.

0

Обычно, если вы не получаете никаких сообщений от ядра или ввода в X, то это означает, что ядро отбрасывает ключи. Я не уверен, что могу порекомендовать кроме отладки printk в ядре (что может быть немного излишним).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .