Journal De l'écriture d'un pilote Linux pour un gadget

Posté par  .
Étiquettes :
96
24
juil.
2010
Bonjour cher journal

Depuis des années, j'aide des amis ou de la famille avec l'installation et l'utilisation de Linux sur leurs machines. C'est une activité pouvait être souvent pénible (les questions et problèmes récurrents quoi, le pilote ATI qui plante par exemple).
Parfois, une question plus amusante apparaît, comme "comment gérer les diodes de décoration du clavier de mon PC portable Toshiba ?".
Et là commence un merveilleux périple, entre pilote Linux, ACPI, DSDT, pilote Windows, reverse engineering et écriture de code C...
Je vous prie par avance de m'excuser pour mes méthodes, mes explications ou encore les outils potentiellement non libres que j'ai pu utiliser (le principal étant probablement IDA sous Windows). Je ne vais tenter ici que de donner une information que j'estime pouvoir être utile à tout un chacun, et tenter de retranscrire la progression dans l'écriture du bout de code de quelques lignes qui arrivera peut être dans un futur noyau...

0) Description exacte du problème
Le PC en question est un Toshiba Qosmio G50 122.
Première difficulté : ce modèle est réservé à la france ou à l'europe, et les informations à son sujet sur internet sont donc relativement rares.
Le PC en question dispose d'un clavier assez classique, avec une série de boutons "tactiles" représentés juste par une petite diode chacun pour les touches multimedia et 2/3 conneries (activer la webcam, lancer le lecteur multimedia...)
Sur le côté du clavier, dans les enceintes, deux diodes également, et un cercle lumineux autour d'un bouton de contrôle du volume.
Bref, un clavier bien lumineux, fort peu pratique pour regarder un film par exemple.
Heureusement, sur le clavier, une touche est présente pour éteindre ces diodes. Problème : elle ne marche que sous windows, et ne fait rien sous Linux...
Idem pour les 2/3 conneries à côté des touches multimedia...

1) Première étape, l'analyse du système
Les périphériques visibles par lspci, lsusb ou une recherche dans /sys ne donnait pas de résultat intéressant. Juste les périphériques classiques (carte réseau, carte graphique) ou encore le contrôleur bluetooth...
Les touches n'émettent également aucun signal, ni dans xev, ni à la lecture d'un des fichiers de /dev/input.
Fort heureusement, un article lu tantôt sur LWN ne met sur la voie : http://lwn.net/Articles/367630/
Toshiba a implémenté "dans" l'ACPI le bouton d'activation/désactivation du bluetooth.
Pourquoi n'auraient-ils pas fait de même pour le contrôle des diodes ?

2) Explication sur l'ACPI
Attention, il s'agit uniquement de ce que j'en ai compris !
L'ACPI est une norme complexe pour la configuration, la découverte du matériel et la gestion de l'énergie. Norme particulièrement peu pratique d'un point de vue de geek/libriste (on se souvient du mail de Bill Gates montré lors du procès Microsoft exigeant que l'ACPI ne marche bien qu'avec "Windows NT").
Dans cette norme, le BIOS exporte une série de tables à l'OS, sous forme d'un bytecode, que l'OS devra interpréter. Parmi ces tables, la plus importante est sûrement la "Discrete System Descriptor Table", contenant des informations de configuration, une description du matériel donc, comment accéder à diverses informations sur le matériel...
Il y a à peu près 4 ans, il était fréquent, si vous vous souvenez bien, de devoir patcher cette DSDT (c'est-à-dire de forcer le noyau à lire une autre DSDT que celle du BIOS) afin de pouvoir avoir l'état de la batterie par exemple...
Dans le cas du portable Toshiba de mon pote, un grep sur sa DSDT renvoie 2 périphériques avec un ID "prometteur" : TOS6205 (nommé BT) et TOS6208 (nommé VALZ). TOS6205 sert pour le bluetooth, aucun intérêt pour nous, par contre VALZ est un système générique de toshiba pour le contrôle de nombreuses choses sur le matériel. Le pilote Linux existant pour ce module, toshiba_acpi, nous le prouvera : gestion des ventilateurs, des sorties video, du rétro éclairage, de certains raccourcis clavier... Le choix ne manque pas !

3) Premières tentatives : support des touches "à la con"
La structure du pilote toshiba_acpi.c semble assez simple au premier abord : un tableau de structures fournit une sorte de "keymap", et aucune des touches ne semble correspondre aux touches bizarres du clavier.
Mais la lecture du code montre un soucis : l'appui sur une touche inconnue doit déclencher un warning indiquant le code de la touche... Or aucun warning n'est émis...
La lecture du code et de la section de la DSDT sur le VALZ sont néanmoins fort instructifs : tout semble être exécuté dans une seule fonction "magique", prenant en entrée 6 entiers non signés sur 32 bits, et renvoyant 6 entiers également. Cette fonction magique se nomme GHCI. Une autre fonction est sensée activer les raccourcis clavier, elle s'appelle "ENAB". Mais ne marche apparemment pas.
Pour rendre la DSDT plus lisible, j'ai décidé de "traduire" en à-peu-près C la fonction GHCI. Disons le clairement : ça n'a pas servi à grand chose, il est quasiment impossible de tirer quelque chose d'utile de ce blob. J'en ai déduit, au mieux, l'existence de valeurs non gérées par le pilote linux, mais sans la moindre idée sur la signification de ces valeurs.
Le support des touches à la con étant malheureusement impossible en l'état de mes connaissances de ce périphérique toshiba bizarre, je suis passé au problème des diodes.

