So erstellen Sie einen eingebetteten Linux-Gerätetreiber (9)

9. Mal: Eine Funktion eines anderen Kernelmoduls aufrufen / GPIO-Steuerfunktion verwenden

Über diese Serie

HowTo-Artikel zum Entwickeln eingebetteter Linux-Gerätetreiber als Kernelmodule. Der gesamte Inhalt dieses Artikels kann auf Raspberry Pi ausgeführt werden.

Der gesamte Quellcode, der in diesem Artikel angezeigt wird

https://github.com/take-iwiw/DeviceDriverLesson/tree/master/09_01 https://github.com/take-iwiw/DeviceDriverLesson/tree/master/09_02

Inhalt dieser Zeit

In der 4. Folge dieser Serie habe ich einen GPIO-Gerätetreiber für Raspeye implementiert. Zu diesem Zeitpunkt wurde die Steuerung durch direktes Schlagen des Registers durchgeführt. Die Registeradresse und der eingestellte Wert wurden beim Betrachten des Datenblattes von BCM2835 eingestellt. Dies sind "chipabhängige" Informationen. Wenn ich einen Gerätetreiber erstelle, der ein externes Gerät wie einen Sensor oder einen Motor steuert, möchte ich nicht das Datenblatt jedes Chips sehen. Es gibt eine Funktion für die GPIO-Steuerung. Verwenden wir sie also.

In diesem Zusammenhang möchte ich zunächst versuchen, die in anderen Kernelmodulen definierten Funktionen aufzurufen.

Rufen Sie eine in einem anderen Kernelmodul definierte Funktion auf

Wie bereits in 4 .: Lese- / Schreibimplementierung und Speicherstory erwähnt, verfügt der Kernel über insgesamt einen Speicherplatz. Teilen Dies schließt auch Kernelmodule ein. Daher können Sie die Funktionen anderer Kernelmodule von dem Kernelmodul aus aufrufen, das Sie implementieren, oder Sie können die Funktionen aufrufen, die statisch in den Kernel selbst integriert sind.

Erstellen Sie das Kernelmodul A, das Funktionen bereitstellt

Um eine Funktion zu erstellen, die von anderen Modulen aufgerufen werden kann, definieren Sie einfach die Funktion und exportieren Sie sie dann mit "EXPORT_SYMBOL". EXPORT_SYMBOL registriert die Funktion in der Symboltabelle des Kernels, damit sie von anderen Kernelmodulen aufgerufen werden kann.

Erstellen Sie ein Modul, das nur die Eingabefunktionen zum Laden (insmod) und Entladen (rmmod) und die Funktion (mydevicea_func ()) definiert. Nennen wir das MyDeviceDriverA. Ursprünglich sollte die Funktionsdeklaration im Header beschrieben werden, sie ist jedoch problematisch und wird daher weggelassen.

make um MyDeviceDriverA.ko zu machen.

myDeviceDriverA.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

/***Informationen zu diesem Gerät***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDeviceA"				/* /proc/Gerätename in Geräten usw. angezeigt.*/

void mydevicea_func(void)
{
	printk("This is a message in mydevicea_func\n");
}
/*Registrieren Sie sich in der Symboltabelle des Kernels. Machen Sie es von anderen Kernelmodulen aus aufrufbar*/
EXPORT_SYMBOL(mydevicea_func);


/*Straße(insmod)Funktionen, die manchmal aufgerufen werden*/
static int mydevicea_init(void)
{
	printk("[A]: mydevicea_init\n");
	mydevicea_func();

	return 0;
}

/*Entladen(rmmod)Funktionen, die manchmal aufgerufen werden*/
static void mydevicea_exit(void)
{
	printk("[A]: mydevicea_exit\n");
}

module_init(mydevicea_init);
module_exit(mydevicea_exit);

Erstellen Sie das Kernelmodul B, das die bereitgestellte Funktion aufruft

Erstellen Sie das Kernelmodul B, das die zuvor vorbereitete Funktion aufruft. Sie können es wie eine normale C-Sprache nennen. Da der Funktionsdeklarationsheader diesmal weggelassen wird, wird er auf der Aufruferseite mit extern deklariert. Ich bin nicht sehr brav. Versuchen Sie zum Zeitpunkt des Ladens (insmod) dieses Moduls B, die vorherige Funktion (mydevicea_func ()) aufzurufen.

