Open Source im professionellen Einsatz
Linux-Magazin 05/2009
© Alexandra-Bucurescu, Pixelio.de

© Alexandra-Bucurescu, Pixelio.de

Perl-Skript steuert USB-Spielzeugkanone

Statt mit Zinnsoldaten

Auch wenn ein USB-Spielzeug - etwa ein Styropor-Raketenwerfer - nur mit einer Windows-CD daherkommt, lässt es sich dennoch mit etwas Reverse Engineering unter Linux betreiben. Mit »libusb« sogar ohne Treiber, vom Userspace aus und mit Perl gesteuert.

910

Ärger im Büro? Der muss nicht gleich in einer Schlacht eskalieren wie in dem Video "The Great Office War" [2] des Spielzeugherstellers Hasbro. Auch ohne Kriegserklärung und Aufmarschplan lohnt sich die Anschaffung des USB-gesteuerten Raketenwerfers Rocket Baby (Abbildung 1) der chinesischen Firma Cheeky Dream für etwa 20 Euro. Dient er doch nicht nur der Erheiterung der Kollegen, sondern bietet auch Gelegenheit, das recht komplexe USB-Subsystem des Linux-Kernels zu studieren [5].

Abbildung 1: Der USB-Raketenwerfer Rocket Baby von Cheeky Dream.

In der Verpackung des Spielzeugs findet sich allerdings nur eine CD für Windows XP, keine Spur von einem Linux-Treiber. Dies spornte einige spielverliebte Entwickler offensichtlich dazu an, das verwendete USB-Protokoll unter Windows mit USB-Schnüffelwerkzeugen wie USBsniff zu ergründen und mittels Reverse Engineering Schnittstellen für Sprachen wie Python oder für andere Betriebssysteme zu basteln [3].

Ubuntu erkennt das Spielzeug automatisch nach dem Anstöpseln. Die Nachrichten des Kernels sind in der Logdatei »/var/log/messages« nachzulesen (Abbildung 2). Sie zeigen an, dass die Spielzeug-Stalinorgel nun am UHCI-Controller des Intel-PC hängt.

Abbildung 2: Nach dem Einstöpseln des Spielzeug-Raketenwerfers erkennt der Kernel das Device und weist ihm einen USB-Eintrag zu.

Das USB-Subsystem des Kernels hat den Raketenwerfer laut Logfile unter »usb 5-1« aufgenommen. Details zeigt der Sys-FS-Baum unter »/sys/bus/usb/devices/5-1«. Das USB-Filesystem »usbfs« projiziert hier Kernel-interne USB-Daten in den Userspace. Abbildung 3 zeigt, dass die Hersteller-ID (»idVendor«) des vom Kernel erkannten Kriegsspielzeugs 0x0a81 ist und die Produkt-ID (»idProduct«) 0x0701. Der Kernel nimmt eingesteckte USB-Geräte unter zufälligen USB-Nummern auf, statt »usb 5-1« könnte es nächstes Mal »usb 3-1« sein.

Abbildung 3: Im »/sys«-Baum zeigt Linux Details des mittels Hotplug eingehängten Geräts an.

Doch hängt nur ein Gerät mit den gerade ermittelten Werten für »idVendor« und »idProduct« am PC, kann ein Programm dessen Adresse zuverlässig und relativ zügig ermitteln, indem es den ganzen USB-Baum durchstöbert, bis es die gesuchte Kombination findet.

Nicht nur Chefsache

Um nun Kontakt mit dem USB-Gerät aufzunehmen, verwendet Linux normalerweise Devicetreiber im Kernel. Diese sind allerdings schwer zu schreiben, da der kleinste Pointerfehler das gesamte Linux-System von der Klippe schubst und einen Reboot erforderlich macht.

Außerdem muss der Anwender Devicetreiber für jeden Kernel neu kompilieren. Gerade dort ändern sich Datenstrukturen zudem unheimlich schnell, sodass es durchaus passieren kann, dass der Sourcecode eines mühevoll für den Kernel 2.6.22 geschriebenen Treibers mit Version 2.6.24 nicht mehr funktioniert.

Benötigt man allerdings keinen hohen Datendurchsatz oder Antworten in Echtzeit, muss die Steuerungslogik nicht im Kernel laufen. Bietet der Kernel zudem eine Schnittstelle wie »usbfs« an, um mit USB-Geräten auf Hardware-Ebene zu kommunizieren, ist es möglich, den gesamten Treiber im Userspace zu implementieren. Das Open-Source-Projekt »libusb« [6] stellt eine bequeme Library für C-Programme zur Verfügung, das Perl-Modul Device::USB vom CPAN wickelt Perl-Funktionen drumherum.

Steuerung mit einem Byte

