/** * struct nfp_cpp - main nfpcore device structure * Following fields are read-only after probe() exits or netdevs are spawned. * @dev: embedded device structure * @op: low-level implementation ops * @priv: private data of the low-level implementation * @model: chip model * @interface: chip interface id we are using to reach it * @serial: chip serial number * @imb_cat_table: CPP Mapping Table * @mu_locality_lsb: MU access type bit offset * * Following fields use explicit locking: * @resource_list: NFP CPP resource list * @resource_lock: protects @resource_list * * @area_cache_list: cached areas for cpp/xpb read/write speed up * @area_cache_mutex: protects @area_cache_list * * @waitq: area wait queue
*/ struct nfp_cpp { struct device dev;
if (area->cpp->op->area_release)
area->cpp->op->area_release(area);
__release_cpp_area(&area->kref);
}
if (cpp->op->free)
cpp->op->free(cpp);
device_unregister(&cpp->dev);
kfree(cpp);
}
/** * nfp_cpp_model() - Retrieve the Model ID of the NFP * @cpp: NFP CPP handle * * Return: NFP CPP Model ID
*/
u32 nfp_cpp_model(struct nfp_cpp *cpp)
{ return cpp->model;
}
/** * nfp_cpp_interface() - Retrieve the Interface ID of the NFP * @cpp: NFP CPP handle * * Return: NFP CPP Interface ID
*/
u16 nfp_cpp_interface(struct nfp_cpp *cpp)
{ return cpp->interface;
}
/** * nfp_cpp_serial() - Retrieve the Serial ID of the NFP * @cpp: NFP CPP handle * @serial: Pointer to NFP serial number * * Return: Length of NFP serial number
*/ int nfp_cpp_serial(struct nfp_cpp *cpp, const u8 **serial)
{
*serial = &cpp->serial[0]; returnsizeof(cpp->serial);
}
/** * nfp_cpp_area_alloc_with_name() - allocate a new CPP area * @cpp: CPP device handle * @dest: NFP CPP ID * @name: Name of region * @address: Address of region * @size: Size of region * * Allocate and initialize a CPP area structure. The area must later * be locked down with an 'acquire' before it can be safely accessed. * * NOTE: @address and @size must be 32-bit aligned values. * * Return: NFP CPP area handle, or NULL
*/ struct nfp_cpp_area *
nfp_cpp_area_alloc_with_name(struct nfp_cpp *cpp, u32 dest, constchar *name, unsignedlonglong address, unsignedlong size)
{ struct nfp_cpp_area *area;
u64 tmp64 = address; int err, name_len;
/* Remap from cpp_island to cpp_target */
err = nfp_target_cpp(dest, tmp64, &dest, &tmp64, cpp->imb_cat_table); if (err < 0) return NULL;
address = tmp64;
if (!name)
name = "(reserved)";
name_len = strlen(name) + 1;
area = kzalloc(sizeof(*area) + cpp->op->area_priv_size + name_len,
GFP_KERNEL); if (!area) return NULL;
/** * nfp_cpp_area_alloc() - allocate a new CPP area * @cpp: CPP handle * @dest: CPP id * @address: Start address on CPP target * @size: Size of area in bytes * * Allocate and initialize a CPP area structure. The area must later * be locked down with an 'acquire' before it can be safely accessed. * * NOTE: @address and @size must be 32-bit aligned values. * * Return: NFP CPP Area handle, or NULL
*/ struct nfp_cpp_area *
nfp_cpp_area_alloc(struct nfp_cpp *cpp, u32 dest, unsignedlonglong address, unsignedlong size)
{ return nfp_cpp_area_alloc_with_name(cpp, dest, NULL, address, size);
}
/** * nfp_cpp_area_alloc_acquire() - allocate a new CPP area and lock it down * @cpp: CPP handle * @name: Name of region * @dest: CPP id * @address: Start address on CPP target * @size: Size of area * * Allocate and initialize a CPP area structure, and lock it down so * that it can be accessed directly. * * NOTE: @address and @size must be 32-bit aligned values. * The area must also be 'released' when the structure is freed. * * Return: NFP CPP Area handle, or NULL
*/ struct nfp_cpp_area *
nfp_cpp_area_alloc_acquire(struct nfp_cpp *cpp, constchar *name, u32 dest, unsignedlonglong address, unsignedlong size)
{ struct nfp_cpp_area *area;
area = nfp_cpp_area_alloc_with_name(cpp, dest, name, address, size); if (!area) return NULL;
if (nfp_cpp_area_acquire(area)) {
nfp_cpp_area_free(area); return NULL;
}
return area;
}
/** * nfp_cpp_area_free() - free up the CPP area * @area: CPP area handle * * Frees up memory resources held by the CPP area.
*/ void nfp_cpp_area_free(struct nfp_cpp_area *area)
{ if (atomic_read(&area->refcount))
nfp_warn(area->cpp, "Warning: freeing busy area\n");
nfp_cpp_area_put(area);
}
staticbool nfp_cpp_area_acquire_try(struct nfp_cpp_area *area, int *status)
{
*status = area->cpp->op->area_acquire(area);
return *status != -EAGAIN;
}
staticint __nfp_cpp_area_acquire(struct nfp_cpp_area *area)
{ int err, status;
if (atomic_inc_return(&area->refcount) > 1) return 0;
if (!area->cpp->op->area_acquire) return 0;
err = wait_event_interruptible(area->cpp->waitq,
nfp_cpp_area_acquire_try(area, &status)); if (!err)
err = status; if (err) {
nfp_warn(area->cpp, "Warning: area wait failed: %d\n", err);
atomic_dec(&area->refcount); return err;
}
nfp_cpp_area_get(area);
return 0;
}
/** * nfp_cpp_area_acquire() - lock down a CPP area for access * @area: CPP area handle * * Locks down the CPP area for a potential long term activity. Area * must always be locked down before being accessed. * * Return: 0, or -ERRNO
*/ int nfp_cpp_area_acquire(struct nfp_cpp_area *area)
{ int ret;
mutex_lock(&area->mutex);
ret = __nfp_cpp_area_acquire(area);
mutex_unlock(&area->mutex);
return ret;
}
/** * nfp_cpp_area_acquire_nonblocking() - lock down a CPP area for access * @area: CPP area handle * * Locks down the CPP area for a potential long term activity. Area * must always be locked down before being accessed. * * NOTE: Returns -EAGAIN is no area is available * * Return: 0, or -ERRNO
*/ int nfp_cpp_area_acquire_nonblocking(struct nfp_cpp_area *area)
{
mutex_lock(&area->mutex); if (atomic_inc_return(&area->refcount) == 1) { if (area->cpp->op->area_acquire) { int err;
/** * nfp_cpp_area_release() - release a locked down CPP area * @area: CPP area handle * * Releases a previously locked down CPP area.
*/ void nfp_cpp_area_release(struct nfp_cpp_area *area)
{
mutex_lock(&area->mutex); /* Only call the release on refcount == 0 */ if (atomic_dec_and_test(&area->refcount)) { if (area->cpp->op->area_release) {
area->cpp->op->area_release(area); /* Let anyone waiting for a BAR try to get one.. */
wake_up_interruptible_all(&area->cpp->waitq);
}
}
mutex_unlock(&area->mutex);
nfp_cpp_area_put(area);
}
/** * nfp_cpp_area_release_free() - release CPP area and free it * @area: CPP area handle * * Releases CPP area and frees up memory resources held by the it.
*/ void nfp_cpp_area_release_free(struct nfp_cpp_area *area)
{
nfp_cpp_area_release(area);
nfp_cpp_area_free(area);
}
/** * nfp_cpp_area_read() - read data from CPP area * @area: CPP area handle * @offset: offset into CPP area * @kernel_vaddr: kernel address to put data into * @length: number of bytes to read * * Read data from indicated CPP region. * * NOTE: @offset and @length must be 32-bit aligned values. * Area must have been locked down with an 'acquire'. * * Return: length of io, or -ERRNO
*/ int nfp_cpp_area_read(struct nfp_cpp_area *area, unsignedlong offset, void *kernel_vaddr,
size_t length)
{ return area->cpp->op->area_read(area, kernel_vaddr, offset, length);
}
/** * nfp_cpp_area_write() - write data to CPP area * @area: CPP area handle * @offset: offset into CPP area * @kernel_vaddr: kernel address to read data from * @length: number of bytes to write * * Write data to indicated CPP region. * * NOTE: @offset and @length must be 32-bit aligned values. * Area must have been locked down with an 'acquire'. * * Return: length of io, or -ERRNO
*/ int nfp_cpp_area_write(struct nfp_cpp_area *area, unsignedlong offset, constvoid *kernel_vaddr,
size_t length)
{ return area->cpp->op->area_write(area, kernel_vaddr, offset, length);
}
/** * nfp_cpp_area_size() - return size of a CPP area * @cpp_area: CPP area handle * * Return: Size of the area
*/
size_t nfp_cpp_area_size(struct nfp_cpp_area *cpp_area)
{ return cpp_area->size;
}
/** * nfp_cpp_area_name() - return name of a CPP area * @cpp_area: CPP area handle * * Return: Name of the area, or NULL
*/ constchar *nfp_cpp_area_name(struct nfp_cpp_area *cpp_area)
{ return cpp_area->resource.name;
}
/** * nfp_cpp_area_priv() - return private struct for CPP area * @cpp_area: CPP area handle * * Return: Private data for the CPP area
*/ void *nfp_cpp_area_priv(struct nfp_cpp_area *cpp_area)
{ return &cpp_area[1];
}
/** * nfp_cpp_area_resource() - get resource * @area: CPP area handle * * NOTE: Area must have been locked down with an 'acquire'. * * Return: struct resource pointer, or NULL
*/ struct resource *nfp_cpp_area_resource(struct nfp_cpp_area *area)
{ struct resource *res = NULL;
if (area->cpp->op->area_resource)
res = area->cpp->op->area_resource(area);
return res;
}
/** * nfp_cpp_area_phys() - get physical address of CPP area * @area: CPP area handle * * NOTE: Area must have been locked down with an 'acquire'. * * Return: phy_addr_t of the area, or NULL
*/
phys_addr_t nfp_cpp_area_phys(struct nfp_cpp_area *area)
{
phys_addr_t addr = ~0;
if (area->cpp->op->area_phys)
addr = area->cpp->op->area_phys(area);
return addr;
}
/** * nfp_cpp_area_iomem() - get IOMEM region for CPP area * @area: CPP area handle * * Returns an iomem pointer for use with readl()/writel() style * operations. * * NOTE: Area must have been locked down with an 'acquire'. * * Return: __iomem pointer to the area, or NULL
*/ void __iomem *nfp_cpp_area_iomem(struct nfp_cpp_area *area)
{ void __iomem *iomem = NULL;
if (area->cpp->op->area_iomem)
iomem = area->cpp->op->area_iomem(area);
return iomem;
}
/** * nfp_cpp_area_readl() - Read a u32 word from an area * @area: CPP Area handle * @offset: Offset into area * @value: Pointer to read buffer * * Return: 0 on success, or -ERRNO
*/ int nfp_cpp_area_readl(struct nfp_cpp_area *area, unsignedlong offset, u32 *value)
{
u8 tmp[4]; int n;
n = nfp_cpp_area_read(area, offset, &tmp, sizeof(tmp)); if (n != sizeof(tmp)) return n < 0 ? n : -EIO;
*value = get_unaligned_le32(tmp); return 0;
}
/** * nfp_cpp_area_writel() - Write a u32 word to an area * @area: CPP Area handle * @offset: Offset into area * @value: Value to write * * Return: 0 on success, or -ERRNO
*/ int nfp_cpp_area_writel(struct nfp_cpp_area *area, unsignedlong offset, u32 value)
{
u8 tmp[4]; int n;
put_unaligned_le32(value, tmp);
n = nfp_cpp_area_write(area, offset, &tmp, sizeof(tmp));
return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO;
}
/** * nfp_cpp_area_readq() - Read a u64 word from an area * @area: CPP Area handle * @offset: Offset into area * @value: Pointer to read buffer * * Return: 0 on success, or -ERRNO
*/ int nfp_cpp_area_readq(struct nfp_cpp_area *area, unsignedlong offset, u64 *value)
{
u8 tmp[8]; int n;
n = nfp_cpp_area_read(area, offset, &tmp, sizeof(tmp)); if (n != sizeof(tmp)) return n < 0 ? n : -EIO;
*value = get_unaligned_le64(tmp); return 0;
}
/** * nfp_cpp_area_writeq() - Write a u64 word to an area * @area: CPP Area handle * @offset: Offset into area * @value: Value to write * * Return: 0 on success, or -ERRNO
*/ int nfp_cpp_area_writeq(struct nfp_cpp_area *area, unsignedlong offset, u64 value)
{
u8 tmp[8]; int n;
put_unaligned_le64(value, tmp);
n = nfp_cpp_area_write(area, offset, &tmp, sizeof(tmp));
return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO;
}
/** * nfp_cpp_area_fill() - fill a CPP area with a value * @area: CPP area * @offset: offset into CPP area * @value: value to fill with * @length: length of area to fill * * Fill indicated area with given value. * * Return: length of io, or -ERRNO
*/ int nfp_cpp_area_fill(struct nfp_cpp_area *area, unsignedlong offset, u32 value, size_t length)
{
u8 tmp[4];
size_t i; int k;
put_unaligned_le32(value, tmp);
if (offset % sizeof(tmp) || length % sizeof(tmp)) return -EINVAL;
for (i = 0; i < length; i += sizeof(tmp)) {
k = nfp_cpp_area_write(area, offset + i, &tmp, sizeof(tmp)); if (k < 0) return k;
}
return i;
}
/** * nfp_cpp_area_cache_add() - Permanently reserve and area for the hot cache * @cpp: NFP CPP handle * @size: Size of the area - MUST BE A POWER OF 2.
*/ int nfp_cpp_area_cache_add(struct nfp_cpp *cpp, size_t size)
{ struct nfp_cpp_area_cache *cache; struct nfp_cpp_area *area;
/* Allocate an area - we use the MU target's base as a placeholder, * as all supported chips have a MU.
*/
area = nfp_cpp_area_alloc(cpp, NFP_CPP_ID(7, NFP_CPP_ACTION_RW, 0),
0, size); if (!area) return -ENOMEM;
/* Early exit when length == 0, which prevents * the need for special case code below when * checking against available cache size.
*/ if (length == 0 || id == 0) return NULL;
/* Remap from cpp_island to cpp_target */
err = nfp_target_cpp(id, addr, &id, &addr, cpp->imb_cat_table); if (err < 0) return NULL;
mutex_lock(&cpp->area_cache_mutex);
if (list_empty(&cpp->area_cache_list)) {
mutex_unlock(&cpp->area_cache_mutex); return NULL;
}
addr += *offset;
/* See if we have a match */
list_for_each_entry(cache, &cpp->area_cache_list, entry) { if (id == cache->id &&
addr >= cache->addr &&
addr + length <= cache->addr + cache->size) gotoexit;
}
/* No matches - inspect the tail of the LRU */
cache = list_entry(cpp->area_cache_list.prev, struct nfp_cpp_area_cache, entry);
/* Can we fit in the cache entry? */ if (round_down(addr + length - 1, cache->size) !=
round_down(addr, cache->size)) {
mutex_unlock(&cpp->area_cache_mutex); return NULL;
}
/* If id != 0, we will need to release it */ if (cache->id) {
nfp_cpp_area_release(cache->area);
cache->id = 0;
cache->addr = 0;
}
/* Adjust the start address to be cache size aligned */
cache->addr = addr & ~(u64)(cache->size - 1);
/* Re-init to the new ID and address */ if (cpp->op->area_init) {
err = cpp->op->area_init(cache->area,
id, cache->addr, cache->size); if (err < 0) {
mutex_unlock(&cpp->area_cache_mutex); return NULL;
}
}
/* Attempt to acquire */
err = nfp_cpp_area_acquire(cache->area); if (err < 0) {
mutex_unlock(&cpp->area_cache_mutex); return NULL;
}
if (cache)
area_cache_put(cpp, cache); else
nfp_cpp_area_release_free(area);
return err;
}
/** * nfp_cpp_read() - read from CPP target * @cpp: CPP handle * @destination: CPP id * @address: offset into CPP target * @kernel_vaddr: kernel buffer for result * @length: number of bytes to read * * Return: length of io, or -ERRNO
*/ int nfp_cpp_read(struct nfp_cpp *cpp, u32 destination, unsignedlonglong address, void *kernel_vaddr,
size_t length)
{
size_t n, offset; int ret;
if (cache)
area_cache_put(cpp, cache); else
nfp_cpp_area_release_free(area);
return err;
}
/** * nfp_cpp_write() - write to CPP target * @cpp: CPP handle * @destination: CPP id * @address: offset into CPP target * @kernel_vaddr: kernel buffer to read from * @length: number of bytes to write * * Return: length of io, or -ERRNO
*/ int nfp_cpp_write(struct nfp_cpp *cpp, u32 destination, unsignedlonglong address, constvoid *kernel_vaddr, size_t length)
{
size_t n, offset; int ret;
/* make first write smaller to align to safe window */
n = min_t(size_t, length - offset,
ALIGN(w_addr + 1, NFP_CPP_SAFE_AREA_SIZE) - w_addr);
ret = __nfp_cpp_write(cpp, destination, address + offset,
kernel_vaddr + offset, n); if (ret < 0) return ret; if (ret != n) return offset + n;
}
return length;
}
/* Return the correct CPP address, and fixup xpb_addr as needed. */ static u32 nfp_xpb_to_cpp(struct nfp_cpp *cpp, u32 *xpb_addr)
{ int island;
u32 xpb;
xpb = NFP_CPP_ID(14, NFP_CPP_ACTION_RW, 0); /* Ensure that non-local XPB accesses go * out through the global XPBM bus.
*/
island = (*xpb_addr >> 24) & 0x3f; if (!island) return xpb;
/* Accesses to the ARM Island overlay uses Island 0 / Global Bit */
*xpb_addr &= ~0x7f000000; if (*xpb_addr < 0x60000) {
*xpb_addr |= 1 << 30;
} else { /* And only non-ARM interfaces use the island id = 1 */ if (NFP_CPP_INTERFACE_TYPE_of(nfp_cpp_interface(cpp))
!= NFP_CPP_INTERFACE_TYPE_ARM)
*xpb_addr |= 1 << 24;
}
return xpb;
}
/** * nfp_xpb_readl() - Read a u32 word from a XPB location * @cpp: CPP device handle * @xpb_addr: Address for operation * @value: Pointer to read buffer * * Return: 0 on success, or -ERRNO
*/ int nfp_xpb_readl(struct nfp_cpp *cpp, u32 xpb_addr, u32 *value)
{
u32 cpp_dest = nfp_xpb_to_cpp(cpp, &xpb_addr);
/** * nfp_xpb_writel() - Write a u32 word to a XPB location * @cpp: CPP device handle * @xpb_addr: Address for operation * @value: Value to write * * Return: 0 on success, or -ERRNO
*/ int nfp_xpb_writel(struct nfp_cpp *cpp, u32 xpb_addr, u32 value)
{
u32 cpp_dest = nfp_xpb_to_cpp(cpp, &xpb_addr);
/** * nfp_xpb_writelm() - Modify bits of a 32-bit value from the XPB bus * @cpp: NFP CPP device handle * @xpb_tgt: XPB target and address * @mask: mask of bits to alter * @value: value to modify * * KERNEL: This operation is safe to call in interrupt or softirq context. * * Return: 0 on success, or -ERRNO
*/ int nfp_xpb_writelm(struct nfp_cpp *cpp, u32 xpb_tgt,
u32 mask, u32 value)
{ int err;
u32 tmp;
/* NOTE: cpp_lock is NOT locked for op->init, * since it may call NFP CPP API operations
*/ if (cpp->op->init) {
err = cpp->op->init(cpp); if (err < 0) {
dev_err(parent, "NFP interface initialization failed\n"); goto err_out;
}
}
err = nfp_cpp_model_autodetect(cpp, &cpp->model); if (err < 0) {
dev_err(parent, "NFP model detection failed\n"); goto err_out;
}
/** * nfp_cpp_priv() - Get the operations private data of a CPP handle * @cpp: CPP handle * * Return: Private data for the NFP CPP handle
*/ void *nfp_cpp_priv(struct nfp_cpp *cpp)
{ return cpp->priv;
}
/** * nfp_cpp_device() - Get the Linux device handle of a CPP handle * @cpp: CPP handle * * Return: Device for the NFP CPP bus
*/ struct device *nfp_cpp_device(struct nfp_cpp *cpp)
{ return &cpp->dev;
}
/** * nfp_cpp_explicit_set_target() - Set target fields for explicit * @expl: Explicit handle * @cpp_id: CPP ID field * @len: CPP Length field * @mask: CPP Mask field * * Return: 0, or -ERRNO
*/ int nfp_cpp_explicit_set_target(struct nfp_cpp_explicit *expl,
u32 cpp_id, u8 len, u8 mask)
{
expl->cmd.cpp_id = cpp_id;
expl->cmd.len = len;
expl->cmd.byte_mask = mask;
return 0;
}
/** * nfp_cpp_explicit_set_data() - Set data fields for explicit * @expl: Explicit handle * @data_master: CPP Data Master field * @data_ref: CPP Data Ref field * * Return: 0, or -ERRNO
*/ int nfp_cpp_explicit_set_data(struct nfp_cpp_explicit *expl,
u8 data_master, u16 data_ref)
{
expl->cmd.data_master = data_master;
expl->cmd.data_ref = data_ref;
return 0;
}
/** * nfp_cpp_explicit_set_signal() - Set signal fields for explicit * @expl: Explicit handle * @signal_master: CPP Signal Master field * @signal_ref: CPP Signal Ref field * * Return: 0, or -ERRNO
*/ int nfp_cpp_explicit_set_signal(struct nfp_cpp_explicit *expl,
u8 signal_master, u8 signal_ref)
{
expl->cmd.signal_master = signal_master;
expl->cmd.signal_ref = signal_ref;
return 0;
}
/** * nfp_cpp_explicit_set_posted() - Set completion fields for explicit * @expl: Explicit handle * @posted: True for signaled completion, false otherwise * @siga: CPP Signal A field * @siga_mode: CPP Signal A Mode field * @sigb: CPP Signal B field * @sigb_mode: CPP Signal B Mode field * * Return: 0, or -ERRNO
*/ int nfp_cpp_explicit_set_posted(struct nfp_cpp_explicit *expl, int posted,
u8 siga, enum nfp_cpp_explicit_signal_mode siga_mode,
u8 sigb, enum nfp_cpp_explicit_signal_mode sigb_mode)
{
expl->cmd.posted = posted;
expl->cmd.siga = siga;
expl->cmd.sigb = sigb;
expl->cmd.siga_mode = siga_mode;
expl->cmd.sigb_mode = sigb_mode;
return 0;
}
/** * nfp_cpp_explicit_put() - Set up the write (pull) data for a explicit access * @expl: NFP CPP Explicit handle * @buff: Data to have the target pull in the transaction * @len: Length of data, in bytes * * The 'len' parameter must be less than or equal to 128 bytes. * * If this function is called before the configuration * registers are set, it will return -EINVAL. * * Return: 0, or -ERRNO
*/ int nfp_cpp_explicit_put(struct nfp_cpp_explicit *expl, constvoid *buff, size_t len)
{ return NFP_EXPL_OP(explicit_put, expl, buff, len);
}
/** * nfp_cpp_explicit_do() - Execute a transaction, and wait for it to complete * @expl: NFP CPP Explicit handle * @address: Address to send in the explicit transaction * * If this function is called before the configuration * registers are set, it will return -1, with an errno of EINVAL. * * Return: 0, or -ERRNO
*/ int nfp_cpp_explicit_do(struct nfp_cpp_explicit *expl, u64 address)
{ return NFP_EXPL_OP(explicit_do, expl, &expl->cmd, address);
}
/** * nfp_cpp_explicit_get() - Get the 'push' (read) data from a explicit access * @expl: NFP CPP Explicit handle * @buff: Data that the target pushed in the transaction * @len: Length of data, in bytes * * The 'len' parameter must be less than or equal to 128 bytes. * * If this function is called before all three configuration * registers are set, it will return -1, with an errno of EINVAL. * * If this function is called before nfp_cpp_explicit_do() * has completed, it will return -1, with an errno of EBUSY. * * Return: 0, or -ERRNO
*/ int nfp_cpp_explicit_get(struct nfp_cpp_explicit *expl, void *buff, size_t len)
{ return NFP_EXPL_OP(explicit_get, expl, buff, len);
}
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.