make um MyDeviceDriverB.ko zu machen. Der aktuelle Status von "mydevicea_func ()" ist nicht hier, aber es gibt kein Problem beim Erstellen eines Kernelmoduls. Dies liegt daran, dass make for kernel modules nur Objektdateien kompiliert und erstellt, keine Links.

myDeviceDriverB.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

/***Informationen zu diesem Gerät***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDeviceB"				/* /proc/Gerätename in Geräten usw. angezeigt.*/

/*Straße(insmod)Funktionen, die manchmal aufgerufen werden*/
static int mydeviceb_init(void)
{
	printk("[B]: mydeviceb_init\n");

	extern void mydevicea_func(void);
	mydevicea_func();

	return 0;
}

/*Entladen(rmmod)Funktionen, die manchmal aufgerufen werden*/
static void mydeviceb_exit(void)
{
	printk("[B]: mydeviceb_exit\n");
}

module_init(mydeviceb_init);
module_exit(mydeviceb_exit);

Versuche zu rennen

Laden Sie zuerst MyDeviceDriverA.ko.

sudo insmod MyDeviceModuleA.ko
dmesg
[16909.979207] [A]: mydevicea_init
[16909.979223] This is a message in mydevicea_func

Wenn Sie sich nach dem Laden das Protokoll mit dmesg ansehen, sehen Sie, dass der Init-Prozess von Modul A und die Funktion in init aufgerufen wurden. Ich denke nicht, dass dies ein Problem ist.

Laden Sie dann MyDeviceDriverB.ko.

sudo insmod MyDeviceModuleB.ko
dmesg
[17087.119434] [B]: mydeviceb_init
[17087.119449] This is a message in mydevicea_func

Dann können Sie sehen, dass Modul B auf diese Weise auch die in Modul A definierte Funktion aufrufen kann.

Abhängigkeiten

Die Abhängigkeit besteht darin, dass Modul B Modul A verwendet. Daher muss Modul A vor dem Laden von Modul B geladen werden. Andernfalls tritt beim Laden von Modul B ein Fehler auf. Ebenso muss Modul B entladen werden, bevor Modul A entladen werden kann. Wenn Sie zuerst versuchen, Modul A zu entladen, wird eine Fehlermeldung angezeigt.

Wenn Sie den erforderlichen Inhalt ordnungsgemäß implementieren und das erstellte Kernelmodul (.ko) an der entsprechenden Stelle platzieren, können Sie "modprobe" anstelle von "insmod" verwenden, um auch die abhängigen Module automatisch zu laden. Es scheint, dass sie es tun werden.

Erstellen Sie ein Kernelmodul, das GPIO-Steuerfunktionen verwendet

Chipabhängige GPIO-Steuerfunktion

Wie eingangs erwähnt, werden Sie, solange Sie Gerätetreiber für externe Geräte und Onboard-Geräte erstellen, keine Registereinstellungen vornehmen, indem Sie sich das Chip-Datenblatt ansehen. Wenn Sie Ingenieur eines SoC-Herstellers sind, die Funktionen erweitern möchten oder freiwillig Chip-abhängige Geräte entwickeln möchten, benötigen Sie diese. (Es kann also nicht völlig irrelevant sein. Es kann Fehler geben.)

Bei der Steuerung von GPIO über den Gerätetreiber rufen wir die von diesen Personen erstellten Funktionen auf. Derzeit wird es nicht von allen in einem nicht zusammenhängenden Format implementiert, sondern so, dass es eine Schnittstelle wie die in "linux / gpio.h" hat. Infolgedessen können Benutzer (obwohl Devadora-Entwickler) die Funktionen in "linux / gpio.h" verwenden, um GPIO zu steuern. Sie können denselben Code auf einem anderen Chip verwenden, wenn Sie die Funktionen hier verwenden. (In der Dokumentation heißt es etwa "GPIO hat einen breiten Funktionsumfang, folgen Sie also so weit wie möglich" linux / gpio.h "." Daher kann es je nach verwendetem Chip unterschiedlich sein. Es gibt.)

