Kernel Updates


In zwei älteren Artikeln in diesem Blog, einen zum Thema Zweiter SPI Chipselect für den Orange-Pi Zero, einen über das Hitachi HD44780 Text Anzeigemodul unter Linux habe ich darüber geschrieben, die Änderungen in den Linux Kernel zu bekommen.

Im Fall des Orange-Pi war das ein Bug-Fix des SPI-Treibers auf der Allwinner sun6i Architektur. Danke an diesem Punkt an den Autor Mirko, den Autor des Patches, der es mir erlaubt hat, den Patch in seinem Namen zu submitten. Der Patch ist im Linux Kernel kurz vor dem 5.13. Release. Weil er als Bug-Fix markiert war, wurde er auf einige Stable Release-Serien zurückportiert, bis zur 4.4 Stable Serie.

Im Fall des Hitachi Anzeigemodul war es ein Update der Dokumentation, die es Personen gestatten sollte, rauszufinden, wie man ein solches Display anschließt und die nötige Device-Tree Magie verwendet ohne Software ändern zu müssen. Dieser Patch wurde rechtzeitig für das 5.15 Release im Kernel akzeptiert.

Modding von einem PC Netzteil


WARNUNG: Im Folgenden werden ich beschreiben, wie man ein Netzteil ändert. Netzteile können hohe Spannungen führen – auch wenn der Stecker gezogen wurde weil in Kondensatoren noch Ladung gespeichert sein kann. In einigen Ländern darf nur eine geprüfte Fachkraft oder eine Person unter Aufsicht einer geprüften Fachkraft Geräte mit Netzspannung ändern. Du solltest also wissen was Du tust und dazu authorisiert sein.

PC Netzteile sind günstig und gut verfügbar aber sie geben die angegebenen Spannungen nur dann aus wenn der gezogene Strom bei jeder der unterschiedlichen Spannungen innerhalb der Minima und Maxima des Netzteils sind. Wenn man hohe Ströme (aber innerhalb der Spezifikation des Netzteils) nur bei einer einzigen Spannung zieht, sinkt die Spannung unter die in der Spezifikation angegebenen Mindestspannung.

Ein Beispiel ist das Heizbett meines 3D-Druckers: Er verwendete früher ein PC-Netzteil für das Heizbett. Das Heizbett zieht 12A und verwendet nur die 12V Stromversorgung. Das Resultat ist eine Spannung von etwa 10V wenn das Heizbett heizt.

Ein anderer Anwendungsfall ist die Versorgung von ARM-basierten Single-Board Computern (z.B. Raspberry-Pi or Orange-Pi) aus einer 5V Leitung. Wenn man dafür ein PC-Netzteil verwendet (ohne Strom aus der 12V Versorgung zu benötigen) kann die nominelle 5V Spannung unter den Bereich sinken wo der Single-Board Computer noch zuverlässig funktioniert.

Ein drittes Beispiel ist die Stromversorgunge von Funkgeräten eines Funkamateurs: Diese Geräte liefern üblicherweise nicht die volle Sendeleistung wenn sie mit 12V oder weniger versorgt werden, sie brauchen typischerweise 13.6-13.8V für volle Sendeleistung.

In allen diesen Fällen wäre eine Modifikation des Netzteils so dass die ausgewählte Spannung stabil bleibt oder wo es sogar möglich ist, die ausgewählte Spannung leicht zu verändern (von 12V auf 13.8V für Funkamateure), sehr nett. Wie können wir das erreichen?

Viele PC-Netzteile haben als Spannungsregler die integrierten Schaltkreise TL494 oder KA7500 (diese sind Pin-Kompatibel) verbaut. Wenn unser Netzteil einen davon hat kann man es üblicherweise modifizieren um eines der oben genannten Zwecke zu erreichen.

Ein Schaltungsdetail solcher Netzgeräte ist ein Rückkopplungszweig der die 5V und 12V Spannungen zum Regler-IC führt. Auf Dan's PC power supply page findet man viele Schaltungen von PC-Netzteilen. Nehmen wir das zweite in der Liste von TL494 und KA7500 basierten Geräten. Es hat einige parallele Widerstände von pin 1 des TL494 nach Masse und einen 27kΩ-Widerstand von 12V an pin 1 sowie einen 4.7kΩ-Widerstand von 5V zum selben Pin.

