HowTo-Artikel zum Entwickeln eingebetteter Linux-Gerätetreiber als Kernelmodule. Der gesamte Inhalt dieses Artikels kann auf Raspberry Pi ausgeführt werden.
https://github.com/take-iwiw/DeviceDriverLesson/tree/master/11_01
Bisher haben wir mehrere Gerätetreiber implementiert. Jeder Gerätetreiber hatte jedoch "hardwarespezifische Informationen" direkt im Gerätetreiber.
Zum Beispiel habe ich in 5. Mal GPIO Devadler implementiert, indem ich direkt auf das speicherabgebildete Register geklickt habe. Zu diesem Zeitpunkt hatte ich Chip (SoC) -spezifische Informationen wie die Offset-Adresse im Gerät. Das ist nicht gut. (Erstens wird GPIO selbst nicht erstellt, und normalerweise werden Standardfunktionen des Linux-Kernels verwendet.)
Selbst wenn Sie die Standardfunktionen des Linux-Kernels verwenden, kann Devadora über hardwarespezifische Informationen verfügen. In 10th wurde auf I2C-Geräte mit Standard-I2C-Steuerfunktionen des Linux-Kernels zugegriffen. Die kartenspezifischen Informationen wie der I2C-Bus, an den das Ziel-I2C-Gerät angeschlossen ist, und die Slave-Adresse wurden jedoch im Gerät beschrieben.
Dies ist nach den Regeln nicht mehr zulässig. Der Grund ist, dass der Code mit jeder Variation von SoC und Board stetig zunimmt. Diese Art von hardwarespezifischen Informationen wird heute vom Gerätebaum verwaltet.
Letztes Mal habe ich einen Gerätetreiber für einen an I2C angeschlossenen Beschleunigungssensor (LIS3DH) erstellt. Zu diesem Zeitpunkt war es jedoch erforderlich, dem Kernel manuell Informationen zum I2C-Gerät bereitzustellen. Dieses Mal werde ich versuchen, diese Informationen in den Gerätebaum aufzunehmen. Dieser Artikel ist für Raspberry Pi2 Model B.
Obwohl es als "Gerätebaum" bezeichnet wird, handelt es sich tatsächlich um eine Firmware im Binärformat. Die Erweiterung lautet .dtb. Es ist unter / boot
in Raspeye. Für RaspberryPi2 Modell B wäre dies beispielsweise "/ boot / bcm2709-rpi-2-b.dtb".
ls /boot/*.dtb
/boot/bcm2708-rpi-0-w.dtb /boot/bcm2709-rpi-2-b.dtb /boot/bcm2835-rpi-a-plus.dtb /boot/bcm2835-rpi-zero.dtb
/boot/bcm2708-rpi-b.dtb /boot/bcm2710-rpi-3-b.dtb /boot/bcm2835-rpi-b.dtb /boot/bcm2836-rpi-2-b.dtb
/boot/bcm2708-rpi-b-plus.dtb /boot/bcm2710-rpi-cm3.dtb /boot/bcm2835-rpi-b-plus.dtb
/boot/bcm2708-rpi-cm.dtb /boot/bcm2835-rpi-a.dtb /boot/bcm2835-rpi-b-rev2.dtb
SoC-spezifische Informationen sollen in der DTSI-Datei gespeichert werden, und kartenspezifische Informationen sollen in der DTS-Datei gespeichert werden. Durch Kompilieren dieser mit einem speziellen Compiler namens DTC wird die zuvor erwähnte DTB-Datei erstellt. Für Raspeye 2 befindet es sich unter / linux / arch / arm / boot / dts / bcm2709-rpi-2-b.dts
.
Wie ich später schreiben werde, ist der richtige Weg, den Linux-Quellbaum einzubringen, / linux / arch / arm / boot / dts / bcm2709-rpi-2-b.dts
zu bearbeiten und zu kompilieren. In dieser Serie habe ich es jedoch auf Razpai so einfach wie möglich überprüft. Wieder werde ich es zuerst einfach versuchen.
Bitte sichern Sie bcm2709-rpi-2-b.dts
auf der SD-Karte im Voraus auf einem PC usw. Im schlimmsten Fall startet es möglicherweise nicht. Außerdem wird der Compiler (DTC) für den Gerätebaum installiert. sudo apt-get install device-tree-compiler
Kompilieren Sie anschließend die aktuell verwendete DTB-Datei in DTS zurück.
dtc -I dtb -O dts /boot/bcm2709-rpi-2-b.dtb > dis_bcm2709-rpi-2-b.dts
Dadurch wird eine Textdatei mit dem Namen "dis_bcm2709-rpi-2-b.dts" erstellt. Diese Datei enthält einen Baum mit Geräteverbindungsinformationen auf dieser Karte (Raspy 2). Zusätzlich werden auch die Registeradresse etc. aufgelistet. Angenommen, Sie verbinden diesmal LIS3DH (Slave-Adresse = 0x18) mit I2C_1. Suchen Sie zuerst den Knoten für I2C1. Es steht in der Adresse, aber anstelle von "i2c @ 7e804000". Fügen Sie dort einen Knoten namens "mydevice" hinzu. Wenn Sie "kompatibel" auf "mycompany, myoriginaldevice" setzen, wird dies als "Gerät mit dem Namen myoriginaldevice eines Herstellers namens mycompany" angegeben. Stellen Sie außerdem die Slave-Adresse anstelle von reg
ein. Dadurch erkennt der Kernel, dass auf dieser Karte ein Gerät namens "mycompany, myoriginaldevice" mit 0x18 auf I2C_1 verbunden ist.
dis_bcm2709-rpi-2-b.dts
Kürzung
i2c@7e804000 {
compatible = "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
interrupts = <0x2 0x15>;
clocks = <0x7 0x14>;
#address-cells = <0x1>;
#size-cells = <0x0>;
status = "disabled";
pinctrl-names = "default";
pinctrl-0 = <0x10>;
clock-frequency = <0x186a0>;
phandle = <0x20>;
/*Fügen Sie Ihr eigenes Gerät hinzu*/
mydevice@18 {
compatible = "mycompany,myoriginaldevice";
reg = <0x18>;
};
};
Kürzung
Kompilieren Sie nach der Bearbeitung mit dem folgenden Befehl und überschreiben Sie die ursprüngliche DTB-Datei. Stellen Sie vorerst das Zugriffsrecht ein.
dtc -O dtb -o bcm2709-rpi-2-b.dtb dis_bcm2709-rpi-2-b.dts
chmod 755 bcm2709-rpi-2-b.dtb
sudo cp bcm2709-rpi-2-b.dtb /boot/bcm2709-rpi-2-b.dtb
sudo reboot yes
Sie können die Gerätebauminformationen unter / proc / device-tree
überprüfen. Nach dem Neustart ist es erfolgreich, wenn mydevice unter I2C1 hinzugefügt wird.
ls /proc/device-tree/soc/i2c@7e804000/mydevice@18/
compatible name reg
Auf der Gerätetreiberseite muss das entsprechende Gerät registriert werden. Zuvor habe ich ein Gerät mit dem Namen "mycompany, myoriginaldevice" im Gerätebaum registriert, damit es diesem entspricht. Setzen Sie dazu "struct of_device_id" auf "mycompany, myoriginaldevice" und registrieren Sie es in ".of_match_table" von "struct i2c_driver". Davon abgesehen ist es dasselbe wie bei der vorherigen Implementierung. Der Quellcode wird unten angezeigt.
myDeviceDriver.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/of_platform.h>
/***Informationen zu diesem Gerät***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDevice" /* /proc/Gerätename in Geräten usw. angezeigt.*/
/*Übereinstimmende Tabelle der Geräte, die von diesem Gerätetreiber verarbeitet werden*/
/*Entspricht dem folgenden in dts
i2c@7e804000 {
mydevice@18 {
compatible = "mycompany,myoriginaldevice";
reg = <0x18>;
};
*/
static const struct of_device_id mydevice_of_match_table[] = {
{.compatible = "mycompany,myoriginaldevice",},
{ },
};
MODULE_DEVICE_TABLE(of, mydevice_of_match_table);
/*Registrieren Sie eine Tabelle, in der Geräte aufgeführt sind, die von diesem Gerätetreiber verarbeitet werden*/
/*Wichtig ist das Vornamenfeld. Dies bestimmt den Gerätenamen. Die Rückseite enthält Daten, die mit diesem Treiber frei verwendet werden können. Fügen Sie Zeiger und Identifikationsnummern ein*/
static struct i2c_device_id mydevice_i2c_idtable[] = {
{"MyI2CDevice", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, mydevice_i2c_idtable);
static int mydevice_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
printk("mydevice_i2c_probe\n");
if(id != NULL)
printk("id.name = %s, id.driver_data = %d", id->name, id->driver_data);
if(client != NULL)
printk("slave address = 0x%02X\n", client->addr);
/*Normalerweise hier, um zu überprüfen, ob das Gerät von diesem Devadora unterstützt wird*/
int version;
version = i2c_smbus_read_byte_data(client, 0x0f);
printk("id = 0x%02X\n", version);
return 0;
}
static int mydevice_i2c_remove(struct i2c_client *client)
{
printk("mydevice_i2c_remove\n");
return 0;
}
static struct i2c_driver mydevice_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = mydevice_of_match_table,
},
.id_table = mydevice_i2c_idtable, //Von diesem Gerätetreiber unterstützte I2C-Geräte
.probe = mydevice_i2c_probe, //Prozess, der aufgerufen wird, wenn das Ziel-I2C-Gerät erkannt wird
.remove = mydevice_i2c_remove, //Prozess wird aufgerufen, wenn das Ziel-I2C-Gerät entfernt wird
};
module_i2c_driver(mydevice_driver);
Der Kernel ruft den entsprechenden Gerätetreiber basierend auf den Verbindungsinformationen in der Gerätestruktur auf. Die im Gerätebaum festgelegte Slave-Adresse wird in der an mydevice_i2c_probe () übergebenen Struktur i2c_client gespeichert.
Ein zu beachtender Punkt ist, dass in diesem Fall struct i2c_device_id
NULL enthält. Ich weiß nicht, ob das der Grund ist, aber es scheint, dass ".probe" bald abgeschafft wird. Es scheint stattdessen ".probe_new" zu verwenden. In .probe_new wird struct i2c_device_id
gelöscht.
Erstellen und laden Sie mit dem folgenden Befehl.
make && sudo insmod MyDeviceModule.ko
dmesg
[ 3507.003163] mydevice_i2c_probe
[ 3507.003175] slave address = 0x18
[ 3507.003656] id = 0x33
Wenn Sie sich das Protokoll ansehen, sehen Sie, dass die Sonde ordnungsgemäß aufgerufen wird und auch eine I2C-Kommunikation möglich ist. Beim letzten Mal musste ich die Geräteverbindung manuell benachrichtigen, diesmal jedoch nicht.
Erstellen wir einen DTB aus dem Linux-Quellcode für Raspeye. Holen Sie sich zuerst den Quellcode. Installieren Sie auch die zum Erstellen erforderlichen Werkzeuge. Grundsätzlich ist es wie folgt (https://www.raspberrypi.org/documentation/linux/kernel/building.md). Da es sich um einen nativen Build handelt, ist keine weitere Vorbereitung erforderlich.
sudo apt-get install git bc
git clone https://github.com/raspberrypi/linux.git
Die DTS-Datei für Raspberry Pi 2 Model B lautet "/ linux / arch / arm / boot / dts / bcm2709-rpi-2-b.dts". (Diese Datei ist viel sauberer als die zuvor kompilierte dts-Datei, da sie in mehrere Dateien aufgeteilt ist. Diese dts-Datei enthält am Anfang bcm2709.dtsi
. Es scheint, dass es durcheinander ist, weil alle umgekehrt kompilierten dts-Dateien enthalten sind.)
Bearbeiten Sie / linux / arch / arm / boot / dts / bcm2709-rpi-2-b.dts
auf die gleiche Weise wie zuvor. Fügen Sie das Gerät anstelle von I2C_1 hinzu.
/linux/arch/arm/boot/dts/bcm2709-rpi-2-b.dts
Kürzung
&i2c1 {
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
clock-frequency = <100000>;
/*Fügen Sie Ihr eigenes Gerät hinzu*/
mydevice@18 {
compatible = "mycompany,myoriginaldevice";
reg = <0x18>;
};
};
Kürzung
Kompilieren Sie nach dem Bearbeiten der DTS-Datei nur den DTB mit dem folgenden Befehl. Das Kompilieren auf Raspeye dauert weniger als eine Minute. Kopieren Sie die resultierende DTB-Datei nach / boot.
cd ~/linux
KERNEL=kernel7
make bcm2709_defconfig
make -j4 dtbs
sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
DTB für Overlays scheint verwendet zu werden, wenn sich der Verbindungsstatus nach dem Start des Kernels dynamisch ändert. Sie müssen es also nicht kopieren.
Der Gerätetreiber ist derselbe wie die vorherige ausgeschnittene Version und in Ordnung. Ein Neustart sollte das gleiche Ergebnis liefern.
Ich habe versucht, mit dem Gerätebaum am Beispiel eines I2C-Geräts zu spielen. Was ich in diesem Artikel angesprochen habe, ist nur eine Berührung des Gerätebaums. Weitere Informationen zum Gerätebaum finden Sie im Artikel + Link unter hier.
Recommended Posts