#include <linux/dsa/ocelot.h>
#include "tag.h"
#define OCELOT_NAME "ocelot"
#define SEVILLE_NAME "seville"
static void ocelot_xmit_get_vlan_info(struct sk_buff *skb, struct dsa_port *dp,
u64 *vlan_tci, u64 *tag_type)
{
struct net_device *br = dsa_port_bridge_dev_get(dp);
struct vlan_ethhdr *hdr;
u16 proto, tci;
if (!br || !br_vlan_enabled(br)) {
*vlan_tci = 0;
*tag_type = IFH_TAG_TYPE_C;
return;
}
hdr = skb_vlan_eth_hdr(skb);
br_vlan_get_proto(br, &proto);
if (ntohs(hdr->h_vlan_proto) == proto) {
vlan_remove_tag(skb, &tci);
*vlan_tci = tci;
} else {
rcu_read_lock();
br_vlan_get_pvid_rcu(br, &tci);
rcu_read_unlock();
*vlan_tci = tci;
}
*tag_type = (proto != ETH_P_8021Q) ? IFH_TAG_TYPE_S : IFH_TAG_TYPE_C;
}
static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
__be32 ifh_prefix, void **ifh)
{
struct dsa_port *dp = dsa_slave_to_port(netdev);
struct dsa_switch *ds = dp->ds;
u64 vlan_tci, tag_type;
void *injection;
__be32 *prefix;
u32 rew_op = 0;
u64 qos_class;
ocelot_xmit_get_vlan_info(skb, dp, &vlan_tci, &tag_type);
qos_class = netdev_get_num_tc(netdev) ?
netdev_get_prio_tc_map(netdev, skb->priority) : skb->priority;
injection = skb_push(skb, OCELOT_TAG_LEN);
prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN);
*prefix = ifh_prefix;
memset(injection, 0, OCELOT_TAG_LEN);
ocelot_ifh_set_bypass(injection, 1);
ocelot_ifh_set_src(injection, ds->num_ports);
ocelot_ifh_set_qos_class(injection, qos_class);
ocelot_ifh_set_vlan_tci(injection, vlan_tci);
ocelot_ifh_set_tag_type(injection, tag_type);
rew_op = ocelot_ptp_rew_op(skb);
if (rew_op)
ocelot_ifh_set_rew_op(injection, rew_op);
*ifh = injection;
}
static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct dsa_port *dp = dsa_slave_to_port(netdev);
void *injection;
ocelot_xmit_common(skb, netdev, cpu_to_be32(0x8880000a), &injection);
ocelot_ifh_set_dest(injection, BIT_ULL(dp->index));
return skb;
}
static struct sk_buff *seville_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct dsa_port *dp = dsa_slave_to_port(netdev);
void *injection;
ocelot_xmit_common(skb, netdev, cpu_to_be32(0x88800005), &injection);
seville_ifh_set_dest(injection, BIT_ULL(dp->index));
return skb;
}
static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
struct net_device *netdev)
{
u64 src_port, qos_class;
u64 vlan_tci, tag_type;
u8 *start = skb->data;
struct dsa_port *dp;
u8 *extraction;
u16 vlan_tpid;
u64 rew_val;
skb_push(skb, ETH_HLEN);
skb_pull(skb, OCELOT_SHORT_PREFIX_LEN);
extraction = skb->data;
skb_pull(skb, OCELOT_TAG_LEN);
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
skb_pull(skb, ETH_HLEN);
skb_postpull_rcsum(skb, start, OCELOT_TOTAL_TAG_LEN);
ocelot_xfh_get_src_port(extraction, &src_port);
ocelot_xfh_get_qos_class(extraction, &qos_class);
ocelot_xfh_get_tag_type(extraction, &tag_type);
ocelot_xfh_get_vlan_tci(extraction, &vlan_tci);
ocelot_xfh_get_rew_val(extraction, &rew_val);
skb->dev = dsa_master_find_slave(netdev, 0, src_port);
if (!skb->dev)
return NULL;
dsa_default_offload_fwd_mark(skb);
skb->priority = qos_class;
OCELOT_SKB_CB(skb)->tstamp_lo = rew_val;
dp = dsa_slave_to_port(skb->dev);
vlan_tpid = tag_type ? ETH_P_8021AD : ETH_P_8021Q;
if (dsa_port_is_vlan_filtering(dp) &&
eth_hdr(skb)->h_proto == htons(vlan_tpid)) {
u16 dummy_vlan_tci;
skb_push_rcsum(skb, ETH_HLEN);
__skb_vlan_pop(skb, &dummy_vlan_tci);
skb_pull_rcsum(skb, ETH_HLEN);
__vlan_hwaccel_put_tag(skb, htons(vlan_tpid), vlan_tci);
}
return skb;
}
static const struct dsa_device_ops ocelot_netdev_ops = {
.name = OCELOT_NAME,
.proto = DSA_TAG_PROTO_OCELOT,
.xmit = ocelot_xmit,
.rcv = ocelot_rcv,
.needed_headroom = OCELOT_TOTAL_TAG_LEN,
.promisc_on_master = true,
};
DSA_TAG_DRIVER(ocelot_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT, OCELOT_NAME);
static const struct dsa_device_ops seville_netdev_ops = {
.name = SEVILLE_NAME,
.proto = DSA_TAG_PROTO_SEVILLE,
.xmit = seville_xmit,
.rcv = ocelot_rcv,
.needed_headroom = OCELOT_TOTAL_TAG_LEN,
.promisc_on_master = true,
};
DSA_TAG_DRIVER(seville_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SEVILLE, SEVILLE_NAME);
static struct dsa_tag_driver *ocelot_tag_driver_array[] = {
&DSA_TAG_DRIVER_NAME(ocelot_netdev_ops),
&DSA_TAG_DRIVER_NAME(seville_netdev_ops),
};
module_dsa_tag_drivers(ocelot_tag_driver_array);
MODULE_LICENSE("GPL v2"