1/* 2 * Broadcom tag support 3 * 4 * Copyright (C) 2014 Broadcom Corporation 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 */ 11 12#include <linux/etherdevice.h> 13#include <linux/list.h> 14#include <linux/slab.h> 15#include "dsa_priv.h" 16 17/* This tag length is 4 bytes, older ones were 6 bytes, we do not 18 * handle them 19 */ 20#define BRCM_TAG_LEN 4 21 22/* Tag is constructed and desconstructed using byte by byte access 23 * because the tag is placed after the MAC Source Address, which does 24 * not make it 4-bytes aligned, so this might cause unaligned accesses 25 * on most systems where this is used. 26 */ 27 28/* Ingress and egress opcodes */ 29#define BRCM_OPCODE_SHIFT 5 30#define BRCM_OPCODE_MASK 0x7 31 32/* Ingress fields */ 33/* 1st byte in the tag */ 34#define BRCM_IG_TC_SHIFT 2 35#define BRCM_IG_TC_MASK 0x7 36/* 2nd byte in the tag */ 37#define BRCM_IG_TE_MASK 0x3 38#define BRCM_IG_TS_SHIFT 7 39/* 3rd byte in the tag */ 40#define BRCM_IG_DSTMAP2_MASK 1 41#define BRCM_IG_DSTMAP1_MASK 0xff 42 43/* Egress fields */ 44 45/* 2nd byte in the tag */ 46#define BRCM_EG_CID_MASK 0xff 47 48/* 3rd byte in the tag */ 49#define BRCM_EG_RC_MASK 0xff 50#define BRCM_EG_RC_RSVD (3 << 6) 51#define BRCM_EG_RC_EXCEPTION (1 << 5) 52#define BRCM_EG_RC_PROT_SNOOP (1 << 4) 53#define BRCM_EG_RC_PROT_TERM (1 << 3) 54#define BRCM_EG_RC_SWITCH (1 << 2) 55#define BRCM_EG_RC_MAC_LEARN (1 << 1) 56#define BRCM_EG_RC_MIRROR (1 << 0) 57#define BRCM_EG_TC_SHIFT 5 58#define BRCM_EG_TC_MASK 0x7 59#define BRCM_EG_PID_MASK 0x1f 60 61static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev) 62{ 63 struct dsa_slave_priv *p = netdev_priv(dev); 64 u8 *brcm_tag; 65 66 if (skb_cow_head(skb, BRCM_TAG_LEN) < 0) 67 goto out_free; 68 69 skb_push(skb, BRCM_TAG_LEN); 70 71 memmove(skb->data, skb->data + BRCM_TAG_LEN, 2 * ETH_ALEN); 72 73 /* Build the tag after the MAC Source Address */ 74 brcm_tag = skb->data + 2 * ETH_ALEN; 75 76 /* Set the ingress opcode, traffic class, tag enforcment is 77 * deprecated 78 */ 79 brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) | 80 ((skb->priority << BRCM_IG_TC_SHIFT) & BRCM_IG_TC_MASK); 81 brcm_tag[1] = 0; 82 brcm_tag[2] = 0; 83 if (p->port == 8) 84 brcm_tag[2] = BRCM_IG_DSTMAP2_MASK; 85 brcm_tag[3] = (1 << p->port) & BRCM_IG_DSTMAP1_MASK; 86 87 return skb; 88 89out_free: 90 kfree_skb(skb); 91 return NULL; 92} 93 94static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, 95 struct packet_type *pt, struct net_device *orig_dev) 96{ 97 struct dsa_switch_tree *dst = dev->dsa_ptr; 98 struct dsa_switch *ds; 99 int source_port; 100 u8 *brcm_tag; 101 102 if (unlikely(dst == NULL)) 103 goto out_drop; 104 105 ds = dst->ds[0]; 106 107 skb = skb_unshare(skb, GFP_ATOMIC); 108 if (skb == NULL) 109 goto out; 110 111 if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) 112 goto out_drop; 113 114 /* skb->data points to the EtherType, the tag is right before it */ 115 brcm_tag = skb->data - 2; 116 117 /* The opcode should never be different than 0b000 */ 118 if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK)) 119 goto out_drop; 120 121 /* We should never see a reserved reason code without knowing how to 122 * handle it 123 */ 124 WARN_ON(brcm_tag[2] & BRCM_EG_RC_RSVD); 125 126 /* Locate which port this is coming from */ 127 source_port = brcm_tag[3] & BRCM_EG_PID_MASK; 128 129 /* Validate port against switch setup, either the port is totally */ 130 if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) 131 goto out_drop; 132 133 /* Remove Broadcom tag and update checksum */ 134 skb_pull_rcsum(skb, BRCM_TAG_LEN); 135 136 /* Move the Ethernet DA and SA */ 137 memmove(skb->data - ETH_HLEN, 138 skb->data - ETH_HLEN - BRCM_TAG_LEN, 139 2 * ETH_ALEN); 140 141 skb_push(skb, ETH_HLEN); 142 skb->pkt_type = PACKET_HOST; 143 skb->dev = ds->ports[source_port]; 144 skb->protocol = eth_type_trans(skb, skb->dev); 145 146 skb->dev->stats.rx_packets++; 147 skb->dev->stats.rx_bytes += skb->len; 148 149 netif_receive_skb(skb); 150 151 return 0; 152 153out_drop: 154 kfree_skb(skb); 155out: 156 return 0; 157} 158 159const struct dsa_device_ops brcm_netdev_ops = { 160 .xmit = brcm_tag_xmit, 161 .rcv = brcm_tag_rcv, 162}; 163