]>
Commit | Line | Data |
---|---|---|
8b8010fb WH |
1 | /* |
2 | * net/dsa/tag_ksz.c - Microchip KSZ Switch tag format handling | |
3 | * Copyright (c) 2017 Microchip Technology | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | */ | |
10 | ||
11 | #include <linux/etherdevice.h> | |
12 | #include <linux/list.h> | |
13 | #include <linux/slab.h> | |
14 | #include <net/dsa.h> | |
15 | #include "dsa_priv.h" | |
16 | ||
17 | /* For Ingress (Host -> KSZ), 2 bytes are added before FCS. | |
18 | * --------------------------------------------------------------------------- | |
19 | * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|tag1(1byte)|FCS(4bytes) | |
20 | * --------------------------------------------------------------------------- | |
21 | * tag0 : Prioritization (not used now) | |
22 | * tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5) | |
23 | * | |
24 | * For Egress (KSZ -> Host), 1 byte is added before FCS. | |
25 | * --------------------------------------------------------------------------- | |
26 | * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes) | |
27 | * --------------------------------------------------------------------------- | |
28 | * tag0 : zero-based value represents port | |
29 | * (eg, 0x00=port1, 0x02=port3, 0x06=port7) | |
30 | */ | |
31 | ||
32 | #define KSZ_INGRESS_TAG_LEN 2 | |
33 | #define KSZ_EGRESS_TAG_LEN 1 | |
34 | ||
35 | static struct sk_buff *ksz_xmit(struct sk_buff *skb, struct net_device *dev) | |
36 | { | |
d945097b | 37 | struct dsa_port *dp = dsa_slave_to_port(dev); |
8b8010fb WH |
38 | struct sk_buff *nskb; |
39 | int padlen; | |
40 | u8 *tag; | |
41 | ||
42 | padlen = (skb->len >= ETH_ZLEN) ? 0 : ETH_ZLEN - skb->len; | |
43 | ||
44 | if (skb_tailroom(skb) >= padlen + KSZ_INGRESS_TAG_LEN) { | |
49716679 FF |
45 | /* Let dsa_slave_xmit() free skb */ |
46 | if (__skb_put_padto(skb, skb->len + padlen, false)) | |
e71cb9e0 VD |
47 | return NULL; |
48 | ||
8b8010fb WH |
49 | nskb = skb; |
50 | } else { | |
51 | nskb = alloc_skb(NET_IP_ALIGN + skb->len + | |
52 | padlen + KSZ_INGRESS_TAG_LEN, GFP_ATOMIC); | |
fe47d563 | 53 | if (!nskb) |
8b8010fb | 54 | return NULL; |
8b8010fb WH |
55 | skb_reserve(nskb, NET_IP_ALIGN); |
56 | ||
57 | skb_reset_mac_header(nskb); | |
58 | skb_set_network_header(nskb, | |
59 | skb_network_header(skb) - skb->head); | |
60 | skb_set_transport_header(nskb, | |
61 | skb_transport_header(skb) - skb->head); | |
62 | skb_copy_and_csum_dev(skb, skb_put(nskb, skb->len)); | |
e71cb9e0 | 63 | |
49716679 FF |
64 | /* Let skb_put_padto() free nskb, and let dsa_slave_xmit() free |
65 | * skb | |
66 | */ | |
67 | if (skb_put_padto(nskb, nskb->len + padlen)) | |
e71cb9e0 | 68 | return NULL; |
e71cb9e0 | 69 | |
2b33bc8a | 70 | consume_skb(skb); |
8b8010fb WH |
71 | } |
72 | ||
8b8010fb WH |
73 | tag = skb_put(nskb, KSZ_INGRESS_TAG_LEN); |
74 | tag[0] = 0; | |
d945097b | 75 | tag[1] = 1 << dp->index; /* destination port */ |
8b8010fb WH |
76 | |
77 | return nskb; | |
78 | } | |
79 | ||
7b954ed7 | 80 | static struct sk_buff *ksz_rcv(struct sk_buff *skb, struct net_device *dev, |
89e49506 | 81 | struct packet_type *pt) |
8b8010fb | 82 | { |
8b8010fb WH |
83 | u8 *tag; |
84 | int source_port; | |
85 | ||
8b8010fb WH |
86 | tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN; |
87 | ||
88 | source_port = tag[0] & 7; | |
3775b1b7 | 89 | |
2231c43b | 90 | skb->dev = dsa_master_find_slave(dev, 0, source_port); |
3775b1b7 | 91 | if (!skb->dev) |
8b8010fb WH |
92 | return NULL; |
93 | ||
94 | pskb_trim_rcsum(skb, skb->len - KSZ_EGRESS_TAG_LEN); | |
95 | ||
8b8010fb WH |
96 | return skb; |
97 | } | |
98 | ||
99 | const struct dsa_device_ops ksz_netdev_ops = { | |
100 | .xmit = ksz_xmit, | |
101 | .rcv = ksz_rcv, | |
102 | }; |