Für welchen Chip der GPIO-Steuerungsprozess verwendet wird, wird durch die Einstellungen zum Zeitpunkt der Kernel-Erstellung bestimmt. Im Fall von Raspetorte sollten Sie das Verfahren für bcm2835 verwenden. Die GPIO-Verarbeitung für BCM2835 erfolgte in "pinctrl-bcm2835.c". Ich habe es nicht genau verfolgt, aber ich denke, wenn Sie die Funktion in linux / gpio.h aufrufen, werden Sie endlich jeden Prozess von pinctrl-bcm2835.c erreichen. (Zusätzlich zur Grundsteuerung von GPIO gibt es auch eine MUX-Steuerung als Funktions-Pin, so dass dies ziemlich kompliziert zu sein scheint.)

__ Wie auch immer, Sie können GPIO mit den Funktionen in linux / gpio.h steuern __

GPIO-Steuerfunktion

Diese Funktion ist für die grundlegende GPIO-Steuerung erforderlich.

Erstellen Sie ein Kernelmodul, das GPIO-Steuerfunktionen verwendet

Da das Implementieren von Lesen / Schreiben schwierig ist, erstellen Sie einen Gerätetreiber (Kernelmodul) mit den folgenden einfachen Spezifikationen. Als Voraussetzung wird angenommen, dass die LED an GPIO4 von Raspeye und die Taste an GPIO17 angeschlossen ist. Die LED ist über einen Widerstand mit 3,3 V verbunden. Die Taste ist mit GND verbunden und die GPIO17-Seite ist hochgezogen. Wenn es problematisch ist, können Sie den Ausgang von GPIO4 mit einem Tester sehen, oder der Eingang von GPIO17 kann direkt an 3,3 V / GND angeschlossen werden.

Spezifikationen des zu erstellenden Kernelmoduls

Der Code sieht folgendermaßen aus: Der Interrupt-Handler ist "mydevice_gpio_intr ()". Dies wird beim Laden mit request_irq () registriert.

myDeviceDriver.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>

/***Informationen zu diesem Gerät***/
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "MyDevice"				/* /proc/Gerätename in Geräten usw. angezeigt.*/

#define GPIO_PIN_LED 4
#define GPIO_PIN_BTN 17

static irqreturn_t mydevice_gpio_intr(int irq, void *dev_id)
{
	printk("mydevice_gpio_intr\n");

	int btn;
	btn = gpio_get_value(GPIO_PIN_BTN);
	printk("button = %d\n", btn);
	return IRQ_HANDLED;
}

/*Straße(insmod)Funktionen, die manchmal aufgerufen werden*/
static int mydevice_init(void)
{
	printk("mydevice_init\n");

	/*GPIO4 für LED ausgeben. Der Anfangswert ist 1(High) */
	gpio_direction_output(GPIO_PIN_LED, 1);
	/*0 bis GPIO4 für LED(Low)Ausgabe*/
	gpio_set_value(GPIO_PIN_LED, 0);

	/*Geben Sie GPIO17 für die Schaltfläche ein*/
	gpio_direction_input(GPIO_PIN_BTN);

	/*Rufen Sie die GPIO17-Interrupt-Nummer für die Schaltfläche ab*/
	int irq = gpio_to_irq(GPIO_PIN_BTN);
	printk("gpio_to_irq = %d\n", irq);

	/*Registrieren Sie den GPIO17-Interrupt-Handler für die Schaltfläche*/
	if (request_irq(irq, (void*)mydevice_gpio_intr, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "mydevice_gpio_intr", (void*)mydevice_gpio_intr) < 0) {
		printk(KERN_ERR "request_irq\n");
		return -1;
	}

	return 0;
}

/*Entladen(rmmod)Funktionen, die manchmal aufgerufen werden*/
static void mydevice_exit(void)
{
	printk("mydevice_exit\n");
	int irq = gpio_to_irq(GPIO_PIN_BTN);
	free_irq(irq, (void*)mydevice_gpio_intr);
}

module_init(mydevice_init);
module_exit(mydevice_exit);

Versuche zu rennen

Erstellen und laden Sie wie folgt.

make
sudo insmod MyDeviceModule.ko

Die LED sollte aufleuchten. Versuchen Sie danach mehrmals, die Taste zu drücken oder GPIO17 an 3,3 V / GND anzuschließen.

