Plattformtreiberimplementierung des Treibergerätebaums im Linux-System

Jeden Tag ein einfacher Treiber, mit der Zeit werden Sie mit Linux-Treibern immer vertrauter und es wird immer einfacher, das Schreiben von Treiberprogrammen zu erlernen. Heute werden wir mit dem Plattformgerätetreiber unter der Gerätestruktur fortfahren.

Im vorherigen Artikel haben wir die traditionelle Methode zum Schreiben von Plattformgeräten und -treibern erläutert, bei der kein Gerätebaum verwendet wird. Der neueste Linux-Kernel unterstützt bereits den Gerätebaum. Daher ist es besonders wichtig, wie der Plattformtreiber unter den Gerätebaum geschrieben wird. In diesem Kapitel erfahren Sie, wie Sie den Plattformtreiber unter den Gerätebaum schreiben.

1. Einführung in den Plattformtreiber unter dem Gerätebaum

Das Plattformtreiber-Framework ist in Bus, Gerät und Treiber unterteilt. Der Bus muss nicht von uns Treiberprogrammierern verwaltet werden. Dies wird vom Linux-Kernel bereitgestellt. Wir müssen uns beim Schreiben nur auf die spezifische Implementierung des Geräts und des Treibers konzentrieren der Fahrer.

Unter dem Linux-Kernel ohne Gerätebaum müssen wir platform_device bzw. platform_driver schreiben und registrieren, die das Gerät bzw. den Treiber darstellen.

Bei Verwendung des Gerätebaums wird die Gerätebeschreibung im Gerätebaum platziert, sodass wir „platform_device“ nicht schreiben müssen, sondern nur „platform_driver“ implementieren müssen.

Beim Schreiben eines Plattformtreibers basierend auf dem Gerätebaum müssen wir die folgenden Punkte beachten:

  • 1. Erstellen Sie einen Geräteknoten im Gerätebaum

Es besteht kein Zweifel, dass Sie zuerst einen Geräteknoten im Gerätebaum erstellen müssen, um die Geräteinformationen zu beschreiben. Der entscheidende Punkt besteht darin, den Wert des kompatiblen Attributs festzulegen, da der Plattformbus über den kompatiblen Attributwert mit dem Treiber übereinstimmen muss der Geräteknoten! Dies ist wichtig, sich daran zu erinnern.

  • 2. Achten Sie beim Schreiben von Plattformtreibern auf die Kompatibilitätsattribute.

Bei Verwendung des Gerätebaums speichert der Plattformtreiber den Kompatibilitätswert über of_match_table, der angibt, mit welchen Geräten der Treiber kompatibel ist. Daher wird of_match_table besonders wichtig sein

  • 3. Plattformtreiber schreiben

Der auf dem Gerätebaum basierende Plattformtreiber ist grundsätzlich derselbe wie der Plattformtreiber ohne Gerätebaum im vorherigen Kapitel. Die Sondenfunktion wird ausgeführt, wenn Treiber und Gerät erfolgreich übereinstimmen. Wir müssen den in der Sondenfunktion festgelegten Zeichengerätetreiber implementieren. Wenn das Treibermodul abgebrochen wird, wird die Entfernungsfunktion ausgeführt, die ähnlich ist.

2. Ändern Sie die Gerätebaumdatei

Ändern Sie die Gerätebaumdatei und fügen Sie die benötigten Geräteinformationen hinzu. In diesem Kapitel verwenden wir eine LED-Leuchte.

2.1 LED-Geräteknoten hinzufügen

Erstellen Sie einen LED-Lichtknoten unter dem Stammknoten „/“. Der Knotenname lautet „gpioled“ und der Knoteninhalt lautet wie folgt:

gpioled {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "imx6ull-gpioled";
    pinctrl-name = "default";
    pinctrl-0 = <&pinctrl_led>;
    led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;

    status = "okay";
};
2.2 Pinctrl-Knoten hinzufügen

