Minimales lauffähiges Beispiel
Getestet in einer vollständig reproduzierbaren QEMU + Buildroot-Umgebung, könnte also anderen helfen, ihren ioctl
zu bekommen Arbeiten. GitHub-Upstream:Kernelmodul |gemeinsamer Header |userland.
Der ärgerlichste Teil war zu verstehen, dass einige niedrige IDs entführt werden:ioctl wird nicht aufgerufen, wenn cmd =2 , Sie müssen _IOx
verwenden Makros.
Kernelmodul:
#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/printk.h> /* printk */
#include "ioctl.h"
MODULE_LICENSE("GPL");
static struct dentry *dir;
static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp)
{
void __user *arg_user;
union {
int i;
lkmc_ioctl_struct s;
} arg_kernel;
arg_user = (void __user *)argp;
pr_info("cmd = %x\n", cmd);
switch (cmd) {
case LKMC_IOCTL_INC:
if (copy_from_user(&arg_kernel.i, arg_user, sizeof(arg_kernel.i))) {
return -EFAULT;
}
pr_info("0 arg = %d\n", arg_kernel.i);
arg_kernel.i += 1;
if (copy_to_user(arg_user, &arg_kernel.i, sizeof(arg_kernel.i))) {
return -EFAULT;
}
break;
case LKMC_IOCTL_INC_DEC:
if (copy_from_user(&arg_kernel.s, arg_user, sizeof(arg_kernel.s))) {
return -EFAULT;
}
pr_info("1 arg = %d %d\n", arg_kernel.s.i, arg_kernel.s.j);
arg_kernel.s.i += 1;
arg_kernel.s.j -= 1;
if (copy_to_user(arg_user, &arg_kernel.s, sizeof(arg_kernel.s))) {
return -EFAULT;
}
break;
default:
return -EINVAL;
break;
}
return 0;
}
static const struct file_operations fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = unlocked_ioctl
};
static int myinit(void)
{
dir = debugfs_create_dir("lkmc_ioctl", 0);
/* ioctl permissions are not automatically restricted by rwx as for read / write,
* but we could of course implement that ourselves:
* https://stackoverflow.com/questions/29891803/user-permission-check-on-ioctl-command */
debugfs_create_file("f", 0, dir, NULL, &fops);
return 0;
}
static void myexit(void)
{
debugfs_remove_recursive(dir);
}
module_init(myinit)
module_exit(myexit)
Gemeinsamer Header zwischen Kernelmodul und Userland:
ioctl.h
#ifndef IOCTL_H
#define IOCTL_H
#include <linux/ioctl.h>
typedef struct {
int i;
int j;
} lkmc_ioctl_struct;
#define LKMC_IOCTL_MAGIC 0x33
#define LKMC_IOCTL_INC _IOWR(LKMC_IOCTL_MAGIC, 0, int)
#define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct)
#endif
Benutzerland:
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "../ioctl.h"
int main(int argc, char **argv)
{
int fd, arg_int, ret;
lkmc_ioctl_struct arg_struct;
if (argc < 2) {
puts("Usage: ./prog <ioctl-file>");
return EXIT_FAILURE;
}
fd = open(argv[1], O_RDONLY);
if (fd == -1) {
perror("open");
return EXIT_FAILURE;
}
/* 0 */
{
arg_int = 1;
ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int);
if (ret == -1) {
perror("ioctl");
return EXIT_FAILURE;
}
printf("arg = %d\n", arg_int);
printf("ret = %d\n", ret);
printf("errno = %d\n", errno);
}
puts("");
/* 1 */
{
arg_struct.i = 1;
arg_struct.j = 1;
ret = ioctl(fd, LKMC_IOCTL_INC_DEC, &arg_struct);
if (ret == -1) {
perror("ioctl");
return EXIT_FAILURE;
}
printf("arg = %d %d\n", arg_struct.i, arg_struct.j);
printf("ret = %d\n", ret);
printf("errno = %d\n", errno);
}
close(fd);
return EXIT_SUCCESS;
}
Den benötigten Beispielcode finden Sie in drivers/watchdog/softdog.c
(von Linux 2.6.33 zu der Zeit, als dies geschrieben wurde), die die ordnungsgemäßen Dateioperationen veranschaulicht und wie man Userland erlaubt, eine Struktur mit ioctl() zu füllen.
Es ist tatsächlich ein großartiges, funktionierendes Tutorial für jeden, der triviale Zeichengerätetreiber schreiben muss.
Ich habe die ioctl-Schnittstelle von softdog seziert, als ich meine eigene Frage beantwortete, was für Sie hilfreich sein könnte.
Hier ist das Wesentliche (wenn auch bei weitem nicht erschöpfend) ...
In softdog_ioctl()
Sie sehen eine einfache Initialisierung von struct watchdog_info, die Funktionalität, Version und Geräteinformationen ankündigt:
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "Software Watchdog",
};
Wir betrachten dann einen einfachen Fall, in dem der Benutzer nur diese Fähigkeiten erhalten möchte:
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
... was natürlich den entsprechenden Userspace watchdog_info mit den oben initialisierten Werten füllt. Wenn copy_to_user() fehlschlägt, wird -EFAULT zurückgegeben, was dazu führt, dass der entsprechende Userspace-ioctl()-Aufruf -1 zurückgibt, wobei eine aussagekräftige Fehlernummer gesetzt wird.
Beachten Sie, dass die magischen Anforderungen tatsächlich in linux/watchdog.h definiert sind, sodass der Kernel und der Benutzerbereich sie gemeinsam nutzen:
#define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_GETSTATUS _IOR(WATCHDOG_IOCTL_BASE, 1, int)
#define WDIOC_GETBOOTSTATUS _IOR(WATCHDOG_IOCTL_BASE, 2, int)
#define WDIOC_GETTEMP _IOR(WATCHDOG_IOCTL_BASE, 3, int)
#define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int)
WDIOC bedeutet offensichtlich "Watchdog ioctl"
Sie können leicht einen Schritt weiter gehen, indem Sie Ihren Treiber etwas tun lassen und das Ergebnis dieses Etwas in die Struktur einfügen und es in den Benutzerbereich kopieren. Zum Beispiel, wenn struct watchdog_info auch ein Mitglied __u32 result_code
hatte . Beachten Sie, __u32
ist nur die Kernel-Version von uint32_t
.
Mit ioctl() übergibt der Benutzer die Adresse eines Objekts, sei es eine Struktur, eine Ganzzahl oder was auch immer, an den Kernel und erwartet, dass der Kernel seine Antwort in ein identisches Objekt schreibt und die Ergebnisse an die angegebene Adresse kopiert.
Als Zweites müssen Sie sicherstellen, dass Ihr Gerät weiß, was zu tun ist, wenn jemand es öffnet, daraus liest, darauf schreibt oder einen Hook wie ioctl() verwendet, was Sie leicht erkennen können, indem Sie softdog.
Interessant ist:
Wo Sie sehen, dass der unlocked_ioctl-Handler zu ... Sie haben es erraten, softdog_ioctl().
Ich denke, Sie stellen vielleicht eine Ebene der Komplexität gegenüber, die wirklich nicht existiert, wenn Sie sich mit ioctl() befassen, es ist wirklich so einfach. Aus dem gleichen Grund missbilligen die meisten Kernel-Entwickler das Hinzufügen neuer ioctl-Schnittstellen, es sei denn, sie sind absolut notwendig. Es ist einfach zu leicht, den Überblick über den Typ zu verlieren, den ioctl() füllen wird, im Vergleich zu der Magie, die Sie dafür verwenden, was der Hauptgrund dafür ist, dass copy_to_user() oft fehlschlägt, was dazu führt, dass der Kernel mit Horden von Userspace-Prozessen verrottet Festplattenruhezustand.
Für einen Timer, da stimme ich zu, ist ioctl() der kürzeste Weg zur Vernunft.
Ihnen fehlt ein
Versuchen Sie, den Linux Kernel Module Programming Guide durchzulesen, insbesondere die Kapitel 4 (Character Device Files) und 7 (Talking to Device Files).
Kapitel 4 stellt den
Kapitel 7 enthält Informationen zur Kommunikation mit einem Modul/Laufwerk über ioctls.
Linux Device Drivers, Third Edition ist eine weitere gute Ressource. static const struct file_operations softdog_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = softdog_write,
.unlocked_ioctl = softdog_ioctl,
.open = softdog_open,
.release = softdog_release,
};
.open
Funktionszeiger in Ihrem file_operations
-Struktur, um die Funktion anzugeben, die aufgerufen werden soll, wenn ein Prozess versucht, die Gerätedatei zu öffnen. Sie müssen einen .ioctl
angeben Funktionszeiger auch für Ihre ioctl-Funktion.file_operations
vor Struktur, die Zeiger auf vom Modul/Treiber definierte Funktionen enthält, die verschiedene Operationen wie open
ausführen oder ioctl
.