GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Was ist der Unterschied zwischen einem Linux-Plattformtreiber und einem normalen Gerätetreiber?

Ihre Referenzen sind gut, aber es fehlt eine Definition dessen, was ein Plattformgerät ist . Es gibt eine auf LWN. Was wir von dieser Seite lernen können:

  1. Plattformgeräte sind von Natur aus nicht erkennbar , d.h. die Hardware kann nicht "Hey! Ich bin anwesend!" sagen zur Software. Typische Beispiele sind i2c-Geräte, kernel/Documentation/i2c/instantiating-devices Zustände:

    Im Gegensatz zu PCI- oder USB-Geräten werden I2C-Geräte nicht auf Hardwareebene (zur Laufzeit) aufgelistet. Stattdessen muss die Software (zur Kompilierzeit) wissen, welche Geräte an jedem I2C-Bussegment angeschlossen sind. Also sind USB und PCI nicht Plattformgeräte.

  2. Plattformgeräte werden durch übereinstimmende Namen an Treiber gebunden ,

  3. Plattformgeräte sollten sehr früh registriert werden während des Systemstarts. Weil sie oft entscheidend für den Rest des Systems (Plattform) und seine Treiber sind.

Also im Grunde die Frage "Ist es ein Plattformgerät oder ein Standardgerät? " ist mehr eine Frage dessen, welchen Bus es verwendet . Um mit einem bestimmten Plattformgerät zu arbeiten, müssen Sie:

  1. Plattformtreiber registrieren die dieses Gerät verwalten wird. Es sollte ein eindeutiges definieren Name,
  2. Registrieren Sie Ihr Plattformgerät , der den gleichen Namen wie der Treiber definiert.

Der Plattformtreiber ist für die Geräte, die sich auf dem Chip befinden.

Stimmt nicht (in der Theorie, aber in der Praxis). i2c-Geräte sind keine OnChip-, sondern Plattformgeräte, da sie nicht erkennbar sind. Wir können uns auch OnChip-Geräte vorstellen, die normal sind Geräte. Beispiel:ein integrierter PCI-GPU-Chip auf einem modernen x86-Prozessor. Es ist erkennbar, also kein Plattformgerät.

Normale Gerätetreiber sind für diejenigen, die mit dem Prozessorchip verbunden sind. bevor Sie auf einen i2c-Treiber stoßen.

Nicht wahr. Viele normal Geräte sind mit dem Prozessor verbunden, jedoch nicht über einen i2c-Bus. Beispiel:eine USB-Maus.

[BEARBEITEN] Sehen Sie sich in Ihrem Fall drivers/usb/host/ohci-pnx4008.c an , bei dem es sich um ein USB-Host-Controller-Plattformgerät handelt (hier ist der USB-Host-Controller nicht erkennbar, USB-Geräte, die sich mit ihm verbinden, sind es jedoch). Es ist ein Plattformgerät, das von der Board-Datei registriert wird (arch/arm/mach-pnx4008/core.c:pnx4008_init ). Und innerhalb seiner Probe-Funktion registriert es sein i2c-Gerät am Bus mit i2c_register_driver . Wir können daraus schließen, dass der Chipsatz des USB-Host-Controllers spricht die CPU über einen i2c-Bus.

Warum diese Architektur? Denn einerseits kann dieses Gerät als reines i2c-Gerät betrachtet werden, das einige Funktionen für das System bereitstellt. Andererseits ist es ein USB-Host-fähiges Gerät. Es muss sich beim USB-Stack registrieren (usb_create_hcd ). Es wird also nicht ausreichen, nur i2c zu prüfen. Siehe Documentation/i2c/instantiating-devices .


Beispiele für minimalen Modulcode

Vielleicht wird der Unterschied auch an einigen konkreten Beispielen deutlicher.

Beispiel für Plattformgerät

Code:

  • Treiber stromaufwärts
  • minimales virtuelles QEMU-Gerät gesteuert.
  • DTS-Eintragsänderungen im Linux-Kernel

Weitere Integrationshinweise unter:https://stackoverflow.com/a/44612957/895245

