aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa/tag_rtl4_a.c
blob: c17d39b4a1a04826fc9d631679d41ec62e4fb979 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: GPL-2.0
/*
 * Handler for Realtek 4 byte DSA switch tags
 * Currently only supports protocol "A" found in RTL8366RB
 * Copyright (c) 2020 Linus Walleij <linus.walleij@linaro.org>
 *
 * This "proprietary tag" header looks like so:
 *
 * -------------------------------------------------
 * | MAC DA | MAC SA | 0x8899 | 2 bytes tag | Type |
 * -------------------------------------------------
 *
 * The 2 bytes tag form a 16 bit big endian word. The exact
 * meaning has been guessed from packet dumps from ingress
 * frames.
 */

#include <linux/etherdevice.h>
#include <linux/bits.h>

#include "dsa_priv.h"

#define RTL4_A_HDR_LEN		4
#define RTL4_A_ETHERTYPE	0x8899
#define RTL4_A_PROTOCOL_SHIFT	12
/*
 * 0x1 = Realtek Remote Control protocol (RRCP)
 * 0x2/0x3 seems to be used for loopback testing
 * 0x9 = RTL8306 DSA protocol
 * 0xa = RTL8366RB DSA protocol
 */
#define RTL4_A_PROTOCOL_RTL8366RB	0xa

static struct sk_buff *rtl4a_tag_xmit(struct sk_buff *skb,
				      struct net_device *dev)
{
	struct dsa_port *dp = dsa_slave_to_port(dev);
	u8 *tag;
	u16 *p;
	u16 out;

	/* Pad out to at least 60 bytes */
	if (unlikely(eth_skb_pad(skb)))
		return NULL;
	if (skb_cow_head(skb, RTL4_A_HDR_LEN) < 0)
		return NULL;

	netdev_dbg(dev, "add realtek tag to package to port %d\n",
		   dp->index);
	skb_push(skb, RTL4_A_HDR_LEN);

	memmove(skb->data, skb->data + RTL4_A_HDR_LEN, 2 * ETH_ALEN);
	tag = skb->data + 2 * ETH_ALEN;

	/* Set Ethertype */
	p = (u16 *)tag;
	*p = htons(RTL4_A_ETHERTYPE);

	out = (RTL4_A_PROTOCOL_RTL8366RB << 12) | (2 << 8);
	/* The lower bits is the port numer */
	out |= (u8)dp->index;
	p = (u16 *)(tag + 2);
	*p = htons(out);

	return skb;
}

static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb,
				     struct net_device *dev,
				     struct packet_type *pt)
{
	u16 protport;
	__be16 *p;
	u16 etype;
	u8 *tag;
	u8 prot;
	u8 port;

	if (unlikely(!pskb_may_pull(skb, RTL4_A_HDR_LEN)))
		return NULL;

	/* The RTL4 header has its own custom Ethertype 0x8899 and that
	 * starts right at the beginning of the packet, after the src
	 * ethernet addr. Apparantly skb->data always points 2 bytes in,
	 * behind the Ethertype.
	 */
	tag = skb->data - 2;
	p = (__be16 *)tag;
	etype = ntohs(*p);
	if (etype != RTL4_A_ETHERTYPE) {
		/* Not custom, just pass through */
		netdev_dbg(dev, "non-realtek ethertype 0x%04x\n", etype);
		return skb;
	}
	p = (__be16 *)(tag + 2);
	protport = ntohs(*p);
	/* The 4 upper bits are the protocol */
	prot = (protport >> RTL4_A_PROTOCOL_SHIFT) & 0x0f;
	if (prot != RTL4_A_PROTOCOL_RTL8366RB) {
		netdev_err(dev, "unknown realtek protocol 0x%01x\n", prot);
		return NULL;
	}
	port = protport & 0xff;

	skb->dev = dsa_master_find_slave(dev, 0, port);
	if (!skb->dev) {
		netdev_dbg(dev, "could not find slave for port %d\n", port);
		return NULL;
	}

	/* Remove RTL4 tag and recalculate checksum */
	skb_pull_rcsum(skb, RTL4_A_HDR_LEN);

	/* Move ethernet DA and SA in front of the data */
	memmove(skb->data - ETH_HLEN,
		skb->data - ETH_HLEN - RTL4_A_HDR_LEN,
		2 * ETH_ALEN);

	skb->offload_fwd_mark = 1;

	return skb;
}

static const struct dsa_device_ops rtl4a_netdev_ops = {
	.name	= "rtl4a",
	.proto	= DSA_TAG_PROTO_RTL4_A,
	.xmit	= rtl4a_tag_xmit,
	.rcv	= rtl4a_tag_rcv,
	.overhead = RTL4_A_HDR_LEN,
};
module_dsa_tag_driver(rtl4a_netdev_ops);

MODULE_LICENSE("GPL");
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL4_A);