Wir können die Spannungsregelung verändern, indem wir die Rückkopplungs Widerstände ändern. Es sei angemerkt, dass üblicherweise jedes Netzteil unterschiedliche Widerstände nach Masse und unterschiedliche Rückkopplungs Widerstände von 5V und 12V hat. Als Beispiel ändern wir nun die Rückkopplungs-Widerstände so dass das Netzteil stabile 5V liefert ohne dass wir uns um die 12V Spannung kümmern.

WARNUNG: Wenn man ein Netzteil für stabile 5V oder 12V Versorgung modifiziert hat, sind die anderen Spannungen nicht mehr stabil und können zu hoch für die Nutzung in einem PC werden. Man sollte niemals ein modifiziertes Netzteil in einem PC einsetzen.

Der erste Schritt ist mal, die beiden Rückkopplungs Widerstände im Netzteil zu finden. Einmal gefunden verifizieren wir dass die Seite die nicht am Pin 1 des Reglers liegt mit 0Ω mit dem 5V bzw. 12V Ausgang verbunden ist. Dann löten wir beide Widerstände aus. Bevor wir jetzt den neuen Widerstand zwischen Pin 1 und 5V berechnen, messen wir den Widerstand von Pin 1 nach Masse: Weil wir ja nun die beiden Widerstände nach 12V und 5V ausgelötet haben, kann der Widerstand nach Masse störungsfrei gemessen werden. Es ist an dieser Stelle sinnvoll, sicher zu sein, dass der gemessene Widerstandswert und der berechnete aus den drei parallel geschalteten Widerständen übereinstimmen: Im Beispiel-Schaltplan haben wir 100kΩ, 390kΩ und 10kΩ parallelgeschaltet, die gemessen den Wert haben:

\begin{equation*} \frac{1}{\frac{1}{100000}+\frac{1}{390000}+\frac{1}{10000}} = 8883.83 \end{equation*}

Als ich neulich ein Netzteil umgebaut habe, waren die drei Widerständen nach Masse 470kΩ, 100kΩ und was ich dachte dass 8.9kΩ wären. Ich hatte die Farben des letzten Widerstands als Grau-Weiss-Schwarz-Braun-Braun interpretiert. Als ich die drei parallelen Widerstände gemessen habe war das Ergebnis 4.61kΩ statt der erwarteten 8033Ω. Es stellte sich heraus (nach genauem Ansehen des Widerstands im Sonnenlicht) dass das was ich als Grau eingestuft hatte eher gelblich war. Der Widerstand hatte also in Wirklichkeit 4.9kΩ und der berechnete Gesamtwiderstand war 4625Ω.

Um ein Netzteil nur die 5V regeln zu lassen verbinden wir einen neuen Widerstand von Pin 1 des Regler-ICs mit 5V und lassen die 12V Rückkopplung unbeschaltet. Aber wie wählen wir den neuen Widerstand? Um den zu berechnen müssen wir ein Gleichungssystem lösen. Wir wissen vom Ohmschen Gesetz die Verhältnisse von Strömen, Spannungen und Widerständen. Von Kirchhoff wissen wir, dass die Summe des Stroms durch den 5V Widerstand und durch den 12V Widerstand gleich dem Strom durch die Massewiderstände sein muss. Diese Verhältnisse sind in einem Maxima Spreadsheet zusammengefasst.

Wir berechnen die Referenzspannung an Pin 1 wenn sowohl der Widerstand von 5V als auch der von 12V angeschlossen sind. Dann wählen wir einen neuen Widerstand der nur an 5V angeschlossen ist so, dass sich die selbe Referenzspannung ergibt.

