]>
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 | { | |
41 | struct dsa_slave_priv *p = netdev_priv(dev); | |
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 | | |
57 | QCA_HDR_XMIT_FROM_CPU | | |
afdcf151 | 58 | BIT(p->dp->index); |
cafdc45c JC |
59 | |
60 | *phdr = htons(hdr); | |
61 | ||
62 | return skb; | |
cafdc45c JC |
63 | } |
64 | ||
a86d8bec FF |
65 | static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, |
66 | struct packet_type *pt, | |
67 | struct net_device *orig_dev) | |
cafdc45c JC |
68 | { |
69 | struct dsa_switch_tree *dst = dev->dsa_ptr; | |
3cc9f257 | 70 | struct dsa_port *cpu_dp = dsa_get_cpu_port(dst); |
cafdc45c JC |
71 | struct dsa_switch *ds; |
72 | u8 ver; | |
73 | int port; | |
74 | __be16 *phdr, hdr; | |
75 | ||
cafdc45c | 76 | if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) |
54709795 | 77 | return NULL; |
cafdc45c JC |
78 | |
79 | /* The QCA header is added by the switch between src addr and Ethertype | |
80 | * At this point, skb->data points to ethertype so header should be | |
81 | * right before | |
82 | */ | |
83 | phdr = (__be16 *)(skb->data - 2); | |
84 | hdr = ntohs(*phdr); | |
85 | ||
86 | /* Make sure the version is correct */ | |
87 | ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S; | |
88 | if (unlikely(ver != QCA_HDR_VERSION)) | |
54709795 | 89 | return NULL; |
cafdc45c JC |
90 | |
91 | /* Remove QCA tag and recalculate checksum */ | |
92 | skb_pull_rcsum(skb, QCA_HDR_LEN); | |
93 | memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - QCA_HDR_LEN, | |
94 | ETH_HLEN - QCA_HDR_LEN); | |
95 | ||
96 | /* This protocol doesn't support cascading multiple switches so it's | |
97 | * safe to assume the switch is first in the tree | |
98 | */ | |
3cc9f257 | 99 | ds = cpu_dp->ds; |
cafdc45c | 100 | if (!ds) |
54709795 | 101 | return NULL; |
cafdc45c JC |
102 | |
103 | /* Get source port information */ | |
104 | port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK); | |
105 | if (!ds->ports[port].netdev) | |
54709795 | 106 | return NULL; |
cafdc45c JC |
107 | |
108 | /* Update skb & forward the frame accordingly */ | |
cafdc45c | 109 | skb->dev = ds->ports[port].netdev; |
cafdc45c | 110 | |
a86d8bec | 111 | return skb; |
cafdc45c JC |
112 | } |
113 | ||
114 | const struct dsa_device_ops qca_netdev_ops = { | |
115 | .xmit = qca_tag_xmit, | |
116 | .rcv = qca_tag_rcv, | |
117 | }; |