dmesg
[19652.388837] mydevice_init
[19652.388873] gpio_to_irq = 183
[19654.100437] mydevice_gpio_intr
[19654.100457] button = 0
[19656.061705] mydevice_gpio_intr
[19656.061727] button = 1

Wenn Sie sich das Protokoll mit dmesg ansehen, können Sie sehen, dass der registrierte Interrupt-Handler aufgerufen und der GPIO-Eingabewert darin gedruckt wird. Übrigens können Sie den Interrupt-Status in / proc / interrupts überprüfen.

cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
 183:          7          0          0          0  pinctrl-bcm2835  17 Edge      mydevice_gpio_intr

Was sind die Pull-Up / Pull-Down-Einstellungen?

Es scheint keine allgemeine Möglichkeit zu geben, es über den Code festzulegen (https://raspberrypi.stackexchange.com/questions/44924/how-to-set-pull-up-down-resistors-in-a-kernel-module). Es scheint im Gerätebaum eingestellt zu sein. Wenn man sich brcm, bcm2835-gpio.txt ansieht, scheint es, dass es mit brcm, pull in der dts-Datei festgelegt wird.

Recommended Posts

So erstellen Sie einen eingebetteten Linux-Gerätetreiber (11)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (8)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (1)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (4)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (7)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (2)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (3)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (6)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (5)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (10)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (9)
So erstellen Sie einen eingebetteten Linux-Gerätetreiber (12) (vollständig)
Wie man Yubico Yubikey von Manjaro Linux erkennt
So erstellen Sie ein interaktives CLI-Tool mit Golang
So erstellen Sie einen HTTPS-Server mit Go / Gin
[Python] Erstellen einer Adjazenzmatrix / Adjazenzliste [Graphentheorie]
So erstellen Sie ein Hacking-Labor - Kali Linux (2020.1) VirtualBox 64-Bit Teil 2-
So erstellen Sie ein Hacking-Labor - Kali Linux (2020.1) VirtualBox 64-Bit-Edition -
Wie erstelle ich ein Python-Paket (geschrieben für Praktikanten)
So erstellen Sie eine ISO-Datei (CD-Image) unter Linux
Wie erstelle ich eine japanisch-englische Übersetzung?
So installieren Sie VMware-Tools unter Linux
Wie erstelle ich einen Crawler?
So erstellen Sie eine rekursive Funktion
So installieren Sie MBDyn (Linux Ubuntu)
[Blender] So erstellen Sie ein Blender-Plug-In
[Blender] So erstellen Sie Blender-Skripte mehrsprachig
Wie erstelle ich einen Crawler?
So erstellen Sie den MongoDB C-Sprachtreiber
So überprüfen Sie die Linux-Betriebssystemversion
So machen Sie einen String in Python zu einem Array oder ein Array zu einem String
So bringen Sie den Druckertreiber für Oki Mac in Linux
So machen Sie Word Cloud-Zeichen monochromatisch
Wie baue ich meinen eigenen Linux-Server?
So erstellen Sie einen LINE-Bot mit künstlicher Intelligenz mit der Flask + LINE Messaging-API
[Python] Wie man eine Klasse iterierbar macht
python3 So installieren Sie ein externes Modul
So erstellen Sie eine NVIDIA Docker-Umgebung
So konvertieren Sie Python in eine exe-Datei
Ich möchte wissen, wie LINUX funktioniert!
[Linux] Verwendung des Befehls echo
So aktualisieren Sie PHP unter Amazon Linux 2
So zeigen Sie Piktogramme unter Manjaro Linux an
So installieren Sie Pakete unter Alpine Linux
[Cocos2d-x] So erstellen Sie eine Skriptbindung (Teil 2)
So bedienen Sie Linux von der Konsole aus
So installieren Sie das Windows-Subsystem für Linux
So aktualisieren Sie die Sicherheit unter CentOS Linux 8
Ich möchte ein Automatisierungsprogramm erstellen!
Wie installiere ich php7.4 unter Linux (Ubuntu)
So machen Sie Multi-Boot-USB (Windows 10-kompatibel)
[Cocos2d-x] So erstellen Sie eine Skriptbindung (Teil 1)
So finden Sie große Dateien unter Linux