Studienarbeit embedded Linux mit dem Mini2440

Versuche aus der Technischen Informatik

DVA Praktikum
Hochschule Augsburg
Fakultät für Informatik (Prof. Dr. Hubert Högl)
Technische Informatik (TI4)
Sommersemester 2011
2011-07-26 11:07

Michael Morscher, <michael.morscher@hs-augsburg.de>, #921468


Dieser Text steht unter der Creative Commons Lizenz "Namensnennung/Keine kommerizelle Nutzung".
In dieser Studienarbeit soll mit Hilfe des FriendlyARM mini2440 ARM Entwicklungsboards der Umgang mit embedded Linux Systemen erlernt werden.

Inhaltsverzeichnis

1   Einleitung

Embedded Systeme gewinnen in der heutigen Zeit immer mehr an Bedeutung. Inzwischen möchte man alles überwachen und alles fernsteuern, möglichst mit seinem bereits vorhandenen Smartphone. Auch dies zählt bereits zu den embedded Systemen der heutigen Zeit, das den Sprung in die Marktreife und Verbreitung geschafft hat. In den meisten Smartphones, z.B. von Apple oder HTC, werden bereits Prozessoren mit einem oder gar mehreren Kernen eigesetzt. Diese basieren auf der ARM Architektur und sind teilweise bereits bis zu 1 Ghz oder gar 1,5 Ghz schnell. Da aber genau bei solchen Typen von Systemen der Energieverbrauch, ebenso wie die Leistung, wichtig sind, muss hier aus Software Sicht ein Spagat zwischen Funktionsumfang und Notwendigkeit gefunden werden. Hier greift die Effizienz und der Verbreitungsgrad der weiten Open Source Community rund um Linux und dessen Kernel. Auf vielen Smartphones wird z.B. Android eingesetzt, was eine abgespeckte Linux Distribution von Google ist - Hier arbeiten aber dank Community nicht nur Entwickler von Google daran, sondern auch viele Programmier weltweit (vgl. z.B. Cyanogen Mod).
Die Einsatzzwecke für solche Systeme sind in der heutigen Zeit schier unendlich, was im Rahmen des Verbreitungsgrades deren Preise senkt. Das ist insbesondere Gut und von Nöten für Programmierer, da damit auch die Entwicklungsboards in bezahlbare Regionen vordringen.
In dieser Arbeit wurde z.B. das mini2440 von FriendlyARM eingesetzt, welches mit Touchscreen bereits für 85 Euro inklusive Versand besorgt werden konnte. Die Leistung des Boards sollte für die meisten Aufgaben wie kleine Webserver, Heimautomatisierung oder gar Android Entwicklung locker ausreichen.

2   Ziel der Arbeit

In dieser Arbeit soll der Umgang mit Linux auf einem kleinen embedded Testsystem geübt werden. Es sollen alle möglichen Arbeitsschritte für das Aufsetzen dieses System durchlaufen und ausgeführt werden. Darauf aufbauend sollen kleine Skripte und Programme geschrieben werden, die z.B. einen Temperatursensor auslesen, grafisch auswerten und auf verschiede Wege anzeigen.

3   Hardwaregegebenheiten

3.1   Entwicklungssystem

Da für embedded Systeme einige Einschränkungen gelten, empfiehlt es sich, den Kernel und das Dateisystem seinen Wünschen anzupassen. Oft ist eine vollständige Linux Distribution auf einem embedded Board overkill und belastet das Board, das sowieso nicht sonderlich viel Leistung besitzt, nur unnötig.
Da für die ARM Architektur die meisten Pakete sowie der Kernel cross-kompiliert werden müssen, hilft hier ein Leistungsstarkes System. Dabei wurde auf folgendes System gesetzt:
* Intel Core i7-2600K, 4x 3.40GHz, 8MB L3 Cache shared (TurboModus aus, Übertaktet mit Wasserkühlung auf 4,5 Ghz)
* ASRock P67 Extreme4 Mainboard
* 8GB G.Skill Ripjaws 1600 CL7
Als Host Betriebssystem kam Windows 7 x64 mit VMWare Workstation 7.1.4 zum Einsatz, in der ein Ubuntu 10.04 LTS x86 mit 4 Cores und 3GB Arbeitsspeicher lief.

3.2   Entwicklungsboard

In dieser Arbeit wird als Entwicklungsboard das bereits erwähnte mini2440 von FriendlyARM eingesetzt - In der Variante mit 3,5" Sony Touchscreen. Die Spezifikationen des Boards lesen sich für den relativ geringen Preis bereits relativ gut:
Dimension: 100 x 100 mm
CPU: 400 MHz Samsung S3C2440A ARM920T (max freq. 533 MHz)
RAM: 64 MB SDRAM, 32 bit Bus
Flash: 256 MB NAND Flash and 2 MB NOR Flash with BIOS
EEPROM: 1024 Byte (I2C)
Ext. Memory: SD-Card socket
Serial Ports: 1x DB9 connector (RS232), total: 3x serial port connectors
USB: 1x USB-A Host 1.1, 1x USB-B Device 1.1
Audio Output: 3.5 mm stereo jack
Audio Input: Connector + Condenser microphone
Ethernet: RJ-45 10/100M (DM9000)
RTC: Real Time Clock with battery
Beeper: PWM buzzer
Camera: 20 pin Camera interface (2.0 mm)
LCD Interface
    STN Displays
    TFT Displays
    41 pin connector for FriendlyARM Displays and VGA Board
Touch Panel: 4 wire resistive
User Inputs: 6x push buttons and 1x A/D pot
User Outputs: 4x LEDs
Expansion: 40 pin System Bus, 34 pin GPIO, 10 pin Buttons (2.0 mm)
Debug: 10 pin JTAG (2.0 mm)
Power: regulated 5V (DC-Plug: 1.35mm inner x 3.5mm outer diameter)
Power Consumption: Mini2440 + 3.5" LCD: 0.6 A
OS Support
    Windows CE 5 and 6
    Linux 2.6
    Android
Des weiteren verfügt das Board über eine Vielzahl an verbreiteten Schnittstellen und Bussystemen, z.B. :
  • 3x UART Interfaces with IrDA1.0
  • 2x SPI Interfaces
  • 130x GPIO Pins
  • 8 Kanal 10-Bit ADC
  • I2C-Interface, Multi-Master support, I2S Audio Interface
  • AC'97 Sound Interface
  • LCD Controller
  • Touchscreen Interface via ADC
  • Camera Interface
  • SD/MMC Slot
  • 2 USB Host-Controller, 1 USB Device-Controller
  • 4 PWM-Timer, 1 internal Timer, 1 Watchdog-Timer
images/board.jpg
Das Ganze kommt in einer schmucken Verpackung mit vielen chinesischen Schriftzeichen:
images/package.png
Das Touchscreen Display selber ist hiermit dem Board verschraubt, damit ist das Board wirklich sehr kompakt. Löst man diese Schrauben, bekommt man Zugriff auf die externen Schnittstellen wie Schalter und LEDs.
Erwähnenswert sind sonst noch der Powerschalter mit LED, der seitlich daneben angebrachte Reset Schalter und der Boot-Mode-Switch. Über diesen wählt man aus, ob man von NOR oder NAND Speicher booten möchte.

3.3   Anschlussbelegung

Auf dem Board, genauer Connector 4, sind einige Spannungen, analoge Eingangspins und digitale Ausgangspins vorhanden. Benutzt werden diese im Folgenden für den I2C und GPIO Bus.
CON4<2> = VDD3.3V
CON4<3> = GND
CON4<5> = AIN0 = W1 (A/D INPUT TEST ON-BOARD POT)
CON4<6> = AIN1
CON4<7> = AIN2
CON4<8> = AIN3
CON4<9> = EINT0 = GPF0
CON4<10> = EINT1 = GPF1
CON4<11> = EINT2 = GPF2
CON4<12> = EINT3 = GPF3 = CON5<5> (SYSTEM INTERFACE EXPANSION BUS)
CON4<13> = EINT4 = GPF4
CON4<14> = EINT5 = GPF5
CON4<15> = EINT6 = GPF6
CON4<16> = EINT8 = GPG0 = K1 (PULLUP ON-BOARD)
CON4<17> = EINT9 = GPG1 = CON5<6> (SYSTEM INTERFACE EXPANSION BUS)
CON4<18> = EINT11 = GPG3 = nSS1 = K2 (PULLUP ON-BOARD)
CON4<19> = EINT13 = GPG5 = SPIMISO1 = K3 (PULLUP ON-BOARD)
CON4<20> = EINT14 = GPG6 = SPIMOSI1 = K4 (PULLUP ON-BOARD)
CON4<21> = EINT15 = GPG7 = SPICLK1 = K5 (PULLUP ON-BOARD)
CON4<22> = EINT17 = GPG9 = nRST1 = CON5<3> (SYSTEM INTERFACE EXPANSION BUS)
CON4<23> = EINT18 = GPG10 = nCTS1 = CON5<4> (SYSTEM INTERFACE EXPANSION BUS)
CON4<24> = EINT19 = GPG11 = K6 (PULLUP ON-BOARD)
CON4<25> = SPIMISO = GPE11
CON4<26> = CON4<20>
CON4<27> = SPICLK = GPE13
CON4<28> = nSS_SPI = GPG2 = EINT10 = CON20<3> (CAMERA)
CON4<29> = I2CSCL = GPE14 = CON20<2> (CAMERA)
CON4<30> = I2CSDA = GPE15 = CON20<1> (CAMERA)
CON4<31> = GPB0 = TOUT0 = BUZZER (ON-BOARD)
CON4<32> = GPB1 = TOUT1 = LED BACKLIGHT (LCD_PWR PWM OUTPUT)
CON4<33> = CLKOUT0 = GPH9
CON4<34> = CLKOUT1 = GPH10

4   Software einrichten

Als Betriebssystem kam im Entwicklungsrechner ein Ubuntu 10.04 LTS x86 auf den aktuellsten Stand gepatcht zum Einsatz. Zugegriffen wurde darauf via SSH Protokoll.

4.1   NFS Server

Damit wir das Root Dateisystem des Boards über das Netzwerk vom Entwicklungssystem starten können, benötigen wir einen NFS Server. (NFS = Network File System)
Dieser lässt sich recht einfach mit:
apt-get install nfs-kernel-server nfs-common
installieren. Daraufhin muss noch die Konfigurationsdatei /etc/exports unseren Wünschen angepasst werden:
/nfs    192.168.1.0/255.255.255.0(rw,sync,no_root_squash)
Damit sollte nun der Zugriff auf den Ordner /nfs über das Netzwerk und das NFS Protokoll möglich sein. Testen kann man dies kurz durch:
mkdir /test
mount -t nfs localhost:/nfs /test
Damit sollte der Ordner /nfs nun lokal unter /test gemountet sein.

4.2   NTPd Server

Da das Board nicht dauerhaft am Internet hängt, aber für die späteren Messungen immer die aktuellste Uhrzeit hat, wird auf dem Entwicklungssystem der Zeitserver ntpd installiert.
Dies erfolgt durch:
apt-get install ntp
Danach muss die Konfigurationsdatei /etc/ntp.conf angepasst werden:
server pool.ntp.org iburst
server 127.127.1.0
fudge 127.127.1.0 stratum 10
restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap
Damit wird der Zeitserver im Internet und die Erlaubnis für die lokalen IP Netze angegeben. Nach einem Neustart des Daemon, sollte die Zeit synchronisiert und abfragbar sein. z.B. im syslog:
Jun  3 16:23:52 buildroot ntpd[16750]: synchronized to 134.34.3.19, stratum 2
Jun  3 16:26:18 buildroot ntpd[16750]: time reset +145.652714 s

5   Buildroot verwenden

