Linux Embedded

Le blog des technologies libres et embarquées

How-to: Android Mass Storage USB Gadget

Ce How-to a pour but de décrire la procédure permettant d'utiliser le module Android USB Gadget de la version Android du noyau Linux.

Nous allons utiliser une carte SD sur une carte de développement A13 Micro d'Olimex et la faire apparaître comme un périphérique USB sur un PC de développement.

L'idée est de reproduire ce que fait Android lorsqu'on branche un téléphone sur un PC. La carte SD du téléphone apparaît comme un disque dur externe sur le PC.

Cette procédure devrait être reproductible sur toute carte disposant d'un port USB On-The-Go et n'utilise que les modifications kernel d'Android, le reste du système est un Linux embarqué standard.

Compiler un noyau avec les Android Gadget

Dans notre cas nous utilisons un noyau spécifique pour les processeurs Allwinner A13, le noyau du dépôt linux-sunxi. Ce noyau dispose de base des Android USB Gadget, tout autre noyau Androidisé fera néanmoins l'affaire. De plus, nous utilisons ici la configuration propre à la carte A13 Micro d'Olimex, a13om_defconfig (disponible ici) :

git clone https://github.com/linux-sunxi/linux-sunxi.git
cd linux-sunxi/
make a13om_defconfig

Dans le menu de configuration du noyau (make menuconfig à la racine des sources du noyau) il faut s'assurer que les options

Device Drivers → USB support → USB Mass Storage Support

et

Device Drivers → HID Devices

sont activées.

Ensuite assurons nous que le driver USB Gadget Support est bien Android Gadget en allant dans le menu correspondant :

Device Drivers → USB support → USB Gadget Support → USB Gadget Drivers → Android Gadget

Lançons maintenant la compilation de notre nouveau noyau, puis démarrons notre carte avec ce dernier.

Partager la partition en Mass Storage

Le protocole mass storage suppose que le périphérique monté (la clé USB) ne peut pas se modifier seul. En d'autre termes, il ne faut pas que la carte de développement puisse modifier le contenu vu par le PC pendant que celui-ci s'en sert. Linux permet donc d'exporter des partitions entières (ou des images à la façon des loopback) mais pas des répertoires.

Tout d'abord assurons nous que notre partition n'est pas montée. Un simple appel à mount nous permet de le vérifier.

Il faut alors paramétrer le noyau pour qu'il soit considéré comme mass storage quand il est branché au PC.

echo mass_storage > /sys/class/android_usb/android0/functions

Ensuite, nous devons activer le port USB :

echo 1 > /sys/class/android_usb/android0/enable

Enfin, il ne nous reste plus qu'à indiquer au kernel quelle partition doit être partagée :

echo /dev/mmcblk0p2 > /sys/class/android_usb/f_mass_storage/lun/file

Branchons la carte depuis le port USB OTG à notre ordinateur hôte et, magie ! Notre partition apparaît sur l'ordinateur hôte comme un périphérique mass storage. A partir du moment ou nous démontons le système de fichier sur l'hôte, l'entrée file du lun (logical unit) que nous avons utilisée devient vide et par la même occasion, le partage désactivé.

L'Android Gadget nous permet de monter plusieurs lun à la fois, par défaut 3. Il est également possible de partager un « backing file », image de partition sur le système de fichier. Je vous laisse explorer toutes ces options dans le répertoire /sys/class/android_usb/f_mass_storage.

Notez que les systèmes Windows ne savent monter que les partitions en FAT32 (W95 FAT32, code 0xb dans fdisk). Pensez-y au moment de créer la partition en question...

Automatiser le processus avec Hotplug

Maintenant que nous avons vu comment partager notre partition, il s'agit d'automatiser le processus.

Sur une distribution classique, le programme Udev est chargé de réagir à la détection de nouveau hardware. Dans un contexte embarqué, nous allons directement utiliser hotplug pour automatiser le processus.

Quand un événement hardware se produit (dans notre cas, la connexion d'un PC en tant qu'hôte USB) le noyau vérifie le contenu du fichier /proc/sys/kernel/hotplug et si il n'est pas vide, appelle l’exécutable correspondant avec des variables d'environnement décrivant l’évènement. Nous allons donc écrire un petit script shell qui traitera ça pour nous.

Tout d'abord, indiquons au kernel où trouver notre script :

echo "/sbin/hotplug" > /proc/sys/kernel/hotplug

Ensuite, voici le contenu du script /sbin/hotplug :

#!/bin/sh
if [ "${SUBSYSTEM}" = "android_usb" ]; then
    # Connexion du câble USB
    if [ "${USB_STATE}" = "CONNECTED" ]; then
        # On libère les ressources utilisant notre partition
        #ex : killall myappusingthepartition
        # On démonte la partition
        umount /my/mount/point/
        # Activation du gadget
        echo /dev/mmcblk0p2 >
        /sys/class/android_usb/f_mass_storage/lun/file
    fi
    # Déconnexion du câble USB
    if [ "${USB_STATE}" = "DISCONNECTED" ]; then
        # On remonte la partition
        mount /my/mount/point/
        # Puis ce qui s'avère nécessaire suivant le cas, par exemple
        # lancer l'application que nous avions tué et qui
        # utilisais la partition.
    fi
fi
exit 1

Nous utilisons ici les variables $SUBSYSTEM et $USB_STATE pour ne réagir que sur l'évènement qui nous intéresse.

Intégration dans un démon hotplug complet

Cette petite automatisation via hotplug est suffisante pour un système embarqué dont tous les devices sont connus et qui n'a que très peu de hotplug à gérer.

Lorsqu'il y a un grand nombre de services devant réagir à des évènements hotplug, l'approche par script atteint rapidement ses limites et il faut alors utiliser des gestionnaires d’évènements plus complets.

Le programme utilisé dans les distributions Linux classiques est Udev. Il surveille le pseudo-filesystem /sys et réagit à l’apparition de devices (c'est à dire de fichiers dans ce répertoire) en chargeant des firmwares, des modules kernel et en envoyant des messages sur le bus système d-bus.

Dans le monde de Linux embarqué, busybox fournit une version allégée de Udev appelée Mdev. Contrairement à Udev, Mdev utilise le mécanisme de hotplug que nous avons décrit ci-dessus et est assez facile à mettre en place. Mdev est capable d'appeler un script sur les évènements de hotplug et peut créer tous les devices nécessaires. Vous trouverez la documentation de Mdev ici.

Et après ?

Nous avons utilisé un noyau Android car la gestion des gadgets USB (et en particulier le hotplug) est plus aboutie. Si l'on souhaite faire la même chose avec un noyau mainline, on pourra utiliser le Mass Storage Gadget. A noter que le Mass Storage Gadget est le remplaçant du File-backed Storage Gadget qui est maintenant obsolète et ne sera plus disponible dans la version 3.8 du noyau à venir. D'après sa documentation, le fonctionnement du Mass Storage Gadget semble assez proche du fonctionnement de son homologue Android. Toutefois la mise en œuvre de celui-ci sort du cadre de cet article.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.