GNU/Linux >> Znalost Linux >  >> Linux

Jaký je rozdíl mezi ovladačem platformy Linux a normálním ovladačem zařízení?

Vaše reference jsou dobré, ale chybí jim definice toho, co je platformové zařízení . Jeden je na LWN. Co se můžeme z této stránky naučit:

  1. Platformová zařízení není ze své podstaty zjistitelná , tj. hardware nemůže říct „Ahoj! Jsem přítomen!“ do softwaru. Typickými příklady jsou zařízení i2c, kernel/Documentation/i2c/instantiating-devices uvádí:

    Na rozdíl od zařízení PCI nebo USB nejsou zařízení I2C vyčíslována na úrovni hardwaru (za běhu). Místo toho musí software vědět (při kompilaci), která zařízení jsou připojena na každém segmentu sběrnice I2C. USB a PCI jsou tedy ne zařízení platformy.

  2. Platformová zařízení jsou spojena s ovladači shodnými názvy ,

  3. Zařízení na platformě by měla být registrována velmi brzy během spouštění systému. Protože jsou často kritické pro zbytek systému (platformu) a jeho ovladače.

Takže v podstatě otázka „je to platformové zařízení nebo standardní zařízení? “ je spíše otázkou, kterou sběrnici používá . Chcete-li pracovat s konkrétním platformovým zařízením, musíte:

  1. zaregistrujte si ovladač platformy který bude toto zařízení spravovat. Měl by definovat jedinečný jméno,
  2. zaregistrujte své platformové zařízení , definující stejný název jako ovladač.

Ovladač platformy je určen pro zařízení, která jsou na čipu.

Není to pravda (teoreticky, ale v praxi). Zařízení i2c nejsou na čipu, ale jsou platformovými zařízeními, protože je nelze zjistit. Také můžeme myslet na zařízení onChip, která jsou normální zařízení. Příklad:integrovaný čip PCI GPU na moderním procesoru x86. Je zjistitelný, nejde tedy o platformové zařízení.

Normální ovladače zařízení jsou pro ty, které jsou propojeny s čipem procesoru. než narazíte na jeden ovladač i2c.

Není pravda. Mnoho normálních zařízení jsou propojena s procesorem, ale ne přes sběrnici i2c. Příklad:myš USB.

[UPRAVIT] Ve vašem případě se podívejte na drivers/usb/host/ohci-pnx4008.c , což je zařízení platformy hostitelského řadiče USB (zde hostitelský řadič USB není zjistitelný, zatímco zařízení USB, která se k němu připojí, ano). Je to platformové zařízení registrované souborem desky (arch/arm/mach-pnx4008/core.c:pnx4008_init ). A v rámci funkce sondy zaregistruje své zařízení i2c na sběrnici s i2c_register_driver . Můžeme odvodit, že čipová sada USB Host controller komunikuje CPU přes sběrnici i2c.

Proč ta architektura? Protože na jedné straně lze toto zařízení považovat za holé zařízení i2c poskytující systému některé funkce. Na druhou stranu je to zařízení podporující USB Host. Musí se zaregistrovat do zásobníku USB (usb_create_hcd ). Takže sondování pouze i2c bude nedostatečné. Podívejte se na Documentation/i2c/instantiating-devices .


Příklady kódu minimálního modulu

Možná bude rozdíl také jasnější s některými konkrétními příklady.

Příklad zařízení na platformě

Kód:

  • řidič proti proudu
  • minimálně řízené virtuálním zařízením QEMU.
  • Úpravy záznamu DTS v jádře Linux

Další poznámky k integraci na:https://stackoverflow.com/a/44612957/895245

Podívejte se, jak:

  • adresy registru a přerušení jsou pevně zakódovány ve stromu zařízení a odpovídají QEMU -M versatilepb popis stroje, který představuje SoC
  • není žádný způsob, jak odebrat hardware zařízení (protože je součástí SoC)
  • správný ovladač je vybrán pomocí compatible vlastnost stromu zařízení, která odpovídá platform_driver.name v ovladači
  • platform_driver_register je hlavní rozhraní registru
#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)

Příklad neplatformního zařízení PCI

  • řidič proti proudu
  • minimální ovládání virtuálního zařízení QEMU

Podívejte se, jak:

  • Adresy registru a přerušení jsou dynamicky přidělovány systémem PCI, nepoužívá se žádný strom zařízení
  • správný ovladač je vybrán pomocí PCI vendor:device ID (QEMU_VENDOR_ID, EDU_DEVICE_ID na příkladu). Toto je zapečeno v každém zařízení a prodejci musí zajistit jedinečnost.
  • můžeme vložit a vyjmout zařízení PCI pomocí device_add edu a device_del edu jak můžeme v reálném životě. Snímání není automatické, ale lze jej provést po spuštění pomocí echo 1 > /sys/bus/pci/rescan . Viz také:Proč je v ovladačích zařízení pro Linux kromě init potřeba i metoda sondy?
#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. Jaký je rozdíl mezi příkazem locate a find v Linuxu

  2. Jaký je rozdíl mezi vyrovnávací pamětí a mezipamětí v Linuxu?

  3. Jaký je rozdíl mezi strtok_r a strtok_s v C?

  1. rozdíl mezi souborem zařízení a ovladačem zařízení

  2. Jaký je rozdíl mezi „vyhledat“ a „najít“ v Linuxu?

  3. Jaký je rozdíl mezi ls a l?

  1. Jaký je rozdíl mezi ovladači jádra a moduly jádra?

  2. Jaký je rozdíl mezi Unixem, Linuxem, BSD a GNU?

  3. Jaký je rozdíl mezi partx a kpartx?