images/buildroot_logo.png
Buildroot ist im großen und ganzen eine Skriptsammlung, die die Einrichtung und Benutzung eines embedded Linux Systems vereinfachen soll. Es kann das Dateisystem, den Bootloader und den Kernel erstellen und liefert bereits eine Menge installierbarer Pakete und Programme mit. Zusätzlich sind dort für die häufigsten Embedded Boards bereits standard Konfigurationsdateien hinterlegt, damit auch unwissende Leute die korrekte Kernelkonfiguration haben um alle Schnittstellen ansprechen zu können.
Über ein optisches Menü kann man in Buildroot einfach seine gewünschte Konfiguration zusammenklicken. Hat man dies erledigt, lädt Buildroot automatisch alle Pakete selbstständig herunter und kompiliert Sie. Da das System, auf dem man dies durchführt, meistens nicht derselben Prozessorarchitektur wie das Zielsystem entspricht, muss Buildroot dafür eine eigene cross-kompilierende Toolchain der Compiler herunterladen und installieren. Daraufhin folgen Kernel, Bootloader und ausgewählte Pakete.
Selbstverständlich kann man dies auch alles per Hand erledigen, wenn man z.B. eigene Anpassungen oder spezielle Versionen benutzen möchte.
Eine Alternative zu Buildroot sei noch kurz erwähnt, das BSP Paket von Pengutronix ( http://www.oselas.org/oselas/bsp/pengutronix/mini2440_bsp_en.html ).

5.1   Installation und Einrichtung

Als Erstes muss man sich entscheiden, ob man lieber auf das letzte stable Release von der BR Website oder auf das neueste Development Release aus dem GIT Repository setzt. Hier wurde das letzte Development Release verwendet, da die finale 2011.05 Version die 2.6.39 Kernelheaders (welcher verwendet werden soll) nicht enthalten hatte. Zusätzlich sind dort bereits die neueste uClibc und ein paar weitere Updates als stable mit eingepflegt.
Um auf dem Laufenden der aktuellen Änderungen zu bleiben, empfiehlt sich das mitlesen in der Mailingliste von Buildroot: http://lists.busybox.net/pipermail/buildroot/
Um das eigentliche Entwicklungssystem vorzubereiten, sollten folgende Pakete installiert werden:
apt-get install binutils build-essential g++ gcc ncurses-dev bison flex gettext texinfo subversion git-core
Herunterladen tut man die GIT Version mit einem einfachen
git clone git://git.buildroot.net/buildroot
Darauf wird im aktuellen Verzeichnis der Order buildroot angelegt. In diesen wechselt man und führt als erstes
make mini2440_defconfig
aus. Hiermit erstellt Buildroot eine erste Konfigurationsdatei (.config im BR Verzeichnis) auf Basis einer standard Konfigurationsdatei, welche mitgeliefert wird. (configs/mini2440_defconfig)
Um die aktuelle Konfiguration mit der ncurses Oberfläche editieren zu können muss
make menuconfig
eingegeben werden. Alternativ kann man auch die .config mit einem beliebigen Texteditor öffnen. Mit ncurses sieht das z.B. so aus:
images/buildroot_menuconfig.png
Sobald man seine Einstellungen vorgenommen hat, kann man Buildroot das Dateisystem und u.U. den Bootloader oder auch den Kernel kompilieren lassen. Den Bootloader lässt man am besten nur beim Ersten Mal mitkompilieren, um später Zeit zu sparen. Über Buildroot kann der 2.6.32.2 Kernel von FriendlyARM mitkompiliert werden. Alternativ geht man nach der Anleitung später in dieser Arbeit vor.
Um den Kompiliervorgang nun zu starten, führt man lediglich
make
aus und wartet eine gewisse Zeit. Buildroot erstellt jetzt einen Ordner dl und lädt die benötigten Pakete herunter. In der Anfangsphase, wo man öfters mal ausprobiert und sein Buildroot zurücksetzen muss, hat sich ein symbolischer Link als sehr praktisch erwiesen. Hierzu erstellt man in dem Buildroot übergeordneten Ordner einen neuen Ordner (hier: buildroot-dl) und setzt in den Buildroot Ordner den Link mit:
ln -s ../buildroot-dl dl
Nun müssen etwaige Sources nicht mehr extra jedes Mal heruntergeladen werden. Sollte man Fehler finden, die evtl. mit alten oder falsch kompilierten Sources zusammenhängen kann man auch ein
make clean
ausführen. Damit werden alle bereits kompilierten Dateien in Buildroot gelöscht. (make distclean löscht auch den Downloadordner!)
Dies wird öfters benötigt, da Buildroot die Funktion des späteren Entfernens eines Packages leider nicht unterstützt. Buildroot lädt den Sourcecode herunter, entpackt und kompiliert ihn. Daraufhin wird dieser in den output/target Ordner geschoben - Sollte nun ein Paket entfernt werden, bleiben die kompilierten Dateien jedoch weiterhin in diesem Verzeichnis liegen.
Um ein bestimmtes Paket neu zu kompilieren, reicht es den entsprechenden Ordner des Paketes unter output/build zu löschen und make erneut auszuführen.
Schlussendlich liegt das fertige Dateisystem, falls ausgewählt, gepackt im output/images Ordner unter dem Namen rootfs.tar. Zusätzlich sind dort auch der Kernel (uImage) und der Bootloader zu finden, falls diese mit angegeben werden.

5.2   Konfiguration

Der grundsätzliche Aufbau und das Prozedere wurden ja bereits erklärt. Wichtig ist nun die sinnvolle Auswahl der Pakete, da das System später Platz- und Ressourcensparend auf einer SD-Karte untergebracht werden soll. Folgende Einstellungen wurden geändert oder aktiviert:
Toolchain:
* Kernel Headers (Linux 2.6.39.x kernel headers)
* uClibc C library Version (uClibc 0.9.32.x)
* Binutils Version (binutils 2.21)
* GCC Compiler Version (gcc 4.6.x)
* Purge unwanted locals
* Enable large file (files > 2 GB) support
* Enable RPC support
* Enable WCHAR support
* Enable toolchain locale/i18n support
* Enable C++ support
Da ein aktueller Kernel eingesetzt werden soll, sollten auch die entsprechenden Kernel-Headerfiles möglichst aktuell sein. Auch bei der uClibc und den Binutils sowie GCC wird auf die neueste Version gesetzt. Die restlichen Optionen werden zum kompilieren und ausführen einiger Pakete benötigt und sollten hinzugefügt werden.
System configuration:
* System hostname (deathstar)
* System banner (Surrender! Resistance is futile!)
* (ttySAC0) Port to run a getty (login prompt) on
Für Debugging Zwecke sollte man hier die Standard Konsole auf ttySAC0 umleiten, das serielle Interface. Dann kann das Board direkt über einen USB seriell Adapter und ein Konsolenprogramm wie Putty oder Hyperterm unter Windows oder minicom unter Linux angesprochen werden.
Je nach Leistung des Systems sollte man die Option (4) Number of jobs to run simultaneously auf den entsprechenden Wert der Prozessor Cores des Entwicklungsrechners setzen. Damit werden alle Kompiliervorgänge, falls möglich, beschleunigt, da alle Cores benutzt werden.
In einem späteren Kapitel wird darauf eingegangen, welche zusätzliche Pakete und Dienste (unter package selection) kompiliert und installiert werden sollen.

5.3   U-Boot kompilieren

Buildroot kann als Feature auch verschiedene Bootloader für Entwicklungsboards kompilieren, darunter auch das mini2440. Hierfür gibt es in der Buildroot Konfiguration einen eigenen Menüpunkt. Dort werden unter anderem der Typ des Bootloaders, die Version und die etwaige Netzwerkkonfiguration angegeben:
Bootloaders -->
[X] U-Boot -->
U-Boot Boardname (mini2440)
U-Boot Version (2011-03)
U-Boot Binary Format (u-boot.bin)
[X] Network Settings
Hier lädt Buildroot gleich die neuesten U-Boot Sourcecodes runter und kompiliert den Bootloader in das benötigte binäre .bin Format. Verwenden tut man diese Datei im nächsten Schritt.

6   Bootloader flashen

Das Board wird ab Werk mit einem 2MB NOR und einem 256MB NAND Speicherbaustein ausgeliefert. Mittels eines Schiebeschalter, lässt sich der Boot Mode zwischen beiden hin und her schalten.
Auf dem NOR-Speicher ist ein relativ unbekannter Bootloader namens SuperVivi, während auf dem NAND-Speicher von Werk aus ein fertiges Linux mit QT Anwendungen installiert ist. Da dies aber nicht gewollt ist, wird das Linux gelöscht und durch den U-Boot Bootloader ersetzt.
Vielen Dank an dieser Stelle an Christoph Schwarz für seine Rolle als Vorreiter des Flashvorgangs. Die im Folgenden durchgeführten Schritte basieren größtenteils auf seinen Ergebnissen.

6.1   SuperVivi

SuperVivi ist ein kleiner Bootloader von FriendlyARM, der im NOR Speicher des Boards liegt. Er hat eine recht einfache, aber übersichtliche und mächtige Oberfläche
##### FriendlyARM BIOS 2.0 for 2440 #####
[x] format NAND FLASH for Linux
[v] Download vivi
[k] Download linux kernel
[y] Download root_yaffs image
[a] Absolute User Application
[n] Download Nboot for WinCE
[l] Download WinCE boot-logo
[w] Download WinCE NK.bin
[d] Download & Run
[z] Download zImage into RAM
[g] Boot linux from RAM
[f] Format the nand flash
[b] Boot the system
[s] Set the boot parameters
[u] Backup NAND Flash to HOST through USB(upload)
[r] Restore NAND Flash from HOST through USB
[q] Goto shell of vivi
[i] Version: 1026-2K
Enter your selection: f
Um nun den Flash zu löschen, gibt man f ein.

6.2   Flash U-Boot

Der nächste Schritt ist nun das Flashen des U-Boot Bootloaders in den NAND Speicher. Dazu sagen wir SuperVivi wie groß unsere u-boot.bin Datei ist und dass wir Sie jetzt gerne übertragen möchten:
Supervivi> load flash 0 248932 u
USB host is connected. Waiting a download.
Now, Downloading [ADDRESS:30000000h,TOTAL:248942]
RECEIVED FILE SIZE:  248942 (48KB/S, 5S)
Downloaded file at 0x30000000, size = 248932 bytes
Found block size = 0x00040000
Erasing...    ... done
Writing...    ... done
Written 248932 bytes
Nun sollte man das USB-Host Kabel anstecken, da der Dateitransfer hier über USB durchgeführt wird. Das mini2440 bzw. SuperVivi sollte sich entsprechend im Betriebssystem melden.
Als nächstes müssen wir das Programm zum USB-Transfer herunterladen oder von der CD kopieren. Hier ist nur der Sourcecode enthalten, der zuerst kompiliert werden muss:
root@ubuntu:/.../make cc -o boot_usb.o -c boot_usb.c cc -lusb -o s3c2410_boot_usb boot_usb.o
Danach sollte die benötigte Binärdatei im Ordner liegen:
root@ubuntu:/.../s3c2410_boot_usb# ls -al
total 296
drwxr-xr-x 2       500 users       4096 2011-05-10 19:55 .
drwxr-xr-x 6 gr3yh0und gr3yh0und   4096 2011-05-10 19:51 ..
-rw-r--r-- 1       500 users       5462 2006-08-07 09:00 boot_usb.c
-rw-r--r-- 1 root      root        5464 2011-05-10 19:51 boot_usb.o
-rw-r--r-- 1       500 users       1852 2006-07-15 14:32 lsusb.txt
-rw-r--r-- 1       500 users        220 2006-08-07 09:00 Makefile
-rw-r--r-- 1       500 users        703 2006-07-15 19:16 README
-rwxr-xr-x 1 root      root       13700 2011-05-10 19:51 s3c2410_boot_usb
-rwxr-xr-x 1 root      root      248932 2011-05-10 19:55 u-boot.bin
Der Einfachheit halber kopiert man die u-boot.bin auch gleich in denselben Ordner. Danach kann man den Transfer starten:
root@ubuntu:/.../s3c2410_boot_usb# ./s3c2410_boot_usb u-boot.bin
csum = 0x 377
send_file: addr = 0x33f80000, len = 0x0003cc64
Error downloading program
Der angebliche Fehler der gemeldet wird, kann unbeachtet gelassen werden.
SuperVivi meldet dagegen den erfolgreichen Download des U-Boot in den Flash des Boards:
Now, Downloading [ADDRESS:30000000h,TOTAL:248942]
RECEIVED FILE SIZE:  248942 (121KB/S, 2S)
Downloaded file at 0x30000000, size = 248932 bytes
Found block size = 0x00040000
Erasing...    ... done
Writing...    ... done
Written 248932 bytes
Supervivi>
Wenn man jetzt den Schalter für den Bootbetrieb wieder umlegt, startet U-Boot aus dem NAND Flash heraus mit folgendem Post:
U-Boot 1.3.2-mini2440 (May 10 2011 - 17:55:30)

I2C:   ready
DRAM:  64 MB
NOR Flash not found. Use hardware switch and 'flinit'
Flash:  0 kB
NAND:  Bad block table not found for chip 0
Bad block table not found for chip 0
256 MiB

*** Warning - bad CRC or NAND, using default environment
USB:   S3C2410 USB Deviced
In:    serial
Out:   serial
Err:   serial
MAC: 04:25:fe:ed:00:18
Hit any key to stop autoboot:  0
Da in dem U-Boot Image aber außer den Netzwerkeinstellungen keine weiteren Umgebungsvariablen gespeichert sind, wirft U-Boot erst einmal Fehler aus.
MINI2440 #  nand scrub
NAND scrub: device 0 whole chip
Warning: scrub option will erase all factory set bad blocks!
There is no reliable way to recover them.
Use this command only for testing purposes if you
are sure of what you are doing!

Really scrub this NAND flash? <y/N>
Erasing at 0x3840000 --  22% complete.
NAND 256MiB 3,3V 8-bit: MTD Erase failure: -5
Erasing at 0x7840000 --  47% complete.
NAND 256MiB 3,3V 8-bit: MTD Erase failure: -5
Erasing at 0x7d60000 --  49% complete.
NAND 256MiB 3,3V 8-bit: MTD Erase failure: -5
Erasing at 0xccc0000 --  80% complete.
NAND 256MiB 3,3V 8-bit: MTD Erase failure: -5
Erasing at 0xffe0000 -- 100% complete.
Bad block table not found for chip 0
Bad block table not found for chip 0
OK
Damit die Fehler verschwinden, muss man erst einen kompletten Check (scrub = putzen) und danach eine Bad Block Tabelle anlegen lassen.
MINI2440 # nand createbbt
Create BBT and erase everything ? <y/N>
Skipping bad block at  0x03980000
Skipping bad block at  0x07880000
Skipping bad block at  0x07ec0000
Skipping bad block at  0x0ce80000
Skipping bad block at  0x0ff80000
Skipping bad block at  0x0ffa0000
Skipping bad block at  0x0ffc0000
Skipping bad block at  0x0ffe0000
Creating BBT. Please wait ...Bad block table not found for chip 0
Bad block table not found for chip 0
Bad block table written to 0x0ffe0000, version 0x01
Bad block table written to 0x0ffc0000, version 0x01
Da man nun jedoch einen Teil der Partitionierung überschrieben haben, muss noch einmal per Schalter in den NOR-Modus mit SuperVivi geschalten werden und das gesamte Prozedere (siehe oben) wiederholt werden. Danach meckert U-Boot wieder, diesmal jedoch wegen fehlenden Umgebungsvariablen:
U-Boot 1.3.2-mini2440 (May 10 2011 - 17:55:30)

I2C:   ready
DRAM:  64 MB
NOR Flash not found. Use hardware switch and 'flinit'
Flash:  0 kB
NAND:  256 MiB
*** Warning - bad CRC or NAND, using default environment
USB:   S3C2410 USB Deviced
In:    serial
Out:   serial
Err:   serial
MAC: 04:25:fe:ed:00:18
Hit any key to stop autoboot:  0


MINI2440 # dynpart
mtdparts mtdparts=mini2440-nand:0x00040000(u-boot),0x00020000(u-boot_env),0x00500000(kernel),0x0faa0000(rootfs)

MINI2440 # dynenv set u-boot_env
device 0 offset 0x40000, size 0x20000
45 4e 56 30 - 00 00 04 00
Die Konfiguration dieser wird im nächsten Kapitel erklärt.
Wenn wir jetzt einen Blick auf die Partitionierung werfen, sollte es wie folgt aussehen:
MINI2440 # mtd
device nand0 <mini2440-nand>, # parts = 4
 #: name                        size            offset          mask_flags
 0: u-boot              0x00040000      0x00000000      0
 1: u-boot_env          0x00020000      0x00040000      0
 2: kernel              0x00500000      0x00060000      0
 3: rootfs              0x0faa0000      0x00560000      0
active partition: nand0,0 - (u-boot) 0x00040000 @ 0x00000000
defaults:
mtdids  : nand0=mini2440-nand
mtdparts: <NULL>

6.3   Konfiguration U-Boot

Das Ziel ist es, den Kernel im uImage Format und das entpackte root Dateisystem über NFS bei jedem Bootvorgang zu laden. Dies ist für Entwicklungszwecke erst einmal sehr praktisch, da man nicht jedesmal z.B. eine SD Karte neu flashen muss.
Nach dem Flashen des U-Boot sind dort bereits eine Handvoll Umgebungsvariablen vorhanden. Einige davon müssen wir nun unseren Wünschen anpassen:
setenv root_nfs /nfs/rootfs
setenv baudrate 115200
setenv netmask  255.255.255.0
setenv serverip 192.168.1.1
setenv ipaddr   192.168.1.2
root_nfs gibt den Remote Order an, wo das root Dateissystem über NFS geladen werden soll. baudrate gibt die Übertragungsgeschwindigkeit des seriellen Ports an. Der Rest sind entsprechende Netzwerkeinstellungen.
Da das Skript ifconfig_static ein run zuviel enthält, muss man dies erst anpassen:
setenv ifconfig_static setenv ifconfig ip=${ipaddr}:${serverip}::${netmask}:mini2440:eth0
Als nächstes:
run ifconfig_static
Mit run ifconfig_static wird die Netzwerkkonfiguration des U-Boot auf statisch mit den entsprechenden vorher eingegebenen Variablen gesetzt:
run setenv ifconfig ip=${ipaddr}:${serverip}::${netmask}:mini2440:eth0
Bei der Verwendung des Displays kam es zuerst zu Problemen, da standardmäßig die Konsole auf das Display gelegt wird. Diese geht jedoch nach 7-8 Minuten in einen Energiesparzustand und schaltet dabei gleichzeitig die Hintergrundbeleuchtung ab. Wenn man ein USB Keyboard ansteckt und eine Taste drückt, wurde diese wieder reaktiviert. Um das Problem zu umgehen und die Konsole dauerhaft anzeigen zu lassen muss der bootargs_base Parameter im U-Boot um folgendes erweitert werden. Hierfür editieren wir die Werte erst in andere Umgebungsvariablen hinein und greifen danach dynamisch darauf zu:
setenv bootargs_base console=ttySAC0,${baudrate} noinitrd consoleblank=0
Der Parameter consoleblank macht hier den Unterschied. console lässt man für debugging Zwecke weiterhin lieber auf den seriellen Port zeigen. Als nächstes wird
setenv mini2440 mini2440=5tb
die Variable mini2440 angepasst, damit das korrekte Display, das auf dem Board ausgeliefert wird, ausgewählt wird. 5tb sagt hier die Version des Display aus, in diesem Falle ist es das X35 von Sony. 0-4 sind andere Typen (T35 etc.), die je nach Produktionszeitraum ausgeliefert wurden.
Die Option 5tb wählt die entsprechende Displaykonfiguration im Kernel aus, wobei 5 die Version/Hersteller/Typ des Displays angibt, t die Touchscreen Funktion und b die Möglichkeit des Backlights.
Hier ein beispielhafter Auszug aus der Kernelconfig für das X35:
[5] = { /* mini2440 + 3.5" TFT + touchscreen -- SONY X35 */
                           .width           = 240,
                           .xres            = 240,
                           .height          = 320,
                           .yres            = 320,
                           .left_margin     = 1,
                           .right_margin    = 26,
                           .upper_margin    = 1,
                           .lower_margin    = 5,
                           .hsync_len       = 5,
                           .vsync_len       = 9,
                           .pixclock        = 170000,
                           .bpp             = 16,
                           .type            = (S3C2410_LCDCON1_TFT16BPP |
                                                                   S3C2410_LCDCON1_TFT),
                           .lcdcon5         = (S3C2410_LCDCON5_FRM565 |
                                                                   S3C2410_LCDCON5_INVVDEN |
                                                                   S3C2410_LCDCON5_INVVFRAME |
                                                                   S3C2410_LCDCON5_INVVLINE |
                                                                   S3C2410_LCDCON5_INVVCLK |
                                                                   S3C2410_LCDCON5_HWSWP ),
                },
Um nun die finalen Booteinstellungen zu konfigurieren, greift man auf die Umgebungsvariablen zuvor zu:
setenv bootargs_init
run set_bootargs_nfs
Hier wird bootargs_init geleert und das Kommando run set_bootargs_nfs ausgeführt, das den wichtigsten Parameter bootargs setzt. Er gibt wie der Name schon sagt, die Boot Argumente an, darunter Netzwerkkonfiguration, Displaytyp, Konsoleneinstellungen etc:
run set_root_nfs; setenv bootargs ${bootargs_base} ${bootargs_init} ${mini2440} ${root_nfs} ${ifconfig}
run set_root_nfs setzt den Rootpfad zum booten über NFS aufbauend auf den vorherigen Eingaben:
setenv root_nfs root=/dev/nfs rw nfsroot=${serverip}:${root_nfs}
Nun muss das eigentliche Boot Kommando bootcmd editiert werden. Dieses wird bei jedem Start des Boards ausgeführt: In diesem Fall das Booten per NFS und der Pfad zum Kernel:
setenv bootcmd nfs 0x32000000 ${serverip}:/nfs/uImage; bootm
Um den Bootvorgang zu beschleunigen kann nun noch die Wartezeit des U-Boot Loaders auf 1 Sekunde heruntergesetzt werden:
setenv bootdelay 1
Alle Einstellungen können mit
printenv
aufgelistet und begutachtet werden. Sollte man die Umgebungsvariablen jetzt in den NAND Speicher des U-Boot speichern wollen, führt man aus:
saveenv
Hier der finale Auszug der Umgebungsvariablen, die zum Booten gebraucht werden:
MINI2440 #   printenv
baudrate=115200
ethaddr=04:25:fe:ed:00:18
netmask=255.255.255.0
usbtty=cdc_acm
root_nand=root=/dev/mtdblock3 rootfstype=jffs2
root_mmc=root=/dev/mmcblk0p2 rootdelay=2
set_root_nfs=setenv root_nfs root=/dev/nfs rw nfsroot=${serverip}:${root_nfs}
ifconfig_dhcp=run setenv ifconfig ip=dhcp
set_bootargs_mmc=setenv bootargs ${bootargs_base} ${bootargs_init} ${mini2440} ${root_mmc}
set_bootargs_nand=setenv bootargs ${bootargs_base} ${bootargs_init} ${mini2440} ${root_nand}
set_bootargs_nfs=run set_root_nfs; setenv bootargs ${bootargs_base} ${bootargs_init} ${mini2440} ${root_nfs} ${ifconfig}
mtdids=nand0=mini2440-nand
mtdparts=mtdparts=mini2440-nand:0x00040000(u-boot),0x00020000(u-boot_env),0x00500000(kernel),0x0faa0000(rootfs)
ipaddr=192.168.1.2
serverip=192.168.1.1
gatewayip=192.168.1.1
bootcmd=nfs 0x32000000 192.168.1.1:/nfs/uImage; bootm
bootdelay=1
mini2440=mini2440=5tb
ifconfig_static=setenv ifconfig ip=192.168.1.2:192.168.1.1::255.255.255.0:mini2440:eth0
ifconfig=ip=192.168.1.2:192.168.1.1::255.255.255.0:mini2440:eth0
bootargs_base=console=ttySAC0,115200 noinitrd consoleblank=0
root_nfs=root=/dev/nfs rw nfsroot=192.168.1.1:root=/dev/nfs rw nfsroot=192.168.1.1:/nfs/rootfs
bootargs=console=ttySAC0,115200 noinitrd consoleblank=0 mini2440=5tb root=/dev/nfs rw nfsroot=192.168.1.1:root=/dev/nfs rw nfsroot=192.168.1.1:/nfs/rootfs ip=192.168.1.2:192.168.1.1::255.255.255.0:mini2440:eth0
partition=nand0,0
mtddevnum=0
mtddevname=u-boot

Environment size: 1449/131068 bytes

7   Erstellung des Kernels

FriendlyARM pflegt aktuell leider nur den relativ alten 2.6.32er Branch des Kernels für das mini2440. Hier war der Wunsch vorhanden, auf eine neuere Version zu gehen. Nach einigem rumprobieren mit einem bereits gepatchtem git repository wurde schnell klar, dass ein eigener selbstständiger Weg gefunden werden musste. Der folgende Weg wurde für 2.6.38.6/7 und 2.6.39.X getestet.

7.1   Download Toolchain und Sourcecode

Zuerst mussten auf dem System noch einige Packages, die zum Ausführen der Toolchain und der Kernelumwandlung benötigt werden, nachinstalliert werden:
apt-get install ia32-libs uboot-mkimage patch
Als nächstes muss eine ARM Toolchain besorgt werden um den Kernel zu cross-compilen. Die Wahl fiel auf die Variante von CodeSourcery:
wget http://www.codesourcery.com/sgpp/lite/arm/portal/package3696/public/arm-none-linux-gnueabi/arm-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
tar xjvfo arm-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2 -C /usr/local
export PATH=$PATH:/usr/local/arm-2008q3/bin
export CROSS_COMPILE=arm-none-linux-gnueabi-
Hier wird die Toolchain heruntergeladen, entpackt und die nötigen Umgebungsvariablen im user space angepasst.
Nun muss der Kernel Sourcecode von kernel.org heruntergeladen und entpackt werden. Hier wird der aktuelle 2.6.39er Kernel (.1) verwendet:
wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.39.1.tar.bz2
tar xfvj linux-2.6.39.1.tar.bz2

7.2   Kernel anpassen

Seit einigen Commits ist, von Linus Torvalds freigegeben, eine default Konfigurationsdatei (mini2440_defconfig) im Sourcecode des Kernels vorhanden. Als erstes klont man diese config in die aktuelle:
CROSS_COMPILE=/usr/local/arm-2008q3/bin/arm-none-linux-gnueabi- ARCH=arm make mini2440_defconfig

7.2.1   Display und Touchscreen

Die Aktuell ausgelieferte Version des Displays X35 wird noch nicht direkt vom Kernel unterstützt, hierzu müssen einige Änderungen vorgenommen werden.
Als Erstes werden die Patches heruntergeladen:
wget http://www.friendlyarm.net/forum/attachment/11933
tar xzf 11933
Diese Patches fügen, wie die Namen bereits verraten, ein paar Zeilen Code für die Displays und den Touchscreen hinzu. Ausgeführt werden Sie mit:
patch -p1 < 0001-s3c2440-mini2440-Add-support-for-new-LCD-panels.patch
patch -p1 < 0002-s3c2440-mini2440-Add-touchscreen-support-for-mini244.patch
patch -p1 < 0003-s3c2440-mini2440-Select-touchscreen-by-default.patch
patch -p1 < 0004-s3c2440-mini2440-Enable-the-backlight-LED-earlier-at.patch

7.2.2   NFS Boot

In den aktuellen Sources sind soweit eigentlich alle wichtigen Punkte bereits in der standard Konfiguration enthalten.
Trotzdem sollte man sich vergewissern, dass folgende Punkte für NFS aktiviert sind, wenn man Sie verwenden möchte:
Networking support --> Networking options -->
        [X] IP: kernel level autoconfiguration

File systems --> Network File Systems -->
        [X] NFS client support (v3)
        [X] Root file system on NFS
Dies wird benötigt, da man das Root Dateisystem für Entwicklungszwecke erst einmal über NFS starten und verwenden möchte.

7.2.3   NFS Boot Patch

Leider wurde im Herbst 2010 in allen Kernelcommits im NFS Sourcecode eine Option des NFS mount option parsers entfernt und stattdessen ein allgemeiner eingeführt. Dadurch werden die options jedoch ähnlich wie im User Space des Benutzers geparsed, was auf embedded System zu Problemen führen kann.
Hier muss man manuell eingreifen und im Sourcecode folgendes ändern:
vim fs/nfs/nfsroot.c
Dort muss die Zeile
/* Default NFSROOT mount options. */
#define NFS_DEF_OPTIONS         "udp"
gesucht werden und durch
/* Default NFSROOT mount options. */
#define NFS_DEF_OPTIONS         "vers=2,udp,rsize=4096,wsize=4096"
ersetzt werden.
Der Vorteil dieser Variante gegenüber der Möglichkeit einen bereits gepatchten Kernel aus einem git repository zu ziehen ist der, dass diese Patches aufwärtskompatibel sein sollten. Damit kann man auch neuere Kernelversionen einfach weiterverwenden. Laut dem User, der die Patches erstellt hat, sind diese auch bereits in den mainline geupstreamed worden, damit dürfte es sowieso nur eine Frage der Zeit sein, bis dieser umständliche Patch Teil komplett wegfallen dürfte.

7.2.4   Schnittstellen

Nun sollten die benötigten Schnittstellen des Boards ausgewählt oder gecheckt werden:
Device Drivers -->
        [X] I2C support -->
                [X] Enable compatibility bits for old user-space
                [X] I2C device interface
                [X] I2C bus multiplexing support
                [X] Autoselect pertinent helper modules
                [X] I2C Hardware Bus support -->
                        [X] S3C2410 I2C Driver
                        [X] Simtec Generic I2C interface
        [X] GPIO support -->
                [X] /sys/class/gpio/... sysfs interface
        [X] SPI support
                [X] Samsung S4C24XX series SPI
        [X] MMC/SD/SDIO card support
        [X] Real Time Clock
        [X] LED support
                [X] LED Support for Samsung S3C24XX GPIO LEDs
                [X] LED Support for GPIO connected LEDs
                [X] LED backlight trigger
                ... einige mehr

Input device support -->
        [X] Touchscreen
        ...
Mit dem benutzen der mitgelieferten mini2440_defconfig sollten diese Optionen standardmäßig bereits aktiviert sein und mitgeladen werden.

7.2.5   Soundkarte

Um den Sound zu aktivieren sollten folgende Einstellungen vorgenommen werden:
Device Drivers -->
        [X] Sound card support -->
                [X] Preclaim OSS device numbers
                [X] Advanced Linux Sound Architecture -->
                        [X] OSS Mixer API
                        [X] OSS PCM (digital audio) API
                                [X] OSS PCM (digital audio) API - Include plugin system
                        [X] Support for old ALSA API
                        [X] Verbose procfs contents
                        [X] Debug
                        [X] ALSA for SoC audio support -->
                                [X] ASoC support for Samsung
                                        [X] SoC I2S Audio support UDA134X wired to a S3C24XX
Die restlichen Optionen können deaktiviert bleiben. Um auf die Funktionen zugreifen zu können, ist es sinnvoll in Buildroot die Pakete für alsa-lib und alsa-utils mitzukompilieren.
Um sich etwas Zeit und Platz zu ersparen, kann man einige von Haus aus aktivierte Punkte aussparen, darunter Wireless LAN-, Bluetooth- oder Virtualisierungs-Module.

7.3   Kernel kompilieren

Nun kann man den Kernel noch nach Wunsch mit
CROSS_COMPILE=/usr/local/arm-2008q3/bin/arm-none-linux-gnueabi- ARCH=arm make menuconfig
anpassen. Damit wird mit ncurses eine kleine Konsole gestartet, die ähnlich wie Buildroot aufgebaut ist. Hier empfiehlt es sich, nicht benötigte Optionen wie Wireless oder Bluetooth zu deaktivieren. Das ganze sieht dann so aus:
images/kernel_menuconfig.png
Der nächste Schritt ist nun endlich das eigentliche Kompilieren des Kernels und das Umwandeln in das benötigte U-Boot uImage Format:
CROSS_COMPILE=/usr/local/arm-2008q3/bin/arm-none-linux-gnueabi- ARCH=arm make -j4 all
CROSS_COMPILE=/usr/local/arm-2008q3/bin/arm-none-linux-gnueabi- ARCH=arm make uImage
Die Zahl hinter -j gibt hierbei die Anzahl gleichlaufender paralleler Prozesse an, daher kann diese mit entsprechend vielen Prozessorkernen auch munter erhöht werden. Um bereits kompilierte Dateien zu löschen und alles auf 0 zurückzusetzen, kann ein einfaches make clean verwendet werden.
Das fertige uImage liegt dann im arch/arm/boot Order und kann in den nfs Ordner kopiert werden.
Link Cross Compiling Kernel: http://arm9.in/?p=35

8   Erste Gehversuche

Da nun U-Boot geflasht, der Kernel kompiliert und ein root Dateisystem erstellt ist, kann der erste Bootversuch gewagt werden. Hier ein beispielhafter Output:
        U-Boot 1.3.2-mini2440 (May 10 2011 - 17:55:30)

I2C:   ready
DRAM:  64 MB
NOR Flash not found. Use hardware switch and 'flinit'
Flash:  0 kB
NAND:  256 MiB
Found Environment offset in OOB..
USB:   S3C2410 USB Deviced
In:    serial
Out:   serial
Err:   serial
MAC: 04:25:fe:ed:00:18
Hit any key to stop autoboot:  0
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 04:25:fe:ed:00:18
File transfer via NFS from server 192.168.1.1; our IP address is 192.168.1.2
Filename '/nfs/uImage'.
Load address: 0x32000000
Loading: #################################################################
                 #################################################################
                 #################################################################
                 #################################################################
                 #################################################################
                 #################################################################
                 ##############################*** ERROR: Cannot umount
## Booting kernel from Legacy Image at 32000000 ...
   Image Name:   Linux-2.6.39
   Created:      2011-06-02  16:16:15 UTC
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    2145800 Bytes =  2 MB
   Load Address: 30008000
   Entry Point:  30008000
   Verifying Checksum ... OK
   Loading Kernel Image ... OK
OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.
Linux version 2.6.39 (root@workstation) (gcc version 4.3.2 (Sourcery G++ Lite 2008q3-72) ) #1 Thu Jun 2 18:15:54 CEST 2011
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
CPU: VIVT data cache, VIVT instruction cache
Machine: MINI2440
Memory policy: ECC disabled, Data cache writeback
CPU S3C2440A (id 0x32440001)
S3C24XX Clocks, Copyright 2004 Simtec Electronics
S3C244X: core 405.000 MHz, memory 101.250 MHz, peripheral 50.625 MHz
CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 16256
Kernel command line: console=ttySAC0,115200 noinitrd consoleblank=0 mini2440=5tb root=/dev/nfs rw nfsroot=192.168.1.1:root=/dev/nfs rw nfsroot=192.168.1.1:/nfs/rootfs ip=192.168.1.2:192.168.1.1::255.255.255.0:mini2440:eth0
PID hash table entries: 256 (order: -2, 1024 bytes)
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 64MB = 64MB total
Memory: 60476k/60476k available, 5060k reserved, 0K highmem
Virtual kernel memory layout:
        vector  : 0xffff0000 - 0xffff1000   (   4 kB)
        fixmap  : 0xfff00000 - 0xfffe0000   ( 896 kB)
        DMA     : 0xffc00000 - 0xffe00000   (   2 MB)
        vmalloc : 0xc4800000 - 0xf6000000   ( 792 MB)
        lowmem  : 0xc0000000 - 0xc4000000   (  64 MB)
        modules : 0xbf000000 - 0xc0000000   (  16 MB)
          .init : 0xc0008000 - 0xc0029000   ( 132 kB)
          .text : 0xc0029000 - 0xc03f8f8c   (3904 kB)
          .data : 0xc03fa000 - 0xc041e6c0   ( 146 kB)
SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS:85
irq: clearing pending status 02000000
irq: clearing subpending status 00000002
Console: colour dummy device 80x30
console [ttySAC0] enabled
Calibrating delay loop... 201.52 BogoMIPS (lpj=503808)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
gpiochip_add: gpios 288..303 (GPIOK) failed to register
gpiochip_add: gpios 320..334 (GPIOL) failed to register
gpiochip_add: gpios 352..353 (GPIOM) failed to register
NET: Registered protocol family 16
MINI2440: Option string mini2440=5tb
MINI2440: LCD 0:240x320 1:800x480 2:1024x768 3:240x320 4:640x480 [5:240x320]
S3C2440: Initialising architecture
S3C2440: IRQ Support
S3C244X: Clock Support, DVS off
s3c-adc s3c24xx-adc: attached adc driver
bio: create slab <bio-0> at 0
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
s3c-i2c s3c2440-i2c: slave address 0x10
s3c-i2c s3c2440-i2c: bus frequency set to 98 KHz
s3c-i2c s3c2440-i2c: i2c-0: S3C I2C adapter
Advanced Linux Sound Architecture Driver Version 1.0.24.
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 2048 (order: 2, 16384 bytes)
TCP bind hash table entries: 2048 (order: 1, 8192 bytes)
TCP: Hash tables configured (established 2048 bind 2048)
TCP reno registered
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
JFFS2 version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
ROMFS MTD (C) 2007 Red Hat, Inc.
msgmni has been set to 118
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
Console: switching to colour frame buffer device 60x53
fb0: s3c2410fb frame buffer device
s3c2440-uart.0: ttySAC0 at MMIO 0x50000000 (irq = 70) is a S3C2440
s3c2440-uart.1: ttySAC1 at MMIO 0x50004000 (irq = 73) is a S3C2440
s3c2440-uart.2: ttySAC2 at MMIO 0x50008000 (irq = 76) is a S3C2440
brd: module loaded
at24 0-0050: 1024 byte 24c08 EEPROM, writable, 16 bytes/write
S3C24XX NAND Driver, (c) 2004 Simtec Electronics
s3c24xx-nand s3c2440-nand: Tacls=1, 9ns Twrph0=3 29ns, Twrph1=2 19ns
s3c24xx-nand s3c2440-nand: NAND soft ECC
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Creating 4 MTD partitions on "nand":
0x000000000000-0x000000040000 : "u-boot"
uncorrectable error :
0x000000040000-0x000000060000 : "u-boot-env"
ftl_cs: FTL header not found.
0x000000060000-0x000000560000 : "kernel"
ftl_cs: FTL header not found.
0x000000560000-0x000010000000 : "root"
ftl_cs: FTL header not found.
dm9000 Ethernet Driver, V1.31
dm9000 dm9000: eth0: Features changed: 0x00004800 -> 0x00004000
eth0: dm9000e at c486a300,c486e304 IRQ 51 MAC: 04:25:fe:ed:00:18 (chip)
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
s3c2410-ohci s3c2410-ohci: S3C24XX OHCI
s3c2410-ohci s3c2410-ohci: new USB bus registered, assigned bus number 1
s3c2410-ohci s3c2410-ohci: irq 42, io mem 0x49000000
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 2 ports detected
usbcore: registered new interface driver libusual
s3c2440-usbgadget s3c2440-usbgadget: S3C2440: increasing FIFO to 128 bytes
mousedev: PS/2 mouse device common for all mice
input: gpio-keys as /devices/platform/gpio-keys/input/input0
samsung-ts s3c2440-ts: driver attached, registering input device
input: S3C24XX TouchScreen as /devices/virtual/input/input1
S3C24XX RTC, (c) 2004,2006 Simtec Electronics
s3c-rtc s3c2410-rtc: rtc disabled, re-enabling
s3c-rtc s3c2410-rtc: rtc core: registered s3c as rtc0
i2c /dev entries driver
S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics
s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq enabled
cpuidle: using governor ladder
sdhci: Secure Digital Host Controller Interface driver
sdhci: Copyright(c) Pierre Ossman
s3c-sdi s3c2440-sdi: powered down.
s3c-sdi s3c2440-sdi: mmc0 - using pio, sw SDIO IRQ
usbcore: registered new interface driver usbhid
usbhid: USB HID core driver
ALSA device list:
  No soundcards found.
TCP cubic registered
NET: Registered protocol family 17
Registering the dns_resolver key type
s3c-rtc s3c2410-rtc: setting system clock to 2000-01-15 17:15:54 UTC (947956554)
dm9000 dm9000: eth0: link up, 100Mbps, full-duplex, lpa 0xCDE1
IP-Config: Complete:
         device=eth0, addr=192.168.1.2, mask=255.255.255.0, gw=255.255.255.255,
         host=mini2440, domain=, nis-domain=(none),
         bootserver=192.168.1.1, rootserver=192.168.1.1, rootpath=
VFS: Mounted root (nfs filesystem) on device 0:14.
Freeing init memory: 132K
Initializing random number generator... done.
Starting network...
ip: RTNETLINK answers: File exists

Welcome to Buildroot
buildroot login:
Standardmäßig werden von Buildroot zwei Benutzer ohne Passwort angelegt: root und default. Neue können mit useradd hinzugefügt werden.

9   Zusatz-Platine

Um die verschiedenen Schnittstellen des mini2440 Boards kennen zu lernen, wurde eine kleine Extra Platine angefertigt, die eine Ampelschaltung aus LEDs und einen Temperatursensor enthält.
images/platine.png

10   Der LM75 Temperatursensors über I2C

10.1   Allgemein

Um mit dem mini2440 die Umgebungstemperatur messen zu können, muss ein Sensor angeschlossen werden. Hier fiel die Wahl auf den LM75 von National Semiconductor. Die LM7X Serie ist weit verbreitet und wird sehr gut unterstützt. Die Anschaffung ist aufgrund der Verbreitung auch sehr günstig, knapp über 1 Euro pro Sensor. Leider ist er nur in SMD Bauweise in Form eines SO8 Gehäuses verfügbar, was für Leute mit wenig Löterfahrung einige Probleme aufwirft.
Der Sensor selber kann die Temperaturbereiche von -55°C bis +125°C abdecken und wird über den I2C Bus angesteuert. Die Auflösung der Messwerte beträgt hier bis zu 0,5°C bei Einer Genauigkeit von 2-3°, je nach Temperaturbereich.
images/lm75_accuracy.png

10.2   Anschluss

images/lm75_package.png
Für das eigentlich Ansteuern werden nur 7 der 8 Pins benötigt. Der Adressbus kann verwendet werden, wird aber hier auch nur pauschal gegen Masse gelötet, daher kommen dort 3x Nullen an. SDA(Pin 30) und SCL(Pin 29) sind die beiden Buseingänge für I2C, welchen das mini2440 bereits unterstützt. Zusätzlich muss der LM75 in dieser Form hier noch gegen die 3,3V VSS(Pin 2) Leitung des Boards gelötet werden. Es gibt hier eine 3,3V und eine 5V Version des Sensors (das Board bietet auch beide Ausgänge), gewählt wurde aber die 3,3V Version.
Der letzte Pin ist natürlich Masse(Pin 3).
Nachdem der Sensor angeschlossen wurde, meldet der Kernel beim Bootvorgang:
i2c /dev entries driver
lm75 0-0048: hwmon0: sensor 'lm75'

10.3   Auslesen über i2ctools

Ist der Sensor korrekt angeschlossen, wird dieser bereits beim booten des Systems erkannt. Checken kann man dies durch einen kurzen Scan des I2C Busses mit dem Programm i2cdetect. Damit dieses verfügbar ist, musst die i2ctools in Buildroot mit hineinkompilieren.
# i2cdetect -y 0
        0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
50: UU UU UU UU -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Der HEX Bereich 0x50 bis 0x54 ist das I2C EEPROM, der Sensor wird aufgrund der festen Adressbelegung auf 0x48 angezeigt. Hier wird der Bus mit einem UU dargestellt, da bereits der sysfs Treiber geladen wurde, der auf den I2C Bus zugreift und entsprechende Mappings durchführt. Ohne sysfs kann mittels i2cget auf die entsprechende Adresse zugegriffen werden.

10.4   Auslesen über Hwmon Treiber

Fügt man dem Kernel die I2C sysfs Treiber unter Device Drivers / Hardware Monitoring support / National Semiconductor LM75 hinzu, legt der Kernel gleich automatisch den benötigten link auf dem Dateisystem an.
# ls -al /sys/devices/platform/s3c2440-i2c/i2c-0/0-0048/
total 0
drwxr-xr-x    4 root     root             0 Jun 10 01:01 .
drwxr-xr-x    9 root     root             0 Jun 10 01:01 ..
lrwxrwxrwx    1 root     root             0 Jun 10 01:05 driver -> ../../../../../bus/i2c/drivers/lm75
drwxr-xr-x    3 root     root             0 Jun 10 01:01 hwmon
-r--r--r--    1 root     root          4096 Jun 10 01:05 modalias
-r--r--r--    1 root     root          4096 Jun 10 01:01 name
drwxr-xr-x    2 root     root             0 Jun 10 01:05 power
lrwxrwxrwx    1 root     root             0 Jun 10 01:01 subsystem -> ../../../../../bus/i2c
-r--r--r--    1 root     root          4096 Jun 10 01:01 temp1_input
-rw-r--r--    1 root     root          4096 Jun 10 01:01 temp1_max
-rw-r--r--    1 root     root          4096 Jun 10 01:01 temp1_max_hyst
-rw-r--r--    1 root     root          4096 Jun 10 01:05 uevent
Hier kann nun mit einem einfachen Aufruf die Temperatur über den Bus ausgelesen werden:
# cat /sys/devices/platform/s3c2440-i2c/i2c-0/0-0048/temp1_input
23000
Dieser Wert kann nun weiterverwendet und ausgewertet werden. Über lm-sensors kann der LM75 auch ausgelesen werden
# sensors
lm75-i2c-0-48
Adapter: s3c2410-i2c
temp1:       +22.5 C  (high = +80.0 C, hyst = +75.0 C)

11   Ampel LEDs über GPIO

11.1   Anschluss

Als weiteres kleines Projekt sollen 3 LEDs in Ampelfarben direkt über den GPIO Bus angesprochen werden. Das Vorgehen ist dabei dem beim I2C Bus relativ ähnlich.
Hierfür werden die LEDs auf dieselbe Platine wie der Temperatursensor gelötet und 3 Pins(9-11) auf dem Connector 4 ausgewählt (vgl. Liste oben).
Laut Handbuch sind den Pins hier die entsprechenden EINT Werte zugeordnet, die wiederrum unter GPIO Interface (Seite 274) nachlesen werden können.
images/GPIO.png
Nun muss im Kernel die GPIO Erweiterung ausgewählt werden, damit im sysfs wieder der entsprechende Eintrag angelegt wird:
# ls -al /sys/class/gpio/
total 0
drwxr-xr-x    2 root     root             0 Jun 10 01:46 .
drwxr-xr-x   29 root     root             0 Jun 10  2011 ..
--w-------    1 root     root          4096 Jun 10 01:46 export
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip0 -> ../../devices/virtual/gpio/gpiochip0
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip128 -> ../../devices/virtual/gpio/gpiochip128
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip160 -> ../../devices/virtual/gpio/gpiochip160
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip192 -> ../../devices/virtual/gpio/gpiochip192
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip224 -> ../../devices/virtual/gpio/gpiochip224
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip256 -> ../../devices/virtual/gpio/gpiochip256
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip32 -> ../../devices/virtual/gpio/gpiochip32
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip64 -> ../../devices/virtual/gpio/gpiochip64
lrwxrwxrwx    1 root     root             0 Jun 10 01:46 gpiochip96 -> ../../devices/virtual/gpio/gpiochip96
--w-------    1 root     root          4096 Jun 10 01:46 unexport
Hier müssen wir nun den entsprechenden GPIO Port berechnen. Dieser ergibt sich aus der Nummerierung des GPIO Headers, hier GPF-X.
Da jeder Header 32 Ports hat, muss man hier 5 * 32 + das entsprechende Bit Rechnen, um den entsprechenden GPIO Bereich auszuwählen. (Hier also z.B. für GPF0: 32 * 5 + 0 = 160)
# ls -al /sys/class/gpio/gpiochip160/
total 0
drwxr-xr-x    3 root     root             0 Jun 10 01:46 .
drwxr-xr-x   11 root     root             0 Jun 10 01:46 ..
-r--r--r--    1 root     root          4096 Jun 10 01:48 base
-r--r--r--    1 root     root          4096 Jun 10 01:48 label
-r--r--r--    1 root     root          4096 Jun 10 01:48 ngpio
drwxr-xr-x    2 root     root             0 Jun 10 01:48 power
lrwxrwxrwx    1 root     root             0 Jun 10 01:48 subsystem -> ../../../../class/gpio
-rw-r--r--    1 root     root          4096 Jun 10 01:48 uevent

# cat label
GPIOF
Hier kann man sich vergewissern, dass man auch wirklich auf dem richtigen Header unterwegs ist. Indem man die entsprechend gewünschte Pinnummer mittels echo in die export Datei schreibt, bewegt man den Kernel dazu, einen entsprechenden neuen Ordner anzulegen:
# echo 160 > export
# cd gpio160/
# ls -al
total 0
drwxr-xr-x    3 root     root             0 Jun 10 01:50 .
drwxr-xr-x   12 root     root             0 Jun 10 01:46 ..
-rw-r--r--    1 root     root          4096 Jun 10 01:50 active_low
-rw-r--r--    1 root     root          4096 Jun 10 01:50 direction
-rw-r--r--    1 root     root          4096 Jun 10 01:50 edge
drwxr-xr-x    2 root     root             0 Jun 10 01:50 power
lrwxrwxrwx    1 root     root             0 Jun 10 01:50 subsystem -> ../../../../class/gpio
-rw-r--r--    1 root     root          4096 Jun 10 01:50 uevent
-rw-r--r--    1 root     root          4096 Jun 10 01:50 value
Hier wird nun der Ordner gpio160 erstellt. Darin muss nun die Richtung des GPIO Ports eingestellt werden, da die meisten Pins sowohl als Input wie auch als Output fungieren können.
# cat direction
in
# echo out > direction
# cat direction
out
Der aktuelle Wert kann über die value ausgelesen werden:
# cat value
0
# echo 1 > value
# cat value
1
Schreibt man hier entsprechend eine 1 auf den value Wert, geht der jeweiligen LED ein Lichtlein auf...
images/platine2.png

11.2   Testskript

Um den Ampelcharakter nochmal zu testen, ist ein kleines Bashskript entstanden, das auch über das Webinterface aufrufbar ist:
#!/bin/bash

# LED GPIO Ports
LEDR=160
LEDY=161
LEDG=162

# Path to GPIO ports
GPIOPATH=/sys/class/gpio

# Amount of times to test traffic lights
AMOUNT=3

# Function to write on LED
led()
{
                echo $2 > $GPIOPATH/gpio$1/value
}

# Check if gpio folder exists for each LED
if  [ ! -d $GPIOPATH/gpio$LEDR/ ]; then
                echo $LEDR > $GPIOPATH/export
                echo "out" > $GPIOPATH/gpio$LEDR/direction
fi
if  [ ! -d $GPIOPATH/gpio$LEDY/ ]; then
                echo $LEDY > $GPIOPATH/export
                echo "out" > $GPIOPATH/gpio$LEDY/direction
fi
if  [ ! -d $GPIOPATH/gpio$LEDG/ ]; then
                echo $LEDG > $GPIOPATH/export
                echo "out" > $GPIOPATH/gpio$LEDG/direction
fi

# Start traffic light
while [ $AMOUNT -gt 0 ]; do

                # do the show
                led $LEDR 1
                sleep 1
                led $LEDY 1
                sleep 1
                led $LEDG 1
                sleep 2
                led $LEDG 0
                sleep 1
                led $LEDY 0
                sleep 1

                #decrement counter
                let AMOUNT=AMOUNT-1
done

led $LEDR 0

11.3   Buzzer über GPIO

Zusätzlich zur Ampel soll für eine mögliche Alarmschaltung auch der onboard Sound Buzzer verwendet werden. Dieser ist onboard mit dem GPIO Pin 32 verlötet. Das Vorgehen über sysFS ist hier ähnlich zu den LEDs
echo 32 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio32/direction
echo 1 > /sys/class/gpio/gpio32/value
Daraufhin ertönt ein schriller Ton, der schnell wieder abgeschaltet werden sollte. Mit seiner hohen Frequenz eignet er sich aber z.B. optimal für einen Alarm.

12   Grafikausgaben

Für Ausgaben auf das Display können mehrere Grafikbuffer verwendet werden. QT, SDL, GTK oder Framebuffer - die Auswahl ist hier kaum überschaubar. Der Einfachheit halber wird der Framebuffer aus directfb verwendet, da man mit diesem mit einem Bashkommando sofort ein Ergebnis hat, ohne große Konfigurationen zu machen.
# fbv /var/www/rrd/temp_lcd.png

fbv - The Framebuffer Viewer
/var/www/rrd/temp_lcd.png
240 x 321
Hier wird ein durch rrdtool erzeugter Graph, auf den näher eingegangen wird, direkt auf das Display bzw. den Framebuffer geschrieben:
images/display.png
Eine einfache Methode um Fließtext auf das Display auszugeben ist ein simples
echo "Hallo World" > /dev/tty1
Hier ist die Systemkonsole tty1 direkt auf das Display umgelenkt.

13   Erweiterung des Dateisystems

Damit das System nun auch wirklich sinnvolle Tätigkeiten vollziehen kann, müssen neue Pakete mit Funktionen hinzugefügt werden. Im Folgenden wird die Buildroot Konfiguration dargestellt und teilweise erklärt.
Package Selection for the target:
* BusyBox Version (BusyBox 1.18.x)
* Show packages that are also provided by busybox
BusyBox ist eine Art eierlegende Wollmilchsau: Es ist eine Sammlung der wichtigsten Kommandozeilenprogramme die auf Linux Systemen vorhanden sind in einer einzigen ausführbaren Datei. Der Vorteil darin besteht in der Größe dieser einzelnen kleinen Datei, die dadurch optimal für embedded Systeme geeignet ist. Durch die Menge der vorhanden kleinen Programme ist eine sehr hohe Vielfältigkeit gegeben, die kombiniert mit der erreichten Größe aber auch ihren Tribut zollt: Die Programmvarianten sind abgespeckt auf die üblichsten Befehle. Sollte man aber von einem Programm die ausführliche Version benötigen, muss man halt deren Sourcecode runterladen und Cross kompilieren.
Um noch etwas an Platz zu sparen wird BusyBox gegen die uClibc gelinkt, die viele der benötigten Operationen und Funktionen bereits beinhaltet.
Graphic libraries and applications:
* rrdtool
* directfb
* fbdump
* fbgrab
* fbset
* fbv
* imagemagick
Um Grafiken zu erzeugen und zu verwenden, benötigt man z.B. den Framebuffer (fb). imagemagick ist ein leistungsfähiges Paket um Bilder über die Kommandozeile zu bearbeiten. Hier gibt es auch noch mächtige Grafik Frameworks wie SDL, GTK, QT oder gar X Window, die auch noch mehr Möglichkeiten bieten, u.a. die Benutzung des Touchscreens.
Hardware handling:
* e2fsprogs
* i2c-tools
* input-tools
* lm-sensors
* mtd/jffs2 tools
* setserial
Auf dem Board selber ist bereits Flashspeicher verbaut - Um diesen Bearbeiten oder Löschen zu können, werden die sogenannten mtd-tools benötigt. Bootloader und co verwenden als Dateisystem JFFS2, das eigentliche System setzt aber auf ext2. Um hier Partitionen bearbeiten zu können, können die eventuell e2fsprogs sinnvoll sein. Die i2c-tools werden für den Zugriff auf den I2C Bus benötigt, lm-sensors wiederrum hilft beim Auslesen von Sensordaten, u.a. dem LM75.
Interpreter languagues and scripting:
* microperl
microperl ist eine abgespeckte Version von PERL, einer sehr mächtigen Skriptsprache. Diese vereint u.a. Webausgaben mit Bashbefehlen und ist weit verbreitet.
Network applications:
* dropbear
* lighttpd
* netsnmp
* ntp
* wget
dropbear dient hier als sparsamer SSH Server, der sehr nützlich ist wenn das Board an einem anderen Standort ist oder man sich nicht per seriellem Port verbinden möchten - hier benutzt man stattdessen das SSH Protokoll. lighttpd dient als kleiner Webserver, doch dazu später mehr. netsnmp heißt das Paket, was die benötigten libraries und binaries für den SNMP daemon bereit stellt und ntp eben das gleiche für den Network time daemon.
wget kann nützlich sein, wenn man Daten aus dem Internet oder von einem anderen Computer laden möchte, eine Alternative hierzu wäre curl. Die Version von wget, die Busybox mitbringt, ist recht eingeschränkt.
Shell and utilites:
* screen
Mit screen lassen sich Konsolen virtualisieren. Möchte man z.B. die Ausgabe eines Programmes in nur eine bestimmte Konsole haben, startet man diese. Damit ist man dort komplett abgeschottet von der eigentlichen Konsolensitzung, kann aber jederzeit wechseln und benötigt nur ein Fenster.
System tools:
* htop
* syslogd
htop ist ein sehr gutes Überwachungstool für aktuelle Prozesse und die Systemauslastung. Hiermit kann schnell und einfach überprüft werden, wie viel Last und Speicher jeder Prozess produziert und wie viel Speicher er wirklich benötigt.
syslogd ist einer der verbreitesten und beliebtesten Systemlog-daemons überhaupt. Er leitet mehrere standard Inputs in entsprechende Logfiles, hier u.a. /var/log/messages.
Text editors:
* nano
* less
In der aktuellsten Version von Buildroot wird auch der Texteditor vim mitgeliefert, jedoch in einer älteren Version und mit einigen umständlichen Bedienbarkeitseinschränkungen. Daher wird hier der etwas Kindergartenähnliche nano verwendet. less wiederum kann hilfreich sein, wenn man länge Konfigurations- oder Logdateien z.B. per cat ausgibt.

13.1   Gerätetreiber

Will man über das /dev Verzeichnis auf gewisse Funktionen des Boards zurückgreifen, muss man noch ein paar Verknüpfungen erstellen. Diese werden leider mit dem entpacken des Root Dateisystems nicht miterstellt:
mknod -m 0660 /dev/adc c 10 59
mknod -m 0660 /dev/audio c 14 4
mknod -m 0660 /dev/backlight c 10 63
mknod -m 0660 /dev/buttons c 10 61
mknod -m 0660 /dev/camera c 10 58
mknod -m 0660 /dev/controlC0 c 116 0
mknod -m 0660 /dev/cpu_dma_latency c 10 57
mknod -m 0660 /dev/device c 90 8
mknod -m 0660 /dev/dsp c 14 3
mknod -m 0660 /dev/full c 1 7
mknod -m 0660 /dev/leds c 10 62
mknod -m 0660 /dev/mixer c 14 0
mknod -m 0660 /dev/pwm c 10 60
mknod -m 0660 /dev/timer c 116 33
mknod -m 0660 /dev/watchdog c 10 130
Damit andere Benutzer auf den Framebuffer schreiben können, muss man /dev/fb0 noch die 777er Rechte geben.

13.2   Hardware Clock

Das mini2440 ist mit einem RTC Modul (real time clock) und einer Batterie ausgestattet, somit läuft die Uhr immer konstant mit. Um die Konsolenanwendung hwclock benutzen zu können, muss man jedoch erst noch einen weiteren nod anlegen und den entsprechenden Ordner erstellen:
mkdir /dev/misc
mknod /dev/misc/rtc c 254 0
Nun kann man die aktuelle Zeit im RTC Modul mit
hwclock -r
überprüfen und mit
hwclock -w
die aktuelle Systemzeit, die optimalerweise vorher mit ntp synchronisiert wurde, in das RTC Modul schreiben. Danach sollte man die Zeiten noch einmal vergleichen.
Link: http://linux.die.net/man/8/hwclock oder die entsprechende manpage

13.3   start-stop-daemon

Um einen daemon beim Bootvorgang auszuführen, kann der mitgelieferte start-stop-daemon benutzt werden. Im Ordner /etc/init.d liegen alle Startupskripte, die auf dem System vorhanden sind. Das rCS-Skript ruft beim Bootvorgang alle Skripte, der Nummerierung nach aufsteigend, in dem Ordner auf. Die meisten Dienste werden über diese Hilfestellung gestartet, müssen jedoch nicht!
Gestartet wird der daemon mit dem Parameter --start, gestoppt wird er mit --stop - der Pfad wird mit --exec angegeben.
Der start-stop-daemon liefert die Möglichkeit der automatischen Erstellung des sogenannten PID Files mit. In so einem File steht die Prozess ID des entsprechenden daemon threads. Dadurch kann der jeweilige Prozess schnell und einfach ausfindig gemacht werden und es kann verhindert werden, dass zweimal der gleiche daemon gestartet wird.
Mit dem Parameter --pidfile wird der Pfad der Pidfile angegeben und --make-pidfile erstellt diese bei Aufruf.
Wird kein PID File verwendet, kann man auch mit --name der entsprechende daemon Name beim Kill Prozess mitgegeben werden. Zusätzlich kann auch der User mitgegeben werden, der zum Starten verwendet werden soll.

13.4   NTPd

Damit Messwerte immer mit dem richten Zeitstempel versehen werden, muss natürlich kontrolliert werden, dass die eingestellte Uhrzeit auf dem Board auch korrekt ist. Diesen Job erledigt der Network Time Protocol Daemon (NTPd). Er kann jedoch in diesem Fall (Board ist abgekapselt vom Internet) nur funktionieren, wenn ein weiterer ntpd auf dem Entwicklungsrechner (siehe oben) eingerichtet wurde.
In Buildroot muss das package ntp und als Unterpunkt ntpd ausgewählt werden. Auf dem Board muss die Datei /etc/TZ auf die richtige Zeitzone angepasst werden:
GMT-1CET
Die eigentliche Konfigurationsdatei des Dienstes liegt unter /etc/default/ntpd. Dort muss der Server auf den Entwicklungsrechner angepasst werden:
NTPSERVERS="192.168.1.1"
Danach sollte der NTP daemon bei jedem Bootvorgang starten und innerhalb von 5 Sekunden die Zeit vom Entwicklungsrechner beziehen.
Getting initial time via ntp.
Starting network time protocol daemon: ntpd.

13.5   tslib

Die tslib, oder wie Buildroot Sie nennt, libts, ist eine Bibliothek, die eine Abstraktionsschicht für Touchscreen Events liefert. Mit ihr lässt sich relativ einfach auf das Touchpanel an sich zugreifen und mitgelieferte Tests durchführen.
Zu Testzwecken wurden hier einige Versuche durchgeführt. Der Kernel legt einen Symlink für das Touchpanel unter /dev/input an, hier event1. Zuerst wird eine neue Umgebungsvariable erstellt:
export TSLIB_TSDEVICE=/dev/input/event1
Danach startet man als Erstes die Kalibrierung des Displays und führt diese durch. Im Anschluss kann das Testprogramm ausführt werden, mit dem auf dem Bildschirm geschrieben und gemalt werden kann.
ts_calibrate
ts_test
images/tslib.png
Um zu checken, ob der Touchscreen überhaupt funktioniert (kernelseitig), kann man einen einfachen Check
# cat /dev/input/event1 | hexdump
0000000 6cd6 4df7 e31f 0006 0003 0000 01fa 0000
0000010 6cd6 4df7 e33f 0006 0003 0001 01c6 0000
0000020 6cd6 4df7 e345 0006 0001 014a 0001 0000
0000030 6cd6 4df7 e34a 0006 0000 0000 0000 0000
0000040 6cd6 4df7 f6ec 0006 0003 0000 01ff 0000
...
machen, worauf man bei Berührung des Panels die HEX Werte angezeigt bekommen sollte.

13.6   Perl

In Buildroot ist die Skriptsprache Perl mit entsprechendem Interpreter in der Version 5.8.8 enthalten, jedoch in einer abgespeckten Variante namens microperl. Die Sprache an sich ist sehr mächtig und lässt sich super in einer Linux-Umgebung als Alternative zu Bash oder Python integrieren.
Leider machte das Kompilieren dieser Pakete auf Ubuntu 11.04 erhebliche Probleme - egal welche Buildroot oder PERL Version verwendet wurde. Das Problem konnte leider nicht näher eingegrenzt werden, der Fehler war aber auf mehreren Systemen und Variationen mit Ubuntu 11.04 reproduzierbar. Aus diesem Grund wurde ein System Downgrade auf 10.04 LTS durchgeführt, um möglichen weiteren Fehlern aus dem Weg zu gehen.
Nach dem erfolgreichen Kompiliervorgang meldet sich microperl mit
# microperl -v
This is perl, v5.8.8 built for unknown
Leider kopiert Buildroot hier lediglich die Binary Datei von microperl auf das Root Dateisystem und kein einziges der Module. Das schränkt den Funktionsumfang von PERL erheblich ein, da eine Stärke von Perl eben in den vielfältigen Modulen liegt. Dies ist unverständlich, da die Module von Buildroot mitkompiliert werden und nur wenig Platz benötigen.
Deshalb wurden die wichtigsten Module händisch kopiert:
cp /elinux/buildroot/output/build/perl-5.8.8/lib/*.pm /nfs/rootfs/usr/lib/perl5/5.8.8
cp /elinux/buildroot/output/build/perl-5.8.8/lib/Config_heavy.pl /nfs/rootfs/usr/lib/perl5/5.8.8
Ein kurzes microperl -V gibt danach mehr Informationen als nur Fehler aufgrund fehlender Module aus:
Summary of my perl5 (revision 5 version 8 subversion 8) configuration:
Platform:
        osname=linux, osvers=2.6.32-32-generic, archname=i686-linux
        uname='linux buildroot 2.6.32-32-generic #62-ubuntu smp wed apr 20 21:54:21 utc 2011 i686 gnulinux '

Characteristics of this binary (from libperl):
  Compile-time options: PERL_USE_SAFE_PUTENV
  Built under unknown
  Compiled at Jun  3 2011 15:35:02
  @INC:
        /usr/lib/perl5/5.8.8
Nun können Skripte ausgeführt werden.

13.7   Lighttpd

Wenn man einen Webserver unter Unix benötigt, fällt zuerst der Name Apache. Die Apache Foundation administriert hier neben dem Microsoft IIS einen der meist verbreitesten Webserver der Welt. Leider ist dieser inzwischen so komplex, dass er für kleine ressourcenarme embedded Systeme nicht sonderlich geeignet ist.
Alternativ setzt man hier z.B. auf lighttpd, einen sehr schlanken und dadurch schnellen kleinen Webserver, der die rudimentärsten Funktionen mitbringt. Vorteilhaft ist hier die Standard Konfiguration, die lediglich html darstellen kann - und das sehr schnell und korrekt.
Hier wird der Ansatz des all-opt-out Prinzips wahr, da man jedes Modul einzeln erst aktivieren und konfigurieren muss. Praktisch ist hier auch die Art und Weise, wie man die Konfigdatei schreiben kann, denn dort wird ein IF/ELSE Prinzip mit Regular Expressions verwendet. Damit kann man mehrere Virtuelle Server schnell und einfach anlegen, ohne Seitenweise Handbücher mit den richtigen Parametern wälzen zu müssen.
Damit lighttpd nun korrekt ausgeführt wird, müssen im Root Dateisystem einige Dinge hinzugefügt werden, die Buildroot weglässt:
mkdir /etc/lighttpd
mkdir /var/www
addgroup -S www-data
Damit legt man die Konfigurations- und Dateiordner, sowie die benötigte Benutzergruppe www-data an. Als Nächstes benötigt man die Konfigurationsdatei:
# Verzeichnisse
server.document-root    = "/var/www/"
server.errorlog         = "/var/log/lighttpd.error.log"
server.pid-file         = "/var/run/lighttpd.pid"

# Einstellungen
server.port             = 80
server.username         = "www-data"
server.groupname        = "www-data"

# Dateitypen
mimetype.assign = (
  ".html" => "text/html",
  ".txt" => "text/plain",
  ".jpg" => "image/jpeg",
  ".png" => "image/png"
)

static-file.exclude-extensions = ( ".fcgi", ".rb", "~", ".inc" )
index-file.names = ( "index.html" )

# CGI/PERL/BASH Ausführung
server.modules += ( "mod_cgi" )
cgi.assign   = ( ".sh"  => "/bin/sh",
                                 ".pl"  => "/usr/bin/perl",
                                 ".cgi" => "/usr/bin/perl" )

# Directory Listing
dir-listing.encoding        = "utf-8"
server.dir-listing          = "enable"
Die meisten Einstellungen wie Ordner, Log-Pfade und Server-Port sollten selbsterklärend sein. Zusätzlich müssen die zu interpretierenden Dateitypen mit Optionen angegeben werden. Optional kann man hier noch einige möglicherweise gefährliche Dateitypen ausschließen und den Namen der default Website angeben (hier: index.html). Directory Listing ermöglicht das Durchsuchen eines Ordners auf dem Webserver, wenn dort keine default Website Datei vorhanden ist.
Damit der Webserver nun bei jedem Booten auch mitstartet, wurde ein Startup/Stop Skript geschrieben:
#! /bin/sh
# Lighttpd Startup Script by Morschi

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="lighttpd HTTP daemon"
NAME=lighttpd
CONFIG=/etc/lighttpd/lighttpd.conf
DAEMON=/usr/sbin/$NAME

# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0

case "$1" in
  start)
                echo -n "Starting $DESC: OK"
                start-stop-daemon -S -q -x $DAEMON -- -f $CONFIG
                echo "."
                chmod 777 /sys/class/leds/led2/brightness
                ;;
  stop) echo -n "Stopping $DESC: OK"
                start-stop-daemon -K -q -n $NAME
                echo "."
                ;;
  reload|force-reload) echo -n "Reloading $DESC configuration..."
                start-stop-daemon -K -q -n $NAME -s 1
                echo "done."
  ;;
  restart) echo "Restarting $DESC: OK"
                $0 stop
                sleep 1
                $0 start
                ;;
  *) echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
                  exit 1
                ;;
esac

exit 0
Dieses Skript namens S51lighttpd wird mit den Rechten 755 im Ordner /etc/init.d platziert. Dadurch wird es durch die rcS bei jedem Start ausgeführt.
Nun können HTML Dateien in den /var/www Ordner abgelegt und über einen beliebigen Browser aufgerufen werden. Beispielhaft wurde hier der Ordner cgi-bin angelegt und zwei kleine Skripte eingefügt, die die LED2 auf dem Board zum Leuchten bringen:
  • index.html
<html><head></head><body>Example Document powered by Morschi
<form action="cgi-bin/toggle_led2.cgi" method="post">
<input type="submit" name="sub" value="Toggle LED2">
</form></body></html>
  • cgi-bin/led.sh
#!/bin/sh
echo $2 > /sys/class/leds/led$1/brightness
  • cgi-bin/toggle_led2.cgi
#!/usr/bin/perl -w
use strict;
use warnings;
my $URL = "../index.html";
my $status = `cat /sys/class/leds/led2/brightness`;
if($status == 0){
        system("/var/www/cgi-bin/led.sh 2 1");
}else{
        system("/var/www/cgi-bin/led.sh 2 0");
}
print "Location: $URL\n\n
Aufgerufen im Browser ist das ganze dann simpel aber funktionell gehalten:
images/browser.png
Per Knopfdruck wird hier das Perlskript aufgerufen, welches den aktuellen Status der Helligkeit der LED2 ausliest. Sollte diese ausgeschaltet sein, wird Sie angeschaltet und umgekehrt. Realisiert wird dies über das erwähnte parametrisierte Bashskript - In diesem Beispiel wird aber nur die LED2 verwendet.

13.8   RRDTool

rrdtool wird in Buildroot in der Version 1.2.30 mitkompiliert, wenn dies ausgewählt wurde. Es dient dazu zeitbezogene Messdaten zu speichern, zusammenzufassen und schlussendlich auch zu visualisieren. Daten werden dabei in sogenannten .rrd Dateien gespeichert, Abkürzung für Round Robin Database. Dieser Name kommt aufgrund der wichtigsten Eigenschaft von RRD Dateien: Ihrer festgelegten Größe. Beim Erstellen der Dateien gibt man sogenannte DataStores an, die einzelne Werte oder Eigenschaften wiederspiegeln, z.B. die Temperatur des Sensors.
Zusätzlich definiert man RRA's, Round Robin Archives - die eigentlichen Datenbanken. Hier gibt man an welche Art Daten in welchem Intervall eingelesen werden, wie diese verarbeitet und wie lange Sie vorgehalten werden sollen.
Mit der festgelegten Größe einer Datenbank eignet sich rrdtool optimal für embedded Systeme, da man hier volle Kontrolle über den Speicherplatz hat. Über rrdtool werden kontinuierlich Daten in das rrd File geschrieben und sollte das Ende der Vorhaltzeit erreicht werden, also das File quasi voll sein, wird vorne der neue Wert angefügt, der Durchschnitt der zwei oder mehreren ältester Messwerte mit den angegeben Parametern berechnet und geschrieben.
Somit hat man über die gewünschte Vorhaltezeit hin genaue Ergebnisse und trotzdem gute Durchschnittswerte aus der Vergangenheit.
Die Erstellung einer RRD Datei ist nicht sonderlich kompliziert:
rrdtool create temp.rrd --step 3 \
DS:data:GAUGE:12:U:U \
RRA:AVERAGE:0.5:20:1440 \
RRA:AVERAGE:0.5:200:4320 \
RRA:MAX:0.5:20:1440 \
RRA:MAX:0.5:200:4320 \
RRA:MIN:0.5:20:1440 \
RRA:MIN:0.5:200:4320 \
Hier wird der Name der Datei temp.rrd und zusätzlich die Schrittweite von 3 Sekunden zwischen den einzelnen Messwerten deklariert. DS deklariert ein Datastore mit dem Namen temperature mit der Eigenschaft GAUGE, welches, sollte 12 Sekunden lang keine Daten erhalten werden, den Wert Unknown in die Datenbank schreibt.
RRA steht für Round Robin Archive, also eine einzelne Datenbank oder Archiv. Hier werden 2 Zeiträume definiert, jeweils mit Minimaler-, Maximaler- und Durchschnittstemperatur. Die Vorhaltezeit der Werte sind hier 1 Tag und 30 Tage und die Anzahl der zusammengerechneten Minuten (pro Eintrag) 1 bei 1 Tag und 10 bei 30 Tagen. (20 steps à 3s = 60s, 1440 Zeilen)
Daraus lässt sich auch gleich die Größe der Datei errechnen, die sofort erstellt wird und mit Unknown Werten aufgefüllt wird:
# ls -al
total 1076
-rw-r--r--    1 www-data root       1074072 Jun  6 00:46 temp.rrd
Befüllen lässt sich das RRD File ziemlich simpel mit dem Befehl
rrdtool update temp.rrd N:23
wobei zuerst der Zeitstempel (hier now) und dann der Wert übergeben wird. Man kann somit auch Daten für eine bereits vergangen Periode hinzufügen lassen.
Will man die Daten eines RRD Files nun visualisiert in Form eines Graphen ausgeben, benutzt man am einfachsten den in rrdtool mitgelieferten Grapher. Als Alternative kann man auch auf den auch frei verfügbaren GNUplot zurückgreifen.
rrdtool graph temp.png -t "Average Temperature over 1h" -s -1h --width 239 --height 175 DEF:avg=temp.rrd:temperature:AVERAGE LINE2:avg#ff0000
Hiermit deklariert man den Dateinamen und die Überschrift (-t) auf dem Bild. Als weitere Parameter können Start- (--start) und Endzeitpunkte (--end) angegeben werden, entweder mit einigen Worten oder Linux timestamps. Hier kann mit Minus und h/d/m für Stunden/Tage/Monate gearbeitet werden. Für das Ende ist standardmäßig NOW angegeben, sollte kein Parameter mitgegeben werden. Sollte hier auch für Start kein Wert angegeben werden, nimmt rrdtool den Zeitraum der letzten 24 Stunden her.
images/rrdgraph.png
Erklärung: DEF beginnt eine neue Definition, also eine neue Linie im Graphen. Danach kann eine frei gewählte Variable benannt werden, die man für weitere Zuweisungen benötigt. Zusätzlich wird das entsprechende RRD File und der DataStore Name angegeben. Das letzte Argument definiert die Art des Speichers - AVERAGE, MINIMUM oder MAXIMUM.
# rrdtool graph temp.png -t "Temperature over last 24h" --height 220 --start -24h --end now \
DEF:avg=temp.rrd:temperature:AVERAGE \
DEF:max=temp.rrd:temperature:MAX \
DEF:min=temp.rrd:temperature:MIN \
LINE2:avg#0000FF:"Average" \
LINE2:max#FF0000:"Max" \
LINE2:min#00FF00:"Min"
# 481x299
Die Zahl hinter LINE gibt die Dicke der Linie an, der Farbcode, beginnend mit einer Raute, legt die Farbe des Graphen fest. Zusätzlich kann man noch eine Benennung für die Legende hinzufügen.
Dieses Beispiel hier zeichnet einen Graphen über die letzten 30 Tage mit 3 Linien der Pixeldicke 2 in 3 unterschiedlichen Farben (für den jeweiligen Typ) und einer Legende:
images/rrdgraph_long.png
Die Anzeige macht für diese Art der Daten nur bedingt Sinn, aber man kann durchaus kurze Ausbrüche nach oben erkennen, die durch die Average Berechnung herausfallen.
Es gibt hier noch viele zusätzliche Parameter, womit man die Darstellung anpassen und optisch aufbessern kann, je nach Zweck und Aufgabengebiet. Hier einige Beispiele:
images/rrdgraph_different.png
Sollten einige Werte mal nicht passen, weil u.U. ein Hardwarefehler vorlag, kann man das RRD auch in ein XML File konvertieren und mit einem Texteditor editieren
rrdtool dump temp.rrd > temp.xml
Mit
rrdtool restore -f -r temp.xml temp.rrd
verwandelt man das XML wieder in RRD und hat die falschen Werte damit beseitigt.
Möchte man nur kurz checken, ob z.B. der daemon aktuell Werte in das rrd File pumpt oder deren exakten Wert wissen, kann man
rrdtool fetch temp.rrd AVERAGE -s -1h
verwenden.
Für das X35 Display muss man rrdtool jedoch das Bild kleiner berechnen lassen. Das Standardformat ist normalerweise 481x165 Pixel - Das Display hat jedoch nur eine physikalische Auflösung von 320x240. Dies erledigt man mit den 2 Parametern --width und --height beim Aufrufen der Grapherfunktion. Hier sollte man beim Berechnen beachten, das Überschriften, Legenden und Rand auch ein paar Pixel benötigen.
Hat man in Buildroot das Paket imagemagick hinzugefügt, so ist man mit
convert -rotate 270 temp_lcd.png temp_lcd.png
in der Lage, das Bild um 270° drehen zu lassen, um die Breite des Displays voll auszunutzen. Hat man auch die framebuffer Pakete mit installiert, kann das Bild jetzt Displayfüllend ausgegeben werden:
fbv temp_lcd.png
Damit der blinkende Cursor der Konsole unterdrückt wird, schaltet man das Blinken aus. Danach wird die Grafik einfach vom nächsten Bild überschrieben:
echo 0 > /sys/class/graphics/fbcon/cursor_blink
Link zu einem nützlichen Rechner: http://eccentric.cx/misc/rrdcalc.html

13.9   SNMPd

Sollte man z.B Wetterstationen o.ä. mit dem mini2440 und dem Temperatursensor verwirklich wollen und diese zentral auslesen über ein Netzwerk Management System wie z.B. Nagios, dann ist es hilfreich, wenn die benötigten Werte der Stationen über SNMP auslesbar sind. SNMP ist die Kurzform von Simple Network Management Protocol und wird wie der Name bereits sagt für einfache Abfragen von Werten über ein verteiltes Netzwerk benutzt.
Die abzufragenden Werte mit OIDs hierarchisch durchnummeriert und in MIBs für verschiedene Typen oder Produkte gesammelt. In diesem Rahmen ist z.B. die LM-SENSORS-MIB.txt (vgl. Website lmsensors) von Bedeutung. Eine weitere Ausführung würde den Rahmen hier sprengen.
Das Net-SNMP Paket unterstützt mit der lmsensors MIB auch den LM75 Temperatursensor (http://projects.debianfr.net/projects/pkg-net-snmp/browser/net-snmp/trunk/agent/mibgroup/ucd-snmp/lmSensors.c?rev=1). Dafür muss jedoch das entsprechende lmsensors Paket in Buildroot mit hineinkompiliert werden (vgl. BR Konfiguration).
Ob der Sensor erkannt wird, kann man mit dem kurzen Aufruf
# sensors
        lm75-i2c-0-48
        Adapter: s3c2410-i2c
        temp1:       +23.0 C  (high = +80.0 C, hyst = +75.0 C)
testen. Anschließend muss die package/netsnmp/netsnmp.mk, also das automatische Makefile von Buildroot, um folgende configure Anweisungen erweitert werden
--with-mib-modules="ucd-snmp/lmsensorsMib" --with-ldflags="-lsensors"
Ob die entsprechende Konfiguration nun klappt, kann mit einem kurzen Check der von snmpd benutzten libraries getestet werden:
# ldd /usr/sbin/snmpd
libnetsnmpagent.so.25 => /usr/lib/libnetsnmpagent.so.25 (0x40181000)
libnetsnmpmibs.so.25 => /usr/lib/libnetsnmpmibs.so.25 (0x40103000)
libnetsnmp.so.25 => /usr/lib/libnetsnmp.so.25 (0x401af000)
libdl.so.0 => /lib/libdl.so.0 (0x400ef000)
libsensors.so.4 => /usr/lib/libsensors.so.4 (0x4013b000)
libc.so.0 => /lib/libc.so.0 (0x4022b000)
libm.so.0 => /lib/libm.so.0 (0x40150000)
ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x40071000)
Ist hier die libsensors vermerkt, sollte alles klappen.
Eine simple Konfigurationsdatei, die alle Informationen über das System - einschließlich Prozesse und Sensoren, ausgibt könnte so aussehen:
# snmpd Config File
syslocation  home
syscontact  "Michael Morscher, morscher@hs-augsburg.de"
sysdescr  "mini2440 Wetterstation 1"
sysservices 12

# Access policy
rocommunity  public
Platziert wird diese unter dem Namen /etc/snmp/snmpd.conf und das bereits enthaltene Startskript /etc/init.d/S59snmpd lädt diese standardmäßig bei jedem Bootvorgang mit.
Der allgemeine Test, ob Berechtigungen stimmen und der daemon läuft, kann über einen einfachen schnelle snmpwalk durchgeführt werden:
snmpwalk -v1 -c public 192.168.1.2
Darauf werden alle MIB Dateien und verfügbaren Werte ausgegeben
SNMPv2-MIB::sysDescr.0 = STRING: "mini2440 Wetterstation 1"
SNMPv2-MIB::sysObjectID.0 = OID: SNMPv2-SMI::enterprises.8072.3.2.10
SNMPv2-MIB::sysUpTime.0 = Timeticks: (61681) 0:10:16.81
SNMPv2-MIB::sysContact.0 = STRING: "Michael Morscher, morscher@hs-augsburg.de"
SNMPv2-MIB::sysName.0 = STRING: deathstar
SNMPv2-MIB::sysLocation.0 = STRING: home
Unter anderem die allgemeinen Systeminformationen, die wir zuvor festgelegt haben.
IF-MIB::ifNumber.0 = INTEGER: 2
IF-MIB::ifIndex.1 = INTEGER: 1
IF-MIB::ifIndex.2 = INTEGER: 2
IF-MIB::ifDescr.1 = STRING: lo
IF-MIB::ifDescr.2 = STRING: eth0
IF-MIB::ifType.1 = INTEGER: softwareLoopback(24)
IF-MIB::ifType.2 = INTEGER: ethernetCsmacd(6)
IF-MIB::ifMtu.1 = INTEGER: 16436
IF-MIB::ifMtu.2 = INTEGER: 1500
IF-MIB::ifSpeed.1 = Gauge32: 10000000
IF-MIB::ifSpeed.2 = Gauge32: 100000000
IF-MIB::ifPhysAddress.1 = STRING:
IF-MIB::ifPhysAddress.2 = STRING: 4:25:fe:ed:0:18
IF-MIB::ifAdminStatus.1 = INTEGER: up(1)
IF-MIB::ifAdminStatus.2 = INTEGER: up(1)
IF-MIB::ifOperStatus.1 = INTEGER: up(1)
IF-MIB::ifOperStatus.2 = INTEGER: up(1)
IF-MIB::ifLastChange.1 = Timeticks: (0) 0:00:00.00
IF-MIB::ifLastChange.2 = Timeticks: (0) 0:00:00.00
IF-MIB::ifInOctets.1 = Counter32: 40527
IF-MIB::ifInOctets.2 = Counter32: 8100788
Hier sind die zwei TCP/IP Netzwerkinterfaces aufgelistet: Loopback und eth0
HOST-RESOURCES-MIB::hrSWRunIndex.776 = INTEGER: 776
HOST-RESOURCES-MIB::hrSWRunIndex.779 = INTEGER: 779
HOST-RESOURCES-MIB::hrSWRunIndex.783 = INTEGER: 783
HOST-RESOURCES-MIB::hrSWRunIndex.787 = INTEGER: 787
HOST-RESOURCES-MIB::hrSWRunIndex.788 = INTEGER: 788
HOST-RESOURCES-MIB::hrSWRunIndex.789 = INTEGER: 789
HOST-RESOURCES-MIB::hrSWRunIndex.965 = INTEGER: 965
HOST-RESOURCES-MIB::hrSWRunPath.776 = STRING: "/usr/sbin/ntpd"
HOST-RESOURCES-MIB::hrSWRunPath.779 = STRING: "/usr/sbin/dropbear"
HOST-RESOURCES-MIB::hrSWRunPath.783 = STRING: "/usr/sbin/lighttpd"
HOST-RESOURCES-MIB::hrSWRunPath.787 = STRING: "-sh"
HOST-RESOURCES-MIB::hrSWRunPath.788 = STRING: "/sbin/syslogd"
HOST-RESOURCES-MIB::hrSWRunPath.789 = STRING: "/sbin/klogd"
HOST-RESOURCES-MIB::hrSWRunPath.965 = STRING: "/usr/sbin/snmpd"
HOST-RESOURCES-MIB::hrSWRunParameters.783 = STRING: "-f /etc/lighttpd/lighttpd.conf"
HOST-RESOURCES-MIB::hrSWRunParameters.788 = STRING: "-n -m 0"
HOST-RESOURCES-MIB::hrSWRunParameters.789 = STRING: "-n"
HOST-RESOURCES-MIB::hrSWRunParameters.790 = STRING: "-f /var/log/messages"
HOST-RESOURCES-MIB::hrSWRunParameters.965 = STRING: "-Lsd -Lf /dev/null -p /var/run/snmpd.pid 127.0.0.1"
Damit kann auch überprüft werden, ob alle gewollten Prozesse laufen.
Wenn man jetzt den aktuellen Wert des LM75 Temperatursensor remote abfragen möchte, benötigt man folgendes Kommando:
# snmpwalk -v1 -c public 192.168.1.2 lmSensors
LM-SENSORS-MIB::lmTempSensorsIndex.1 = INTEGER: 1
LM-SENSORS-MIB::lmTempSensorsDevice.1 = STRING: temp1
LM-SENSORS-MIB::lmTempSensorsValue.1 = Gauge32: 23000

13.10   cron

Cron ist eine Art Verwaltung für wiederkehrende Aufträge. Man kann hier das Ausführen von Kommandos oder Skripten automatisieren und zu bestimmten Zeitpunkten aufrufen lassen, wie alle 5 Minuten, jeden Tag, der Erste des Monats, etc.
Zuerst löscht man den Symlink, den Buildroot erstellt hat (/var/spool -> /tmp) und erstellt die Ordner /var/spool/cron/crontab
rm /var/spool
mkdir /var/spool
mkdir /var/spool/cron
mkdir /var/spool/cron/crontab
Nun legt man dort ein File an, unter welchem Benutzer der cronjob ausgeführt werden soll, in diesem Falle wäre das root:
# #For more information see the manual pages of crontab(5) and cron(8)
#
## m h  dom mon dow   command
*/5 * * * *   /var/www/cgi-bin/pic_reload.cgi
Hier wird der jeweilige Befehl und die Häufigkeit des Ausführens eingetragen. Hier wird das Skript pic_reload.cgi alle 5 Minuten erneut aufgerufen, dazu später mehr.
Damit der cronjob daemon bei jedem Bootvorgang startet und handlich benutzt werden kann, legt man unter /etc/init.d eine Datei mit dem Namen S52crond an und kopiert folgenden Inhalt hinein:
#! /bin/sh
# Crond Startup Script by Morschi

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="cronjob daemon"
LOGFILE=/var/log/crond.log
NAME=crond
DAEMON=/usr/sbin/$NAME

# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0

case "$1" in
  start)
                echo -n "Starting $DESC: OK"
                start-stop-daemon -S -q -x $DAEMON -- -L $LOGFILE
                echo "."
                ;;
  stop) echo -n "Stopping $DESC: OK"
                start-stop-daemon -K -q -n $NAME
                echo "."
                ;;
  reload|force-reload) echo -n "Reloading $DESC configuration..."
                start-stop-daemon -K -q -n $NAME -s 1
                echo "done."
  ;;
  restart) echo "Restarting $DESC: OK"
                $0 stop
                sleep 1
                $0 start
                ;;
  *) echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
                  exit 1
                ;;
