FILE
tag_yt921x.c
File preview with syntax highlighting.
linux-6.19.14/net/dsa/tag_yt921x.c
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Motorcomm YT921x Switch Extended CPU Port Tagging 4 * 5 * Copyright (c) 2025 David Yang <mmyangfl@gmail.com> 6 * 7 * +----+----+-------+-----+----+--------- 8 * | DA | SA | TagET | Tag | ET | Payload ... 9 * +----+----+-------+-----+----+--------- 10 * 6 6 2 6 2 N 11 * 12 * Tag Ethertype: CPU_TAG_TPID_TPID (default: ETH_P_YT921X = 0x9988) 13 * * Hardcoded for the moment, but still configurable. Discuss it if there 14 * are conflicts somewhere and/or you want to change it for some reason. 15 * Tag: 16 * 2: VLAN Tag 17 * 2: Rx Port 18 * 15b: Rx Port Valid 19 * 14b-11b: Rx Port 20 * 10b-0b: Cmd? 21 * 2: Tx Port(s) 22 * 15b: Tx Port(s) Valid 23 * 10b-0b: Tx Port(s) Mask 24 */ 25 26#include <linux/etherdevice.h> 27 28#include "tag.h" 29 30#define YT921X_TAG_NAME "yt921x" 31 32#define YT921X_TAG_LEN 8 33 34#define YT921X_TAG_PORT_EN BIT(15) 35#define YT921X_TAG_RX_PORT_M GENMASK(14, 11) 36#define YT921X_TAG_RX_CMD_M GENMASK(10, 0) 37#define YT921X_TAG_RX_CMD(x) FIELD_PREP(YT921X_TAG_RX_CMD_M, (x)) 38#define YT921X_TAG_RX_CMD_FORWARDED 0x80 39#define YT921X_TAG_RX_CMD_UNK_UCAST 0xb2 40#define YT921X_TAG_RX_CMD_UNK_MCAST 0xb4 41#define YT921X_TAG_TX_PORTS GENMASK(10, 0) 42 43static struct sk_buff * 44yt921x_tag_xmit(struct sk_buff *skb, struct net_device *netdev) 45{ 46 __be16 *tag; 47 u16 tx; 48 49 skb_push(skb, YT921X_TAG_LEN); 50 dsa_alloc_etype_header(skb, YT921X_TAG_LEN); 51 52 tag = dsa_etype_header_pos_tx(skb); 53 54 tag[0] = htons(ETH_P_YT921X); 55 /* VLAN tag unrelated when TX */ 56 tag[1] = 0; 57 tag[2] = 0; 58 tx = FIELD_PREP(YT921X_TAG_TX_PORTS, dsa_xmit_port_mask(skb, netdev)) | 59 YT921X_TAG_PORT_EN; 60 tag[3] = htons(tx); 61 62 return skb; 63} 64 65static struct sk_buff * 66yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) 67{ 68 unsigned int port; 69 __be16 *tag; 70 u16 cmd; 71 u16 rx; 72 73 if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN))) 74 return NULL; 75 76 tag = dsa_etype_header_pos_rx(skb); 77 78 if (unlikely(tag[0] != htons(ETH_P_YT921X))) { 79 dev_warn_ratelimited(&netdev->dev, 80 "Unexpected EtherType 0x%04x\n", 81 ntohs(tag[0])); 82 return NULL; 83 } 84 85 /* Locate which port this is coming from */ 86 rx = ntohs(tag[2]); 87 if (unlikely((rx & YT921X_TAG_PORT_EN) == 0)) { 88 dev_warn_ratelimited(&netdev->dev, 89 "Unexpected rx tag 0x%04x\n", rx); 90 return NULL; 91 } 92 93 port = FIELD_GET(YT921X_TAG_RX_PORT_M, rx); 94 skb->dev = dsa_conduit_find_user(netdev, 0, port); 95 if (unlikely(!skb->dev)) { 96 dev_warn_ratelimited(&netdev->dev, 97 "Couldn't decode source port %u\n", port); 98 return NULL; 99 } 100 101 cmd = FIELD_GET(YT921X_TAG_RX_CMD_M, rx); 102 switch (cmd) { 103 case YT921X_TAG_RX_CMD_FORWARDED: 104 /* Already forwarded by hardware */ 105 dsa_default_offload_fwd_mark(skb); 106 break; 107 case YT921X_TAG_RX_CMD_UNK_UCAST: 108 case YT921X_TAG_RX_CMD_UNK_MCAST: 109 /* NOTE: hardware doesn't distinguish between TRAP (copy to CPU 110 * only) and COPY (forward and copy to CPU). In order to perform 111 * a soft switch, NEVER use COPY action in the switch driver. 112 */ 113 break; 114 default: 115 dev_warn_ratelimited(&netdev->dev, 116 "Unexpected rx cmd 0x%02x\n", cmd); 117 break; 118 } 119 120 /* Remove YT921x tag and update checksum */ 121 skb_pull_rcsum(skb, YT921X_TAG_LEN); 122 dsa_strip_etype_header(skb, YT921X_TAG_LEN); 123 124 return skb; 125} 126 127static const struct dsa_device_ops yt921x_netdev_ops = { 128 .name = YT921X_TAG_NAME, 129 .proto = DSA_TAG_PROTO_YT921X, 130 .xmit = yt921x_tag_xmit, 131 .rcv = yt921x_tag_rcv, 132 .needed_headroom = YT921X_TAG_LEN, 133}; 134 135MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT921x switches"); 136MODULE_LICENSE("GPL"); 137MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT921X, YT921X_TAG_NAME); 138 139module_dsa_tag_driver(yt921x_netdev_ops);
~/
Read-only file preview.