Zum Beispiel in obigem Schaltplan bekommen wir ziemlich genau 1800Ω für R_new, den neuen Widerstand. Für das Netzteil, das ich vor kurzem modifiziert habe, habe ich schon die Widerstände nach Masse angegeben. Der Widerstand nach 12V war 39kΩ und der Widerstand nach 5V war 9.1kΩ. Der resultierende neue Widerstand R_new für dieses Netzteil war 4865Ω was durch Serienschaltung von einem 4.7kΩ und einem 150Ω Widerstand erreicht wurde, damit ist die 5V Ausgangsspannung genau genug erreicht. Diese Berechnung findet sich als zweites Beispiel im Spreadsheet.

Noch ein Wort zur Vorsicht: Wenn man ein Netzteil für stabile 5V Versorgung modifiziert oder für eine höhere Spannung am 12V Ausgang sollte man sich bewusst sein, dass die meisten PC-Netzteile im 12V-Zweig Kondensatoren haben die für 16V spezifiziert sind. Wenn man dann hohe Ströme bei 5V zieht, kann die Spannung am 12V Ausgang zu hoch für diese Kondensatoren werden. Um auf der sicheren Seite zu sein, sollte man diese Kondensatoren durch 25V-Typen ersetzen.

Um die 12V Stromversorgung zu fixieren wird im Prinzip die selbe Prozedur wie für 5V verwendet, nur dass wir den 12V Rückkopplungs-Widerstand anschließen und den 5V Widerstand unbeschaltet lassen. Wie man den Widerstand für 12V berechnet ist Hausaufgabe.

Du teilst Deine Downloads mit deinem Antivirus Provider


Ich habe vor kurzem einem Kunden einen Link zu einem Firewall Image (für den Turris MOX Router mit einer Variante von OpenWRT) auf meinem eigenen Webserver geschickt. Das Image enthielt Key-Material für eine OpenVPN Verbindung. Die Datei war in einem versteckten Verzeichnis auf meinem Projekt-Webserver. Ich habe genau geschaut, ob es noch andere Downloads gab, zusätzlich zu dem meines Kunden.

Ich bin mir bewusst, dass es nicht die beste Sicherheitspraxis ist, Key-Material über einen unsicheren Kanal zu übertragen. Und am Ende musste ich das VPN Key-Material das im Image war revoken und meinem Kunden über einen sicheren Kanal einen neuen Schlüssel zukommen lassen.

Nun, ich hatte schon erwähnt, dass ich die Downloads beobachtet hatte. Etwa eine Stunde (!) nachdem mein Kunde das Image geholt hatte (um genau zu sein um 21/Mar/2021:17:35:50) wurde von einer anderen IP darauf zugegriffen:

77.74.177.4 - - [21/Mar/2021:18:43:51 +0100] "GET /turris-image-XXXXXXX.zip HTTP/1.1" 200 77244886 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36"

Wenn man diese IP über whois sucht findet man:

> whois 77.74.177.4
% This is the RIPE Database query service.
...
netname:        KL-NET3
descr:          Kaspersky Lab Internet
country:        RU
...
source:         RIPE
organisation:   ORG-KL28-RIPE
org-name:       Kaspersky Lab AO
country:        RU

Mein Kunde hat die Kaspersky Antivirus-Software installiert. Der Link wurde also vermutlich an Kaspersky über diese installierte Software weitergegeben. Es kann ja einerseits sein, dass der Grund, warum Kaspersky das Image heruntergeladen hat ein harmloser war, möglicherweise um ein Service zu erbringen (wie z.B. der Scan auf Viren) aber in meinem Fall hat das bedeutet dass nicht öffentliche Informationen Dritten bekannt wurden. Auf der anderen Seite könnte es sein, dass diese Informationen auch für andere Zwecke verwendet werden – wir wissen es nicht.

Also beachten Sie dass Ihr Antivirus-Produkt Ihnen beim Download im Web über die Schulter schaut.

Interaktion von libvirt und AppArmor


Ich unterrichte an der Fachhochschule Burgenland in Eisenstadt (Österreich). Dort hatten wir vor kurzem eine Laborübung (die zwar im Labor stattfand aber alle Studenten arbeiteten von zuhause auf den Laborrechnern aus Covid-Gründen) wo die Aufgabe war, mit libvirt eine laufende virtuelle Maschine auf einen anderen Server zu migrieren (wir verwenden die Kommandozeile und virsh).

