linux/net/dsa/tag_lan9303.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2017 Pengutronix, Juergen Borleis <jbe@pengutronix.de>
   4 */
   5#include <linux/dsa/lan9303.h>
   6#include <linux/etherdevice.h>
   7#include <linux/list.h>
   8#include <linux/slab.h>
   9
  10#include "dsa_priv.h"
  11
  12/* To define the outgoing port and to discover the incoming port a regular
  13 * VLAN tag is used by the LAN9303. But its VID meaning is 'special':
  14 *
  15 *       Dest MAC       Src MAC        TAG    Type
  16 * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 | 1 2 |...
  17 *                                |<------->|
  18 * TAG:
  19 *    |<------------->|
  20 *    |  1  2 | 3  4  |
  21 *      TPID    VID
  22 *     0x8100
  23 *
  24 * VID bit 3 indicates a request for an ALR lookup.
  25 *
  26 * If VID bit 3 is zero, then bits 0 and 1 specify the destination port
  27 * (0, 1, 2) or broadcast (3) or the source port (1, 2).
  28 *
  29 * VID bit 4 is used to specify if the STP port state should be overridden.
  30 * Required when no forwarding between the external ports should happen.
  31 */
  32
  33#define LAN9303_TAG_LEN 4
  34# define LAN9303_TAG_TX_USE_ALR BIT(3)
  35# define LAN9303_TAG_TX_STP_OVERRIDE BIT(4)
  36# define LAN9303_TAG_RX_IGMP BIT(3)
  37# define LAN9303_TAG_RX_STP BIT(4)
  38# define LAN9303_TAG_RX_TRAPPED_TO_CPU (LAN9303_TAG_RX_IGMP | \
  39                                        LAN9303_TAG_RX_STP)
  40
  41/* Decide whether to transmit using ALR lookup, or transmit directly to
  42 * port using tag. ALR learning is performed only when using ALR lookup.
  43 * If the two external ports are bridged and the frame is unicast,
  44 * then use ALR lookup to allow ALR learning on CPU port.
  45 * Otherwise transmit directly to port with STP state override.
  46 * See also: lan9303_separate_ports() and lan9303.pdf 6.4.10.1
  47 */
  48static int lan9303_xmit_use_arl(struct dsa_port *dp, u8 *dest_addr)
  49{
  50        struct lan9303 *chip = dp->ds->priv;
  51
  52        return chip->is_bridged && !is_multicast_ether_addr(dest_addr);
  53}
  54
  55static struct sk_buff *lan9303_xmit(struct sk_buff *skb, struct net_device *dev)
  56{
  57        struct dsa_port *dp = dsa_slave_to_port(dev);
  58        __be16 *lan9303_tag;
  59        u16 tag;
  60
  61        /* provide 'LAN9303_TAG_LEN' bytes additional space */
  62        skb_push(skb, LAN9303_TAG_LEN);
  63
  64        /* make room between MACs and Ether-Type */
  65        dsa_alloc_etype_header(skb, LAN9303_TAG_LEN);
  66
  67        lan9303_tag = dsa_etype_header_pos_tx(skb);
  68
  69        tag = lan9303_xmit_use_arl(dp, skb->data) ?
  70                LAN9303_TAG_TX_USE_ALR :
  71                dp->index | LAN9303_TAG_TX_STP_OVERRIDE;
  72        lan9303_tag[0] = htons(ETH_P_8021Q);
  73        lan9303_tag[1] = htons(tag);
  74
  75        return skb;
  76}
  77
  78static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev)
  79{
  80        __be16 *lan9303_tag;
  81        u16 lan9303_tag1;
  82        unsigned int source_port;
  83
  84        if (unlikely(!pskb_may_pull(skb, LAN9303_TAG_LEN))) {
  85                dev_warn_ratelimited(&dev->dev,
  86                                     "Dropping packet, cannot pull\n");
  87                return NULL;
  88        }
  89
  90        lan9303_tag = dsa_etype_header_pos_rx(skb);
  91
  92        if (lan9303_tag[0] != htons(ETH_P_8021Q)) {
  93                dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid VLAN marker\n");
  94                return NULL;
  95        }
  96
  97        lan9303_tag1 = ntohs(lan9303_tag[1]);
  98        source_port = lan9303_tag1 & 0x3;
  99
 100        skb->dev = dsa_master_find_slave(dev, 0, source_port);
 101        if (!skb->dev) {
 102                dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n");
 103                return NULL;
 104        }
 105
 106        /* remove the special VLAN tag between the MAC addresses
 107         * and the current ethertype field.
 108         */
 109        skb_pull_rcsum(skb, 2 + 2);
 110
 111        dsa_strip_etype_header(skb, LAN9303_TAG_LEN);
 112
 113        if (!(lan9303_tag1 & LAN9303_TAG_RX_TRAPPED_TO_CPU))
 114                dsa_default_offload_fwd_mark(skb);
 115
 116        return skb;
 117}
 118
 119static const struct dsa_device_ops lan9303_netdev_ops = {
 120        .name = "lan9303",
 121        .proto  = DSA_TAG_PROTO_LAN9303,
 122        .xmit = lan9303_xmit,
 123        .rcv = lan9303_rcv,
 124        .needed_headroom = LAN9303_TAG_LEN,
 125};
 126
 127MODULE_LICENSE("GPL");
 128MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN9303);
 129
 130module_dsa_tag_driver(lan9303_netdev_ops);
 131