linux/net/dsa/tag_qca.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/etherdevice.h>
   7
   8#include "dsa_priv.h"
   9
  10#define QCA_HDR_LEN     2
  11#define QCA_HDR_VERSION 0x2
  12
  13#define QCA_HDR_RECV_VERSION_MASK       GENMASK(15, 14)
  14#define QCA_HDR_RECV_VERSION_S          14
  15#define QCA_HDR_RECV_PRIORITY_MASK      GENMASK(13, 11)
  16#define QCA_HDR_RECV_PRIORITY_S         11
  17#define QCA_HDR_RECV_TYPE_MASK          GENMASK(10, 6)
  18#define QCA_HDR_RECV_TYPE_S             6
  19#define QCA_HDR_RECV_FRAME_IS_TAGGED    BIT(3)
  20#define QCA_HDR_RECV_SOURCE_PORT_MASK   GENMASK(2, 0)
  21
  22#define QCA_HDR_XMIT_VERSION_MASK       GENMASK(15, 14)
  23#define QCA_HDR_XMIT_VERSION_S          14
  24#define QCA_HDR_XMIT_PRIORITY_MASK      GENMASK(13, 11)
  25#define QCA_HDR_XMIT_PRIORITY_S         11
  26#define QCA_HDR_XMIT_CONTROL_MASK       GENMASK(10, 8)
  27#define QCA_HDR_XMIT_CONTROL_S          8
  28#define QCA_HDR_XMIT_FROM_CPU           BIT(7)
  29#define QCA_HDR_XMIT_DP_BIT_MASK        GENMASK(6, 0)
  30
  31static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev)
  32{
  33        struct dsa_port *dp = dsa_slave_to_port(dev);
  34        __be16 *phdr;
  35        u16 hdr;
  36
  37        skb_push(skb, QCA_HDR_LEN);
  38
  39        memmove(skb->data, skb->data + QCA_HDR_LEN, 2 * ETH_ALEN);
  40        phdr = (__be16 *)(skb->data + 2 * ETH_ALEN);
  41
  42        /* Set the version field, and set destination port information */
  43        hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S |
  44                QCA_HDR_XMIT_FROM_CPU | BIT(dp->index);
  45
  46        *phdr = htons(hdr);
  47
  48        return skb;
  49}
  50
  51static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev,
  52                                   struct packet_type *pt)
  53{
  54        u8 ver;
  55        u16  hdr;
  56        int port;
  57        __be16 *phdr;
  58
  59        if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN)))
  60                return NULL;
  61
  62        /* The QCA header is added by the switch between src addr and Ethertype
  63         * At this point, skb->data points to ethertype so header should be
  64         * right before
  65         */
  66        phdr = (__be16 *)(skb->data - 2);
  67        hdr = ntohs(*phdr);
  68
  69        /* Make sure the version is correct */
  70        ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S;
  71        if (unlikely(ver != QCA_HDR_VERSION))
  72                return NULL;
  73
  74        /* Remove QCA tag and recalculate checksum */
  75        skb_pull_rcsum(skb, QCA_HDR_LEN);
  76        memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - QCA_HDR_LEN,
  77                ETH_HLEN - QCA_HDR_LEN);
  78
  79        /* Get source port information */
  80        port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK);
  81
  82        skb->dev = dsa_master_find_slave(dev, 0, port);
  83        if (!skb->dev)
  84                return NULL;
  85
  86        return skb;
  87}
  88
  89static const struct dsa_device_ops qca_netdev_ops = {
  90        .name   = "qca",
  91        .proto  = DSA_TAG_PROTO_QCA,
  92        .xmit   = qca_tag_xmit,
  93        .rcv    = qca_tag_rcv,
  94        .needed_headroom = QCA_HDR_LEN,
  95};
  96
  97MODULE_LICENSE("GPL");
  98MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_QCA);
  99
 100module_dsa_tag_driver(qca_netdev_ops);
 101