esac

exit 0
Hier sollte man nicht vergessen, das Startup-Skript wieder ausführbar (775) zu machen.

14   Temperatur Server

Da nun die meisten Grundkomponenten konfiguriert sind und funktionieren, benötige wir noch einige Dinge um den Funktionsumfang des Projekts abzurunden. Hier soll ein Daemon mit Startskript geschrieben werden und dieser in ein eigenes Buildroot package eingepackt werden.

14.1   Daemon

Ein Daemon beschreibt ein Programm was hauptsächlich im Hintergrund läuft und auf Anfragen wartet. Es gibt keinerlei Output auf die Konsole aus, sondern loggt nur in Dateien oder das syslog.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <assert.h>
#include <signal.h>

// allgemein
#define DAEMON_NAME "tempd"
#define LOG_PATH "/var/log/tempd.log"
#define PID_FILE "/var/run/tempd.pid"
#define QUERY_TIME 3

// spezifisch
#define SENSOR_PATH "/sys/devices/platform/s3c2440-i2c/i2c-0/0-0048/temp1_input"
#define RRD_PATH "/usr/bin/rrdtool"
#define RRD_FILE "/var/www/rrd/temp.rrd"
#define RRD_TIME_1 "1h"
#define RRD_TIME_2 "1d"
#define RRD_TIME_3 "30d"

