6 votes

Comment puis-je faire fonctionner les boutons supplémentaires du clavier USB ?

J'ai récemment acheté un clavier USB. Il possède 12 boutons supplémentaires mais seulement 5 d'entre eux fonctionnent. Il n'y a pas de messages "scancode inconnu" dans les journaux. Evtest ne peut pas les détecter, il ne peut même pas détecter les 5 fonctionnels (seulement les touches régulières). Xev détecte les 5 fonctionnels mais pas les autres. "cat /dev/input/by-path/pci-0000:00:02.0-usb-0:4:1.0-event-kbd" est identique à evtest avec une sortie encore plus laide. En fait, la seule façon dont j'ai pu détecter les 7 autres touches est la capture USB avec wireshark. Donc mon clavier n'est pas défectueux.

J'utilise Gentoo Linux avec le noyau gentoo-sources-2.6.30-r4, le serveur xorg-server-1.6.2-r1 et le pilote xf86-input-evdev version 1.6.2-r1. Voici la section pertinente du xorg.conf :

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

J'ai essayé d'en savoir plus sur des options comme XkbModel mais les pages du manuel ne sont pas très utiles. J'ai recherché toutes les questions [keyboard] ici mais n'ai trouvé quelque chose de similaire que sur Windows.

Que puis-je faire pour faire fonctionner les touches ? S'il s'agit d'un bug, où devrais-je le signaler ?

Mise à jour : Voici la sortie de showkeys -s. Le serveur X n'était pas en cours d'exécution lorsque j'ai fait cela.

kb mode était UNICODE
[ si vous essayez ceci sous X, cela pourrait ne pas fonctionner
car le serveur X lit également /dev/console ]

appuyez sur une touche (le programme se termine 10s après la dernière pression sur une touche)...
0xe0 0x22
0xe0 0xa2
0xe0 0x24
0xe0 0xa4
0xe0 0x20
0xe0 0xa0
0xe0 0x32
0xe0 0xb2
0xe0 0x6c
0xe0 0xec

J'ai appuyé sur les touches supplémentaires de gauche à droite. Chaque touche a 2 lignes (pression et relâchement je suppose) et seules les 5 fonctionnelles sont détectées.

Mise à jour : J'ai trouvé une manière vraiment mauvaise de faire cela. Je peux exécuter tshark (interface en ligne de commande de wireshark) en arrière-plan en analysant la sortie et en exécutant des programmes arbitraires sur les paquets USB appropriés. Il y a un problème de sécurité sérieux : tout utilisateur autorisé à utiliser les touches supplémentaires aura la possibilité de voir tout le trafic USB et réseau. Le seul avantage de cette approche est qu'elle fonctionne. Je posterai le programme complet pour cela après un peu de nettoyage.

5voto

Michael La Voie Points 111

Ok donc mon programme a tourné toute la nuit et il fonctionne toujours donc je poste le code. C'est un peu moche mais ça fonctionne. Je vais aussi expliquer comment je l'ai fait car cela sera utile pour les personnes n'ayant pas exactement le même clavier. Le programme nécessite une libpcap suffisamment récente et Wireshark. Le debugfs doit être monté (montage -t debugfs none_debugs /sys/kernel/debug) et le module usbmon chargé (modprobe -v usbmon).

Voici le programme qui s'exécute en arrière-plan :

#!/usr/bin/python                            
# Ce programme doit être exécuté en tant qu'utilisateur connecté. L'utilisateur doit avoir
# autorisations pour exécuter tshark en tant que root.

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

# Variables de configuration
## ID du périphérique à partir de la sortie lsusb
deviceID = "0458:0708"        
## Filtre de sortie pour tshark   
filter = "usb.endpoint_number == 0x82 && usb.data != 00:00:00:00"
## Commande tshark à exécuter                                     
tsharkCmd = "/home/stribika/bin/tshark-wrapper"                  
## Mappage touche - commande                                    
### Touche : données de l'application USB en hex ":" entre les octets "\r\n" à la fin.
### Valeur : La commande à exécuter. Voir 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"
  ],
}

# Noms des interfaces USB changent à chaque redémarrage. Cela détermine quel est le bon
# interface appelée cette semaine. Si c'est le cas avec les numéros d'extrémité,
# lsusb peut le dire aussi.
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 pour Tshark
## -i est l'interface (usbmon[0-9]+)
## -R est le filtre de sortie
tsharkArgs = [
  "-T", "fields", "-e", "usb.data",
  "-i", usbIface,
  "-R", filter
]

# Démarrer la capture
## pexpect est nécessaire pour désactiver le buffering. (Rien d'autre ne le désactive réellement
## ne croyez pas les mensonges sur le bufsize=0 de Popen)
tshark = spawn(tsharkCmd, tsharkArgs, timeout = 3600)
line = "----"