Bei einer von mehreren Gruppen – alle mit identischen Debian Installationen – schlug die Migration mit einer Fehlermeldung fehl. Das Migrations-Kommando war:

virsh -c qemu+ssh://root@primary/system migrate --live --unsafe \
    debian-1 qemu+ssh://root@secondary/system

Für die Laborübung verwenden wir NFS weil ein besseres verteiltes Filesystem zu viel Zeit kosten würde, daher verwenden wir die --unsafe Option bei der Migration. Die folgende Fehlermeldung war das Resultat (die Meldung ist in mehrere Zeilen zerlegt, die Meldung ist normalerweise alles in einer Zeile):

error: internal error: Process exited prior to exec:
libvirt:  error : unable to set AppArmor profile
'libvirt-d22db7ca-50ca-43bd-b6da-1ccecf5a83e7' for '/usr/bin/kvm':
No such file or directory

Es stellte sich dann heraus, dass es diese Gruppe geschafft hatte, die /var Partition mit Log-Dateien zu füllen. Aber auch nach Aufräumen kam noch die gleiche Fehlermeldung. Das Gefühl sagte, dass hier von AppArmor und/oder libvirt dynamisch erzeugte Dateien nicht erzeugt werden konnten und dass dies der Grund für den Fehlschlag war. Ausserdem stellte sich heraus, dass einige AppArmor Dateien die auf der ersten Maschine korrekt installiert waren auf der zweiten Maschine fehlten.

Eine Neuinstallation von AppArmor und den zugehörigen Dateien mit apt-get mit der Option --reinstall funktionierte nicht, die fehlenden Konfigurationdateien in /etc/apparmor.d wurden nicht neu erzeugt. Daher war deinstallieren der Pakete mit dem purge Kommando (dieses entfernt auch die Konfigurationdateien) und dann Neuinstallation der Ausweg um die fehlenden AppArmor Dateien zu bekommen und so die Migration schlussendlich durchzuführen. Ich habe bis jetzt keine Ahnung, welche Dateien gefehlt haben.

Wenn man nach der obigen Fehlermeldung googled, findet man einen Debian Bug-Report wo eine der dynamisch erzeugten Dateien in /etc/apparmor.d/libvirt die Länge Null hatte. Dies war jedoch nicht das Problem in unserem Fall, zeigt aber dass AppArmor nicht besonders sorgfältig die Fehler prüft, z.B. wenn das Dateisystem voll ist. Es gibt also vermutlich noch andere Dateien die dynamisch erzeugt werden die in unserem Fall das Problem waren.

Die folgende Abfolge von Deinstallations und Re-Installations Kommandos hat das Problem in unserem Fall behoben. Es sei darauf hingewiesen dass das Entfernen der beiden Dateien wie im Debian Bug-Report das Problem in unserem Fall nicht gelöst hat:

dpkg --purge apparmor-utils apparmor-profiles
dpkg --purge apparmor
rm -rf /var/cache/apparmor
apt-get install apparmor apparmor-utils apparmor-profiles
dpkg --purge libvirt-daemon-system
apt-get install libvirt-daemon-system
systemctl restart libvirtd.service
systemctl restart virtlogd.service
systemctl restart virtlogd.socket

Ich bin nicht sicher dass man alle Services wirklich neu starten muss, aber es gab ein Problem dass libvirt nicht auf den virtlog Socket verbinden konnte und das ließ sich durch ein Restart von virtlog.{service,socket} beheben.

Dynamisches DNS mit dem bind DNS server


Der bekannte DNS server bind erlaubt Konfigurationen die es ermöglichen, dass clients ihre DNS Einträge selbst verändern können. Weil einige öffentliche dynamische DNS Services zu einem Bezahlmodell gewechselt sind und weil ich sowieso meinen eigenen Mail- und Webserver bei einem Hoster betreibe, suchte ich nach einer Möglichkeit, meinen eigenen dynamischen DNS Server zu verwenden. Das liegt schon ein paar Jahre zurück, aber weil das von mir damals verwendete Howto vom Netz (oder zumindest aus meiner Google-Bubble) verschwunden ist, dokumentiere ich hier wie ich das gemacht habe.