Listing 2 zeigt, wie sich der Geschützturm des Raketenwerfers mit ein paar Zeilen Perl-Code um etwa einen Zentimeter nach oben schwenken lässt. Zunächst sucht die Methode »find_device()« des Moduls Device::USB nach einem Gerät mit den vorher ermittelten Werten für »idVendor« und »idProduct« im USB-Baum. Die Methode »open()« nimmt im Erfolgsfall dann Verbindung mit dem gefundenen Device auf.

Das USB-Subsystem des Kernels unterstützt vier verschiedene Kommunikationsmodi für USB-Controller: Control-Transfers für Kurznachrichten, Bulk-Transfers für größere Datenmengen, Interrupt-Transfers für zeitkritische Daten und Isosynchronous-Transfers für Echtzeitdaten.

Durch Reverse Engineering fanden Entwickler heraus, dass der Raketenwerfer zur Bewegung des Kanzelturms und zum Abfeuern der Styroporraketen Control-Messages mit 1 Byte Länge erwartet. Die Tabelle 1 zeigt die Codes für die verschiedenen Aktionen. Dabei setzt ein Code den Werfer so lange in Bewegung, bis ein weiterer entweder die Richtung ändert oder ein Stopp-Befehl die Bewegung abbricht.

Tabelle 1:
Steuercodes

 

Aktion

Code

down

0x01

up

0x02

left

0x04

right

0x08

fire

0x10

stop

0x20

start

0x40

Die an die Methode »control_msg()« in den Zeilen 12 und 19 übergebenen Hex-Werte legen fest, wie die USB-Schnittstelle das Kontrollbyte (Listing 1) an den Controller weiterreicht: »0x21« steht für den Request-Typ, »0x09« für »USB_REQ_SET_CONFIGURATION«, »0x02« für »USB_RECIP_ENDPOINT« und der Wert »0« für einen nicht benutzten Index. Es folgt der mit der Perl-Funktion »chr()« ermittelte Byte-Wert des angegebenen Integer-Werts »0x02« zur Steuerung des Werfers nach oben.

Die letzten beiden Parameter geben mit »1« die Länge des Strings an (im vorliegenden Fall genau 1 Byte) und die Wartezeit auf eine Antwort in Millisekunden (»1000«), bevor das Programm einen Fehler auslöst.

Danach genehmigt sich das Testprogramm mit Hilfe des Moduls Time::HiRes vom CPAN und dessen Funktion »usleep()« ein kurzes Schläfchen von einer Zehntelsekunde (100000 Mikrosekunden) und setzt anschließend das Kontrollbyte »0x20« ab, was der Empfänger als Stopp-Kommando interpretiert und den Motor des Geschützturms wieder zur Ruhe bringt.

Die zwei Aufrufe von »control_msg()« im Listing 2 haben den Geschützturm also insgesamt eine Zehntelsekunde lang nach oben gefahren. Falls dieser nicht eh schon am oberen Ende angekommen war, hat der Motor kurz aufgeheult und die Styroporraketen haben sich um etwa 20 Grad nach oben gedreht.

Beim Feuern einer Rakete ist zu beachten, dass der Geschützmotor ungefähr zwei Sekunden lang pumpen muss, um intern die nötige Spannung zum Abfeuern der Styroporgranate aufzubauen. Damit das Programm weiß, wann es den Motor nicht mehr benötigt, weil die Rakete in der Luft ist, muss es lesend auf die USB-Schnittstelle zugreifen und Daten vom Controller des Geschützes einholen.

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Prozessor-Schwinger

    Die meisten Programme zur Anzeige der CPU-Auslastung wie Top und Xosview bedienen sich aus dem »/proc«-Dateisystem des Linux-Kernels. Unter bestimmten Umständen liefert diese Schnittstelle jedoch unkorrekte Werte. Das beschriebene Patch behebt das Problem.

  • Perl-Snapshot

    Ob Lampen per Fernsteuerung übers Internet angehen oder eine Pumpe die Topfpflanzen wässert: Home Automation erobert die Heime der Bastler. Die Z-Wave-Technologie bietet Geräte, die zuverlässig steuern und regeln – mit Hilfe von Perl.

  • Der Mörder ist nimmer der Gärtner

    Damit die Balkonpflanzen auch im Urlaub nicht vertrocknen, stellt dieser Snapshot ein Bewässerungssystem vor, bei dem Linux zweimal täglich die Schleusen öffnet.

  • Heimschaltwarte

    Der Billig-Router hat sich aufgehängt, damit ist aus der Ferne auch kein Reset via Netzwerk mehr möglich. Einen Weg aus dem Dilemma gibt es dennoch: Ein X10-Modul, gesteuert von einem Web-GUI mit Ajax-Interface, betätigt den Hauptschalter.

  • Aus Fehlern klug

    Normalerweise bekämpfen Entwickler Programmfehler. Forscher aus Saarbrücken bauen jedoch sogar absichtlich Fehler in Quelltexte ein. So schätzen sie ab, wie gut Testumgebungen arbeiten.

comments powered by Disqus

Ausgabe 06/2017

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.