]>
Commit | Line | Data |
---|---|---|
dfedd3b6 | 1 | // SPDX-License-Identifier: GPL-2.0 |
cafdc45c JC |
2 | /* |
3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | |
cafdc45c JC |
4 | */ |
5 | ||
6 | #include <linux/etherdevice.h> | |
6b045829 | 7 | #include <linux/bitfield.h> |
31eb6b43 | 8 | #include <net/dsa.h> |
3ec762fb | 9 | #include <linux/dsa/tag_qca.h> |
ea5dd34b | 10 | |
bd954b82 | 11 | #include "tag.h" |
cafdc45c | 12 | |
94793a56 VO |
13 | #define QCA_NAME "qca" |
14 | ||
cafdc45c JC |
15 | static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) |
16 | { | |
d945097b | 17 | struct dsa_port *dp = dsa_slave_to_port(dev); |
99bac53d AL |
18 | __be16 *phdr; |
19 | u16 hdr; | |
cafdc45c | 20 | |
cafdc45c JC |
21 | skb_push(skb, QCA_HDR_LEN); |
22 | ||
6bef794d | 23 | dsa_alloc_etype_header(skb, QCA_HDR_LEN); |
a72808b6 | 24 | phdr = dsa_etype_header_pos_tx(skb); |
cafdc45c JC |
25 | |
26 | /* Set the version field, and set destination port information */ | |
6b045829 CM |
27 | hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); |
28 | hdr |= QCA_HDR_XMIT_FROM_CPU; | |
29 | hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(dp->index)); | |
cafdc45c JC |
30 | |
31 | *phdr = htons(hdr); | |
32 | ||
33 | return skb; | |
cafdc45c JC |
34 | } |
35 | ||
29a097b7 | 36 | static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) |
cafdc45c | 37 | { |
31eb6b43 CM |
38 | struct qca_tagger_data *tagger_data; |
39 | struct dsa_port *dp = dev->dsa_ptr; | |
40 | struct dsa_switch *ds = dp->ds; | |
c2ee8181 | 41 | u8 ver, pk_type; |
99bac53d | 42 | __be16 *phdr; |
c2ee8181 CM |
43 | int port; |
44 | u16 hdr; | |
45 | ||
46 | BUILD_BUG_ON(sizeof(struct qca_mgmt_ethhdr) != QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); | |
cafdc45c | 47 | |
31eb6b43 CM |
48 | tagger_data = ds->tagger_data; |
49 | ||
cafdc45c | 50 | if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) |
54709795 | 51 | return NULL; |
cafdc45c | 52 | |
5d928ff4 | 53 | phdr = dsa_etype_header_pos_rx(skb); |
cafdc45c JC |
54 | hdr = ntohs(*phdr); |
55 | ||
56 | /* Make sure the version is correct */ | |
6b045829 | 57 | ver = FIELD_GET(QCA_HDR_RECV_VERSION, hdr); |
cafdc45c | 58 | if (unlikely(ver != QCA_HDR_VERSION)) |
54709795 | 59 | return NULL; |
cafdc45c | 60 | |
c2ee8181 CM |
61 | /* Get pk type */ |
62 | pk_type = FIELD_GET(QCA_HDR_RECV_TYPE, hdr); | |
63 | ||
31eb6b43 CM |
64 | /* Ethernet mgmt read/write packet */ |
65 | if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) { | |
66 | if (likely(tagger_data->rw_reg_ack_handler)) | |
67 | tagger_data->rw_reg_ack_handler(ds, skb); | |
c2ee8181 | 68 | return NULL; |
31eb6b43 | 69 | } |
c2ee8181 | 70 | |
18be654a | 71 | /* Ethernet MIB counter packet */ |
31eb6b43 CM |
72 | if (pk_type == QCA_HDR_RECV_TYPE_MIB) { |
73 | if (likely(tagger_data->mib_autocast_handler)) | |
74 | tagger_data->mib_autocast_handler(ds, skb); | |
18be654a | 75 | return NULL; |
31eb6b43 | 76 | } |
18be654a | 77 | |
cafdc45c JC |
78 | /* Remove QCA tag and recalculate checksum */ |
79 | skb_pull_rcsum(skb, QCA_HDR_LEN); | |
f1dacd7a | 80 | dsa_strip_etype_header(skb, QCA_HDR_LEN); |
cafdc45c | 81 | |
cafdc45c | 82 | /* Get source port information */ |
6b045829 | 83 | port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, hdr); |
cafdc45c | 84 | |
2231c43b | 85 | skb->dev = dsa_master_find_slave(dev, 0, port); |
3775b1b7 VD |
86 | if (!skb->dev) |
87 | return NULL; | |
cafdc45c | 88 | |
a86d8bec | 89 | return skb; |
cafdc45c JC |
90 | } |
91 | ||
31eb6b43 CM |
92 | static int qca_tag_connect(struct dsa_switch *ds) |
93 | { | |
94 | struct qca_tagger_data *tagger_data; | |
95 | ||
96 | tagger_data = kzalloc(sizeof(*tagger_data), GFP_KERNEL); | |
97 | if (!tagger_data) | |
98 | return -ENOMEM; | |
99 | ||
100 | ds->tagger_data = tagger_data; | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | static void qca_tag_disconnect(struct dsa_switch *ds) | |
106 | { | |
107 | kfree(ds->tagger_data); | |
108 | ds->tagger_data = NULL; | |
109 | } | |
110 | ||
f81a43e8 | 111 | static const struct dsa_device_ops qca_netdev_ops = { |
94793a56 | 112 | .name = QCA_NAME, |
056eed2f | 113 | .proto = DSA_TAG_PROTO_QCA, |
31eb6b43 CM |
114 | .connect = qca_tag_connect, |
115 | .disconnect = qca_tag_disconnect, | |
cafdc45c JC |
116 | .xmit = qca_tag_xmit, |
117 | .rcv = qca_tag_rcv, | |
4e500251 | 118 | .needed_headroom = QCA_HDR_LEN, |
101c04c3 | 119 | .promisc_on_master = true, |
cafdc45c | 120 | }; |
0b42f033 | 121 | |
f18bba50 | 122 | MODULE_LICENSE("GPL"); |
94793a56 | 123 | MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_QCA, QCA_NAME); |
d3b8c049 AL |
124 | |
125 | module_dsa_tag_driver(qca_netdev_ops); |