Siehe wie:

  • Register- und Interrupt-Adressen sind im Gerätebaum fest codiert und entsprechen dem QEMU -M versatilepb Maschinenbeschreibung, die den SoC darstellt
  • Es gibt keine Möglichkeit, die Gerätehardware zu entfernen (da sie Teil des SoC ist)
  • der richtige Fahrer wird durch compatible ausgewählt Gerätebaumeigenschaft, die platform_driver.name entspricht im Treiber
  • platform_driver_register ist die Hauptregisterschnittstelle
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>

MODULE_LICENSE("GPL");

static struct resource res;
static unsigned int irq;
static void __iomem *map;

static irqreturn_t lkmc_irq_handler(int irq, void *dev)
{
    /* TODO this 34 and not 18 as in the DTS, likely the interrupt controller moves it around.
     * Understand precisely. 34 = 18 + 16. */
    pr_info("lkmc_irq_handler irq = %d dev = %llx\n", irq, *(unsigned long long *)dev);
    /* ACK the IRQ. */
    iowrite32(0x9ABCDEF0, map + 4);
    return IRQ_HANDLED;
}

static int lkmc_platform_device_probe(struct platform_device *pdev)
{
    int asdf;
    struct device *dev = &pdev->dev;
    struct device_node *np = dev->of_node;

    dev_info(dev, "probe\n");

    /* Play with our custom poperty. */
    if (of_property_read_u32(np, "lkmc-asdf", &asdf) ) {
        dev_err(dev, "of_property_read_u32\n");
        return -EINVAL;
    }
    if (asdf != 0x12345678) {
        dev_err(dev, "asdf = %llx\n", (unsigned long long)asdf);
        return -EINVAL;
    }

    /* IRQ. */
    irq = irq_of_parse_and_map(dev->of_node, 0);
    if (request_irq(irq, lkmc_irq_handler, 0, "lkmc_platform_device", dev) < 0) {
        dev_err(dev, "request_irq");
        return -EINVAL;
    }
    dev_info(dev, "irq = %u\n", irq);

    /* MMIO. */
    if (of_address_to_resource(pdev->dev.of_node, 0, &res)) {
        dev_err(dev, "of_address_to_resource");
        return -EINVAL;
    }
    if  (!request_mem_region(res.start, resource_size(&res), "lkmc_platform_device")) {
        dev_err(dev, "request_mem_region");
        return -EINVAL;
    }
    map = of_iomap(pdev->dev.of_node, 0);
    if (!map) {
        dev_err(dev, "of_iomap");
        return -EINVAL;
    }
    dev_info(dev, "res.start = %llx resource_size = %llx\n",
            (unsigned long long)res.start, (unsigned long long)resource_size(&res));

    /* Test MMIO and IRQ. */
    iowrite32(0x12345678, map);

    return 0;
}

static int lkmc_platform_device_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "remove\n");
    free_irq(irq, &pdev->dev);
    iounmap(map);
    release_mem_region(res.start, resource_size(&res));
    return 0;
}

static const struct of_device_id of_lkmc_platform_device_match[] = {
    { .compatible = "lkmc_platform_device", },
    {},
};

MODULE_DEVICE_TABLE(of, of_lkmc_platform_device_match);

static struct platform_driver lkmc_plaform_driver = {
    .probe      = lkmc_platform_device_probe,
    .remove     = lkmc_platform_device_remove,
    .driver     = {
        .name   = "lkmc_platform_device",
        .of_match_table = of_lkmc_platform_device_match,
        .owner = THIS_MODULE,
    },
};

static int lkmc_platform_device_init(void)
{
    pr_info("lkmc_platform_device_init\n");
    return platform_driver_register(&lkmc_plaform_driver);
}

static void lkmc_platform_device_exit(void)
{
    pr_info("lkmc_platform_device_exit\n");
    platform_driver_unregister(&lkmc_plaform_driver);
}

module_init(lkmc_platform_device_init)
module_exit(lkmc_platform_device_exit)

Beispiel für ein plattformunabhängiges PCI-Gerät

  • Treiber stromaufwärts
  • minimales virtuelles QEMU-Gerät angetrieben

