Früher hatte ich angenommen:
- Der Plattformtreiber ist für die Geräte, die sich auf dem Chip befinden.
- Normale Gerätetreiber sind für diejenigen, die mit dem Prozessorchip verbunden sind.
Bevor ich auf einen i2c-Treiber stoße … Aber hier lese ich den multifunktionalen i2c-Treiber durch, der als Plattformtreiber definiert ist. Ich war durchgegangen https://www.kernel.org/doc/Documentation/driver-model/platform.txt. Aber ich konnte immer noch keine klare Vorstellung davon bekommen, wie man Treiber definiert, sowohl für Onchip- als auch für Schnittstellengeräte.
Bitte jemand erklären.
Ihre Referenzen sind gut, aber es fehlt eine Definition von Was ist ein Plattformgerät. Da ist einer dran LWN. Was wir von dieser Seite lernen können:
-
Plattformgeräte sind grundsätzlich nicht auffindbardh die Hardware kann das nicht sagen “Hey! Ich bin anwesend!” zur Software. Typische Beispiele sind i2c-Geräte, kernel/Documentation/i2c/instantiating-devices
Zustände:
Im Gegensatz zu PCI- oder USB-Geräten werden I2C-Geräte nicht auf Hardwareebene (zur Laufzeit) aufgelistet. Stattdessen muss die Software (zur Kompilierzeit) wissen, welche Geräte an jedem I2C-Bussegment angeschlossen sind. Also USB und PCI nicht Plattformgeräte.
-
Plattformgeräte sind an Treiber gebunden durch übereinstimmende Namen,
- Plattformgeräte sein sollten sehr früh angemeldet während des Systemstarts. Weil sie oft entscheidend für den Rest des Systems (Plattform) und seine Treiber sind.
Also im Grunde die Frage “ist es ein Plattformgerät oder ein Standardgerät?” ist eher eine Frage, welchen Bus es verwendet. Um mit einem bestimmten Plattformgerät zu arbeiten, müssen Sie:
- Registrieren Sie einen Plattformtreiber die dieses Gerät verwalten wird. Es sollte a definieren einzigartig Name,
- Registrieren Sie Ihr Plattformgerätdie den gleichen Namen wie der Treiber definiert.
Der Plattformtreiber ist für die Geräte, die sich auf dem Chip befinden.
Stimmt nicht (in der Theorie, aber in der Praxis). i2c-Geräte sind keine OnChip-, sondern Plattformgeräte, da sie nicht erkennbar sind. Wir können auch an OnChip-Geräte denken, die es sind normal Geräte. Beispiel: ein integrierter PCI-GPU-Chip auf einem modernen x86-Prozessor. Es ist erkennbar, also kein Plattformgerät.
Normale Gerätetreiber sind für diejenigen, die mit dem Prozessorchip verbunden sind. bevor Sie auf einen i2c-Treiber stoßen.
Nicht wahr. Viele normal Geräte sind mit dem Prozessor verbunden, jedoch nicht über einen i2c-Bus. Beispiel: eine USB-Maus.
[EDIT] Schau in deinem Fall mal nach drivers/usb/host/ohci-pnx4008.c
, bei dem es sich um ein USB-Host-Controller-Plattformgerät handelt (hier ist der USB-Host-Controller nicht erkennbar, USB-Geräte, die sich mit ihm verbinden, sind es jedoch). Es ist ein von der registriertes Plattformgerät Board-Datei (arch/arm/mach-pnx4008/core.c:pnx4008_init
). Und innerhalb seiner Probe-Funktion registriert es sein i2c-Gerät am Bus mit i2c_register_driver
. Wir können daraus schließen, dass der USB-Host-Controller-Chipsatz redet mit die CPU über einen i2c-Bus.
Warum diese Architektur? Denn einerseits kann dieses Gerät als reines i2c-Gerät betrachtet werden, das einige Funktionen für das System bereitstellt. Andererseits ist es ein USB-Host-fähiges Gerät. Es muss sich beim USB-Stack registrieren (usb_create_hcd
). Es wird also nicht ausreichen, nur i2c zu prüfen. Schau doch mal vorbei Documentation/i2c/instantiating-devices
.
Minimale Modulcodebeispiele
Vielleicht wird der Unterschied auch an einigen konkreten Beispielen deutlicher.
Beispiel für ein Plattformgerät
Code:
Weitere Integrationshinweise unter: https://stackoverflow.com/a/44612957/895245
Siehe wie:
- Register- und Interrupt-Adressen sind im Gerätebaum fest codiert und entsprechen der QEMU
-M versatilepb
Maschinenbeschreibung, die den SoC darstellt
- Es gibt keine Möglichkeit, die Gerätehardware zu entfernen (da sie Teil des SoC ist)
- der richtige Fahrer wird durch die ausgewählt
compatible
Gerätebaumeigenschaft, die übereinstimmt platform_driver.name
im Fahrer
platform_driver_register
ist die Hauptregisterschnittstelle
#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)
Beispiel für ein plattformunabhängiges PCI-Gerät
Siehe wie:
- Register- und Interrupt-Adressen werden vom PCI-System dynamisch zugewiesen, es wird kein Gerätebaum verwendet
- der richtige Treiber wird von der PCI ausgewählt
vendor:device
ICH WÜRDE (QEMU_VENDOR_ID, EDU_DEVICE_ID
am Beispiel). Dies ist in jedes Gerät eingebrannt, und die Anbieter müssen die Einzigartigkeit sicherstellen.
- wir können das PCI-Gerät mit einfügen und entfernen
device_add edu
und device_del edu
wie wir es im wirklichen Leben können. Das Testen erfolgt nicht automatisch, kann aber nach dem Booten mit durchgeführt werden echo 1 > /sys/bus/pci/rescan
. Siehe auch: Warum wird die Prüfmethode in Linux-Gerätetreibern zusätzlich zu init benötigt?
#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);
Das Gerät ist ein MFD-Multifunktionsgerät. Da ist ein Feld drin
platform_device
;struct mfd cell
was nicht drin isti2c_client
Struktur. Vielleicht ist aus diesem Grund der Treiber als Plattformtreiber registriert. Bitte kommentieren Sie dies. !!– kzs
28. März 2013 um 8:21 Uhr
atmel.com/Images/doc32098.pdf ….. check this out … es könnte helfen
– Kinjal Patel
28. März 2013 um 9:56 Uhr
Ja, das Dokument war gut. Ich denke, ich könnte dieses Dokument irgendwann später verwenden. aber ich konnte noch nicht zu einem Schluss kommen. Ich habe einen Meister gefragt, der gut im Fahren ist. Ich werde hier posten, sobald ich die Antworten habe.
– kzs
29. März 2013 um 9:33 Uhr
linuxseekernel.blogspot.com/2014/05/…
– Jeyaram
23. Mai 2014 um 4:14 Uhr