int main(void) {

                float temperature;
                char command[512];
                char buffer[6];
                int counter;

                // Prozess ID
                pid_t pid, sid; // Prozess und Session ID
                pid = fork(); // Forken des Parent Prozesses
                if (pid < 0) { exit(EXIT_FAILURE); } // Fehler bei merkwürdiger Process ID
                if (pid > 0) { exit(EXIT_SUCCESS); } // Kill Vater Prozess
                umask(0);  // Change file mode mask

                // Setup syslog logging - see SETLOGMASK(3)
                #if defined(DEBUG)
                        setlogmask(LOG_UPTO(LOG_DEBUG));
                        openlog(DAEMON_NAME, LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
                #else
                        setlogmask(LOG_UPTO(LOG_INFO));
                        openlog(DAEMON_NAME, LOG_CONS, LOG_USER);
                #endif

                // Session ID
                sid = setsid(); // Session ID für Kind Prozess erzeugen
                pid = getpid();
                if (sid < 0) { exit(EXIT_FAILURE); } // Session ID Checken
                if ((chdir("/")) < 0) { exit(EXIT_FAILURE); } // Wechsel PATH

                /* Close out the standard file descriptors */
                close(STDIN_FILENO);
                close(STDOUT_FILENO);
                close(STDERR_FILENO);

                // Correct PID file creates by start-stop daemon
                syslog(LOG_INFO, "starting the daemonizing process with pid %i", pid);
                FILE *datei;
                datei = fopen(PID_FILE, "w");
                fprintf(datei, "%i", pid);
                fclose(datei);

                counter = 0;

                // Real Work forever
                while (1) {

                        // Get Temperature
                        FILE *datei;
                        datei = fopen(SENSOR_PATH, "r");
                        fgets(buffer, 6, datei);
                        temperature = atof(buffer);
                        temperature = temperature / 1000;
                        fclose(datei);

                        // Prepare RRD update and do it
                        sprintf(command, "%s update %s N:%.1f", RRD_PATH, RRD_FILE, temperature);
                        system(command);

                        if(counter == 20){

                                // File 1
                                sprintf(command, "%s graph /var/www/rrd/temp_%s.png -t \"Average Temperature over last %s\" -s -%s --height 220 DEF:avg=%s:temperature:AVERAGE LINE2:avg#FF0000", RRD_PATH, RRD_TIME_1, RRD_TIME_1,RRD_TIME_1, RRD_FILE);
                                system(command);

                                // File 2
                                sprintf(command, "%s graph /var/www/rrd/temp_%s.png -t \"Average Temperature over last %s\" -s -%s --height 220 DEF:avg=%s:temperature:AVERAGE LINE:avg#FF0000 AREA:avg#FF0000", RRD_PATH, RRD_TIME_2, RRD_TIME_2, RRD_TIME_2, RRD_FILE);
                                system(command);

                                // File 3
                                sprintf(command, "%s graph /var/www/rrd/temp_%s.png -t \"Average Temperature over last %s\" -s -%s --height 206 DEF:avg=%s:temperature:AVERAGE DEF:max=%s:temperature:MAX DEF:min=%s:temperature:MIN LINE2:avg#0000FF:\"Average\" LINE2:max#FF0000:\"Maximum\" LINE2:min#00FF00:\"Minimum\"", RRD_PATH, RRD_TIME_3, RRD_TIME_3, RRD_TIME_3, RRD_FILE, RRD_FILE, RRD_FILE);
                                system(command);

                                counter = 0;
                        }

                        // Wait
                        sleep(QUERY_TIME);
                        counter++;
                }
   exit(EXIT_SUCCESS);
}
Zuerst werden einige Pfade festgelegt. Normalerweise werden diese über eine eigene Funktion aus einer Konfigurationsdatei geladen, darauf wird hier jedoch der Einfachheit halber verzichtet. Danach wird der Vaterprozess gestartet und geschlossen, das Ergebnis ausgewertet. Nachdem der Kindprozess gestartet ist, wird eine Session ID erzeugt und die aktuelle Prozess ID ausgelesen. Zusätzlich wird das Startverzeichnis auf das Root Verzeichnis / gelegt.
Da der start-stop-daemon offensichtlich in die PID File die Prozess ID des Vater Prozesses und nicht die des Kindprozesses schreibt, muss hier die PID File aktualisiert werden. Logischerweise ist die ProzessID des Kindprozesses meistens eine um 1 erhöhte, da dieser kurz im Anschluss daran gestartet wird.
Die eigentliche Arbeit wird danach in der while Schleife ausgeführt, die den daemon quasi unendlich lang laufen lässt. Hier wird der aktuelle Wert des Temperatursensors ausgelesen, das Kommando generiert und ausgeführt. Aufgerufen wird das rrdtool alle 3 Sekunden mit der Update Funktion, die den aktuellen Temperaturwert in das RRD File schreibt. Zusätzlich werden jede Minute (20*3s) 3 Graphen mit unterschiedlichen Werten und Aussehen produziert (1h, 1d, 30d).
Die eigentliche Arbeit wird danach in der while Schleife ausgeführt, die den daemon quasi unendlich lang laufen lässt. Hier wird der aktuelle Wert des Temperatursensors ausgelesen, das Kommando generiert und ausgeführt. Aufgerufen wird das rrdtool alle 3 Sekunden mit der Update Funktion, die den aktuellen Temperaturwert in das RRD File schreibt. Zusätzlich werden jede Minute (20*3s) 3 Graphen mit unterschiedlichen Werten und Aussehen produziert (1h, 1d, 30d).
Diese können auf einer angepassten Website ausgegeben werden:
<html><head></head><body>
<h2>FriendlyARM mini2440 Studienarbeit by Michael Morscher</h2>
<h3>Actions:</h3>
<form action="cgi-bin/toggle_led2.cgi" method="post">
<input type="submit" name="sub" value="Toggle LED2">
</form>
<form action="cgi-bin/pic_reload.cgi" method="post">
<input type="submit" name="sub" value="Reload graph">
</form>
<h3>Graphs:</h3>
<img src="rrd/temp_1h.png"><br>
<img src="rrd/temp_1d.png"><br>
<img src="rrd/temp_30d.png">
</body></html>
Kompiliert werden kann der daemon für ARM mit der ARM GCC Toolchain mit dem Kommando
arm-linux-gcc -o tempd tempd.c
Das ausführbare übersetzte Binärfile muss nun in das Target Root Filesystem unter /usr/sbin kopiert werden, dort wo auch andere daemons gelagert werden.

14.2   Startup Skript

Damit der tempd nun gestartet wird, wird ein Startup Skript wie bei den anderen daemons erstellt:
#! /bin/sh
# Crond Startup Script by Morschi

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="temperature daemon"
PIDFILE=/var/run/tempd.pid
NAME=tempd
DAEMON=/usr/sbin/$NAME

case "$1" in
  start)
                echo -n "Starting $DESC: OK"
                start-stop-daemon -S -m -q -x $DAEMON -p $PIDFILE
                echo "."
                ;;
  stop) echo -n "Stopping $DESC: OK"
                start-stop-daemon -K -q -p $PIDFILE
                echo "."
                ;;
  restart) echo "Restarting $DESC: OK"
                $0 stop
                sleep 1
                $0 start
                ;;
  *) echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2
                  exit 1
                ;;