Das LED-Licht auf der I.MX6U-ALPHA-Entwicklungsplatine verwendet die PIN GPIO1_IO03, öffnet imx6ul-14x14-evk.dtsi und erstellt einen Unterknoten mit dem Namen „pinctrl_led“ unter dem Unterknoten imx6ul-evk des iomuxc-Knotens, dem Inhalt des Knotens ist wie folgt dargestellt:

pinctrl_led: ledgrp {
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */
    >;
};
2.3 Prüfen Sie, ob die PIN von anderen Peripheriegeräten verwendet wird

3. Schreiben von Plattformtreibern

Erstellen Sie eine neue Treiberdatei mit dem Namen dtsplatform_driver.c und geben Sie den folgenden Inhalt in dtsplatform_driver.c ein:

/***********************************************************
 * Copyright © toto Co., Ltd. 1998-2029. All rights reserved.
 * Description: 
 * Version: 1.0
 * Autor: toto
 * Date: Do not edit
 * LastEditors: Seven
 * LastEditTime: Do not edit
***********************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/irq.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>

#define LEDDEV_CNT  1               /* 设备号数量 */
#define LEDDEV_NAME "dts_platform_led"  /* 设备名字 */
#define LED_ON      1
#define LED_OFF     0

/* led_dev 设备结构体 */
struct led_dev {
    dev_t devid;                /* 设备号 */
    struct cdev cdev;           /* cdev */
    struct class *class;        /* 类 */
    struct device *device;      /* 设备 */
    int major;                  /* 主设备号 */
    struct device_node *node;   /* 设备节点 */
    int led_gpio;               /* led gpio号 */             
};

struct led_dev leddev; /* led 设备 */

/*
 * @Brief   led 打开、关闭接口
 * @Param   sta:1打开,0关闭
 * @Note    NOne
 * @RetVal  NOne
 */
void led_switch(u8 sta)
{
    if (sta == LED_ON) {
        gpio_set_value(leddev.led_gpio, 0);
    } else if (sta == LED_OFF) {
        gpio_set_value(leddev.led_gpio, 1);
    }
}

/*
 * @Brief   打开设备
 * @Param   inode:传递给驱动的inode
 * @Param   filp:设备文件
 * @Note    NOne
 * @RetVal  NOne
 */
static int led_open(struct inode *inode, struct file *filp)
{
    /* 设置私有数据 */
    filp->private_data = &leddev;

    return 0;
}

/*
 * @Brief   向设备写数据
 * @Param   filp:设备文件
 * @Param   buf:要写入设备的数据
 * @Param   cnt:要写入的数据长度
 * @Param   offt:相对于文件首地址的偏移
 * @Note    NOne
 * @RetVal  写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf,
                        size_t cnt, loff_t *offt)
{
    int ret;
    unsigned char databuf[1];
    unsigned char ledstat;

    ret = copy_from_user(databuf, buf, cnt);
    if (ret < 0) {
        return -EFAULT;
    }

    ledstat = databuf[0];
    led_switch(ledstat);

    return 0;
}

/* 设备操作函数 */
static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open  = led_open,
    .write = led_write, 
};

/*
 * @Brief
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static int led_probe(struct platform_device *dev)
{
    printk(KERN_INFO "led driver and device has matched\n");

    /* 注册字符设备驱动 */
    /* 1.创建设备号 */
    if (leddev.major) {
        leddev.devid = MKDEV(leddev.major, 0);
        register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
    } else {
        alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
        leddev.major = MAJOR(leddev.devid);
    }

    /* 2.注册设备 */
    cdev_init(&leddev.cdev, &led_fops);
    cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);

    /* 3.创建类 */
    leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
    if (IS_ERR(leddev.class)) {
        return PTR_ERR(leddev.class);
    }

    /* 4.创建设备 */
    leddev.device = device_create(leddev.class, NULL, leddev.devid,
                                    NULL, LEDDEV_NAME);
    if (IS_ERR(leddev.device)) {
        return PTR_ERR(leddev.device);
    }

    /* 5.初始化IO */
    leddev.node = of_find_node_by_path("/gpioled");
    if (leddev.node == NULL) {
        printk("gpioled node not found\n");
        return -EINVAL;
    }

    leddev.led_gpio = of_get_named_gpio(leddev.node, "led-gpio", 0);
    if (leddev.led_gpio < 0) {
        printk("can't get led-gpio\n");
        return -EINVAL;
    }

    gpio_request(leddev.led_gpio, "my_led");
    /* 设置输出模式,默认高电平 */
    gpio_direction_output(leddev.led_gpio, 1);

    return 0;
}