# Lire les appuis sur les touches pendant que tshark s'exécute et exécuter la commande appropriée.
while line != "":
    try:
        line = tshark.readline()
        Popen(commands[line], stdin = PIPE, stdout = PIPE, stderr = PIPE)
    # Nous ne nous soucions pas du timeout.
    except TIMEOUT:
        pass

Comme vous pouvez le voir, il y a un grand tableau de commandes indexé avec les données d'application des paquets USB. Les valeurs sont les commandes émises. J'utilise DBus pour faire ce qui doit être fait mais vous pouvez utiliser xvkbd pour générer de vrais événements de pression de touche (j'ai trouvé xvkbd très lent, cela prend des secondes pour envoyer une simple combinaison de touches). tshark-wrapper est un simple enrobage autour de tshark; il exécute tshark en tant que root et désactive stderr.

#!/bin/sh

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

Là réside le problème. L'utilisateur doit avoir la permission d'exécuter tshark en tant que root sans mot de passe. C'est vraiment vraiment mauvais. Le risque pourrait être réduit en mettant plus d'arguments dans l'enrobage et moins dans le script python et en autorisant les utilisateurs à exécuter l'enrobage en tant que root.

Maintenant concernant le processus de faire cela avec d'autres claviers. Je sais presque rien sur l'USB et pourtant cela n'a pas été si difficile. La plupart de mon temps a été consacré à comprendre comment effectuer une lecture débufferisée à partir d'un tuyau. Je savais à partir de la sortie lsusb que mon clavier est sur la 2ème interface USB. J'ai donc commencé une capture avec Wireshark sur usbmon2. La souris et les autres matériels génèrent beaucoup de bruit donc débranchez-les ou au moins ne bougez pas la souris.

La première chose que j'ai remarquée était que les touches supplémentaires ont l'ID de l'extrémité 0x82 et les touches normales ont l'ID de l'extrémité 0x81. Il y avait quelques paquets au début avec 0x80. C'est bon, cela peut être filtré facilement :

usb.endpoint_number == 0x82

Pression de touche normale :

Pression de touche normale

Pression de touche supplémentaire :

Pression de touche supplémentaire

Il était facile de voir qu'une pression de touche génère 4 paquets USB : 2 pour la pression, 2 pour la libération. Dans chaque paire, le premier paquet était envoyé par le clavier à l'ordinateur et le deuxième était dans l'autre sens. Cela ressemblait à des ACK-s avec TCP. L'« ACK » était de type URB-SUBMIT et le paquet normal était de type URB-COMPLETE. J'ai donc décidé de filtrer les « ACK » et de ne montrer que les paquets normaux :

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

ACK USB :

texte alternatif

Maintenant, il y avait seulement 2 paquets par pression de touche. Chaque seconde avait un champ de valeur d'application zéro et tout le reste avait des valeurs différentes. J'ai donc filtré les zéros et utilisé les autres valeurs pour identifier les touches.

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

Libération de touche supplémentaire :

texte alternatif

Mon clavier est un Slimstar 220 (J'espère que cela ne sera pas considéré comme du spam. Si c'est le cas, je le retirerai). Si vous avez le même type, les chances sont que le programme non modifié fonctionnera. Sinon, je pense que au moins les trucs de valeur d'application seront différents.

Si quelqu'un se sent motivé pour écrire un vrai pilote basé sur ces données, faites-le moi savoir. Je n'aime pas mon vilain hack.

Mise à jour : Le code est maintenant, espérons-le, à l'épreuve des redémarrages.

0voto

jamting Points 893

Généralement, si vous ne recevez aucun message du noyau OU aucune entrée dans X, cela signifie que le noyau rejette les touches. Je ne suis pas sûr de ce que je peux recommander à part quelques débogages printk dans le noyau (ce qui pourrait être un peu exagéré).

0voto

msanders Points 2921

J'utilise lineakd pour mapper n'importe quelle touche qui génère un code de touche dans xev (1). Sur le clavier actuel, environ la moitié des boutons génèrent des codes de touche, les autres ne le font pas. Je n'ai pas trouvé de solution pour les touches que X ne reconnaît pas comme générant un code de touche.

Evénement KeyPress, série 28, synthétique NON, fenêtre 0x4400001,
    root 0xfd, subw 0x0, temps 9475187, (382,534), root:(417,620),
    état 0x0, **code de touche 69** (keysym 0xffc0, F3), même écran OUI,
    XLookupString donne 0 octets: 
    XmbLookupString donne 0 octets: 
    XFilterEvent renvoie: Faux

Je ai écrit à ce sujet il y a un certain temps, mais je n'ai pas mis à jour l'article avec les détails sur l'utilisation de xev pour compléter les codes de touche fournis.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X