esac

exit 0
Dieses legt auch gleich das entsprechende pid File an, welches jedoch wie erwähnt nicht korrekt ist, da eine um 1 zu niedrige ProzessID darin abgelegt wird.

14.3   Package

14.3.1   Package Erstellung

Nun müssen der Sourcecode und das Startup-Skript zusammen in ein Paket für Buildroot verpackt werden. Dafür müssen wir erst einmal das Verzeichnis anlegen, wo alle Dateien gesammelt werden sollen
mkdir /path/to/buildroot/package/tempd
Die Konfiguration des Paketes übernimmt in diesem Ordner die Config.in. Diese legt man wie folgt an
config BR2_PACKAGE_TEMPD
bool "tempd"
help
  tempd is a small temperature polling daemon that collects data and writes it in a RRD Database

  http://www.hs-augsburg.de
Diese Konfigdatei basiert auf dem Kernel Framework Kconfig und gibt lediglich die Beschreibungsdaten des Paketes an. Damit das Paket korrekt gebaut wird, muss ein entsprechendes *.mk File angelegt werden:
TEMPD_VERSION=1.0
TEMPD_SOURCE=tempd-$(TEMPD_VERSION).tar.bz2
TEMPD_SITE=httpd://www.hs-augsburg.de/~morscher/elinux/tempd
TEMPD_DIR=$(BUILD_DIR)/tempd-$(TEMPD_VERSION)
TEMPD_CAT:=$(BZCAT)
TEMPD_BINARY:=tempd
TEMPD_TARGET_BINARY:=usr/bin/$(TEMP_BINARY)
Hier wird die Version des Paketes festgelegt, welche auch für den Dateinamen und den Pfad, wo alles gebaut wird, benutzt wird. Zusätzlich wird die Downloadadresse und die Kompressionsmethode für Buildroot mit angegeben. Schluss endlich darf der Name des Servers und der Pfad, wo er hinkopiert werden soll, natürlich auch nicht fehlen.
Damit nun etwas geschieht, muss nun die weitere Ausführung genauer beschrieben werden
$(DL_DIR)/$(TEMPD_SOURCE):
        $(WGET) -P $(DL_DIR) $(DUMMY_SITE)/$(DUMMY_SOURCE)