/*
 * @Brief   移除 platform 驱动函数
 * @Param   dev:platform设备
 * @Note    NOne
 * @RetVal  NOne
 */
static int led_remove(struct platform_device *dev)
{
    gpio_set_value(leddev.led_gpio, 1);

    cdev_del(&leddev.cdev);
    unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
    device_destroy(leddev.class, leddev.devid);
    class_destroy(leddev.class);

    return 0;
}

/* 匹配列表 */
static const struct of_device_id led_of_match[] = {
    { .compatible = "imx6ull-gpioled"},
    { /* sentinel */}
};

/* platform 驱动结构体 */
static struct platform_driver led_driver = {
    .driver = {
        .name = "imx6ull-led",          /* 驱动名字,用于和设备匹配 */
        .of_match_table = led_of_match, /* 设备树匹配表 */
    },
    .probe = led_probe,
    .remove = led_remove,
};

/*
 * @Brief   驱动模块加载函数
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static int __init leddriver_init(void)
{
    return platform_driver_register(&led_driver);
}

/*
 * @Brief   驱动模块卸载函数
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static void __exit leddriver_exit(void)
{
    platform_driver_unregister(&led_driver);
}

module_init(leddriver_init);
module_exit(leddriver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("toto");
  • Zeilen 33 bis 112, traditioneller Zeichengerätetreiber, nichts zu sagen.

  • Zeilen 120 bis 164, die Sondenfunktion des Plattformtreibers. Diese Funktion wird ausgeführt, wenn der Geräteknoten im Gerätebaum erfolgreich mit dem Treiber abgeglichen wurde und alle in der Treiberladefunktion durchgeführten Arbeiten nun in der Sondenfunktion abgeschlossen sind .

  • Zeilen 171 bis 180, Funktion entfernen, diese Funktion wird ausgeführt, wenn der Plattformtreiber deinstalliert wird. Geben Sie in dieser Funktion den Speicher frei, brechen Sie das Zeichengerät usw. ab, dh übertragen Sie die gesamte Arbeit in der Deinstallationsfunktion des ursprünglichen Treibers in die Entfernungsfunktion, um sie abzuschließen.

  • Die Zeilen 183–186, die Übereinstimmungstabelle, beschreiben, mit welchen Gerätetypen dieser Treiber übereinstimmt.

  • Zeile 184 fügt einen kompatiblen Attributwert mit dem Wert „atkalpha-gpioled“ hinzu. Wenn der kompatible Attributwert eines Geräteknotens im Gerätebaum ebenfalls „atkalpha-gpioled“ ist, stimmt er mit diesem Treiber überein.

  • Zeilen 189–196, Struktur des Treibers „platform_driver“, Zeile 191 setzt den Namen dieses Plattformtreibers auf „imx6ulled“. Wenn der Treiber erfolgreich geladen wurde, wird daher eine Datei mit dem Namen „imx6uled“ vorhanden sein.

  • Zeile 192 setzt of_match_table auf led_of_match oben.

  • Zeilen 203 bis 206, die Funktion zum Laden des Treibermoduls, bei der der led_driver-Treiber über platform_driver_register beim Linux-Kernel registriert wird.

  • Zeilen 213 bis 216, Funktion zur Deinstallation des Treibermoduls. In dieser Funktion wird platform_driver_unregister verwendet, um den led_driver-Treiber vom Linux-Kernel zu deinstallieren.

4. Testen Sie die APP-Vorbereitung

Erstellen Sie eine neue Testprogrammdatei mit dem Namen dtsplatform_app.c und geben Sie den folgenden Inhalt in dtsplatform_app.c ein:

/***********************************************************
 * Copyright © toto Co., Ltd. 1998-2029. All rights reserved.
 * Description: 
 * Version: 1.0
 * Autor: toto
 * Date: Do not edit
 * LastEditors: Seven
 * LastEditTime: Do not edit
***********************************************************/
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

