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

6. Implementierung von ioctl

Ü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/06_01

Inhalt dieser Zeit

Bis zum letzten Mal habe ich erklärt, wie grundlegende Systemaufrufe implementiert werden (Öffnen, Schließen, Lesen, Schreiben). Außerdem habe ich mit ihnen einen Gerätetreiber für GPIO von Raspeye erstellt. Dieses Mal werde ich zusätzlich einen Systemaufruf namens ioctl implementieren.

Was ist ioctl?

#include <sys/ioctl.h>
int ioctl(int d, int request, ...);

Der oben definierte Systemaufruf. Geben Sie den Dateideskriptor (fd) ein, der durch Öffnen im ersten Argument erhalten wurde. Das zweite Argument heißt Anfrage, aber es ist ein Befehl. Das dritte Argument (variable Länge) ist ein Parameter. Mit diesem ioctl können Sie auf der Treiberseite frei Schnittstellen hinzufügen.

Bisher habe ich cdev verwendet, um Gerätetreiber zu erstellen. Infolgedessen wird es vom Kernel als Gerätetreiber vom Zeichentyp erkannt. Daher wurde bei der Interaktion mit der Benutzeranwendung mit Lesen und Schreiben der Zeichentyp verwendet. Dies ist für einfache Interaktionen in Ordnung, reicht jedoch nicht aus, wenn Sie das Gerät tatsächlich steuern. Beispielsweise muss eine Nummer für die Einstellung der SPI-Kommunikationsgeschwindigkeit oder der I2C-Slave-Adresse angegeben werden. Dies kann jedoch nicht nur durch Lesen und Schreiben erfolgen. Fügen Sie in diesem Fall eine Schnittstelle auf der Gerätetreiberseite hinzu. Verwenden Sie dazu ioctl.

Hinweis

Wie oben erwähnt, definiert ioctl seine eigenen Befehle und Parameter für jeden Gerätetreiber. Daher lautet die bei der Verwendung von ioctl zu überprüfende Spezifikation (Header) nicht ioctl.h, sondern der von jedem Gerätetreiber erstellte Header (oder Quellcode). Dieses Mal werde ich versuchen, selbst einen Gerätetreiber zu erstellen, damit Sie ein Gefühl dafür bekommen.

Implementierung des ioctl System Call Handlers

Header-Definition

Auch hier definiert ioctl seine eigenen Befehle und Parameter auf der Gerätetreiberseite. Befehle sind int-Typennummern, und Parameter sind normalerweise Strukturen. (Es sind keine Parameter erforderlich). Erstellen Sie einen Header mit diesen Definitionen. Es wird referenziert, wenn der Benutzer es verwendet, sodass es sich um eine separate Datei handelt.

myDeviceDriver.h


#ifndef MY_DEVICE_DRIVER_H_
#define MY_DEVICE_DRIVER_H_

#include <linux/ioctl.h>

/***Parameter für ioctl(3. Argument)Definition von***/
struct mydevice_values {
	int val1;
	int val2;
};


/***Befehl für ioctl(request,2. Argument)Definition von***/
/*Der Befehlstyp für IOCTL, der von diesem Gerätetreiber verwendet wird. Alles ist gut'M'Versuchen zu*/
#define MYDEVICE_IOC_TYPE 'M'

/*Ein Befehl zum Festlegen eines Werts für Devadora. Der Parameter ist mydevice_Wertetyp*/
#define MYDEVICE_SET_VALUES _IOW(MYDEVICE_IOC_TYPE, 1, struct mydevice_values)

/*Ein Befehl, um den Wert von Devadora abzurufen. Der Parameter ist mydevice_Wertetyp*/
#define MYDEVICE_GET_VALUES _IOR(MYDEVICE_IOC_TYPE, 2, struct mydevice_values)


#endif /* MY_DEVICE_DRIVER_H_ */

Dieses Mal werde ich vorerst einen Befehl zum Lesen und Schreiben von zwei Werten (val1 und val2) erstellen. Dies hat keine besondere Bedeutung. Die Befehls- (Anforderungs-) Namen sollten "MYDEVICE_SET_VALUES" und "MYDEVICE_GET_VALUES" sein. Dieser Befehl funktioniert logisch, auch wenn Sie die Nummer direkt eingeben. Sie können beispielsweise Folgendes tun.

#define MYDEVICE_SET_VALUES 3	//Die Zahlen haben keine besondere Bedeutung. Alles, was in diesem Treiber einzigartig ist, ist in Ordnung
#define MYDEVICE_GET_VALUES 4	//Die Zahlen haben keine besondere Bedeutung. Alles, was in diesem Treiber einzigartig ist, ist in Ordnung

Tatsächlich aus den Informationen wie "ob der Parameter Lesen oder Schreiben oder Lesen / Schreiben ist", "Typ", "eindeutige Nummer", "Parametergröße", "_IO", "_IOW", "_IOR", " Es scheint üblich zu sein, mit dem Makro _IOWR` zu generieren. Diesmal lautet der Name des Gerätetreibers "myDeviceDriver", daher habe ich den Typ auf "M" gesetzt.

Definieren Sie als Parametertyp auch eine Struktur struct mydevice_values mit zwei Werten.

Implementierung des Gerätetreibers

Der Code auf der Gerätetreiberseite benötigt nur eine Handlerfunktion, die beim Aufruf von ioctl verwendet werden kann, und eine Registrierung in der Tabelle struct file_operations. Unten ist der Code nur aus den relevanten Teilen extrahiert.

myDeviceDriver.c


#include "myDeviceDriver.h"