$(TEMPD_DIR)/.unpacked: $(DL_DIR)/$(TEMPD_SOURCE)
        touch $@

$(TEMPD_DIR)/$(TEMPD_BINARY): $(TEMPD_DIR)/.configured
        $(MAKE) -C $(TEMPD_DIR)

(TARGET_DIR)/$(TEMPD_TARGET_BINARY): $(TEMPD_DIR)/$(TEMPD_BINARY)
        $(INSTALL) -D $(TEMPD_DIR)/$(TEMPD_BINARY) $@
        $(STRIPCMD) $@
Hier wird erst gecheckt, ob das Paket bereits runtergeladen wurde in der aktuellsten Version, sonst wird es geladen. Danach wird gecheckt, ob es erfolgreich entpackt wurde und im letzten Schritt wird der eigentliche Kompilier Prozess angestoßen.
Der letzte Schritt kopiert die entsprechende Binary Datei ins Target Filesystem.
Sinnvoll sind auch Aufräumroutinen á la
tempd-clean:
        -$(MAKE) -C $(TEMPD_DIR) clean
tempd-dirclean:
        rm -rf $(TEMPD_DIR)

14.3.2   Package Einbindung

Buildroot muss das Paket jedoch auch noch bekannt gemacht werden, dazu fügt man der package/Config.in das Paket hinzu, hier in der Hardware handling Sektion:
source "package/tempd/Config.in"
Nun kann in Buildroot das Paket ausgewählt werden und die entsprechenden Skripte werden der Reihe nach ausgeführt