#define LEDON   1
#define LEDOFF  0 

/*
 * @Brief
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
int main(int argc, char *argv[])
{
    int fd, retval;
    char *filename;
    unsigned char databuf[1];

    if (argc != 3) {
        printf("Error argc par cnt\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("file %s open failed\n", filename);
        return -1;
    }

    databuf[0] = atoi(argv[2]);
    retval = write(fd, databuf, sizeof(databuf));
    if (retval < 0) {
        printf("led control failed\n");
        close(fd);
        return -1;
    }

    close(fd);

    return 0;
}

5. Führen Sie den Test durch

5.1 Zusammenstellung

1. Kompilieren Sie den Treiber und schreiben Sie ein Makefile. Der Inhalt des Makefile ist wie folgt:

KERNELDIR := /home/toto/workspace/linux/linux-5.19
CURRENT_PATH := $(shell pwd)
obj-m := dtsplatform_driver.o

build: kernel_modules

kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

Kompilierungsbefehl:

make -j8

Nach erfolgreicher Kompilierung wird eine Treibermoduldatei mit dem Namen „dtsplatform_driver.ko“ generiert.

2. App-Kompilierungsbefehl kompilieren und testen:

arm-linux-gnueabihf-gcc dtsplatform_app.c -o dtsplatform_app

Nach erfolgreicher Kompilierung wird die Anwendung platform_app generiert.

5.2 Durchführung von Tests

Schalten Sie das Entwicklungsboard ein, kopieren Sie die beiden Dateien dtsplatform_driver.ko und dtsplatform_app in das Verzeichnis /lib/modules/5.19.0-g794a2f7be62d-dirty/ und geben Sie den folgenden Befehl ein, um das Treibermodul dtsplatform_driver.ko zu laden:

insmod dtsplatform_driver.ko

Gehen Sie nach dem Laden des Treibermoduls in das Verzeichnis /sys/bus/platform/drivers/, um zu überprüfen, ob der Treiber vorhanden ist. Wir haben das Namensfeld von led_driver (platform_driver-Typ) in dtsplatform_driver.c auf „imx6ull-led“ gesetzt Es befindet sich in /sys/. Im Verzeichnis bus/platform/drivers/ befindet sich eine Datei mit dem Namen „imx6ull-led“. Das Ergebnis sieht wie folgt aus:

Bild

Auf die gleiche Weise gibt es auch LED-Gerätedateien im Verzeichnis /sys/bus/platform/devices/, dem gpioled-Knoten im Gerätebaum. Die Ergebnisse sind wie folgt:

Bild

Nachdem das Treibermodul und das Gerätemodul erfolgreich geladen wurden, wird der Plattformbus abgeglichen. Wenn der Treiber und das Gerät erfolgreich abgeglichen wurden, wird eine Anweisungszeile wie unten gezeigt ausgegeben:

/lib/modules/5.19.0-g794a2f7be62d-dirty # insmod dtsplatform_driver.ko
[   45.657342] led driver and device has matched

Nachdem Treiber und Gerät erfolgreich abgeglichen wurden, können Sie den LED-Lichttreiber testen. Geben Sie den folgenden Befehl ein, um das LED-Licht einzuschalten:

./dtsplatform_app /dev/dts_platform_led 1

Geben Sie den folgenden Befehl ein, um das LED-Licht auszuschalten:

./dtsplatform_app /dev/dts_platform_led 0

Der Befehl zum Deinstallieren des Treibers lautet wie folgt:

rmmod dtsplatform_driver.ko

Ich denke du magst

Origin blog.csdn.net/weixin_41114301/article/details/132776583
Empfohlen
Rangfolge