]>
Commit | Line | Data |
---|---|---|
cafdc45c JC |
1 | /* |
2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 and | |
6 | * only version 2 as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/etherdevice.h> | |
ea5dd34b | 15 | |
cafdc45c JC |
16 | #include "dsa_priv.h" |
17 | ||
18 | #define QCA_HDR_LEN 2 | |
19 | #define QCA_HDR_VERSION 0x2 | |
20 | ||
21 | #define QCA_HDR_RECV_VERSION_MASK GENMASK(15, 14) | |
22 | #define QCA_HDR_RECV_VERSION_S 14 | |
23 | #define QCA_HDR_RECV_PRIORITY_MASK GENMASK(13, 11) | |
24 | #define QCA_HDR_RECV_PRIORITY_S 11 | |
25 | #define QCA_HDR_RECV_TYPE_MASK GENMASK(10, 6) | |
26 | #define QCA_HDR_RECV_TYPE_S 6 | |
27 | #define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) | |
28 | #define QCA_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0) | |
29 | ||
30 | #define QCA_HDR_XMIT_VERSION_MASK GENMASK(15, 14) | |
31 | #define QCA_HDR_XMIT_VERSION_S 14 | |
32 | #define QCA_HDR_XMIT_PRIORITY_MASK GENMASK(13, 11) | |
33 | #define QCA_HDR_XMIT_PRIORITY_S 11 | |
34 | #define QCA_HDR_XMIT_CONTROL_MASK GENMASK(10, 8) | |
35 | #define QCA_HDR_XMIT_CONTROL_S 8 | |
36 | #define QCA_HDR_XMIT_FROM_CPU BIT(7) | |
37 | #define QCA_HDR_XMIT_DP_BIT_MASK GENMASK(6, 0) | |
38 | ||
39 | static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) | |
40 | { | |
d945097b | 41 | struct dsa_port *dp = dsa_slave_to_port(dev); |
cafdc45c JC |
42 | u16 *phdr, hdr; |
43 | ||
44 | dev->stats.tx_packets++; | |
45 | dev->stats.tx_bytes += skb->len; | |
46 | ||
47 | if (skb_cow_head(skb, 0) < 0) | |
fe47d563 | 48 | return NULL; |
cafdc45c JC |
49 | |
50 | skb_push(skb, QCA_HDR_LEN); | |
51 | ||
52 | memmove(skb->data, skb->data + QCA_HDR_LEN, 2 * ETH_ALEN); | |
53 | phdr = (u16 *)(skb->data + 2 * ETH_ALEN); | |
54 | ||
55 | /* Set the version field, and set destination port information */ | |
56 | hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S | | |
d945097b | 57 | QCA_HDR_XMIT_FROM_CPU | BIT(dp->index); |
cafdc45c JC |
58 | |
59 | *phdr = htons(hdr); | |
60 | ||
61 | return skb; | |
cafdc45c JC |
62 | } |
63 | ||
a86d8bec | 64 | static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, |
89e49506 | 65 | struct packet_type *pt) |
cafdc45c | 66 | { |
cafdc45c JC |
67 | u8 ver; |
68 | int port; | |
69 | __be16 *phdr, hdr; | |
70 | ||
cafdc45c | 71 | if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) |
54709795 | 72 | return NULL; |
cafdc45c JC |
73 | |
74 | /* The QCA header is added by the switch between src addr and Ethertype | |
75 | * At this point, skb->data points to ethertype so header should be | |
76 | * right before | |
77 | */ | |
78 | phdr = (__be16 *)(skb->data - 2); | |
79 | hdr = ntohs(*phdr); | |
80 | ||
81 | /* Make sure the version is correct */ | |
82 | ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S; | |
83 | if (unlikely(ver != QCA_HDR_VERSION)) | |
54709795 | 84 | return NULL; |
cafdc45c JC |
85 | |
86 | /* Remove QCA tag and recalculate checksum */ | |
87 | skb_pull_rcsum(skb, QCA_HDR_LEN); | |
88 | memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - QCA_HDR_LEN, | |
89 | ETH_HLEN - QCA_HDR_LEN); | |
90 | ||
cafdc45c JC |
91 | /* Get source port information */ |
92 | port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK); | |
cafdc45c | 93 | |
2231c43b | 94 | skb->dev = dsa_master_find_slave(dev, 0, port); |
3775b1b7 VD |
95 | if (!skb->dev) |
96 | return NULL; | |
cafdc45c | 97 | |
a86d8bec | 98 | return skb; |
cafdc45c JC |
99 | } |
100 | ||
101 | const struct dsa_device_ops qca_netdev_ops = { | |
102 | .xmit = qca_tag_xmit, | |
103 | .rcv = qca_tag_rcv, | |
104 | }; |