14.4   Displaygraph

Um auf dem Display einen aktuellen Graphen anzeigen zu lassen, benötigt man einerseits den oben erwähnten cronjob, der alle 5 Minuten das pic_reload.cgi ausführt:
#!/usr/bin/perl -w
use strict;
use warnings;
my $URL = "../index.html";
my $WWWPATH = "/var/www";
`rrdtool graph $WWWPATH/rrd/temp.png -t Temperature -s -1h --width 240 --height 175 DEF:Var2=$WWWPATH/rrd/temp.rrd:temperature:AVERAGE LINE2:Var2#ff0000`;
`convert -rotate 270 $WWWPATH/rrd/temp.png $WWWPATH/rrd/temp_lcd.png`;
`fbv $WWWPATH/rrd/temp_lcd.png`;
print "Location: $URL\n\n";
Hier wird der Graph bei Ausführung neu gezeichnet, gedreht und auf das Display ausgegeben. Alternativ zum Aufruf via cronjob kann man dieses Skript nach Bedarf auch über die Website aufrufen. Dazu fügt man zur Info am besten das umgedrehte Bild ein und darunter einen Aktualisieren Button ein:
<img src="rrd/temp.png">
<form action="cgi-bin/pic_reload.cgi" method="post">
<input type="submit" name="sub" value="Reload graph">
</form>
images/website.png
Damit allerdings der blinkende Cursor und die fehlenden Schreibrechte des Users www-data auf /dev/fb0 nicht stören, erstellt man am besten ein weiteres init-Skript
#/etc/init.d/S21screenfix
echo -n "Fixing screen issues..."
chmod 777 /dev/fb0
echo 0 > /sys/class/graphics/fbcon/cursor_blink
echo " done."
und versieht auch dieses mit ausführbaren Rechten.

