development

Linux 플랫폼 드라이버와 일반 장치 드라이버의 차이점은 무엇입니까?

big-blog 2021. 1. 7. 20:37
반응형

Linux 플랫폼 드라이버와 일반 장치 드라이버의 차이점은 무엇입니까?


이전에 플랫폼 드라이버와 다음과 같은 일반 장치 드라이버에 대해 생각했습니다.

  • 플랫폼 드라이버는 칩에있는 장치 용입니다.
  • 일반 장치 드라이버는 프로세서 칩에 인터페이스되는 장치 용입니다.

하나의 i2c 드라이버를 만나기 전에 ... 그러나 여기서는 플랫폼 드라이버로 정의 된 다기능 i2c 드라이버를 읽고 있습니다. 나는 https://www.kernel.org/doc/Documentation/driver-model/platform.txt를 통과했습니다 . 그러나 온칩 및 인터페이스 장치 모두와 같이 드라이버를 정의하는 방법에 대한 결론을 내리는 명확한 아이디어를 얻지 못했습니다.

누군가 설명해주세요.


참고 문헌은 좋지만 플랫폼 장치에 대한 정의가 없습니다 . LWN에 하나가 있습니다 . 이 페이지에서 배울 수있는 것 :

  1. 플랫폼 장치는 본질적으로 검색 할 수 없습니다. 즉, 하드웨어가 "Hey! I 'm present!" 라고 말할 수 없습니다 . 소프트웨어에. 일반적인 예는 i2c 장치, kernel/Documentation/i2c/instantiating-devices상태 :

    PCI 또는 USB 장치와 달리 I2C 장치는 런타임에 하드웨어 수준에서 열거되지 않습니다. 대신 소프트웨어는 각 I2C 버스 세그먼트에 연결된 장치를 컴파일 할 때 알아야합니다. 따라서 USB와 PCI는 플랫폼 장치 아닙니다 .

  2. 플랫폼 장치는 이름을 일치 시켜 드라이버 바인딩됩니다 .

  3. 플랫폼 장치는 시스템 부팅 중에 매우 일찍 등록 해야합니다 . 시스템 (플랫폼)과 드라이버의 나머지 부분에 종종 중요하기 때문입니다.

따라서 기본적으로 " 플랫폼 장치입니까 아니면 표준 장치입니까? " 라는 질문은 어떤 버스를 사용하는지에 대한 질문입니다 . 특정 플랫폼 장치로 작업하려면 다음을 수행해야합니다.

  1. 이 장치를 관리 할 플랫폼 드라이버등록하십시오 . 고유 한 이름을 정의해야합니다.
  2. 드라이버와 동일한 이름을 정의 하여 플랫폼 장치를 등록하십시오 .

플랫폼 드라이버는 칩에있는 장치 용입니다.

사실이 아닙니다 (이론적으로는 사실이지만 실제로는 사실입니다). i2c 장치는 onChip이 아니지만 검색 할 수 없기 때문에 플랫폼 장치입니다. 또한 일반적인 장치 인 onChip 장치를 생각할 수 있습니다. 예 : 최신 x86 프로세서의 통합 PCI GPU 칩. 검색 가능하므로 플랫폼 장치가 아닙니다.

일반 장치 드라이버는 프로세서 칩에 인터페이스되는 장치 용입니다. 하나의 i2c 드라이버를 만나기 전에.

사실이 아니다. 많은 일반 장치가 프로세서에 인터페이스되지만 i2c 버스를 통하지는 않습니다. 예 : USB 마우스.

[편집] 귀하의 경우 drivers/usb/host/ohci-pnx4008.c에는 USB 호스트 컨트롤러 플랫폼 장치 인를 살펴보십시오 (여기에서 USB 호스트 컨트롤러는 검색 할 수 없지만 연결할 USB 장치는 있음). 보드 파일 ( arch/arm/mach-pnx4008/core.c:pnx4008_init)에 등록 된 플랫폼 디바이스 입니다. 그리고 프로브 기능 내에서 i2c 장치를 i2c_register_driver. 우리는 USB 호스트 컨트롤러 칩셋 추론 할 수있다 회담 I2C 버스를 통해 CPU를.

왜 그 아키텍처입니까? 한편으로이 장치는 시스템에 몇 가지 기능을 제공하는 베어 i2c 장치로 간주 될 수 있기 때문입니다. 반면에 USB 호스트 가능 장치입니다. USB 스택 ( usb_create_hcd) 에 등록해야합니다 . 따라서 i2c 만 프로빙하는 것만으로는 충분하지 않습니다. 를보세요 Documentation/i2c/instantiating-devices.


최소 모듈 코드 예제

몇 가지 구체적인 예를 통해 차이가 더 명확해질 수도 있습니다.

플랫폼 장치 예

암호:

추가 통합 정보 : https://stackoverflow.com/a/44612957/895245

방법보기 :

  • 레지스터 및 인터럽트 주소는 장치 트리에 하드 코딩되어 -M versatilepb있으며 SoC를 나타내는 QEMU 시스템 설명 과 일치합니다.
  • 장치 하드웨어를 제거 할 방법이 없습니다 (SoC의 일부이기 때문에)
  • 드라이버 에서 compatible일치 하는 장치 트리 속성에 의해 올바른 드라이버가 선택됩니다.platform_driver.name
  • platform_driver_register 주 레지스터 인터페이스입니다.
#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)

PCI 비 플랫폼 장치 예

방법보기 :

  • register and interrupt addresses are dynamically allocated by the PCI system, no device tree is used
  • the correct driver is selected by the PCI vendor:device ID (QEMU_VENDOR_ID, EDU_DEVICE_ID on example). This is baked into every device, and vendors must ensure uniqueness.
  • we can insert and remove the PCI device with device_add edu and device_del edu as we can in real life. Probing is not automatic, but can be done after boot with echo 1 > /sys/bus/pci/rescan. See also: Why is the probe method needed in Linux device drivers in addition to 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);

ReferenceURL : https://stackoverflow.com/questions/15610570/what-is-the-difference-between-a-linux-platform-driver-and-normal-device-driver

반응형