web-dev-qa-db-fra.com

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

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

  • Le pilote de plate-forme est destiné aux périphériques sur puce.
  • Le pilote de périphérique normal est destiné à ceux qui sont interfacés avec la puce du processeur.

Avant de rencontrer un pilote i2c ... Mais ici, je lis le pilote i2c multifonction défini comme pilote de plate-forme. J'étais passé par https://www.kernel.org/doc/Documentation/driver-model/platform.txt . Mais je n'arrivais toujours pas à avoir une idée claire de parvenir à une conclusion sur la façon de définir les pilotes, comme pour les appareils à puce et les appareils bien interfacés.

Veuillez expliquer à quelqu'un.

54
kzs

Vos références sont bonnes mais manquent de définition de ce qu'est un appareil de plateforme . 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 "Hé! 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). 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 des appareils de plate-forme pas.

  2. Les périphériques de la plate-forme sont liés aux pilotes en faisant correspondre les noms ,

  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 (plateforme) et ses pilotes.

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

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

Le pilote de plate-forme est destiné aux périphériques sur puce.

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

Le pilote de périphérique normal est destiné à ceux qui sont interfacés avec la puce du processeur. avant de rencontrer un pilote i2c.

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

[EDIT] 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 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'un autre côté, c'est un appareil compatible USB Host. Il doit s'enregistrer sur la pile USB (usb_create_hcd). Donc, sonder uniquement i2c sera insuffisant. Jetez un œil à Documentation/i2c/instantiating-devices.

82
m-ric

Exemples de code de module minimal

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

Exemple de périphérique de plate-forme

Code:

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

Regarde comment:

  • les adresses d'enregistrement et d'interruption sont codées en dur dans l'arborescence des périphériques et correspondent à la 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 (car il fait partie du SoC)
  • le pilote correct est sélectionné par la propriété d'arborescence des périphériques compatible 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 non plateforme PCI

Regarde 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 le PCI vendor:device ID (QEMU_VENDOR_ID, EDU_DEVICE_ID sur l'exemple). Cela 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 nous pouvons 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 de sonde 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);