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

J'avais déjà pensé au pilote de plate-forme ainsi qu'au pilote de périphérique normal comme:

S'il vous plaît que quelqu'un explique.

49

2 réponses

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

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

    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). Plutôt, le logiciel doit savoir (au moment de la compilation) quels périphériques sont connectés sur chaque segment de bus I2C. Donc, USB et PCI sont Pas périphériques de plate-forme.

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

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

Donc, fondamentalement, la question "est une plate-forme appareil ou un dispositif standard?" est - plus une question de quel bus il utilise. Pour travailler avec un périphérique de plate-forme particulier, vous devez:

  1. enregistrez un pilote de plate-forme qui gérera ce périphérique. Il doit définir un nomunique ,
  2. enregistrez votre périphérique de plate-forme, en définissant le même nom que le pilote.

Pilote de plate-forme est pour les appareils qui sont sur puce.

Pas vrai (en théorie, mais vrai dans la pratique). les périphériques i2c ne sont pas onChip, mais sont des périphériques de plate-forme car ils ne sont pas détectables. Nous pouvons également penser à des périphériques onChip qui sont des périphériquesnormaux . Exemple: une puce GPU PCI intégrée sur un processeur x86 moderne. Il est découvrable, donc pas un dispositif de plate-forme.

Pilote de périphérique Normal SONT POUR CEUX QUI SONT interfacés à la puce du processeur. avant de tomber sur un pilote i2c.

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

[EDIT] dans votre cas, jetez un oeil à 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, alors que les périphériques USB, qui s'y connecteront, le sont). C'est un périphérique de plate-forme enregistré par le fichier board (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? Parce que 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 USB Host. Il doit s'inscrire à la pile USB (usb_create_hcd). Donc, sonder seulement i2c sera insuffisant. Jetez un oeil à Documentation/i2c/instantiating-devices.

74
répondu m-ric 2013-04-03 15:23:13

Minimes module d'exemples de code

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

Exemple de dispositif de plate-forme

Code:

Notes d'intégration supplémentaires à: https://stackoverflow.com/a/44612957/895245

Voir comment:

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

Voir 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 pilote correct est sélectionné par L'ID PCI vendor:device (QEMU_VENDOR_ID, EDU_DEVICE_ID sur l'exemple). Ceci est cuit dans chaque appareil, et les fournisseurs doivent s'assurer 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 vie réelle. 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);
1