4) Problème des diodes : reverse engineering powa...
C'est avec un certain plaisir que j'ai commencé l'étape de reverse engineering du pilote et des utilitaires Toshiba windows afin de pouvoir trouver les séquences magiques à donner à la fonction GHCI.
Naïvement, j'ai commencé mon travail au niveau du pilote en mode noyau directement, en me disant que, en toute logique, un utilitaire quelconque allait l'appeler pour lui dire "allume les diodes", "éteint les diodes"...
J'ai donc téléchargé et lancé le logiciel DriverView (http://www.nirsoft.net/utils/driverview.html) pour savoir quels pilotes sont chargés sur le système.
Un pilote a retenu mon attention, Toshiba Added Value, nommé TVALZ.sys... Hooo, avec le même nom que le périphérique décrit dans la DSDT.
Il était temps de commencer l'étape très pénible de désassemblage et de compréhension du pilote, à l'aide de la version gratuite d'IDA (je n'ai pas encore pu m'offrir la version payante : http://www.hex-rays.com/idapro/idadownfreeware.htm).
Pour ceux qui n'ont jamais utilisé ce genre d'outils, on obtient un code assembleur plus ou moins commenté, divisé en fonctions. Les paramètres donnés à chaque fonction sont partiellement identifiés, mais un gros travail d'analyse manuel reste (par exemple, passer d'une fonction sub_0410230 à une fonction ....).
Heureusement, Toshiba a mal géré le débuggage dans son pilote, et les messages de debug sont donc présents dans le code desassemblé ! J'ai donc pu identifier les différentes fonctions du pilote aisément. Je fus alors déçu : une fonction est présente par méthode dans l'objet VALZ de la DSDT, mais aucune trace d'un appel de la-dite fonction.
Tout penaud, je me dirigeai alors vers l'espace utilisateur et les utilitaires Toshiba.
Un outil permet d'associer à chaque touche à la con un exécutable. Il doit donc y avoir un exécutable permettant de couper ou activer les diodes.
Une méthode stupide a alors marché : surveiller le gestionnaire des tâches windows, et appuyer sans relache sur la touche pour allumer/éteindre les diodes... Et là, on voit apparaître un exécutable "Dimmer.exe", tout petit (50ko).
Je commençai ma recherche par les chaînes de caractères. La première chaîne de la liste retint mon attention, "\\.\TVALD". Il s'agit, sous windows, d'un fichier spécial correspondant à un périphérique (un peu comme si l'on avait un fichier /dev/tvald).
Cette chaîne n'est utilisée qu'à un endroit, dans un appel à la méthode CreateFileA (qui permet aussi d'ouvrir des fichiers).
Victoire, suivons l'itinéraire du handle obtenu (identifiant vers le fichier ouvert)...
Voici une vue approximative du code :
- HDL handle;
- if (!CreateFile("\\.\TVALD", &handle) { return; }
- if (!sub_401120(handle)) { return; }
- sub_401040(handle);
- sub_401170(handle);

On peut en déduire, à peu près, que la fonction sub_401120 contrôle la présence du périphérique.
Les fonctions sub_401040 et sub_401170 sont encore à déterminer.
Debuggueur à la rescousse !
Pour ce faire, j'utilise OllyDbg 2.0 (http://www.ollydbg.de)
Et l'exécution confirme : la fonction sub_401040 est bien responsable du changement d'état des diodes.
Mais comment fonctionnent les trois fonctions sub_401120, sub_401040 et sub_401170 ?

La première fonction, sub_401120, est intéressante : elle dispose, selon IDA, de 7 variables locales et une variable en paramètre (le handle).
Ce qui est intéressant, c'est que ces sept variables sont initialisées ainsi :
xor eax, eax ; mise à zéro de eax
mov [esp+30h+var7], eax
mov [esp+30h+var6], eax
mov [esp+30h+var5], eax
mov [esp+30h+var4], eax
mov [esp+30h+var3], eax
mov [esp+30h+var2], eax

La troisième, identifiée par IDA comme étant un nombre d'octets retournés, n'est pas initialisée.

On initialise donc à zéro ces six variables... Puis l'opération suivante est effectuée :
mov [esp+3Ch+var7], 0F100h

Puis une méthode sub_401000 est appelée. Sa signature, identifiée par IDA, est la suivante :
int __cdecl sub_401000 (LPVOID lpInBuffer, DWORD BytesReturned, HANDLE hDevice);

Dans un monde linuxien, cela serait plutôt :
int sub_401000 (int in[6], int *outLength, FILE *dev);

Elle est appelée avec en paramètre la variable var7 et la variable BytesReturn de la méthode sub_401120...
Et elle se résume à un appel à la méthode DeviceIoControl, et à une vérification des résultats de l'appel.

La méthode DeviceIoControl reçoit en paramètre la première variable, et une indication de longueur de 24 octets...
Et c'est là que l'on comprend que les variables 2 à 7 sont en fait un tableau de 6 entiers de 32 bits... Comme les paramètres de la méthode GHCI. Aussi incroyable que cela puisse paraître, alors que la fonction GHCI permet de contrôler jusqu'aux ventilateurs, un programme pourrait l'exécuter sans contrôle... Mais bon, qui ne tente rien n'a rien, identifions tous les appels à la méthode sub_401120 (merci encore à IDA pour ce travail de fourmi fait automatiquement), et regardons comment sont initialisés les paramètres.

- GHCI(0xF200, 0, 0, 0, 0,0)
renvoie normalement un tableau de six entiers, qu'on peut utiliser pour savoir si l'illumination est disponible.

- GHCI(0xF300, 0x14E, 0, 0, 0, 0)
indique dans le troisième entier si les diodes sont allumées ou éteintes

- GHCI(0xF400, 0x14E, ?, 0, 0, 0)
le troisième paramètre donne le nouvel état des diodes

- GHCI(0xF200, 0, 0, 0, 0, 0)
je ne sais pas, fermeture de la connexion ?

5) Implémentation sous Linux
Cette partie est très simple, et je n'ai pas pensé à récupérer pour l'instant le code source que j'ai écrit... Ainsi, je ne donnerai que la théorie.

3 fonctions C sont nécessaires :
- une pour vérifier la présence du matériel,
- une pour allumer/éteindre la diode,
- une pour connaître l'état de la diode.

L'appel GHCI est assez facile à l'aide d'une des fonctions déjà implémentées dans le pilote, hci_raw. Il nous suffit de construire des tableaux de six uint32 pour faire ce que l'on souhaite...

Une fois chaque méthode implémentée, il suffit d'appeler la méthode de vérification de présence du matériel dans l'initialisation du pilote, puis d'utiliser la structure Linux standard pour la gestion de diodes (la classe de matériel led).

Il suffit d'écrire la structure suivante :
static struct led_classdev toshiba_illumination_led = {
.name = "toshiba::illumination",
.brightness = LED_OFF,
.max_brightness = 1,
.brightness_set = toshiba_illumination_set,
.brightness_get = toshiba_illumination_get,
};

et d'appeler la méthode d'enregistrement led_classdev_register...


Et TADA !!
Ça marche... Un dossier /sys/class/leds/toshiba::illumination apparaît, et une écriture dans le fichier brightness allume ou éteint les diodes !

"Conclusion"
Voilà voilà, je vous invite vraiment, quand vous avez un peu de compétence en C/assembleur, d'implémenter le support pour de tels gadgets si vous en avez. Ce n'est souvent pas très compliqué, et ça fera toujours plaisir à vous, l'un de vos proches, ou un utilisateurs quelque part dans le monde...

Dans le making off, vous avez échappé à l'utilisation du debuggueur avec modification des résultats des méthodes pour faire croire au programme que la matériel était présent, vous avez échappé aux essais avec un debuggueur noyau sous windows, vous n'avez pas vu non plus les appels au hasard des autres méthodes ACPI...

Quand j'aurai nettoyé le code et que j'enverrai un patch pour intégration au noyau, je ferai un journal pour que vous puissiez profiter du code, si cela peut vous servir...

Bon weekend à vous
  • # Acronyme

    Posté par  (site web personnel) . Évalué à 7.

    C'est bizarre mais sur le net on trouve deux explications pour l'acronyme DSTD.
    - Soit "Discrete System Descriptor Table".
    - Soit "Differentiated System Description Table".

    Quelqu'un pour se palucher la norme ACPI afin de nous dire ce qui est juste ?
  • # hwreport

    Posté par  (site web personnel) . Évalué à 9.

    les infos sur ce "gadget" sont-elles détectées par hwreport qui permet de remonter les infos sur http://hardware4linux.info ? (tu dois pouvoir tester en lançant l'utilitaire hwreport sur ton pc et remonter les infos sur le site :D)

    Si ce n'est pas détecté alors que cela semble possible, c'est peut-être l'occasion de l'ajouter à hwreport (et hop une autre suggestion de contribution par la même occasion :D).

    Bravo pour ta démarche en tout cas !
  • # Avant de mettre les mains dans le cambuis

    Posté par  . Évalué à 9.

    As-tu tenté de contacter Toshiba pour ça?

    Même si ça semble être une tâche triviale pour toi, si il y a possibilité de glaner une info ou deux via un ingénieur Toshiba, pourquoi pas.

    En fait, je ne sais même pas si ça se fait pour des trucs aussi bas-niveau.

    Néanmoins, un grand merci pour ce compte rendu, c'est toujours plaisant à lire!
    • [^] # Re: Avant de mettre les mains dans le cambuis

      Posté par  . Évalué à 8.

      Il y a effectivement quelques infos à glaner auprès de Toshiba ici:
      http://linux.toshiba-dme.co.jp/linux/eng/develop.htm
      ... mais c'est assez limité car ils donnent assez peu de réponses en fait. J'en est appris presque autant en lisant le driver Linux.
      On apprend que l'interface utilisée ici est l'implémentation ACPI de l'ancienne interface HCI/SCI utilisée depuis fort longtemps par Toshiba pour contrôler le Bios. Sur mon ancien Toshiba, il n'y avait pas d'interface utilisateur pour le Bios et tout se fait via les utilitaires Windows. :( Cette interface permet donc pas mal de modifications (potentiellement catastrophique pour le bon démarrage de la machine) et c'est donc pas terrible qu'elle soit aussi facilement accessible.
      Pour ma part, mon gadget était (la carte vidéo de ce portable est morte depuis) un petit écran LCD de quelques caractères qui servait à afficher le titre du morceau sous Windows. J'avais arrêté mon étude (c'était exactement le même process que décrit ici) au moment où l'auteur passe au débugueur. J'aurais du écrire un journal, ça lui aurait fait gagné quelques heures...
      • [^] # Re: Avant de mettre les mains dans le cambuis

        Posté par  . Évalué à 5.

        J'étais tombé sur ce lien.
        Ce site est vieux, et ils ne donnent même pas directement les documents.

        Pour info, l'interface HCI est différente de la SCI.
        La SCI permet de consulter/configurer le BIOS (ie. des changements permanents, comme par exemple l'ordre de boot de la machine), la HCI étant là pour manipuler dynamiquement le matériel.

        J'ai oublié d'indiquer un lien, que j'ai trouvé hélas après avoir deviné le rôle du HCI...
        http://www.buzzard.me.uk/toshiba/downloads/hci.pdf
        C'est le document le plus complet à ce sujet à ma connaissance...
        • [^] # Re: Avant de mettre les mains dans le cambuis

          Posté par  . Évalué à 4.

          Le site de Toshiba est malheureusement actuel. Il a été mis à jour le 25 février 2010 d'après la première page. Il montre que l'effort de Toshiba en faveur d'un support de Linux est assez timide et c'est dommage.
          J'avais oublié la documentation de J. Buzzard. Elle montre pour sa part qu'une documentation complète de l'interface ne serait pas si difficile à fournir pour Toshiba; et que cela aurait évité à la communauté de passer des heures à essayer de deviner les maximum 6 valeurs de chaque fonction. Ces fonctions sont relativement universelles sur les portables Toshiba et je pense que certaines datent de plus d'une décennie.
          Pour compléter la doc, je crois que j'utilisais aussi cet utilitaire qui contient quelques fonctions supplémentaires:
          http://www.schwieters.org/toshset/
          • [^] # Re: Avant de mettre les mains dans le cambuis

            Posté par  . Évalué à 4.

            Sic, je ne me suis basé que sur la date des différents documents.

            À titre d'information, quand j'ai écrit le code dans le noyau, je me suis inspiré des autres pilotes de diodes... Et j'ai vu que le pilote dell_led est écrit par des employés de Dell directement...
  • # Chapeau!

    Posté par  . Évalué à 10.

    Merci pour ce cours.
    Et chapeau pour la méthode.
  • # Bravo

    Posté par  . Évalué à 10.

    Merci pour ce journal instructif. C'était très intéressant, et ça donne une bonne idée du boulot que demande le reverse ingineering matériel à ceux qui n'en ont jamais fait.

    Quand à
    Je vous prie par avance de m'excuser pour mes méthodes, mes explications ou encore les outils potentiellement non libres que j'ai pu utiliser
    Tu es sérieux ? Déjà que tu passes du temps pour implémenter le support d'un matériel qui n'est pas à toi, que tu prévoies de soumettre un patch au noyau pour que ça profite à tout le monde, et qu'en prime tu fais un journal pour décrire ta démarche et éventuellement en inciter d'autres à faire de même ? Tout ça pour le bien du support matériel de notre système préféré ?

    Et tu prends la peine de prévoir que quelqu'un pourrait oser te critiquer sur la forme, ou bien sur le fait que tu as dû utiliser des outils non-libre… J'ai pas tes compétences dans ce domaine, mais j'imagine que tu as utilisé IDA parce que tu n'avais pas d'outils libres équivalents à ta disposition. Je vois pas en quoi on pourrait t'en faire le reproche. Même sur linuxfr y'a des limites au troll :-þ
    • [^] # Re: Bravo

      Posté par  . Évalué à -8.

      Richard Stallman serait d'accord avec moi pour dire qu'utiliser des outils privateurs est la pire chose que tu puisses faire, tu as laissé une partie de tes libertés aux mains de Microsoft (pour Windows), de Toshiba (pour le driver privateur) et de Hex-Rays (pour IDA).

      De plus ta connaissance a acheté un ordinateur dont les spécifications ne sont pas libres.

      Si ce que tu veux faire n'existe pas avec des outils libres, alors ne le fait pas, ou fabrique cet outils !

      Au lieu de régler ces petits problèmes inutiles, tu aurais pu contribuer aux projets GNU comme Hurd.

      PS : Bravo pour le driver.

      « En fait, le monde du libre, c’est souvent un peu comme le parti socialiste en France » Troll

      • [^] # Re: Bravo

        Posté par  . Évalué à 2.

        PPS : C'était ironique (j'aurais du rajouter un "PS : Ou pas").

        « En fait, le monde du libre, c’est souvent un peu comme le parti socialiste en France » Troll

        • [^] # Re: Bravo

          Posté par  . Évalué à 4.

          Yep, dommage pour ton commentaire...
          Au passage, petite note pour ceux qui veulent essayer pareil : tentez aussi un coup avec la version shareware d'IDA 5.7, elle est bien plus performante et plus pratique à utiliser... (dommage que ce logiciel coûte si cher)
  • # Bravo, IDA et passage noyau

    Posté par  (site web personnel, Mastodon) . Évalué à 5.

    Bravo pour le boulot et pour le journal.
    Et chapeau pour passer du temps sur un gadget que tu n'utilises même pas toi même (même si c'est excitant :) ).

    IDA est vraiment le roi es désassembleurs, il y avait un espoir de début de clone libre il y a quelques années, mais largement à l'abandon depuis: http://lida.sourceforge.net/ .

    Est-ce que tu peux nous tenir au courant pour le passage du code dans le noyau (méthode, travail d'adaptation, temps nécessaire) ? J'avais fait un petit pilote pour une manette de jeux il y a quelques années (Saitek P 2500), et naïf que j'étais, j'avais juste publié ça sur mon site. Il s'est retrouvé très rapidement obsolète avec les changement de l'API noyau, et je n'avais plus le temps de faire les mise à jour ou même de l'adapter pour tenter une inclusion noyau. Un mec a repris ce pilote il y a quelques mois, je ne sais pas s'il le tient à jour... Tout ça pour dire que si un pilote n'est pas inclus dans le noyau, ça demande trop de boulot pour un particulier de maintenir le code à jour avec l'API kernel.
    • [^] # Re: Bravo, IDA et passage noyau

      Posté par  . Évalué à 2.

      Il y a quelques années, le projet boomerang était intéressant : http://boomerang.sourceforge.net/ (mort lui aussi)

      Sinon dans mon cas l'inclusion au noyau devrait être assez simple puisqu'il s'agit d'une "extension" d'un pilote existant... Après s'il faut séparer le pilote dans un nouveau pilote ou un truc comme ça, ça ne devrait pas poser de problèmes non plus...
  • # Vive l'ACPI .. et WMI !

    Posté par  (site web personnel) . Évalué à 10.

    Depuis peu, ce genre de chose est encore plus chiant à faire, parce qu'il le foutent dans WMI (dans l'ACPI).

    Un petit article que j'ai écrit pour expliquer comment faire dans ces cas là:
    http://lwn.net/Articles/391230/ .

    A noter, que parfois il est plus facile d'essayer de lire la DSDT plutôt que jouer avec IDA ou Ollydbg. Mais parfois ça ne suffit pas :/.

    En tout cas, bravo pour l'article en français, je retrouve (à IDA et Ollydbg près) la méthode utilisée pour asus-laptop et eeepc-laptop :). D'ailleurs, il ne faut jamais hésiter à contacter le constructeur, si possible en passant directement par un type qui bosse là bas (git log | grep toshiba) plutôt que par le formulaire de contact sur le site web. J'ai reçu 2 Eeepc de cette façcon !
    • [^] # Re: Vive l'ACPI .. et WMI !

      Posté par  . Évalué à 4.

      J'avais également lu ton article, mais Toshiba n'utilise pas du tout WMI apparemment (en tout cas je n'ai pas trouvé de WMI dans la DSDT).
      À la limite j'aurais préféré du WM, ça aurait pu être plus simple je pense...
      Dans le cas de Toshiba, vu l'horreur qu'est la fonction GHCI, la DSDT ne sert pas à grand chose je pense. Ou alors c'est dans une partie vraiment compliquée à comprendre, mais je doute que l'on puisse trouver les valeurs magiques sans le pilote windows.

      Sinon j'ai envoyé un mail à un type contribuant au noyau chez toshiba, merci pour l'idée de vérifier le git log (j'ai fait qu'un grep sur le code du noyau...)
      On verra le résultat.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.