Siehe wie:

  • Register- und Interrupt-Adressen werden vom PCI-System dynamisch zugewiesen, es wird kein Gerätebaum verwendet
  • der richtige Treiber wird vom PCI vendor:device ausgewählt ID (QEMU_VENDOR_ID, EDU_DEVICE_ID am Beispiel). Dies ist in jedes Gerät eingebrannt, und die Anbieter müssen die Einzigartigkeit sicherstellen.
  • Wir können das PCI-Gerät mit device_add edu einfügen und entfernen und device_del edu wie wir es im wirklichen Leben können. Das Testen erfolgt nicht automatisch, kann aber nach dem Booten mit echo 1 > /sys/bus/pci/rescan durchgeführt werden . Siehe auch:Warum wird die Prüfmethode in Linux-Gerätetreibern zusätzlich zu init benötigt?
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>

#define BAR 0
#define CDEV_NAME "lkmc_hw_pci_min"
#define EDU_DEVICE_ID 0x11e9
#define QEMU_VENDOR_ID 0x1234

MODULE_LICENSE("GPL");

static struct pci_device_id id_table[] = {
    { PCI_DEVICE(QEMU_VENDOR_ID, EDU_DEVICE_ID), },
    { 0, }
};
MODULE_DEVICE_TABLE(pci, id_table);
static int major;
static struct pci_dev *pdev;
static void __iomem *mmio;
static struct file_operations fops = {
    .owner   = THIS_MODULE,
};

static irqreturn_t irq_handler(int irq, void *dev)
{
    pr_info("irq_handler irq = %d dev = %d\n", irq, *(int *)dev);
    iowrite32(0, mmio + 4);
    return IRQ_HANDLED;
}

static int probe(struct pci_dev *dev, const struct pci_device_id *id)
{
    pr_info("probe\n");
    major = register_chrdev(0, CDEV_NAME, &fops);
    pdev = dev;
    if (pci_enable_device(dev) < 0) {
        dev_err(&(pdev->dev), "pci_enable_device\n");
        goto error;
    }
    if (pci_request_region(dev, BAR, "myregion0")) {
        dev_err(&(pdev->dev), "pci_request_region\n");
        goto error;
    }
    mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR));
    pr_info("dev->irq = %u\n", dev->irq);
    if (request_irq(dev->irq, irq_handler, IRQF_SHARED, "pci_irq_handler0", &major) < 0) {
        dev_err(&(dev->dev), "request_irq\n");
        goto error;
    }
    iowrite32(0x12345678, mmio);
    return 0;
error:
    return 1;
}

static void remove(struct pci_dev *dev)
{
    pr_info("remove\n");
    free_irq(dev->irq, &major);
    pci_release_region(dev, BAR);
    unregister_chrdev(major, CDEV_NAME);
}

static struct pci_driver pci_driver = {
    .name     = CDEV_NAME,
    .id_table = id_table,
    .probe    = probe,
    .remove   = remove,
};

static int myinit(void)
{
    if (pci_register_driver(&pci_driver) < 0) {
        return 1;
    }
    return 0;
}

static void myexit(void)
{
    pci_unregister_driver(&pci_driver);
}

module_init(myinit);
module_exit(myexit);

Linux
  1. Was ist der Unterschied zwischen Puffer- und Cache-Speicher in Linux?

  2. Was ist der Unterschied zwischen strtok_r und strtok_s in C?

  3. Unterschied zwischen Gerätedatei und Gerätetreiber

  4. Was ist der Unterschied zwischen „locate“ und „find“ in Linux?

  5. Was ist der Unterschied zwischen einem Bibliotheksaufruf und einem Systemaufruf in Linux?

Was ist der Unterschied zwischen macOS- und Linux-Kernels

Was ist der Unterschied zwischen Linux und Unix?

Was ist der Unterschied zwischen Rsync und BTRFS unter Linux?

Was ist ein Hypervisor? Was ist der Unterschied zwischen Typ 1 und 2?

Was ist der Unterschied zwischen Curl und Wget?

Was ist der Unterschied zwischen unlink und rm?