Die Software läuft bei mir unter Debian Buster zur Zeit dieses Blog-Eintrags, die Details können sich also bei anderen Systemen ändern. In den folgenden Beispielen nenne ich die Subdomain für die dynamischen DNS Einträge dyn.example.com.

Die Konfigurations-Datei von bind braucht ein zusätzliches include um die Konfigurations-Datei für die dynamische Domain einzubinden. In meiner Konfiguration heisst diese Datei named.conf.handedited. In dieser Datei braucht es einen Entrag für jeden dynamischen DNS Client wie folgt:

zone "dyn.example.com" {
        type master;
        allow-transfer {none;};
        file "/etc/bind/slave/pri.dyn.example.com";
        update-policy {
            grant h1.dyn.example.com. name h1.dyn.example.com. A TXT;
            grant h2.dyn.example.com. name h2.dyn.example.com. A TXT;
            [...]
        };
};

In diesem Beispiel dürfen die Hosts h1 und h2 (und möglicherweise weitere) ihre eigenen DNS Einträge ändern. Ich erlaube hier dass sie den A und TXT Record ändern. Für IPv6 möchte man hier noch AAAA dazugeben.

Die Konfigurations-Datei /etc/bind/slave/pri.dyn.example.com enthält dann:

dyn.example.com         IN SOA  ns1.example.com. admin.example.com. (
                                2020080100 ; serial
                                120      ; refresh (2 minutes)
                                120      ; retry (2 minutes)
                                120      ; expire (2 minutes)
                                120      ; minimum (2 minutes)
                                )
                        NS      ns1.example.com.
h1                      A       127.0.0.1
                        KEY     512 3 10 (
                                AwEAAdEvnGmGO4ku+xms4w1c5RWh5BvugiZ4ty9tkIes
                                <more gibberish lines>
                                ); alg = RSASHA512 ; key id = <number>

Die Einträge in spitzen Klammern sind Kommentare und sollten durch die korrekten Werte in der Zielinstallation ersetzt werden. Die Einträge A und KEY werden von Hand für jeden neuen Host der seine eigene IP-Adresse setzen können soll eingetragen. Der KEY ist der öffentliche Schlüssel (public key) der weiter unten erzeugt wird. Nach meiner Erfahrung braucht es den A-Eintrag damit das ganze funktioniert. Ich setze hier eine localhost Adresse weil der Client diese später sowieso überschreiben wird. Es ist üblich die Admin Email-Adresse (wo @ durch einen Punkt ersetzt ist) im SOA-Eintrag an die Stelle zu setzen wo ich admin.example.com. eingetragen habe.

Um einen neuen Client Host anzulegen geht man wie folgt vor:

  • Erzeugen eines Schlüsselpaares (öffentlicher / privater Schlüssel, public/private key), bevorzugt passiert das beim Client und dieser sendet nur den öffentlichen Schlüssel an den DNS-Administrator um Sicherheitsprobleme bei der Schlüssel-Übermittlung zu vermeiden:

    dnssec-keygen -T key -a RSASHA512 -b 2048 -n HOST newhost.dyn.example.com
    
  • Das erzeugt einen privaten und einen öffentlichen Schlüssel. Zu beachten: Der Client braucht beide Schlüssel, sowohl den öffentlichen als auch den privaten, obwohl nur der private Schlüssel dem dynamischen DNS Client übergeben wird!

  • Das letzte Mal als ich einen neuen Schlüssel erzeugt habe, konnte das Kommando keine Schlüssel mit mehr als 2048 bits erzeugen, obwohl der verwendete Hash-Algorithmus SHA2 modern ist und eine große Bitlänge unterstützt.

  • Damit bind seine Datenbank auf eine Datei schreibt (und man diese editieren darf) muss man bind für diese Domain einfrieren:

    rndc freeze dyn.example.com
    
  • Jetzt darf man das Konfigurationsfile ändern und einen neuen Absatz für einen neuen Host eintragen. Nicht vergessen die serial Nummer zu erhöhen:

    $EDITOR /etc/bind/slave/pri.dyn.example.com
    
  • Dann nicht vergessen, die Domain wieder aufzutauen:

    rndc unfreeze dyn.example.com
    
  • Und schließlich nicht vergessen, dem neuen Host die nötigen Berechtigungen in named.conf.handedited zu geben.

  • Zuletzt muss man wohl dem bind ein reload geben:

    systemctl reload bind9.service
    

