GNU/Linux >> Linux Esercitazione >  >> Linux

Qual è la differenza tra un driver di piattaforma Linux e un normale driver di dispositivo?

I tuoi riferimenti sono validi ma mancano di una definizione di cos'è un dispositivo di piattaforma . Ce n'è uno su LWN. Cosa possiamo imparare da questa pagina:

  1. I dispositivi della piattaforma intrinsecamente non sono rilevabili , ovvero l'hardware non può dire "Ehi! Sono presente!" al software. Esempi tipici sono i dispositivi i2c, kernel/Documentation/i2c/instantiating-devices afferma:

    A differenza dei dispositivi PCI o USB, i dispositivi I2C non sono enumerati a livello hardware (in fase di esecuzione). Invece, il software deve sapere (in fase di compilazione) quali dispositivi sono collegati su ogni segmento di bus I2C. Quindi USB e PCI sono non dispositivi della piattaforma.

  2. I dispositivi della piattaforma sono associati ai driver corrispondendo ai nomi ,

  3. I dispositivi della piattaforma devono essere registrati molto presto durante l'avvio del sistema. Perché sono spesso fondamentali per il resto del sistema (piattaforma) e i suoi driver.

Quindi, in sostanza, la domanda "è un dispositivo di piattaforma o un dispositivo standard? " è più una questione di quale bus utilizza . Per lavorare con un particolare dispositivo della piattaforma, devi:

  1. registrare un driver di piattaforma che gestirà questo dispositivo. Dovrebbe definire un unico nome,
  2. registra il dispositivo della tua piattaforma , definendo lo stesso nome del driver.

Il driver della piattaforma è per quei dispositivi che sono su chip.

Non vero (in teoria, ma vero in pratica). I dispositivi i2c non sono onChip, ma sono dispositivi di piattaforma perché non sono rilevabili. Inoltre possiamo pensare a dispositivi onChip che sono normali dispositivi. Esempio:un chip GPU PCI integrato su un moderno processore x86. È rilevabile, quindi non è un dispositivo della piattaforma.

I normali driver di dispositivo sono per quelli che sono interfacciati al chip del processore. prima di imbattersi in un driver i2c.

Non vero. Molti normali i dispositivi sono interfacciati al processore, ma non tramite un bus i2c. Esempio:un mouse USB.

[MODIFICA] Nel tuo caso, dai un'occhiata a drivers/usb/host/ohci-pnx4008.c , che è un dispositivo della piattaforma del controller host USB (qui il controller host USB non è rilevabile, mentre i dispositivi USB, che si collegheranno ad esso, lo sono). È un dispositivo della piattaforma registrato dal file board (arch/arm/mach-pnx4008/core.c:pnx4008_init ). E all'interno della sua funzione di sonda, registra il suo dispositivo i2c sul bus con i2c_register_driver . Possiamo dedurre che il chipset del controller host USB parla con la CPU attraverso un bus i2c.

Perché quell'architettura? Perché da un lato, questo dispositivo può essere considerato un semplice dispositivo i2c che fornisce alcune funzionalità al sistema. D'altra parte, è un dispositivo compatibile con l'host USB. Deve registrarsi nello stack USB (usb_create_hcd ). Quindi sondare solo i2c sarà insufficiente. Dai un'occhiata a Documentation/i2c/instantiating-devices .


Esempi minimi di codice del modulo

Forse la differenza diventerà anche più chiara con alcuni esempi concreti.

Esempio di dispositivo della piattaforma

Codice:

  • guidatore a monte
  • Dispositivo virtuale QEMU minimo guidato.
  • Modifiche alla voce DTS nel kernel Linux

Ulteriori note di integrazione su:https://stackoverflow.com/a/44612957/895245

Guarda come:

  • gli indirizzi di registro e di interruzione sono codificati nell'albero dei dispositivi e corrispondono a QEMU -M versatilepb descrizione della macchina, che rappresenta il SoC
  • non c'è modo di rimuovere l'hardware del dispositivo (poiché fa parte del SoC)
  • il driver corretto è selezionato dal compatible proprietà dell'albero del dispositivo che corrisponde a platform_driver.name nel driver
  • platform_driver_register è l'interfaccia principale del registro
#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)

Esempio di dispositivo PCI senza piattaforma

  • guidatore a monte
  • Dispositivo virtuale QEMU minimo guidato

Guarda come:

  • gli indirizzi di registro e di interruzione sono allocati dinamicamente dal sistema PCI, non viene utilizzato alcun albero dei dispositivi
  • il driver corretto è selezionato dal PCI vendor:device ID (QEMU_VENDOR_ID, EDU_DEVICE_ID sull'esempio). Questo è integrato in ogni dispositivo e i fornitori devono garantire l'unicità.
  • possiamo inserire e rimuovere il dispositivo PCI con device_add edu e device_del edu come possiamo nella vita reale. Il sondaggio non è automatico, ma può essere eseguito dopo l'avvio con echo 1 > /sys/bus/pci/rescan . Vedi anche:Perché il metodo probe è necessario nei driver di dispositivo Linux oltre a init?
#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. Qual è la differenza tra InnoDB e MyISAM?

  2. Qual è la differenza tra un container Linux e un'immagine?

  3. Qual è la differenza tra "trova" e "trova" in Linux?

  4. Qual è la differenza tra ls e l?

  5. Qual è la differenza tra Unix, Linux, BSD e GNU?

Qual ​​è la differenza tra i kernel macOS e Linux

Qual è la differenza tra Linux e Unix?

Qual è la differenza tra Rsync e BTRFS in Linux?

Che cos'è un hypervisor? Qual è la differenza tra il tipo 1 e 2?

Qual è la differenza tra curl e Wget?

Qual è la differenza tra unlink e rm?