GNU/Linux >> Tutoriels Linux >  >> Linux

Quelle est la différence entre un pilote de plate-forme Linux et un pilote de périphérique normal ?

Vos références sont bonnes mais manquent d'une définition de qu'est-ce qu'un appareil de plate-forme . Il y en a un sur LWN. Ce que nous pouvons apprendre de cette page :

  1. Les appareils de plate-forme ne sont intrinsèquement pas détectables , c'est-à-dire que le matériel ne peut pas dire "Hé ! Je suis présent !" au logiciel. Des exemples typiques sont les appareils i2c, kernel/Documentation/i2c/instantiating-devices indique :

    Contrairement aux périphériques PCI ou USB, les périphériques I2C ne sont pas énumérés au niveau matériel (au moment de l'exécution). Au lieu de cela, le logiciel doit savoir (au moment de la compilation) quels périphériques sont connectés sur chaque segment de bus I2C. Ainsi, USB et PCI sont non appareils de plate-forme.

  2. Les appareils de plate-forme sont liés aux pilotes par des noms correspondants ,

  3. Les appareils de la plate-forme doivent être enregistrés très tôt pendant le démarrage du système. Parce qu'ils sont souvent critiques pour le reste du système (plate-forme) et ses pilotes.

Donc, en gros, la question "est-ce un appareil de plate-forme ou un appareil standard ? " est plutôt une question de quel bus il utilise . Pour travailler avec un appareil de plate-forme particulier, vous devez :

  1. enregistrer un pilote de plate-forme qui gérera cet appareil. Il doit définir un unique nom,
  2. enregistrer votre appareil de plate-forme , définissant le même nom que le pilote.

Le pilote de plate-forme est destiné aux appareils qui sont sur puce.

Pas vrai (en théorie, mais vrai en pratique). Les appareils i2c ne sont pas sur puce, mais sont des appareils de plate-forme car ils ne sont pas détectables. Nous pouvons également penser aux appareils onChip qui sont normaux dispositifs. Exemple :une puce GPU PCI intégrée sur un processeur x86 moderne. Il est détectable, donc pas un appareil de plate-forme.

Les pilotes de périphérique normaux sont destinés à ceux qui sont interfacés avec la puce du processeur. avant de tomber sur un pilote i2c.

Pas vrai. Beaucoup normal les périphériques sont interfacés au processeur, mais pas via un bus i2c. Exemple :une souris USB.

[MODIFIER] Dans votre cas, jetez un œil à drivers/usb/host/ohci-pnx4008.c , qui est un périphérique de plate-forme de contrôleur hôte USB (ici, le contrôleur hôte USB n'est pas détectable, contrairement aux périphériques USB qui s'y connecteront). Il s'agit d'un périphérique de plate-forme enregistré par le fichier de carte (arch/arm/mach-pnx4008/core.c:pnx4008_init ). Et dans sa fonction de sonde, il enregistre son périphérique i2c sur le bus avec i2c_register_driver . Nous pouvons en déduire que le chipset du contrôleur hôte USB parle à le CPU via un bus i2c.

Pourquoi cette architecture ? Car d'une part, cet appareil peut être considéré comme un appareil i2c nu fournissant certaines fonctionnalités au système. D'autre part, il s'agit d'un périphérique compatible avec l'hôte USB. Il doit s'enregistrer sur la pile USB (usb_create_hcd ). Sonder uniquement i2c sera donc insuffisant. Jetez un oeil à Documentation/i2c/instantiating-devices .


Exemples de code de module minimal

Peut-être que la différence deviendra également plus claire avec quelques exemples concrets.

Exemple d'appareil de plate-forme

Code :

  • pilote en amont
  • Périphérique virtuel QEMU minimal piloté.
  • Modifications des entrées DTS sur le noyau Linux

Autres notes d'intégration sur :https://stackoverflow.com/a/44612957/895245

Découvrez comment :

  • les adresses de registre et d'interruption sont codées en dur dans l'arborescence des périphériques et correspondent au QEMU -M versatilepb description de la machine, qui représente le SoC
  • il n'y a aucun moyen de retirer le matériel de l'appareil (puisqu'il fait partie du SoC)
  • le bon pilote est sélectionné par le compatible propriété de l'arborescence des appareils qui correspond à platform_driver.name dans le pilote
  • platform_driver_register est l'interface principale du registre
#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)

Exemple de périphérique PCI hors plate-forme

  • pilote en amont
  • Périphérique virtuel QEMU minimal piloté

Découvrez comment :

  • les adresses de registre et d'interruption sont allouées dynamiquement par le système PCI, aucune arborescence de périphériques n'est utilisée
  • le bon pilote est sélectionné par le PCI vendor:device identifiant (QEMU_VENDOR_ID, EDU_DEVICE_ID sur exemple). Ceci est intégré à chaque appareil, et les fournisseurs doivent garantir l'unicité.
  • nous pouvons insérer et retirer le périphérique PCI avec device_add edu et device_del edu comme on peut dans la vraie vie. Le sondage n'est pas automatique, mais peut être effectué après le démarrage avec echo 1 > /sys/bus/pci/rescan . Voir aussi :Pourquoi la méthode probe est-elle nécessaire dans les pilotes de périphériques Linux en plus d'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. Quelle est la différence entre la mémoire tampon et la mémoire cache sous Linux ?

  2. Quelle est la différence entre strtok_r et strtok_s en C ?

  3. différence entre le fichier de périphérique et le pilote de périphérique

  4. Quelle est la différence entre un appel Library et un appel System sous Linux ?

  5. Quelle est la différence entre les pilotes du noyau et les modules du noyau ?

Quelle est la différence entre les noyaux macOS et Linux

Quelle est la différence entre Linux et Unix ?

Quelle est la différence entre Rsync et BTRFS sous Linux ?

Qu'est-ce qu'un Hyperviseur ? Quelle est la différence entre les types 1 et 2 ?

Quelle est la différence entre curl et Wget ?

Quelle est la différence entre Unix, Linux, BSD et GNU ?