Auf der Client-Seite heisst das Programm um bind mitzuteilen dass sich die IP-Adresse des Client geändert hat nsupdate. Man findet diese Programm für viele Betriebssysteme.

Ich verwende ein simples Script am Client das die Änderung der dynamischen IP Adresse bemerkt und eine Adressänderung bei bind anstößt wenn sich die IP Adresse geändert hat. Nachdem wir unseren eigenen DNS-Server betreiben sind die Chancen groß, dass es dort auch einen eigenen Webserver gibt. Das folgende einfache Script erlaubt es dem Client, seine eigene IP-Adresse rauszufinden (Clients sind oft hinter einer NAT Firewall und wir wollen ja nicht wieder auf ein anderes öffentliches Service angewiesen sein, wenn wir gerade öffentliche dynamische DNS Services losgeworden sind, oder?):

#!/bin/sh
echo Content-Type: text/plain
echo ""
echo $REMOTE_ADDR

Dieses Script installiert man in ein cgi-bin Verzeichnis auf einem Webserver. Es liefert dem anfragenden Client seine eigene IP-Adresse (in Text Format) zurück. Das script heisst bei mir ip.cgi und ist dann unter der URL http://example.com/cgi-bin/ip.cgi in unserem Beispiel verfügbar. Die URL muss dann unten im Client-Script geändert werden.

Mein bind Script (einige Variablen müssen geändert werden) schaut aus wie folgt (bitte beachten: Das Script geht davon aus dass obiges IP-Adressen Script auf der Domain example.com läuft, das muss natürlich auch geändert werden):

#!/bin/sh
ZONE=dyn.example.com
DOMAIN=h1.dyn.example.com
DNSKEY=/etc/nsupdate/Kh1.dyn.example.com.+010+04711.private
NS="ns1.example.com"