/*Variablen, die Werte für ioctl-Tests enthalten*/
static struct mydevice_values stored_values;

/*Funktion zum Zeitpunkt von ioctl aufgerufen*/
static long mydevice_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	printk("mydevice_ioctl\n");
	
	switch (cmd) {
	case MYDEVICE_SET_VALUES:
		printk("MYDEVICE_SET_VALUES\n");
		if (copy_from_user(&stored_values, (void __user *)arg, sizeof(stored_values))) {
			return -EFAULT;
		}
		break;
	case MYDEVICE_GET_VALUES:
		printk("MYDEVICE_GET_VALUES\n");
		if (copy_to_user((void __user *)arg, &stored_values, sizeof(stored_values))) {
			return -EFAULT;
		}
		break;
	default:
		printk(KERN_WARNING "unsupported command %d\n", cmd);
		return -EFAULT;
	}
	return 0;
}

/*Handlertabelle für verschiedene Systemaufrufe*/
static struct file_operations mydevice_fops = {
	.open    = mydevice_open,
	.release = mydevice_close,
	.read    = mydevice_read,
	.write   = mydevice_write,
	.unlocked_ioctl = mydevice_ioctl,
	.compat_ioctl   = mydevice_ioctl,	// for 32-bit App
};

Befehle und Parameter werden in der Header-Datei definiert und sind daher enthalten. Außerdem habe ich diesmal einen Befehl zum Festlegen und Abrufen des Werts als Test ausgeführt, sodass ich eine statische Variable vorbereitet habe, um ihn zu speichern. Verwalten Sie es in der realen Implementierung ordnungsgemäß, indem Sie private_data in der Dateistruktur verwenden.

Dies ist der Prozess, in dem die Funktion mydevice_ioctl von ioctl aufgerufen wird. Wie Sie sehen können, gibt es nur eine switch-case-Anweisung für den Befehl (request). Da sich der Zeiger auf den Parameter in arg befindet, wandeln Sie ihn entsprechend in Lesen und Schreiben um. Dies ist dasselbe wie Lesen und Schreiben. Registrieren Sie es schließlich in der Tabelle struct file_operations. Obwohl hier nicht beschrieben, wird diese Tabelle beim Laden des Kernels mit "cdev_init, cdev_add" registriert.

Hinweis

Der Inhalt dieses Artikels entspricht dem Inhalt von "Linux Device Driver Programming (Yutaka Hirata)". Registrieren Sie die Funktion in struct file_operations, um den ioctl-Handler zu registrieren. In dem Buch habe ich mich mit dem .ioctl Mitglied registriert. Es ist jedoch jetzt veraltet. Verwenden Sie stattdessen .unlocked_ioctl und .compat_ioctl. Es scheint zwei Gründe für die Unterstützung von 32-Bit-Anwendungen in einer 64-Bit-Umgebung zu geben. Details wurden unter hier erläutert.

In diesem Buch wurde auch die Unterstützung beider 32-Bit- / 64-Bit-Umgebungen, z. B. das Hinzufügen von Padding, vorgestellt. Möglicherweise wurde .compat_ioctl hinzugefügt, um dies zu vermeiden. vielleicht.

Versuchen Sie, ioctl über das Anwenderprogramm aufzurufen

Versuchen Sie, ioctl des implementierten Gerätetreibers mit dem folgenden Testprogramm aufzurufen. Stellen Sie den Wert ({1, 2}) mit MYDEVICE_SET_VALUES ein. Versuchen Sie danach, den Wert mit MYDEVICE_GET_VALUES abzurufen.

test.c


#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include "myDeviceDriver.h"

int main()
{
	int fd;
	struct mydevice_values values_set;
	struct mydevice_values values_get;
	values_set.val1 = 1;
	values_set.val2 = 2;

	if ((fd = open("/dev/mydevice0", O_RDWR)) < 0) perror("open");

	if (ioctl(fd, MYDEVICE_SET_VALUES, &values_set) < 0) perror("ioctl_set");
	if (ioctl(fd, MYDEVICE_GET_VALUES, &values_get) < 0) perror("ioctl_get");
	
	printf("val1 = %d, val2 = %d\n", values_get.val1, values_get.val2);

	if (close(fd) != 0) perror("close");
	return 0;
}

Lauf

Erstellen und laden Sie zunächst den Gerätetreiber. Erstellen Sie dann das Testprogramm und führen Sie es aus.

make
sudo insmod MyDeviceModule.ko
gcc test.c
./a.out
val1 = 1, val2 = 2

Dann können Sie sehen, dass der eingestellte Wert so gelesen werden kann.

abschließend

Selbst wenn Sie ein Benutzer sind, der den Gerätetreiber nicht selbst schreibt, ist ioctl immer erforderlich, wenn Sie ein Gerät berühren. Es war immer chaotisch und ich war mir nicht sicher, aber wenn ich es selbst implementierte, konnte ich es gut verstehen.

Recommended Posts

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 -
So erstellen Sie eine ISO-Datei (CD-Image) unter Linux
Wie erstelle ich eine japanisch-englische Übersetzung?
Wie man einen lockeren Bot macht
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?
Wie man Selen so leicht wie möglich macht
[Linux] Unterteilen von Dateien und Ordnern
So erstellen Sie einen LINE-Bot mit künstlicher Intelligenz mit der Flask + LINE Messaging-API
So installieren Sie das aws-session-manager-Plugin unter Manajro Linux
[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 schalten Sie Linux unter Ultra96-V2 aus
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)
So erstellen Sie einen benutzerdefinierten Backtrader-Indikator