Add support for Motorcomm YT921x tags, which includes a configurable ethertype field (default to 0x9988). Signed-off-by: David Yang --- include/net/dsa.h | 2 + net/dsa/Kconfig | 6 +++ net/dsa/Makefile | 1 + net/dsa/tag_yt921x.c | 116 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 net/dsa/tag_yt921x.c diff --git a/include/net/dsa.h b/include/net/dsa.h index d73ea0880066..67762fdaf3c7 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -55,6 +55,7 @@ struct tc_action; #define DSA_TAG_PROTO_LAN937X_VALUE 27 #define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28 #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 +#define DSA_TAG_PROTO_YT921X_VALUE 30 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -87,6 +88,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_RZN1_A5PSW = DSA_TAG_PROTO_RZN1_A5PSW_VALUE, DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE, DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE, + DSA_TAG_PROTO_YT921X = DSA_TAG_PROTO_YT921X_VALUE, }; struct dsa_switch; diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 869cbe57162f..6b94028b1fcc 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -190,4 +190,10 @@ config NET_DSA_TAG_XRS700X Say Y or M if you want to enable support for tagging frames for Arrow SpeedChips XRS700x switches that use a single byte tag trailer. +config NET_DSA_TAG_YT921X + tristate "Tag driver for Motorcomm YT921x switches" + help + Say Y or M if you want to enable support for tagging frames for + Motorcomm YT921x switches. + endif diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 555c07cfeb71..4b011a1d5c87 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) += tag_vsc73xx_8021q.o obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o +obj-$(CONFIG_NET_DSA_TAG_YT921X) += tag_yt921x.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c new file mode 100644 index 000000000000..95354bdb0aff --- /dev/null +++ b/net/dsa/tag_yt921x.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Motorcomm YT921x Switch External CPU tagging + * + * Copyright (c) 2025 David Yang + * + * +----+----+-------+-----+----+--------- + * | DA | SA | TagET | Tag | ET | Payload ... + * +----+----+-------+-----+----+--------- + * 6 6 2 6 2 N + * + * Tag Ethertype: CPU_TAG_TPID_TPIDf (default: 0x9988) + * Tag: + * 2: Service VLAN Tag + * 2: Rx Port + * 15b: Rx Port Valid + * 14b-11b: Rx Port + * 10b-0b: Unknown Value 0x80 + * 2: Tx Port(s) + * 15b: Tx Port(s) Valid + * 10b-0b: Tx Port(s) Mask + */ + +#include +#include +#include + +#include "tag.h" + +#define YT921X_NAME "yt921x" + +#define YT921X_TAG_LEN 8 + +#define ETH_P_YT921X 0x9988 + +#define YT921X_TAG_PORT_ENf BIT(15) +#define YT921X_TAG_RX_PORTf GENMASK(14, 11) +#define YT921X_TAG_TX_PORTf GENMASK(10, 0) +#define YT921X_TAG_TX_PORTnv(port) BIT(port) + +static struct sk_buff * +yt921x_tag_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct dsa_port *dp = dsa_user_to_port(netdev); + __be16 *tag; + + skb_push(skb, YT921X_TAG_LEN); + dsa_alloc_etype_header(skb, YT921X_TAG_LEN); + + tag = (__be16 *)(skb->data + 2 * ETH_ALEN); + + /* Might use yt921x_priv::tag_eth_p, but... */ + tag[0] = htons(ETH_P_YT921X); + /* Service VLAN not used here, set to 1 anyway */ + tag[1] = htons(1); + tag[2] = 0; + tag[3] = htons(YT921X_TAG_PORT_ENf | YT921X_TAG_TX_PORTnv(dp->index)); + + /* Now tell the conduit network device about the desired output queue + * as well + */ + skb_set_queue_mapping(skb, dp->index); + + return skb; +} + +static struct sk_buff * +yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) +{ + __be16 *tag; + u16 rx; + int rx_port; + + if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN))) + return NULL; + + tag = (__be16 *)skb->data; + + /* Locate which port this is coming from */ + rx = ntohs(tag[1]); + if (unlikely((rx & YT921X_TAG_PORT_ENf) == 0)) { + netdev_err(netdev, "Unexpected rx tag 0x%04x\n", rx); + return NULL; + } + + rx_port = FIELD_GET(YT921X_TAG_RX_PORTf, rx); + skb->dev = dsa_conduit_find_user(netdev, 0, rx_port); + if (unlikely(!skb->dev)) { + dev_warn_ratelimited(&netdev->dev, + "Couldn't decode source port\n"); + return NULL; + } + + /* Remove YT921x tag and update checksum */ + skb_pull_rcsum(skb, YT921X_TAG_LEN); + + dsa_default_offload_fwd_mark(skb); + + dsa_strip_etype_header(skb, YT921X_TAG_LEN); + + return skb; +} + +static const struct dsa_device_ops yt921x_netdev_ops = { + .name = YT921X_NAME, + .proto = DSA_TAG_PROTO_YT921X, + .xmit = yt921x_tag_xmit, + .rcv = yt921x_tag_rcv, + .needed_headroom = YT921X_TAG_LEN, +}; + +MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT921x switches"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT921X, YT921X_NAME); + +module_dsa_tag_driver(yt921x_netdev_ops); -- 2.47.2