registered=$(host $DOMAIN $NS 2> /dev/null | grep 'has address' | tail -n1 | cut -d' '  -f4)
current=$(wget -q -O- http://example.com/cgi-bin/ip.cgi)
[ -n "$current" \
-a "(" "$current" != "$registered" ")" \
] && {
 nsupdate -d -v -k $DNSKEY << EOF
server $NS
zone $ZONE
update delete $DOMAIN A
update add $DOMAIN 60 A $current
send
EOF
 logger -t dyndns -p daemon.notice "Updated dyndns: $registered -> $current"
} > /dev/null 2>&1

exit 0

Es sei nochmal darauf hingewiesen, dass der private Schlüssel (in obigem Beispiel in /etc/nsupdate/Kh1.dyn.example.com.+010+04711.private nicht ausreicht, das Programm nsupdate braucht auch den öffentlichen Schlüssel im selben Verzeichnis wie das des privaten Schlüssels.

Hitachi HD44780 Text Anzeigemodul unter Linux


/content/2021/display.jpg

Für ein Projekt verwende ich ein Text Anzeigmodul mit dem Hitachi HD44780 Chip. Diese Displays gibt es in verschiedenen Größen, üblich sind 2 Zeilen mit 16 Zeichen oder 4 Zeilen mit 20 Zeichen. Die letztere Variante wird auch unter dem Namen 2004a vertrieben. Diese Displays arbeiten mit 5V. Weil heutzutage die meisten Microcontroller und CPUs mit 3.3V oder weniger betrieben werden, wird so ein Display oft an einen I²C-Bus über eine I/O Erweiterung (ein sogenannter GPIO Extender) angeschlossen, die meisten davon mit einem PCF8574 Chip. Man kann die Displays und GPIO Extender separat oder zusammen kaufen, auflöten muss man das extender-Modul in jedem Fall selbst.

Das korrekte Vorgehen, so ein Display an einen 3.3V I²C Bus anzuschließen wäre über einen Pegelwandler. Es gibt aber einen Hardware-Hack wo man einfach die Pull-Up Widerstände auf dem PCF8574 Board entfernt (diese hängen direkt an +5V). Diese Änderung macht den GPIO Extender kompatibel mit 3.3V Installationen: Der im Datenblatt des PCF8574 angegebene minimale High-Pegel ist zwar 0.7 * VCC (was 3.5V bedeuten würde), in der Praxis funktioniert er aber einwandfrei mit 3.3V. Es sei darauf hingewiesen, dass die Nummern der Widerstände wie im Hardware-Hack angegeben (R5/R6) je nach Variante des PCF8574 Boards variieren. Ich habe die Nummern R8 und R9 gesehen sowie auch gar keine Beschriftung der Widerstände. Die Widerstände sind 4.7 kΩ, üblicherweise beschriftet mit 472 in der SMD Variante.

Dann habe ich untersucht, ob es einen Linux-Treiber für diese Displays gibt und habe einen in drivers/auxdisplay/hd44780.c im Kernel gefunden. Auf den ersten Blick schaut es so aus als ob dieser Treiber die Ansteuerung mit I²C über den PCF8574 I/O Extender nicht unterstützt. Also habe ich einen Treiber geschrieben und als Kernel-Patch eingereicht.

In der Diskussion (danke an Geert) hat sich dann herausgestellt, dass es einen Treiber für den PCF8574 im Kernel gibt (das hatte ich schon gesehen) und dass man den HD44780 Treiber mit der nötigen Device-Tree Magie so konfigurieren kann dass der PCF8574 Treiber für den I/O Extender verwendet wird. Die folgenden Device-Tree Beschwörungsformeln definieren ein Overlay das den PCF8574 mit seiner Standard I²C-Adress 0x27 konfiguriert und diesen dann für die I/Os des hd44780 Treibers verwendet:

// Note that on most boards another fragment must enable the I2C-1 Bus

/dts-v1/;
/plugin/;

/ {
        fragment@0 {
                target = <&i2c1>;
                __overlay__ {
                        #address-cells = <1>;
                        #size-cells = <0>;

                        pcf8574: pcf8574@27 {
                                compatible = "nxp,pcf8574";
                                reg = <0x27>;
                                gpio-controller;
                                #gpio-cells = <2>;
                        };

                };
        };

        fragment@1 {
                target-path = "/";
                __overlay__ {
                        hd44780 {
                                compatible = "hit,hd44780";
                                display-height-chars = <2>;
                                display-width-chars  = <16>;
                                data-gpios = <&pcf8574 4 0>,
                                             <&pcf8574 5 0>,
                                             <&pcf8574 6 0>,
                                             <&pcf8574 7 0>;
                                enable-gpios = <&pcf8574 2 0>;
                                rs-gpios = <&pcf8574 0 0>;
                                rw-gpios = <&pcf8574 1 0>;
                                backlight-gpios = <&pcf8574 3 0>;
                        };
                };
        };
};

Weil das nicht nur für mich nicht offensichtlich ist (in meiner Nachforschung habe ich mindestens zwei Linux-Treiber Implementierungen für die Kombination von HD44780 und PCF8574 ausserhalb des Linux-Kernels gefunden) habe ich einen Dokumentations-Patch eingereicht um das besser zu dokumentieren für Menschen die auch einen Linux-Treiber für diese Kombination suchen.

Es sei noch angemerkt, dass der Treiber für das Display Escape-Sequenzen verwendet um Spezialfunktionen des Displays (z.B. Ein/Ausschalten der Hintergrundbeleuchtung, löschen des Bildschirms, oder Benutzerdefinierte Bitmap-Zeichen) anzusteuern. Ich glaube diese sind nur im Source-Code in drivers/auxdisplay/charlcd.c dokumentiert.