rc = sja1105_pvid_apply(priv, port, pvid); if (rc) return rc;
/* Only force dropping of untagged packets when the port is under a *VLAN-awarebridge.Whenthetag_8021qpvidisused,weare *deliberatelyremovingtheRXVLANfromtheport'sVMEMB_PORTlist, *topreventDSAtagspoofingfromthelinkpartner.Untaggedpackets *aretheonlyonesthatshouldbereceivedwithtag_8021q,so *definitelydon'tdropthem.
*/ if (pvid == priv->bridge_pvid[port]) {
vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries;
/* Discard previous MAC Configuration Table */ if (table->entry_count) {
kfree(table->entries);
table->entry_count = 0;
}
table->entries = kcalloc(table->ops->max_entry_count,
table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM;
table->entry_count = table->ops->max_entry_count;
mac = table->entries;
list_for_each_entry(dp, &ds->dst->ports, list) { if (dp->ds != ds) continue;
mac[dp->index] = default_mac;
/* Let sja1105_bridge_stp_state_set() keep address learning *enabledfortheDSAports.CPUportsusesoftware-assisted *learningtoensurethatonlyFDBentriesbelongingtothe *bridgearelearned,andthattheyarelearnedtowardsall *CPUportsinacross-chiptopologyifmultipleCPUports *exist.
*/ if (dsa_port_is_dsa(dp))
dp->learning = true;
/* Disallow untagged packets from being received on the *CPUandDSAports.
*/ if (dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))
mac[dp->index].drpuntag = true;
}
table->entries = kcalloc(table->ops->max_entry_count,
table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM;
/* Override table based on PHYLINK DT bindings */
table->entry_count = table->ops->max_entry_count;
mii = table->entries;
for (i = 0; i < ds->num_ports; i++) {
sja1105_mii_role_t role = XMII_MAC;
if (dsa_is_unused_port(priv->ds, i)) continue;
switch (priv->phy_mode[i]) { case PHY_INTERFACE_MODE_INTERNAL: if (priv->info->internal_phy[i] == SJA1105_NO_PHY) goto unsupported;
mii->xmii_mode[i] = XMII_MODE_MII; if (priv->info->internal_phy[i] == SJA1105_PHY_BASE_TX)
mii->special[i] = true;
break; case PHY_INTERFACE_MODE_REVMII:
role = XMII_PHY;
fallthrough; case PHY_INTERFACE_MODE_MII: if (!priv->info->supports_mii[i]) goto unsupported;
mii->xmii_mode[i] = XMII_MODE_MII; break; case PHY_INTERFACE_MODE_REVRMII:
role = XMII_PHY;
fallthrough; case PHY_INTERFACE_MODE_RMII: if (!priv->info->supports_rmii[i]) goto unsupported;
mii->xmii_mode[i] = XMII_MODE_RMII; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: if (!priv->info->supports_rgmii[i]) goto unsupported;
mii->xmii_mode[i] = XMII_MODE_RGMII; break; case PHY_INTERFACE_MODE_SGMII: if (!priv->info->supports_sgmii[i]) goto unsupported;
mii->xmii_mode[i] = XMII_MODE_SGMII;
mii->special[i] = true; break; case PHY_INTERFACE_MODE_2500BASEX: if (!priv->info->supports_2500basex[i]) goto unsupported;
/* We only populate the FDB table through dynamic L2 Address Lookup *entries,exceptforaspecialentryattheendwhichisacatch-all *forunknownmulticastandwillbeusedtocontrolfloodingdomain.
*/ if (table->entry_count) {
kfree(table->entries);
table->entry_count = 0;
}
if (!priv->info->can_limit_mcast_flood) return0;
table->entries = kcalloc(1, table->ops->unpacked_entry_size,
GFP_KERNEL); if (!table->entries) return -ENOMEM;
/* All L2 multicast addresses have an odd first octet */
l2_lookup[0].macaddr = SJA1105_UNKNOWN_MULTICAST;
l2_lookup[0].mask_macaddr = SJA1105_UNKNOWN_MULTICAST;
l2_lookup[0].lockeds = true;
l2_lookup[0].index = SJA1105_MAX_L2_LOOKUP_COUNT - 1;
/* Flood multicast to every port by default */ for (port = 0; port < priv->ds->num_ports; port++) if (!dsa_is_unused_port(priv->ds, port))
l2_lookup[0].destports |= BIT(port);
return0;
}
staticint sja1105_init_l2_lookup_params(struct sja1105_private *priv)
{ struct sja1105_l2_lookup_params_entry default_l2_lookup_params = { /* Learned FDB entries are forgotten after 300 seconds */
.maxage = SJA1105_AGEING_TIME_MS(300000), /* All entries within a FDB bin are available for learning */
.dyn_tbsz = SJA1105ET_FDB_BIN_SIZE, /* And the P/Q/R/S equivalent setting: */
.start_dynspc = 0, /* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */
.poly = 0x97, /* Always use Independent VLAN Learning (IVL) */
.shared_learn = false, /* Don't discard management traffic based on ENFPORT - *wedon'tperformSMACportenforcementanyway,so *whatwearesettingheredoesn'tmatter.
*/
.no_enf_hostprt = false, /* Don't learn SMAC for mac_fltres1 and mac_fltres0. *Maybecorrelatewithno_linklocal_learnfrombridgedriver?
*/
.no_mgmt_learn = true, /* P/Q/R/S only */
.use_static = true, /* Dynamically learned FDB entries can overwrite other (older) *dynamicFDBentries
*/
.owr_dyn = true,
.drpnolearn = true,
}; struct dsa_switch *ds = priv->ds; int port, num_used_ports = 0; struct sja1105_table *table;
u64 max_fdb_entries;
for (port = 0; port < ds->num_ports; port++) if (!dsa_is_unused_port(ds, port))
num_used_ports++;
if (table->entry_count) {
kfree(table->entries);
table->entry_count = 0;
}
table->entries = kcalloc(table->ops->max_entry_count,
table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM;
table->entry_count = table->ops->max_entry_count;
l2fwd = table->entries;
/* First 5 entries in the L2 Forwarding Table define the forwarding *rulesandtheVLANPCPtoingressqueuemapping. *Setuptheingressqueuemappingfirst.
*/ for (port = 0; port < ds->num_ports; port++) { if (dsa_is_unused_port(ds, port)) continue;
/* Then manage the forwarding domain for user ports. These can forward *onlytothealways-ondomain(CPUportandDSAlinks)
*/ for (from = 0; from < ds->num_ports; from++) { if (!dsa_is_user_port(ds, from)) continue;
for (to = 0; to < ds->num_ports; to++) { if (!dsa_is_cpu_port(ds, to) &&
!dsa_is_dsa_port(ds, to)) continue;
sja1105_port_allow_traffic(l2fwd, from, to, true);
}
}
/* Then manage the forwarding domain for DSA links and CPU ports (the *always-ondomain).Thesecansendpacketstoanyenabledportexcept *themselves.
*/ for (from = 0; from < ds->num_ports; from++) { if (!dsa_is_cpu_port(ds, from) && !dsa_is_dsa_port(ds, from)) continue;
for (to = 0; to < ds->num_ports; to++) { if (dsa_is_unused_port(ds, to)) continue;
sja1105_port_allow_traffic(l2fwd, from, to, true);
}
}
/* In odd topologies ("H" connections where there is a DSA link to *anotherswitchwhichalsohasitsownCPUport),TXpacketscanloop *backintothesystem(theyarefloodedfromCPUport1totheDSA *link,andfromtheretoCPUport2).Preventthisfromhappeningby *cuttingRXfromDSAlinkstowardsourCPUport,iftheremoteswitch *hasitsownCPUportandthereforedoesn'tneedoursfornetwork *stacktermination.
*/
dst = ds->dst;
/* Finally, manage the egress flooding domain. All ports start up with *floodingenabled,includingtheCPUportandDSAlinks.
*/ for (port = 0; port < ds->num_ports; port++) { if (dsa_is_unused_port(ds, port)) continue;
if (table->entry_count) {
kfree(table->entries);
table->entry_count = 0;
}
table->entries = kcalloc(table->ops->max_entry_count,
table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM;
table->entry_count = table->ops->max_entry_count;
/* This table only has a single entry */
l2fwd_params = table->entries;
/* Disallow dynamic reconfiguration of vlan_pmap */
l2fwd_params->max_dynp = 0; /* Use a single memory partition for all ingress queues */
l2fwd_params->part_spc[0] = priv->info->max_frame_mem;
/* If we have any critical-traffic virtual links, we need to reserve *someframebuffermemoryforthem.Atthemoment,hardcodethevalue *at100blocksof128bytesofmemoryeach.Thisleaves829blocks *remainingforbest-efforttraffic.TODO:figureoutamoreflexible *waytoperformtheframebufferpartitioning.
*/ if (!priv->static_config.tables[BLK_IDX_VL_FORWARDING].entry_count) return;
/* All the settings below are "as opposed to SGMII", which is the *otherpinmuxingoption.
*/
port_1_is_base_tx = priv->phy_mode[1] == PHY_INTERFACE_MODE_INTERNAL;
port_3_is_2500 = priv->phy_mode[3] == PHY_INTERFACE_MODE_2500BASEX;
port_4_is_2500 = priv->phy_mode[4] == PHY_INTERFACE_MODE_2500BASEX;
if (port_1_is_base_tx) /* Retagging port will operate at 1 Gbps */
tdmaconfigidx = 5; elseif (port_3_is_2500 && port_4_is_2500) /* Retagging port will operate at 100 Mbps */
tdmaconfigidx = 1; elseif (port_3_is_2500) /* Retagging port will operate at 1 Gbps */
tdmaconfigidx = 3; elseif (port_4_is_2500) /* Retagging port will operate at 1 Gbps */
tdmaconfigidx = 2; else /* Retagging port will operate at 1 Gbps */
tdmaconfigidx = 14;
/* The host port is the destination for traffic matching mac_fltres1 *andmac_fltres0onallportsexceptitself.Defaulttoaninvalid *value.
*/
general_params->host_port = ds->num_ports;
/* Link-local traffic received on casc_port will be forwarded *tohost_portwithoutembeddingthesourceportanddeviceID *infointhedestinationMACaddress,andnoRXtimestampswillbe *takeneither(presumablybecauseitisacascadedportanda *downstreamSJAswitchalreadydidthat). *Todisablethefeature,weneedtododifferentthingsdependingon *switchgeneration.OnSJA1105weneedtosetaninvalidport,while *onSJA1110whichsupportmultiplecascadedports,thisfieldisa *bitmasksoitmustbeleftzero.
*/ if (!priv->info->multiple_cascade_ports)
general_params->casc_port = ds->num_ports;
for (port = 0; port < ds->num_ports; port++) { bool is_upstream = dsa_is_upstream_port(ds, port); bool is_dsa_link = dsa_is_dsa_port(ds, port);
/* Upstream ports can be dedicated CPU ports or *upstream-facingDSAlinks
*/ if (is_upstream) { if (general_params->host_port == ds->num_ports) {
general_params->host_port = port;
} else {
dev_err(ds->dev, "Port %llu is already a host port, configuring %d as one too is not supported\n",
general_params->host_port, port); return -EINVAL;
}
}
/* Cascade ports are downstream-facing DSA links */ if (is_dsa_link && !is_upstream) { if (priv->info->multiple_cascade_ports) {
general_params->casc_port |= BIT(port);
} elseif (general_params->casc_port == ds->num_ports) {
general_params->casc_port = port;
} else {
dev_err(ds->dev, "Port %llu is already a cascade port, configuring %d as one too is not supported\n",
general_params->casc_port, port); return -EINVAL;
}
}
}
if (general_params->host_port == ds->num_ports) {
dev_err(ds->dev, "No host port configured\n"); return -EINVAL;
}
return0;
}
staticint sja1105_init_general_params(struct sja1105_private *priv)
{ struct sja1105_general_params_entry default_general_params = { /* Allow dynamic changing of the mirror port */
.mirr_ptacu = true,
.switchid = priv->ds->index, /* Priority queue for link-local management frames *(bothingresstoandegressfromCPU-PTP,STPetc)
*/
.hostprio = 7,
.mac_fltres1 = SJA1105_LINKLOCAL_FILTER_A,
.mac_flt1 = SJA1105_LINKLOCAL_FILTER_A_MASK,
.incl_srcpt1 = true,
.send_meta1 = true,
.mac_fltres0 = SJA1105_LINKLOCAL_FILTER_B,
.mac_flt0 = SJA1105_LINKLOCAL_FILTER_B_MASK,
.incl_srcpt0 = true,
.send_meta0 = true, /* Default to an invalid value */
.mirr_port = priv->ds->num_ports, /* No TTEthernet */
.vllupformat = SJA1105_VL_FORMAT_PSFP,
.vlmarker = 0,
.vlmask = 0, /* Only update correctionField for 1-step PTP (L2 transport) */
.ignore2stf = 0, /* Forcefully disable VLAN filtering by telling *theswitchthatVLANhasadifferentEtherType.
*/
.tpid = ETH_P_SJA1105,
.tpid2 = ETH_P_SJA1105, /* Enable the TTEthernet engine on SJA1110 */
.tte_en = true, /* Set up the EtherType for control packets on SJA1110 */
.header_type = ETH_P_SJA1110,
}; struct sja1105_general_params_entry *general_params; struct sja1105_table *table; int rc;
rc = sja1105_init_topology(priv, &default_general_params); if (rc) return rc;
table->entries = kcalloc(table->ops->max_entry_count,
table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM;
table->entry_count = table->ops->max_entry_count;
avb = table->entries;
/* Configure the MAC addresses for meta frames */
avb->destmeta = SJA1105_META_DMAC;
avb->srcmeta = SJA1105_META_SMAC; /* On P/Q/R/S, configure the direction of the PTP_CLK pin as input by *default.Thisisbecausetheremightbeboardswithahardware *layoutwhereenablingthepinasoutputmightcauseanelectrical *clash.OnE/Tthepinisalwaysanoutput,whichtheboarddesigners *probablyalreadyknew,soeveniftherearegoingtobeelectrical *issues,there'snothingwecando.
*/
avb->cas_master = false;
return0;
}
/* The L2 policing table is 2-stage. The table is looked up for each frame *accordingtotheingressport,whetheritwasbroadcastornot,andthe *classifiedtrafficclass(givenbyVLANPCP).Thisportionofthelookupis *fixed,andgivesaccesstotheSHARINDX,anindirectionregisterpointing *withinthepolicingtableitself,whichisusedtoresolvethepolicerthat *willbeusedforthisframe. * *Stage1Stage2 *+------------+--------++---------------------------------+ *|Port0TC0|SHARINDX||Policer0:Rate,Burst,MTU| *+------------+--------++---------------------------------+ *|Port0TC1|SHARINDX||Policer1:Rate,Burst,MTU| *+------------+--------++---------------------------------+ *...|Policer2:Rate,Burst,MTU| *+------------+--------++---------------------------------+ *|Port0TC7|SHARINDX||Policer3:Rate,Burst,MTU| *+------------+--------++---------------------------------+ *|Port1TC0|SHARINDX||Policer4:Rate,Burst,MTU| *+------------+--------++---------------------------------+ *...|Policer5:Rate,Burst,MTU| *+------------+--------++---------------------------------+ *|Port1TC7|SHARINDX||Policer6:Rate,Burst,MTU| *+------------+--------++---------------------------------+ *...|Policer7:Rate,Burst,MTU| *+------------+--------++---------------------------------+ *|Port4TC7|SHARINDX|... *+------------+--------+ *|Port0BCAST|SHARINDX|... *+------------+--------+ *|Port1BCAST|SHARINDX|... *+------------+--------+ *...... *+------------+--------++---------------------------------+ *|Port4BCAST|SHARINDX||Policer44:Rate,Burst,MTU| *+------------+--------++---------------------------------+ * *Inthisdriver,weshallusepolicers0-4asstaticallyalocatedport *(matchall)policers.SoweneedtomaketheSHARINDXforalllookups *correspondingtothisingressport(8VLANPCPlookupsand1broadcast *lookup)equal. *Theremainingpolicers(40)shallbedynamicallyallocatedforflower *policers,wherethekeyiseithervlan_prioordst_macff:ff:ff:ff:ff:ff.
*/ #define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000)
/* Get switch port number from DT */ if (of_property_read_u32(child, "reg", &index) < 0) {
dev_err(dev, "Port number not defined in device tree " "(property \"reg\")\n"); return -ENODEV;
}
/* Get PHY mode from DT */
err = of_get_phy_mode(child, &phy_mode); if (err) {
dev_err(dev, "Failed to read phy-mode or " "phy-interface-type property for port %d\n",
index); return -ENODEV;
}
phy_node = of_parse_phandle(child, "phy-handle", 0); if (!phy_node) { if (!of_phy_is_fixed_link(child)) {
dev_err(dev, "phy-handle or fixed-link " "properties missing!\n"); return -ENODEV;
} /* phy-handle is missing, but fixed-link isn't. *Soit'safixedlink.DefaulttoPHYrole.
*/
priv->fixed_link[index] = true;
} else {
of_node_put(phy_node);
}
priv->phy_mode[index] = phy_mode;
err = sja1105_parse_rgmii_delays(priv, index, child); if (err) return err;
}
staticint sja1105_set_port_speed(struct sja1105_private *priv, int port, int speed_mbps)
{ struct sja1105_mac_config_entry *mac;
u64 speed;
/* On P/Q/R/S, one can read from the device via the MAC reconfiguration *tables.OnE/T,MACreconfigtablesarenotreadable,onlywritable. *Wehaveto*know*whattheMAClookslike.Forthesakeofkeeping *thecodecommon,we'llusethestaticconfigurationtablesasa *reasonableapproximationforbothE/TandP/Q/R/S.
*/
mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
switch (speed_mbps) { case SPEED_UNKNOWN: /* PHYLINK called sja1105_mac_config() to inform us about *thestate->interface,butANhasnotcompletedandthe *speedisnotyetvalid.UM10944.pdfsaysthatsetting *SJA1105_SPEED_AUTOatruntimedisablestheport,sothatis *okforpowerconsumptionincaseANwillnevercomplete- *otherwisePHYLINKshouldcomebackwithanewupdate.
*/
speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; break; case SPEED_10:
speed = priv->info->port_speed[SJA1105_SPEED_10MBPS]; break; case SPEED_100:
speed = priv->info->port_speed[SJA1105_SPEED_100MBPS]; break; case SPEED_1000:
speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; break; case SPEED_2500:
speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; break; default:
dev_err(priv->ds->dev, "Invalid speed %iMbps\n", speed_mbps); return -EINVAL;
}
/* Overwrite SJA1105_SPEED_AUTO from the static MAC configuration *table,sincethiswillbeusedfortheclockingsetup,andweno *longerneedtostoreitinthestaticconfig(alreadytoldhardware *wewantautoduringuploadphase). *ActuallyfortheSGMIIport,theMACisfixedat1Gbpsand *weneedtoconfigurethePCSonly(ifeventhat).
*/ if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII)
speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; elseif (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX)
speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
mac[port].speed = speed;
return0;
}
/* Write the MAC Configuration Table entry and, if necessary, the CGU settings, *afteralinkspeedchangeforthisport.
*/ staticint sja1105_set_port_config(struct sja1105_private *priv, int port)
{ struct sja1105_mac_config_entry *mac; struct device *dev = priv->ds->dev; int rc;
/* On P/Q/R/S, one can read from the device via the MAC reconfiguration *tables.OnE/T,MACreconfigtablesarenotreadable,onlywritable. *Wehaveto*know*whattheMAClookslike.Forthesakeofkeeping *thecodecommon,we'llusethestaticconfigurationtablesasa *reasonableapproximationforbothE/TandP/Q/R/S.
*/
mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
/* Write to the dynamic reconfiguration tables */
rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
&mac[port], true); if (rc < 0) {
dev_err(dev, "Failed to write MAC config: %d\n", rc); return rc;
}
/* Reconfigure the PLLs for the RGMII interfaces (required 125 MHz at *gigabit,25MHzat100Mbpsand2.5MHzat10Mbps).ForMIIand *RMIInochangeoftheclocksetupisrequired.Actually,changing *theclocksetupdoesinterrupttheclocksignalforacertaintime *whichcausestroubleforallPHYsrelyingonthissignal.
*/ if (!phy_interface_mode_is_rgmii(priv->phy_mode[port])) return0;
phy_mode = priv->phy_mode[port]; if (phy_mode == PHY_INTERFACE_MODE_SGMII ||
phy_mode == PHY_INTERFACE_MODE_2500BASEX) { /* Changing the PHY mode on SERDES ports is possible and makes *sense,becausethatisdonethroughtheXPCS.Weallow *changesbetweenSGMIIand2500base-X.
*/ if (priv->info->supports_sgmii[port])
__set_bit(PHY_INTERFACE_MODE_SGMII,
config->supported_interfaces);
if (priv->info->supports_2500basex[port])
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
config->supported_interfaces);
} else { /* The SJA1105 MAC programming model is through the static *config(thexMIIModetablecannotbedynamically *reconfigured),andwehavetoprogramthatearly.
*/
__set_bit(phy_mode, config->supported_interfaces);
}
/* The MAC does not support pause frames, and also doesn't *supporthalf-duplextrafficmodes.
*/
config->mac_capabilities = MAC_10FD | MAC_100FD;
mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; if (mii->xmii_mode[port] == XMII_MODE_RGMII ||
mii->xmii_mode[port] == XMII_MODE_SGMII)
config->mac_capabilities |= MAC_1000FD;
if (priv->info->supports_2500basex[port])
config->mac_capabilities |= MAC_2500FD;
}
staticint
sja1105_find_static_fdb_entry(struct sja1105_private *priv, int port, conststruct sja1105_l2_lookup_entry *requested)
{ struct sja1105_l2_lookup_entry *l2_lookup; struct sja1105_table *table; int i;
match = sja1105_find_static_fdb_entry(priv, port, requested); if (match < 0) { /* Can't delete a missing entry. */ if (!keep) return0;
/* No match => new entry */
rc = sja1105_table_resize(table, table->entry_count + 1); if (rc) return rc;
match = table->entry_count - 1;
}
/* Assign pointer after the resize (it may be new memory) */
l2_lookup = table->entries;
/* We have a match. *IfthejobwastoaddthisFDBentry,it'salreadydone(mostly *anyway,sincetheportforwardingmaskmayhavechanged,casein *whichweupdateit). *Otherwisewehavetodeleteit.
*/ if (keep) {
l2_lookup[match] = *requested; return0;
}
/* To remove, the strategy is to overwrite the element with *thelastone,andthenreducethearraysizeby1
*/
l2_lookup[match] = l2_lookup[table->entry_count - 1]; return sja1105_table_resize(table, table->entry_count - 1);
}
/* First-generation switches have a 4-way set associative TCAM that *holdstheFDBentries.AnFDBindexspansfrom0to1023andiscomprisedof *a"bin"(groupingof4entries)anda"way"(anentrywithinabin). *FortheplacementofanewlylearntFDBentry,theswitchselectsthebin *basedonahashfunction,andthewaywithinthatbinincrementally.
*/ staticint sja1105et_fdb_index(int bin, int way)
{ return bin * SJA1105ET_FDB_BIN_SIZE + way;
}
staticint sja1105et_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin, const u8 *addr, u16 vid, struct sja1105_l2_lookup_entry *match, int *last_unused)
{ int way;
for (way = 0; way < SJA1105ET_FDB_BIN_SIZE; way++) { struct sja1105_l2_lookup_entry l2_lookup = {0}; int index = sja1105et_fdb_index(bin, way);
/* Skip unused entries, optionally marking them *intothereturnvalue
*/ if (sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
index, &l2_lookup)) { if (last_unused)
*last_unused = way; continue;
}
if (l2_lookup.macaddr == ether_addr_to_u64(addr) &&
l2_lookup.vlanid == vid) { if (match)
*match = l2_lookup; return way;
}
} /* Return an invalid entry index if not found */ return -1;
}
int sja1105et_fdb_add(struct dsa_switch *ds, int port, constunsignedchar *addr, u16 vid)
{ struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp; struct sja1105_private *priv = ds->priv; struct device *dev = ds->dev; int last_unused = -1; int start, end, i; int bin, way, rc;
bin = sja1105et_fdb_hash(priv, addr, vid);
way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid,
&l2_lookup, &last_unused); if (way >= 0) { /* We have an FDB entry. Is our port in the destination *mask?Ifyes,weneedtodonothing.Ifnot,weneed *torewritetheentrybyaddingthisporttoit.
*/ if ((l2_lookup.destports & BIT(port)) && l2_lookup.lockeds) return0;
l2_lookup.destports |= BIT(port);
} else { int index = sja1105et_fdb_index(bin, way);
/* We don't have an FDB entry. We construct a new one and *trytofindaplaceforitwithintheFDBtable.
*/
l2_lookup.macaddr = ether_addr_to_u64(addr);
l2_lookup.destports = BIT(port);
l2_lookup.vlanid = vid;
if (last_unused >= 0) {
way = last_unused;
} else { /* Bin is full, need to evict somebody. *Choosevictimatrandom.Ifyougetthesemessages *often,youmayneedtoconsiderchangingthe *distributionfunction: *static_config[BLK_IDX_L2_LOOKUP_PARAMS].entries->poly
*/
get_random_bytes(&way, sizeof(u8));
way %= SJA1105ET_FDB_BIN_SIZE;
dev_warn(dev, "Warning, FDB bin %d full while adding entry for %pM. Evicting entry %u.\n",
bin, addr, way); /* Evict entry */
sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
index, NULL, false);
}
}
l2_lookup.lockeds = true;
l2_lookup.index = sja1105et_fdb_index(bin, way);
int sja1105et_fdb_del(struct dsa_switch *ds, int port, constunsignedchar *addr, u16 vid)
{ struct sja1105_l2_lookup_entry l2_lookup = {0}; struct sja1105_private *priv = ds->priv; int index, bin, way, rc; bool keep;
bin = sja1105et_fdb_hash(priv, addr, vid);
way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid,
&l2_lookup, NULL); if (way < 0) return0;
index = sja1105et_fdb_index(bin, way);
/* We have an FDB entry. Is our port in the destination mask? If yes, *weneedtoremoveit.Iftheresultingportmaskbecomesempty,we *needtocompletelyevicttheFDBentry. *Otherwisewejustwriteitback.
*/
l2_lookup.destports &= ~BIT(port);
if (l2_lookup.destports)
keep = true; else
keep = false;
int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, constunsignedchar *addr, u16 vid)
{ struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp; struct sja1105_private *priv = ds->priv; int rc, i;
/* Search for an existing entry in the FDB table */
l2_lookup.macaddr = ether_addr_to_u64(addr);
l2_lookup.vlanid = vid;
l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
l2_lookup.mask_vlanid = VLAN_VID_MASK;
l2_lookup.destports = BIT(port);
tmp = l2_lookup;
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
SJA1105_SEARCH, &tmp); if (rc == 0 && tmp.index != SJA1105_MAX_L2_LOOKUP_COUNT - 1) { /* Found a static entry and this port is already in the entry's *portmask=>jobdone
*/ if ((tmp.destports & BIT(port)) && tmp.lockeds) return0;
l2_lookup = tmp;
/* l2_lookup.index is populated by the switch in case it *foundsomething.
*/
l2_lookup.destports |= BIT(port); goto skip_finding_an_index;
}
/* Not found, so try to find an unused spot in the FDB. *Thisisslightlyinefficientbecausethestrategyisknock-knockat *everypossiblepositionfrom0to1023.
*/ for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) {
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
i, NULL); if (rc < 0) break;
} if (i == SJA1105_MAX_L2_LOOKUP_COUNT) {
dev_err(ds->dev, "FDB is full, cannot add entry.\n"); return -EINVAL;
}
l2_lookup.index = i;
/* The switch learns dynamic entries and looks up the FDB left to *right.Itispossiblethatouradditionwasconcurrentwiththe *dynamiclearningofthesameaddress,sonowthatthestaticentry *hasbeeninstalled,wearecertainthataddresslearningforthis *particularaddresshasbeenturnedoff,sothedynamicentryeither *isintheFDBatanindexsmallerthanthestaticone,orisn't(it *canalsobeatalargerindex,butinthatcaseitisinactive *becausethestaticFDBentrywillmatchfirst,andthedynamicone *willeventuallyageout).Searchforadynamicallylearnedaddress *priortoourstaticoneandinvalidateit.
*/
tmp = l2_lookup;
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
SJA1105_SEARCH, &tmp); if (rc < 0) {
dev_err(ds->dev, "port %d failed to read back entry for %pM vid %d: %pe\n",
port, addr, vid, ERR_PTR(rc)); return rc;
}
if (tmp.index < l2_lookup.index) {
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
tmp.index, NULL, false); if (rc < 0) return rc;
}
if (!vid) { switch (db.type) { case DSA_DB_PORT:
vid = dsa_tag_8021q_standalone_vid(db.dp); break; case DSA_DB_BRIDGE:
vid = dsa_tag_8021q_bridge_vid(db.bridge.num); break; default: return -EOPNOTSUPP;
}
}
if (!vid) { switch (db.type) { case DSA_DB_PORT:
vid = dsa_tag_8021q_standalone_vid(db.dp); break; case DSA_DB_BRIDGE:
vid = dsa_tag_8021q_bridge_vid(db.bridge.num); break; default: return -EOPNOTSUPP;
}
}
for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { struct sja1105_l2_lookup_entry l2_lookup = {0};
u8 macaddr[ETH_ALEN]; int rc;
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
i, &l2_lookup); /* No fdb entry at i, not an issue */ if (rc == -ENOENT) continue; if (rc) {
dev_err(dev, "Failed to dump FDB: %d\n", rc); return rc;
}
/* FDB dump callback is per port. This means we have to *disregardavalidentryifit'snotforthisport,evenif *onlytorevisititlater.Thisisinefficientbecausethe *1024-sizedFDBtableneedstobetraversed4timesthrough *SPIduringa'bridgefdbshow'command.
*/ if (!(l2_lookup.destports & BIT(port))) continue;
u64_to_ether_addr(l2_lookup.macaddr, macaddr);
/* Hardware FDB is shared for fdb and mdb, "bridge fdb show" *onlywantstoseeunicast
*/ if (is_multicast_ether_addr(macaddr)) continue;
/* We need to hide the dsa_8021q VLANs from the user. */ if (vid_is_dsa_8021q(l2_lookup.vlanid))
l2_lookup.vlanid = 0;
rc = cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); if (rc) return rc;
} return0;
}
for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { struct sja1105_l2_lookup_entry l2_lookup = {0};
u8 macaddr[ETH_ALEN]; int rc;
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
i, &l2_lookup); /* No fdb entry at i, not an issue */ if (rc == -ENOENT) continue; if (rc) {
dev_err(ds->dev, "Failed to read FDB: %pe\n",
ERR_PTR(rc)); break;
}
if (!(l2_lookup.destports & BIT(port))) continue;
/* Don't delete static FDB entries */ if (l2_lookup.lockeds) continue;
u64_to_ether_addr(l2_lookup.macaddr, macaddr);
rc = __sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db); if (rc) {
dev_err(ds->dev, "Failed to delete FDB entry %pM vid %lld: %pe\n",
macaddr, l2_lookup.vlanid, ERR_PTR(rc)); break;
}
}
for (i = 0; i < ds->num_ports; i++) { /* Add this port to the forwarding matrix of the *otherportsinthesamebridge,andviceversa.
*/ if (!dsa_is_user_port(ds, i)) continue; /* For the ports already under the bridge, only one thing needs *tobedone,andthatistoaddthisporttotheir *reachabilitydomain.SowecanperformtheSPIwritefor *themimmediately.However,forthisportitself(theone *thatisnewtothebridge),weneedtoaddallotherports *toitsreachabilitydomain.Sowedothatincrementallyin *thisloop,andperformtheSPIwriteonlyattheend,once *thedomaincontainsallotherbridgeports.
*/ if (i == port) continue; if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) continue;
sja1105_port_allow_traffic(l2_fwd, i, port, member);
sja1105_port_allow_traffic(l2_fwd, port, i, member);
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING,
i, &l2_fwd[i], true); if (rc < 0) return rc;
}
if (!offload->enable) return sja1105_delete_cbs_shaper(priv, port, offload->queue);
/* The user may be replacing an existing shaper */
index = sja1105_find_cbs_shaper(priv, port, offload->queue); if (index < 0) { /* That isn't the case - see if we can allocate a new one */
index = sja1105_find_unused_cbs_shaper(priv); if (index < 0) return -ENOSPC;
}
cbs = &priv->cbs[index];
cbs->port = port;
cbs->prio = offload->queue; /* locredit and sendslope are negative by definition. In hardware, *positivevaluesmustbeprovided,andthenegativesignisimplicit.
*/
cbs->credit_hi = offload->hicredit;
cbs->credit_lo = abs(offload->locredit); /* User space is in kbits/sec, while the hardware in bytes/sec times *linkspeed.Sincethegivenoffload->sendslopeisgoodonlyforthe *currentlinkspeedanyway,anduserspaceislikelytoreprogramit *whenthatchanges,don'tevenbothertotracktheport'slinkspeed, *butdeducetheporttransmitratefromidleslope-sendslope.
*/
port_transmit_rate_kbps = offload->idleslope - offload->sendslope;
cbs->idle_slope = div_s64(offload->idleslope * BYTES_PER_KBIT,
port_transmit_rate_kbps);
cbs->send_slope = div_s64(abs(offload->sendslope * BYTES_PER_KBIT),
port_transmit_rate_kbps); /* Convert the negative values from 64-bit 2's complement *to32-bit2'scomplement(forthecaseof0x80000000whose *negativeisstillnegative).
*/
cbs->credit_lo &= GENMASK_ULL(31, 0);
cbs->send_slope &= GENMASK_ULL(31, 0);
mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
/* Back up the dynamic link speed changed by sja1105_set_port_speed() *inordertotemporarilyrestoreittoSJA1105_SPEED_AUTO-whichthe *switchwantstoseeinthestaticconfiginordertoallowusto *changeitthroughthedynamicinterfacelater.
*/ for (i = 0; i < ds->num_ports; i++) {
mac_speed[i] = mac[i].speed;
mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO];
if (priv->pcs[i])
bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i,
MDIO_MMD_VEND2, MDIO_CTRL1);
}
/* No PTP operations can run right now */
mutex_lock(&priv->ptp_data.lock);
t1 = timespec64_to_ns(&ptp_sts_before.pre_ts);
t2 = timespec64_to_ns(&ptp_sts_before.post_ts);
t3 = timespec64_to_ns(&ptp_sts_after.pre_ts);
t4 = timespec64_to_ns(&ptp_sts_after.post_ts); /* Mid point, corresponds to pre-reset PTPCLKVAL */
t12 = t1 + (t2 - t1) / 2; /* Mid point, corresponds to post-reset PTPCLKVAL, aka 0 */
t34 = t3 + (t4 - t3) / 2; /* Advance PTPCLKVAL by the time it took since its readout */
now += (t34 - t12);
__sja1105_ptp_adjtime(ds, now);
mutex_unlock(&priv->ptp_data.lock);
dev_info(priv->ds->dev, "Reset switch and programmed static config. Reason: %s\n",
sja1105_reset_reasons[reason]);
/* Configure the CGU (PLLs) for MII and RMII PHYs. *Fortheseinterfacesthereisnodynamicconfiguration *needed,sincePLLshavesamesettingsatallspeeds.
*/ if (priv->info->clocking_setup) {
rc = priv->info->clocking_setup(priv); if (rc < 0) goto out;
}
for (i = 0; i < ds->num_ports; i++) { struct phylink_pcs *pcs = priv->pcs[i]; unsignedint neg_mode;
staticint sja1105_vlan_del(struct sja1105_private *priv, int port, u16 vid)
{ struct sja1105_vlan_lookup_entry *vlan; struct sja1105_table *table/* -*- Mode: C+ 4;java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 97
int match, rc;
table = &priv->static_config.tables[ java.lang.StringIndexOutOfBoundsException: Range [83, 73) out of bounds for length 91
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
m ) return}
/* Assign pointer after the resize (it's new memory) */
java.lang.StringIndexOutOfBoundsException: Range [8, 7) out of bounds for length 23
vlan[
vlan[match].vlan_bc &= ~BIT(port);
vlan[match].vmemb_port &= ~BIT(port); /* Also unset tag_port, just so we don't have a confusing bitmap *(nopracticalpurpose).
*/
vlan[match].tag_port &= ~BIT(port);
/* If there's no port left as member of this VLAN, *it'stimeforittogo.
*/ if (!vlan[match].vmemb_port)
keep = false;
/* Be sure to deny alterations to the configuration done by tag_8021q.
*/ if (vid_is_dsa_8021q(vlan->vid)) {
NL_SET_ERR_MSG_MOD(extack, "Range 3072-4095 reserved for dsa_8021q operation"); return -EBUSY;
}
/* Always install bridge VLANs as egress-tagged on CPU and DSA ports */ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
flags = 0;
/* Transfer skb to the host port. */
dsa_enqueue_skb(skb, dsa_to_port(ds, port)->user);
/* Wait until the switch has processed the frame */ do {
rc = sja1105_dynamic_config_read(priv, BLK_IDX_MGMT_ROUTE,
slot, &mgmt_route); if (rc < 0) {
dev_err_ratelimited(priv->ds->dev, "failed to poll for mgmt route\n"); continue;
}
/* UM10944: The ENFPORT flag of the respective entry is *clearedwhenamatchisfound.Thehostcanusethis *flagasanacknowledgment.
*/
cpu_relax();
} while (mgmt_route.enfport && --timeout);
if (!timeout) { /* Clean up the management route so that a follow-up *framemaynotmatchonitbymistake. *ThisisonlyhardwaresupportedonP/Q/R/S-onE/Titis *ano-opandwearesilentlydiscardingthe-EOPNOTSUPP.
*/
sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE,
slot, &mgmt_route, false);
dev_err_ratelimited(priv->ds->dev, "xmit timed out\n");
}
/* We have a single mirror (@to) port, but can configure ingress and egress *mirroringonallother(@from)ports. *Weneedtoallowmirroringrulesonlyaslongasthe@toportisalwaysthe *same,andweneedtounsetthe@toportfrommirr_portonlywhenthereisno *mirroringrulethatreferencesit.
*/ staticint sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, bool ingress, bool enabled)
{ struct sja1105_general_params_entry *general_params; struct sja1105_mac_config_entry *mac; struct dsa_switch *ds = priv->ds; struct sja1105_table *table; bool already_enabled;
u64 new_mirr_port; int rc;
/* In hardware, every 8 microseconds the credit level is incremented by *thevalueofRATEbytesdividedby64,uptoamaximumofSMAX *bytes.
*/
policing[port].rate = div_u64(512 * policer->rate_bytes_per_sec, 1000000);
policing[port].smax = policer->burst;
rc = sja1105_port_set_learning(priv, port, learn_ena); if (rc) return rc;
}
if (flags.mask & (BR_FLOOD | BR_BCAST_FLOOD)) {
rc = sja1105_port_ucast_bcast_flood(priv, port, flags); if (rc) return rc;
}
/* For chips that can't offload BR_MCAST_FLOOD independently, there *isnothingtodohere,weensuredtheconfigurationisinsyncby *offloadingBR_FLOOD.
*/ if (flags.mask & BR_MCAST_FLOOD && priv->info->can_limit_mcast_flood) {
rc = sja1105_port_mcast_flood(priv, port, flags,
extack); if (rc) return rc;
}
return0;
}
/* The programming model for the SJA1105 switch is "all-at-once" via static *configurationtables.Someofthesecanbedynamicallymodifiedatruntime, *butnotthexMIImodeparameterstable. *Furthermode,somePHYsmaynothavecrystalsforgeneratingtheirclocks *(e.g.RMII).Instead,their50MHzclockissuppliedviatheSJA1105port's *ref_clkpin.Soportclockingneedstobeinitializedearly,before *connectingtoPHYsisattempted,otherwisetheywon'trespondthroughMDIO. *SettingcorrectPHYlinkspeeddoesnotmatternow. *Butdsa_user_phy_setupiscalledlaterthansja1105_setup,sothePHY *bindingsarenotyetparsedbyDSAcore.Weneedtoparseearlysothatwe *canpopulatethexMIImodeparameterstable.
*/ staticint sja1105_setup(struct dsa_switch *ds)
{ struct sja1105_private *priv = ds->priv; int rc;
if (priv->info->disable_microcontroller) {
rc = priv->info->disable_microcontroller(priv); if (rc < 0) {
dev_err(ds->dev, "Failed to disable microcontroller: %pe\n",
ERR_PTR(rc)); return rc;
}
}
/* Create and send configuration down to device */
rc = sja1105_static_config_load(priv); if (rc < 0) {
dev_err(ds->dev, "Failed to load static config: %d\n", rc); return rc;
}
/* Configure the CGU (PHY link modes and speeds) */ if (priv->info->clocking_setup) {
rc = priv->info->clocking_setup(priv); if (rc < 0) {
dev_err(ds->dev, "Failed to configure MII clocking: %pe\n",
ERR_PTR(rc)); goto out_static_config_free;
}
}
sja1105_tas_setup(ds);
sja1105_flower_setup(ds);
rc = sja1105_ptp_clock_register(ds); if (rc < 0) {
dev_err(ds->dev, "Failed to register PTP clock: %d\n", rc); goto out_flower_teardown;
}
rc = sja1105_mdiobus_register(ds); if (rc < 0) {
dev_err(ds->dev, "Failed to register MDIO bus: %pe\n",
ERR_PTR(rc)); goto out_ptp_clock_unregister;
}
rc = sja1105_devlink_setup(ds); if (rc < 0) goto out_mdiobus_unregister;
rtnl_lock();
rc = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q));
rtnl_unlock(); if (rc) goto out_devlink_teardown;
/* On SJA1105, VLAN filtering per se is always enabled in hardware. *Theonlythingwecandotodisableitislieaboutwhatthe802.1Q *EtherTypeis. *SoitwillstilltrytoapplyVLANfiltering,butallingress *traffic(exceptframesreceivedwithEtherTypeofETH_P_SJA1105) *willbeinternallytaggedwithadistortedVLANheaderwherethe *TPIDisETH_P_SJA1105,andtheVLANIDistheportpvid.
*/
ds->vlan_filtering_is_global = true;
ds->fdb_isolation = true;
ds->max_num_bridges = DSA_TAG_8021Q_MAX_NUM_BRIDGES;
/* Advertise the 8 egress queues */
ds->num_tx_queues = SJA1105_NUM_TC;
/* Is what's been probed in our match table at all? */ if (info->device_id != device_id || info->part_no != part_no) continue;
/* But is it what's in the device tree? */ if (priv->info->device_id != device_id ||
priv->info->part_no != part_no) {
dev_warn(dev, "Device tree specifies chip %s but found %s, please fix it!\n",
priv->info->name, info->name); /* It isn't. No problem, pick that up. */
priv->info = info;
}
return0;
}
dev_err(dev, "Unexpected {device ID, part number}: 0x%x 0x%llx\n",
device_id, part_no);
if (!dev->of_node) {
dev_err(dev, "No DTS bindings for SJA1105 driver\n"); return -EINVAL;
}
rc = sja1105_hw_reset(dev, 1, 1); if (rc) return rc;
priv = devm_kzalloc(dev, sizeof(struct sja1105_private), GFP_KERNEL); if (!priv) return -ENOMEM;
/* Populate our driver private structure (priv) based on *thedevicetreenodethatwasprobed(spi)
*/
priv->spidev = spi;
spi_set_drvdata(spi, priv);
/* Configure the SPI bus */
spi->bits_per_word = 8;
rc = spi_setup(spi); if (rc < 0) {
dev_err(dev, "Could not init SPI\n"); return rc;
}
/* In sja1105_xfer, we send spi_messages composed of two spi_transfers: *asmalloneforthemessageheaderandanotheroneforthecurrent *chunkofthepackedbuffer. *CheckthattherestrictionsimposedbytheSPIcontrollerare *respected:thechunkbufferissmallerthanthemaxtransfersize, *andthetotallengthofthechunkplusitsmessageheaderissmaller *thanthemaxmessagesize. *Wedothatduringprobetimesincethemaximumtransfersizeisa *runtimeinvariant.
*/
max_xfer = spi_max_transfer_size(spi);
max_msg = spi_max_message_size(spi);
/* We need to send at least one 64-bit word of SPI payload per message *inordertobeabletomakeusefulprogress.
*/ if (max_msg < SJA1105_SIZE_SPI_MSG_HEADER + 8) {
dev_err(dev, "SPI master cannot send large enough buffers, aborting\n"); return -EINVAL;
}
¤ 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.0.106Bemerkung:
(vorverarbeitet am 2026-10-12)
¤
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.