/** * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices * @drv: target pci driver * @vendor: PCI vendor ID * @device: PCI device ID * @subvendor: PCI subvendor ID * @subdevice: PCI subdevice ID * @class: PCI class * @class_mask: PCI class mask * @driver_data: private driver data * * Adds a new dynamic pci device ID to this driver and causes the * driver to probe for all devices again. @drv must have been * registered prior to calling this function. * * CONTEXT: * Does GFP_KERNEL allocation. * * RETURNS: * 0 on success, -errno on failure.
*/ int pci_add_dynid(struct pci_driver *drv, unsignedint vendor, unsignedint device, unsignedint subvendor, unsignedint subdevice, unsignedintclass, unsignedint class_mask, unsignedlong driver_data)
{ struct pci_dynid *dynid;
dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); if (!dynid) return -ENOMEM;
/** * pci_match_id - See if a PCI device matches a given pci_id table * @ids: array of PCI device ID structures to search in * @dev: the PCI device structure to match against. * * Used by a driver to check whether a PCI device is in its list of * supported devices. Returns the matching pci_device_id structure or * %NULL if there is no match. * * Deprecated; don't use this as it will not catch any dynamic IDs * that a driver might want to check for.
*/ conststruct pci_device_id *pci_match_id(conststruct pci_device_id *ids, struct pci_dev *dev)
{ if (ids) { while (ids->vendor || ids->subvendor || ids->class_mask) { if (pci_match_one_device(ids, dev)) return ids;
ids++;
}
} return NULL;
}
EXPORT_SYMBOL(pci_match_id);
/** * pci_match_device - See if a device matches a driver's list of IDs * @drv: the PCI driver to match against * @dev: the PCI device structure to match against * * Used by a driver to check whether a PCI device is in its list of * supported devices or in the dynids list, which may have been augmented * via the sysfs "new_id" file. Returns the matching pci_device_id * structure or %NULL if there is no match.
*/ staticconststruct pci_device_id *pci_match_device(struct pci_driver *drv, struct pci_dev *dev)
{ struct pci_dynid *dynid; conststruct pci_device_id *found_id = NULL, *ids;
/* When driver_override is set, only bind to the matching driver */ if (dev->driver_override && strcmp(dev->driver_override, drv->name)) return NULL;
/* Look at the dynamic ids first, before the static ones */
spin_lock(&drv->dynids.lock);
list_for_each_entry(dynid, &drv->dynids.list, node) { if (pci_match_one_device(&dynid->id, dev)) {
found_id = &dynid->id; break;
}
}
spin_unlock(&drv->dynids.lock);
if (found_id) return found_id;
for (ids = drv->id_table; (found_id = pci_match_id(ids, dev));
ids = found_id + 1) { /* * The match table is split based on driver_override. * In case override_only was set, enforce driver_override * matching.
*/ if (found_id->override_only) { if (dev->driver_override) return found_id;
} else { return found_id;
}
}
/* driver_override will always match, send a dummy id */ if (dev->driver_override) return &pci_device_id_any; return NULL;
}
/** * new_id_store - sysfs frontend to pci_add_dynid() * @driver: target device driver * @buf: buffer for scanning device ID data * @count: input size * * Allow PCI IDs to be added to an existing driver via sysfs.
*/ static ssize_t new_id_store(struct device_driver *driver, constchar *buf,
size_t count)
{ struct pci_driver *pdrv = to_pci_driver(driver); conststruct pci_device_id *ids = pdrv->id_table;
u32 vendor, device, subvendor = PCI_ANY_ID,
subdevice = PCI_ANY_ID, class = 0, class_mask = 0; unsignedlong driver_data = 0; int fields; int retval = 0;
if (pci_match_device(pdrv, pdev))
retval = -EEXIST;
kfree(pdev);
if (retval) return retval;
}
/* Only accept driver_data values that match an existing id_table
entry */ if (ids) {
retval = -EINVAL; while (ids->vendor || ids->subvendor || ids->class_mask) { if (driver_data == ids->driver_data) {
retval = 0; break;
}
ids++;
} if (retval) /* No match */ return retval;
}
/* * Unbound PCI devices are always put in D0, regardless of * runtime PM status. During probe, the device is set to * active and the usage count is incremented. If the driver * supports runtime PM, it should call pm_runtime_put_noidle(), * or any other runtime PM helper function decrementing the usage * count, in its probe routine and pm_runtime_get_noresume() in * its remove routine.
*/
pm_runtime_get_sync(dev);
pci_dev->driver = pci_drv;
rc = pci_drv->probe(pci_dev, ddi->id); if (!rc) return rc; if (rc < 0) {
pci_dev->driver = NULL;
pm_runtime_put_sync(dev); return rc;
} /* * Probe function should return < 0 for failure, 0 for success * Treat values > 0 as success, but warn.
*/
pci_warn(pci_dev, "Driver probe function unexpectedly returned %d\n",
rc); return 0;
}
/* * Execute driver initialization on node where the device is * attached. This way the driver likely allocates its local memory * on the right node.
*/
node = dev_to_node(&dev->dev);
dev->is_probed = 1;
cpu_hotplug_disable();
/* * Prevent nesting work_on_cpu() for the case where a Virtual Function * device is probed from work_on_cpu() of the Physical device.
*/ if (node < 0 || node >= MAX_NUMNODES || !node_online(node) ||
pci_physfn_is_probed(dev)) {
cpu = nr_cpu_ids;
} else {
cpumask_var_t wq_domain_mask;
/** * __pci_device_probe - check if a driver wants to claim a specific PCI device * @drv: driver to call to check if it wants the PCI device * @pci_dev: PCI device being probed * * returns 0 on success, else error. * side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
*/ staticint __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
{ conststruct pci_device_id *id; int error = 0;
if (drv->probe) {
error = -ENODEV;
id = pci_match_device(drv, pci_dev); if (id)
error = pci_call_probe(drv, pci_dev, id);
} return error;
}
if (drv->remove) {
pm_runtime_get_sync(dev); /* * If the driver provides a .runtime_idle() callback and it has * started to run already, it may continue to run in parallel * with the code below, so wait until all of the runtime PM * activity has completed.
*/
pm_runtime_barrier(dev);
drv->remove(pci_dev);
pm_runtime_put_noidle(dev);
}
pcibios_free_irq(pci_dev);
pci_dev->driver = NULL;
pci_iov_remove(pci_dev);
/* Undo the runtime PM settings in local_pci_probe() */
pm_runtime_put_sync(dev);
/* * If the device is still on, set the power state as "unknown", * since it might change by the next time we load the driver.
*/ if (pci_dev->current_state == PCI_D0)
pci_dev->current_state = PCI_UNKNOWN;
/* * We would love to complain here if pci_dev->is_enabled is set, that * the driver should have called pci_disable_device(), but the * unfortunate fact is there are too many odd BIOS and bridge setups * that don't like drivers doing that all of the time. * Oh well, we can dream of sane hardware when we sleep, no matter how * horrible the crap we have to deal with is when we are awake...
*/
/* * If this is a kexec reboot, turn off Bus Master bit on the * device to tell it to not continue to do DMA. Don't touch * devices in D3cold or unknown states. * If it is not a kexec reboot, firmware will hit the PCI * devices with big hammer and stop their DMA any way.
*/ if (kexec_in_progress && (pci_dev->current_state <= PCI_D3hot))
pci_clear_master(pci_dev);
}
#ifdef CONFIG_PM_SLEEP
/* Auxiliary functions used for system resume */
/** * pci_restore_standard_config - restore standard config registers of PCI device * @pci_dev: PCI device to handle
*/ staticint pci_restore_standard_config(struct pci_dev *pci_dev)
{
pci_update_current_state(pci_dev, PCI_UNKNOWN);
if (pci_dev->current_state != PCI_D0) { int error = pci_set_power_state(pci_dev, PCI_D0); if (error) return error;
}
staticvoid pci_pm_bridge_power_up_actions(struct pci_dev *pci_dev)
{ int ret;
ret = pci_bridge_wait_for_secondary_bus(pci_dev, "resume"); if (ret) { /* * The downstream link failed to come up, so mark the * devices below as disconnected to make sure we don't * attempt to resume them.
*/
pci_walk_bus(pci_dev->subordinate, pci_dev_set_disconnected,
NULL); return;
}
/* * When powering on a bridge from D3cold, the whole hierarchy may be * powered on into D0uninitialized state, resume them to give them a * chance to suspend again
*/
pci_resume_bus(pci_dev->subordinate);
}
#endif/* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
/* * Default "suspend" method for devices that have no driver provided suspend, * or not even a driver at all (second part).
*/ staticvoid pci_pm_set_unknown_state(struct pci_dev *pci_dev)
{ /* * mark its power state as "unknown", since we don't know if * e.g. the BIOS will change its device state when we suspend.
*/ if (pci_dev->current_state == PCI_D0)
pci_dev->current_state = PCI_UNKNOWN;
}
/* * Default "resume" method for devices that have no driver provided resume, * or not even a driver at all (second part).
*/ staticint pci_pm_reenable_device(struct pci_dev *pci_dev)
{ int retval;
/* if the device was enabled before suspend, re-enable */
retval = pci_reenable_device(pci_dev); /* * if the device was busmaster before the suspend, make it busmaster * again
*/ if (pci_dev->is_busmaster)
pci_set_master(pci_dev);
/* * Legacy PM support is used by default, so warn if the new framework is * supported as well. Drivers are supposed to support either the * former, or the latter, but not both at the same time.
*/
pci_WARN(pci_dev, ret && drv->driver.pm, "device %04x:%04x\n",
pci_dev->vendor, pci_dev->device);
if (pm && pm->prepare) { int error = pm->prepare(dev); if (error < 0) return error;
if (!error && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE)) return 0;
} if (pci_dev_need_resume(pci_dev)) return 0;
/* * The PME setting needs to be adjusted here in case the direct-complete * optimization is used with respect to this device.
*/
pci_dev_adjust_pme(pci_dev); return 1;
}
/* Resume device if platform firmware has put it in reset-power-on */ if (pm_runtime_suspended(dev) && pm_resume_via_firmware()) {
pci_power_t pre_sleep_state = pci_dev->current_state;
pci_refresh_power_state(pci_dev); /* * On platforms with ACPI this check may also trigger for * devices sharing power resources if one of those power * resources has been activated as a result of a change of the * power state of another device sharing it. However, in that * case it is also better to resume the device, in general.
*/ if (pci_dev->current_state < pre_sleep_state)
pm_request_resume(dev);
}
#ifdef CONFIG_SUSPEND staticvoid pcie_pme_root_status_cleanup(struct pci_dev *pci_dev)
{ /* * Some BIOSes forget to clear Root PME Status bits after system * wakeup, which breaks ACPI-based runtime wakeup on PCI Express. * Clear those bits now just in case (shouldn't hurt).
*/ if (pci_is_pcie(pci_dev) &&
(pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(pci_dev) == PCI_EXP_TYPE_RC_EC))
pcie_clear_root_pme_status(pci_dev);
}
/* * Disabling PTM allows some systems, e.g., Intel mobile chips * since Coffee Lake, to enter a lower-power PM state.
*/
pci_suspend_ptm(pci_dev);
if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_SUSPEND);
if (!pm) {
pci_pm_default_suspend(pci_dev); return 0;
}
/* * PCI devices suspended at run time may need to be resumed at this * point, because in general it may be necessary to reconfigure them for * system suspend. Namely, if the device is expected to wake up the * system from the sleep state, it may have to be reconfigured for this * purpose, or if the device is not expected to wake up the system from * the sleep state, it should be prevented from signaling wakeup events * going forward. * * Also if the driver of the device does not indicate that its system * suspend callbacks can cope with runtime-suspended devices, it is * better to resume the device from runtime suspend here.
*/ if (!dev_pm_smart_suspend(dev) || pci_dev_need_resume(pci_dev)) {
pm_runtime_resume(dev);
pci_dev->state_saved = false;
} else {
pci_dev_adjust_pme(pci_dev);
}
if (pm->suspend) {
pci_power_t prev = pci_dev->current_state; int error;
error = pm->suspend(dev);
suspend_report_result(dev, pm->suspend, error); if (error) return error;
if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
&& pci_dev->current_state != PCI_UNKNOWN) {
pci_WARN_ONCE(pci_dev, pci_dev->current_state != prev, "PCI PM: State of device not saved by %pS\n",
pm->suspend);
}
}
return 0;
}
staticint pci_pm_suspend_late(struct device *dev)
{ if (dev_pm_skip_suspend(dev)) return 0;
if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev);
if (!pm) {
pci_save_state(pci_dev); goto Fixup;
}
if (pm->suspend_noirq) {
pci_power_t prev = pci_dev->current_state; int error;
error = pm->suspend_noirq(dev);
suspend_report_result(dev, pm->suspend_noirq, error); if (error) return error;
if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
&& pci_dev->current_state != PCI_UNKNOWN) {
pci_WARN_ONCE(pci_dev, pci_dev->current_state != prev, "PCI PM: State of device not saved by %pS\n",
pm->suspend_noirq); goto Fixup;
}
}
if (!pci_dev->state_saved) {
pci_save_state(pci_dev);
/* * If the device is a bridge with a child in D0 below it, * it needs to stay in D0, so check skip_bus_pm to avoid * putting it into a low-power state in that case.
*/ if (!pci_dev->skip_bus_pm && pci_power_manageable(pci_dev))
pci_prepare_to_sleep(pci_dev);
}
pci_dbg(pci_dev, "PCI PM: Suspend power state: %s\n",
pci_power_name(pci_dev->current_state));
if (pci_dev->current_state == PCI_D0) {
pci_dev->skip_bus_pm = true; /* * Per PCI PM r1.2, table 6-1, a bridge must be in D0 if any * downstream device is in D0, so avoid changing the power state * of the parent bridge by setting the skip_bus_pm flag for it.
*/ if (pci_dev->bus->self)
pci_dev->bus->self->skip_bus_pm = true;
}
/* * Some BIOSes from ASUS have a bug: If a USB EHCI host controller's * PCI COMMAND register isn't 0, the BIOS assumes that the controller * hasn't been quiesced and tries to turn it off. If the controller * is already in D3, this can hang or cause memory corruption. * * Since the value of the COMMAND register doesn't matter once the * device has been suspended, we can safely set it to 0 here.
*/ if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
pci_write_config_word(pci_dev, PCI_COMMAND, 0);
/* * If the target system sleep state is suspend-to-idle, it is sufficient * to check whether or not the device's wakeup settings are good for * runtime PM. Otherwise, the pm_resume_via_firmware() check will cause * pci_pm_complete() to take care of fixing up the device's state * anyway, if need be.
*/ if (device_can_wakeup(dev) && !device_may_wakeup(dev))
dev->power.may_skip_resume = false;
/* * In the suspend-to-idle case, devices left in D0 during suspend will * stay in D0, so it is not necessary to restore or update their * configuration here and attempting to put them into D0 again is * pointless, so avoid doing that.
*/ if (!(skip_bus_pm && pm_suspend_no_platform()))
pci_pm_default_resume_early(pci_dev);
/* * This is necessary for the suspend error path in which resume is * called without restoring the standard config registers of the device.
*/ if (pci_dev->state_saved)
pci_restore_standard_config(pci_dev);
pci_resume_ptm(pci_dev);
if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev);
pci_pm_default_resume(pci_dev);
if (pm) { if (pm->resume) return pm->resume(dev);
} else {
pci_pm_reenable_device(pci_dev);
}
if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_FREEZE);
if (!pm) {
pci_pm_default_suspend(pci_dev); return 0;
}
/* * Resume all runtime-suspended devices before creating a snapshot * image of system memory, because the restore kernel generally cannot * be expected to always handle them consistently and they need to be * put into the runtime-active metastate during system resume anyway, * so it is better to ensure that the state saved in the image will be * always consistent with that.
*/
pm_runtime_resume(dev);
pci_dev->state_saved = false;
/* * The pm->thaw_noirq() callback assumes the device has been * returned to D0 and its config state has been restored. * * In addition, pci_restore_state() restores MSI-X state in MMIO * space, which requires the device to be in D0, so return it to D0 * in case the driver's "freeze" callbacks put it into a low-power * state.
*/
pci_pm_power_up_and_verify_state(pci_dev);
pci_restore_state(pci_dev);
if (pci_has_legacy_pm_support(pci_dev)) return 0;
if (pm && pm->thaw_noirq) return pm->thaw_noirq(dev);
if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_HIBERNATE);
if (!pm) {
pci_pm_default_suspend(pci_dev); return 0;
}
/* The reason to do that is the same as in pci_pm_suspend(). */ if (!dev_pm_smart_suspend(dev) || pci_dev_need_resume(pci_dev)) {
pm_runtime_resume(dev);
pci_dev->state_saved = false;
} else {
pci_dev_adjust_pme(pci_dev);
}
if (!pci_dev->state_saved && !pci_has_subordinate(pci_dev))
pci_prepare_to_sleep(pci_dev);
/* * The reason for doing this here is the same as for the analogous code * in pci_pm_suspend_noirq().
*/ if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
pci_write_config_word(pci_dev, PCI_COMMAND, 0);
/* * This is necessary for the hibernation error path in which restore is * called without restoring the standard config registers of the device.
*/ if (pci_dev->state_saved)
pci_restore_standard_config(pci_dev);
if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev);
pci_pm_default_resume(pci_dev);
if (pm) { if (pm->restore) return pm->restore(dev);
} else {
pci_pm_reenable_device(pci_dev);
}
/* * If pci_dev->driver is not set (unbound), we leave the device in D0, * but it may go to D3cold when the bridge above it runtime suspends. * Save its config space in case that happens.
*/ if (!pci_dev->driver) {
pci_save_state(pci_dev); return 0;
}
pci_dev->state_saved = false; if (pm && pm->runtime_suspend) {
error = pm->runtime_suspend(dev); /* * -EBUSY and -EAGAIN is used to request the runtime PM core * to schedule a new suspend, so log the event only with debug * log level.
*/ if (error == -EBUSY || error == -EAGAIN) {
pci_dbg(pci_dev, "can't suspend now (%ps returned %d)\n",
pm->runtime_suspend, error); return error;
} elseif (error) {
pci_err(pci_dev, "can't suspend (%ps returned %d)\n",
pm->runtime_suspend, error); return error;
}
}
pci_fixup_device(pci_fixup_suspend, pci_dev);
if (pm && pm->runtime_suspend
&& !pci_dev->state_saved && pci_dev->current_state != PCI_D0
&& pci_dev->current_state != PCI_UNKNOWN) {
pci_WARN_ONCE(pci_dev, pci_dev->current_state != prev, "PCI PM: State of device not saved by %pS\n",
pm->runtime_suspend); return 0;
}
if (!pci_dev->state_saved) {
pci_save_state(pci_dev);
pci_finish_runtime_suspend(pci_dev);
}
/* * Restoring config space is necessary even if the device is not bound * to a driver because although we left it in D0, it may have gone to * D3cold when the bridge above it runtime suspended.
*/
pci_pm_default_resume_early(pci_dev);
pci_resume_ptm(pci_dev);
/* * If pci_dev->driver is not set (unbound), the device should * always remain in D0 regardless of the runtime PM status
*/ if (!pci_dev->driver) return 0;
if (pm && pm->runtime_idle) return pm->runtime_idle(dev);
/** * __pci_register_driver - register a new pci driver * @drv: the driver structure to register * @owner: owner module of drv * @mod_name: module name string * * Adds the driver structure to the list of registered drivers. * Returns a negative value on error, otherwise 0. * If no error occurred, the driver remains registered even if * no device was claimed during registration.
*/ int __pci_register_driver(struct pci_driver *drv, struct module *owner, constchar *mod_name)
{ /* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
drv->driver.groups = drv->groups;
drv->driver.dev_groups = drv->dev_groups;
/* register with core */ return driver_register(&drv->driver);
}
EXPORT_SYMBOL(__pci_register_driver);
/** * pci_unregister_driver - unregister a pci driver * @drv: the driver structure to unregister * * Deletes the driver structure from the list of registered PCI drivers, * gives it a chance to clean up by calling its remove() function for * each device it was responsible for, and marks those devices as * driverless.
*/
/** * pci_dev_driver - get the pci_driver of a device * @dev: the device to query * * Returns the appropriate pci_driver structure or %NULL if there is no * registered driver for the device.
*/ struct pci_driver *pci_dev_driver(conststruct pci_dev *dev)
{ int i;
if (dev->driver) return dev->driver;
for (i = 0; i <= PCI_ROM_RESOURCE; i++) if (dev->resource[i].flags & IORESOURCE_BUSY) return &pci_compat_driver;
return NULL;
}
EXPORT_SYMBOL(pci_dev_driver);
/** * pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure * @dev: the PCI device structure to match against * @drv: the device driver to search for matching PCI device id structures * * Used by a driver to check whether a PCI device present in the * system is in its list of supported devices. Returns the matching * pci_device_id structure or %NULL if there is no match.
*/ staticint pci_bus_match(struct device *dev, conststruct device_driver *drv)
{ struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *pci_drv; conststruct pci_device_id *found_id;
if (pci_dev_binding_disallowed(pci_dev)) return 0;
/** * pci_dev_get - increments the reference count of the pci device structure * @dev: the device being referenced * * Each live reference to a device should be refcounted. * * Drivers for PCI devices should normally record such references in * their probe() methods, when they bind to a device, and release * them by calling pci_dev_put(), in their disconnect() methods. * * A pointer to the device with the incremented reference counter is returned.
*/ struct pci_dev *pci_dev_get(struct pci_dev *dev)
{ if (dev)
get_device(&dev->dev); return dev;
}
EXPORT_SYMBOL(pci_dev_get);
/** * pci_dev_put - release a use of the pci device structure * @dev: device that's been disconnected * * Must be called when a user of a device is finished with it. When the last * user of the device calls this function, the memory of the device is freed.
*/ void pci_dev_put(struct pci_dev *dev)
{ if (dev)
put_device(&dev->dev);
}
EXPORT_SYMBOL(pci_dev_put);
/** * pci_dma_configure - Setup DMA configuration * @dev: ptr to dev structure * * Function to update PCI devices's DMA configuration using the same * info from the OF node or ACPI node of host bridge's parent (if any).
*/ staticint pci_dma_configure(struct device *dev)
{ conststruct device_driver *drv = READ_ONCE(dev->driver); struct device *bridge; int ret = 0;
ret = acpi_dma_configure(dev, acpi_get_dma_attr(adev));
}
pci_put_host_bridge_device(bridge);
/* @drv may not be valid when we're called from the IOMMU layer */ if (!ret && drv && !to_pci_driver(drv)->driver_managed_dma) {
ret = iommu_device_use_default_domain(dev); if (ret)
arch_teardown_dma_ops(dev);
}
if (!driver->driver_managed_dma)
iommu_device_unuse_default_domain(dev);
}
/* * pci_device_irq_get_affinity - get IRQ affinity mask for device * @dev: ptr to dev structure * @irq_vec: interrupt vector number * * Return the CPU affinity mask for @dev and @irq_vec.
*/ staticconststruct cpumask *pci_device_irq_get_affinity(struct device *dev, unsignedint irq_vec)
{ return pci_irq_get_affinity(to_pci_dev(dev), irq_vec);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.