15   Nagios Ampel

15.1   Allgemein

Als zweites mögliches Anwendungsbeispiel wurde die Verwendung als Nagios Ampel angedacht. Nagios ist ein Open Source Netzwerküberwachungssystem, was sehr weit beliebt und weit verbreitet ist. Genauer gesagt wurde auf den deutschen Icinga Fork des Nagios 3 Projektes gesetzt.
Hier wird z.B. in kleinen Unternehmen Server, Switche und Router abgefragt und deren Lebensdaten ausgelesen. Dazu gehören rudimentäre Pings oder Netzwerkrouten, genauso aber SNMP Abfragen von Prozessen oder gar Windows WMI Queries auf z.B. Exchange Server. Hier werden Schwellwerte für Warnung und Kritisch angegeben, die dann jeweils einen Alarm auslösen. Im Normalfall werden dann an die entsprechenden Administratoren der Systeme Emails oder SMS (je nach Implementierung) verschickt.
Ziel ist es nun hier, das Board aktiv den Überwachungsserver pollen zu lassen, ob Alarme für Kritisch (LED Rot) oder Warnung (LED gelb) vorliegen. Sollte alles in Ordnung sein soll die grüne LED leuchten. Wenn man die Umsetzung der Ampel auf mehrere stärkere LEDs umbaut, könnte man diese auch zentral in einem Büro aufhängen und jeder Mitarbeiter wüsste sofort, wenn etwas vorfällt.

15.2   Ausführung

Da Icinga im Grunde auf nagios aufsetzt, sind dort auch die grundlegenden CGI-Skripte noch vorhanden. Man nimmt hier am einfachsten das status.cgi, welches unter der IP des Servers (z.B. http://192.168.0.152/cgi-bin/status.cgi) zu erreichen ist.
images/nagios.png
Dazu baut man z.B. ein Bash Skript, was den HTTP Request dieser Website ausliest und durchsucht. Hier wurde auf Bash aufgebaut und unter anderem die libcurl benutzt, eine mächtige Konkurrenzbibliothek zu wget um HTTP Dateien zu transferieren.
#!/bin/bash

# Fixed parameters
CURLPATH=/usr/bin/curl
GPIOPATH=/sys/class/gpio
LEDR=160
LEDY=161
LEDG=162

# Icinga Server Parameter
USER=icinga
PASSWORD=th!$i$n0t$3cUR3
IP=192.168.1.200
SCRIPTPATH=icinga/cgi-bin/status.cgi

# Functions to write on LED
led(){
                echo $2 > $GPIOPATH/gpio$1/value
}

# Check if gpio folder exists for each LED
if  [ ! -d $GPIOPATH/gpio$LEDR/ ]; then
                echo $LEDR > $GPIOPATH/export
                echo "out" > $GPIOPATH/gpio$LEDR/direction
fi
if  [ ! -d $GPIOPATH/gpio$LEDY/ ]; then
                echo $LEDY > $GPIOPATH/export
                echo "out" > $GPIOPATH/gpio$LEDY/direction
fi
if  [ ! -d $GPIOPATH/gpio$LEDG/ ]; then
                echo $LEDG > $GPIOPATH/export
                echo "out" > $GPIOPATH/gpio$LEDG/direction
fi


# Get actual status from nagios
HOSTS=`$CURLPATH -s -u $USER:$PASSWORD http://$IP/$SCRIPTPATH | grep hostTotalsPROBLEMS | sed 's/.*\([0-9]\{1,3\}\).*/\1/'`
SWARNING=`$CURLPATH -s -u $USER:$PASSWORD http://$IP/$SCRIPTPATH | grep serviceTotalsWARNING | sed 's/.*\([0-9]\{1,3\}\).*/\1/'`
SUNKNOWN=`$CURLPATH -s -u $USER:$PASSWORD http://$IP/$SCRIPTPATH | grep serviceTotalsUNKNOWN | sed 's/.*\([0-9]\{1,3\}\).*/\1/'`
SCRITICAL=`$CURLPATH -s -u $USER:$PASSWORD http://$IP/$SCRIPTPATH | grep serviceTotalsCRITICAL | sed 's/.*\([0-9]\{1,3\}\).*/\1/'`

# Check if variables are NULL
if [ "$HOSTS" == '' ]; then
                HOSTS=0
fi

if [ "$SWARNING" == '' ]; then
                SWARNING=0
fi

if [ "$SUNKNOWN" == '' ]; then
                SUNKNOWN=0
fi

if [ "$SCRITICAL" == '' ]; then
                SCRITICAL=0
fi

# check for errors (LED red)
if ([ $HOSTS != 0 ] || [ $SCRITICAL != 0 ]); then
                led $LEDR 1
                led $LEDY 0
                led $LEDG 0
                exit
fi

# check for warnings (LED yellow)
if ([ $SUNKNOWN != 0 ] || [ $SWARNING != 0 ]); then
                led $LEDY 1
                led $LEDR 0
                led $LEDG 0
                exit
fi

# else everything "must" be fine (LED green)
led $LEDG 1
led $LEDR 0
led $LEDY 0
Dieses Skript kann man nun wieder als Cronjob z.B. jede Minute ausführen lassen oder z.B. von einem weiteren daemon triggern lassen. Hier könnte man wenn man die Ausführung mit C schreibt und kompiliert bestimmt einen schönen Vergleich für embedded Systeme bezüglich der Ausführungszeit machen. Eventuell wird das noch nachgepflegt, da die Ausführung aktuell gefühlte 2 Sekunden dauert, was ca. 20 Zeilen Code pro Sekunde entsprechen würde. In C würde die bestimmt ein einem Bruchteil der Zeit geschehen.

15.3   Startvorgang aller Dienste

Wenn alle Dienste entsprechend konfiguriert sind, sollte eine Shellausgabe beim Start etwa so aussehen:
VFS: Mounted root (nfs filesystem) on device 0:14.
Freeing init memory: 128K
Initializing random number generator... done.
Fixing screen issues... done.
Starting network...
ip: RTNETLINK answers: File exists
Getting initial time via ntp.
Starting network time protocol daemon: OK.
Starting dropbear sshd: OK
Starting lighttpd HTTP daemon: OK.
Starting cronjob daemon: OK.
Starting temperature daemon: OK.
Starting temp daemon: OK.
Starting network management services: OK.

Surrender! Resistance is futile!
deathstar login:

16   Overclocking

Man findet einige lustige Threads in den Foren im Internet, wo darüber diskutiert wird, ob man das Board bzw. die CPU übertakten kann. Nun, ältere Baureihen der S3C24XX waren wohl teilweise für den Betrieb bei 533 Mhz ausgelegt, die aktuellen jedoch laut Datenblatt nicht mehr. Hier wurde etwas herumprobiert und es konnte ein erfolgreicher Boot und ein stabiler Betrieb bei 460 Mhz, also einem Plus von 55 Mhz entsprechend, garantiert werden.
Man sollte jedoch das Display abmontieren, um dem Chip etwas mehr Luft zum Atmen zu lassen. Meiner Meinung ist mehr Takt nicht möglich, ohne weitere Variablen in U-Boot wie die Taktung und die Latenz des SD Rams zu ändern. Da das Board hier einen zentralen Taktgenerator für die CPU hat und die gesamte Peripherie daran gelinkt ist, wird auch der Rest mit erhöhtem Takt betrieben, was durchaus zu Problemen führen kann, neben der leicht erhöhten Temperaturabgabe.
MINI2440 # s3c24xx speed get
FCLK = 405 MHz, HCLK = 101 MHz, PCLK = 50 MHz, UCLK = 48 MHz

MINI2440 # s3c24xx speed set 460
FCLK = 460 MHz, HCLK = 115 MHz, PCLK = 57 MHz, UCLK = 48 MHz

MINI2440 # s3c24xx speed get
FCLK = 460 MHz, HCLK = 115 MHz, PCLK = 57 MHz, UCLK = 48 MHz
Auch der Kernel gibt uns die Taktänderung an, mit einer erhöhten Angabe der bogomips, also der theoretischen Leistung. Diese beträgt hier immer die Hälfte der tatsächlichen Taktfrequenz. Zu erkennen ist hier der Peripherietakt
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
CPU: VIVT data cache, VIVT instruction cache
Machine: MINI2440
Memory policy: ECC disabled, Data cache writeback
CPU S3C2440A (id 0x32440001)
S3C24XX Clocks, Copyright 2004 Simtec Electronics
S3C244X: core 460.800 MHz, memory 115.200 MHz, peripheral 57.600 MHz
CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on
und
deathstar login: root
# cat /proc/cpuinfo
Processor       : ARM920T rev 0 (v4l)
BogoMIPS        : 229.17
Features        : swp half thumb
Wie sinnvoll das Ganze bleibt, sei dahingestellt. Allerdings ist das Internet auch voll mit Fragen zu QT und Performance - Evtl. können 50Mhz mehr hier eine etwas flüssigere Wiedergabe ermöglichen...

17   Fazit

Der Bereich Embedded Linux umfasst ein sehr großes Gebiet an Wissen, das man trotz Vorlesung und Praktikum gar nicht abdecken kann. Wichtig ist hier vor allem das eigene Engagement, dass man sich hineinfrisst und nicht sofort beim Ersten Fehler auf der Konsole die Flinte ins Korn wirft. Gerade in der Anfangsphase, bevor überhaupt etwas richtig wie geplant läuft, muss man den Frust besiegen und einfach weitersuchen.
Hilfreich sind allgemeine Linux Kenntnisse, da man dann Wege und Mittel für Debugging und Entwicklung bereits kennt. Wenn man sich hier noch nicht richtig auskennt, dann benötigt man gleich sehr viel mehr Zeit als geplant.
Ich selber bringe aus einem Unternehmen und er Ausbildung bereits einiges an Linux Grund Know How mit, war jedoch bisher immer mit virtuellen Maschinen oder starken physikalischen Server beschäftigt - Dort war genug Power etc. vorhanden, so dass erst mal nur das eigentlichen Laufen der Applikationen bzw. Dienste im Vordergrund stand. War etwas Mal nicht so super toll optimiert oder gar programmiert, konnte man mit der obligatorischen Hardwareklatsche mit etwas mehr RAM oder einer Ramdisk oft Probleme lösen. Diese Mittel und Wege gibt es bei embedded Systemen natürlich nicht mehr. Genauso steht dort nun auch der Stromverbrauch im Vordergrund. 0,5 W Unterschied im Verbrauch können hier gleich einiges ausmachen.
Es wurden auch mehrere Kernelvarianten etc. an einem Voltcraft Energy Logger 3000 getestet, hier war keine Veränderung festzustellen. Eingesteckt, hat das schlechte China Netzteil bereits 0,2W verbraucht, im Betrieb bzw. Last zeigte das Energiekostenmessgerät dann bereits 2,2W an. Das ist noch verkraftbar für den 24h/7d Betrieb. Leider reicht die Performance des Boards nicht Leistungsaufwändigere Geschichten im Heimnetzwerk wie z.B. einen Mediaplayer (Video) oder Fileserver. Hierfür steht bei mir zuhause ein Seagate Dockstar mit 1,2Ghz ARM CPU, was jedoch mit 2x USB Sticks auch bereits wieder bei 4,5W liegt. Dafür sind hier u.a. Übertragungsraten am USB Lese Niveau (26MB/s) möglich.
Zusammenfassend kann gesagt werden, dass mich die Vorlesung Embedded Linux wieder ein Stück näher an das Betriebssystem Linux herangebracht hat, vor allem mit dem Fokus auf Effizienz und Verbrauch. Es muss nicht immer das Beste und schnellste Sein, auch ein kleines Board kann vollkommen ausreichen. Für mich als technischen Informatiker eröffnet sich noch die Alternative mit weiteren Mikroprozessoren zu arbeiten, jedoch fühle ich mich eher zur Informatik hingezogen. Daher ist das mini2440 ein sehr gutes Board, da auch genügend IO Spielereien und Projekte mit dem mir trotzdem vertrauten Linux benutzt werden können. Ich brauche keine Prozessoren kaufen, Platinen ätzen oder Programmierboards kaufen - das eigentliche Board reicht bereits. Und für diesen Preis, nehme ich auch ein paar W mehr in Kauf und habe dafür unendlich Möglichkeiten und nur eine kleine Hardwarelimitierung.
Ein weiterer Anwendungsfall wäre für mich eine Heimautomatisierung im Wohnzimmer gewesen. Da ich nun weiß, wie Temperatursensoren und LEDs einfach angesteuert werden, könnte man mit dem Touchscreen und QT z.B. eine nette kleine API entwickeln um jeden Heizkörper auszulesen oder gar die Rollläden oder das Licht zu steuern. Leider fehlt hier die Zeit, aber sollte in den Semesterferien das Wetter schlecht sein...

18   Anhang: Automatisierung durch Skripting

18.1   Grundlagenskript

Um die Installation von Buildroot, der Sourcery Toolchain und des mini2440 Patch Kernels zu vereinfachen, ist ein kleines Script setup.pl entstanden, das den Einstieg erleichtern soll:
#!/usr/bin/perl -w
########################################################################
#     Created by Michael Morscher (michael.morscher@hs-augsburg.de)    #
#                   Version 1.1 created 20.05.2011                                     #
########################################################################
#  Automated Script for Installing Buildroot,Toolchain and Kernel      #
########################################################################

# Print script usage
sub printusage {
        print "FriendlyARM Mini2440 Help Script\n";
        print "################################\n";
        print "Available parameters:\n";
        print "  - install-buildroot\n";
        print "  - install-toolchain\n";
        print "  - install-kernel\n";
        print "################################\n";
}
Es wird im Archiv mitgeliefert, um die Studienarbeit kleiner zu halten. Dasselbe gilt für magic.pl.

18.2   Kopierskript

Dieses Skript (magic.pl) legt bei der ersten Ausführung eine Konfigurationsdatei an, auf die später zugegriffen wird. Ziel ist es, nach dem Kompilieren des Root Dateisystems mit Buildroot alle Schritte des Umkopierens und Patchens nicht per Hand ausführen zu müssen. Das Skript hat folgende Features:
  • Benutzerabfrage nach Ordnern und Wünschen
  • Fixen von vielen Diensten (SNMP, lighttpd, cron, ntp, etc)
  • Kopieren und entpacken des Root Dateisystem aus dem Buildroot Ordner heraus
  • Anlegen von Startskripten und Beispielkonfigurationsdateien
Ausgeführt wird es im buildroot Verzeichnis mit
./scripts/magic.pl
Aufgrund der Größe ist es nur im Anhang dieser Studienarbeit enthalten. Es hilft jedem Einsteiger, innerhalb von Minuten sein mini2440 in Betrieb zu nehmen, sobald der Bootloader und NFS konfiguriert ist.
Aufgrund der Komplexität und dem Zeitmangel wurde die Entwicklung leider immer etwas unsauberer und weniger modular.