/*
* Marvell 88E6060 switch driver
- * Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License v2 as published by the
#define MVSWITCH_MAGIC 0x88E6060
struct mvswitch_priv {
- const struct net_device_ops *ndo_old;
- struct net_device_ops ndo;
- struct vlan_group *grp;
+ netdev_features_t orig_features;
u8 vlans[16];
};
}
-static int
-mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev)
+static struct sk_buff *
+mvswitch_mangle_tx(struct net_device *dev, struct sk_buff *skb)
{
struct mvswitch_priv *priv;
char *buf = NULL;
));
#endif
- return priv->ndo_old->ndo_start_xmit(skb, dev);
+ return skb;
error_expand:
if (net_ratelimit())
error:
/* any errors? drop the packet! */
dev_kfree_skb_any(skb);
- return 0;
+ return NULL;
}
-static int
-mvswitch_mangle_rx(struct sk_buff *skb, int napi)
+static void
+mvswitch_mangle_rx(struct net_device *dev, struct sk_buff *skb)
{
struct mvswitch_priv *priv;
- struct net_device *dev;
- int vlan = -1;
unsigned char *buf;
+ int vlan = -1;
int i;
- dev = skb->dev;
- if (!dev)
- goto error;
-
priv = dev->phy_ptr;
- if (!priv)
- goto error;
-
- if (!priv->grp)
- goto error;
+ if (WARN_ON_ONCE(!priv))
+ return;
#ifdef HEADER_MODE
buf = skb->data;
#else
buf = skb->data + skb->len - MV_TRAILER_SIZE;
if (buf[0] != 0x80)
- goto error;
+ return;
#endif
/* look for the vlan matching the incoming port */
}
if (vlan == -1)
- goto error;
+ return;
- skb->protocol = eth_type_trans(skb, skb->dev);
-
- if (napi)
- return vlan_hwaccel_receive_skb(skb, priv->grp, vlan);
- else
- return vlan_hwaccel_rx(skb, priv->grp, vlan);
-
-error:
- /* no vlan? eat the packet! */
- dev_kfree_skb_any(skb);
- return 0;
-}
-
-
-static int
-mvswitch_netif_rx(struct sk_buff *skb)
-{
- return mvswitch_mangle_rx(skb, 0);
-}
-
-static int
-mvswitch_netif_receive_skb(struct sk_buff *skb)
-{
- return mvswitch_mangle_rx(skb, 1);
-}
-
-
-static void
-mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
-{
- struct mvswitch_priv *priv = dev->phy_ptr;
- priv->grp = grp;
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
}
MV_SWITCHCTL_DROP
);
- /* hook into the tx function */
- priv->ndo_old = dev->netdev_ops;
- memcpy(&priv->ndo, priv->ndo_old, sizeof(struct net_device_ops));
- priv->ndo.ndo_start_xmit = mvswitch_mangle_tx;
- priv->ndo.ndo_vlan_rx_register = mvswitch_vlan_rx_register;
- dev->netdev_ops = &priv->ndo;
+ dev->eth_mangle_rx = mvswitch_mangle_rx;
+ dev->eth_mangle_tx = mvswitch_mangle_tx;
+ priv->orig_features = dev->features;
- pdev->pkt_align = 2;
- pdev->netif_receive_skb = mvswitch_netif_receive_skb;
- pdev->netif_rx = mvswitch_netif_rx;
#ifdef HEADER_MODE
- dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
+ dev->priv_flags |= IFF_NO_IP_ALIGN;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX;
#else
- dev->features |= NETIF_F_HW_VLAN_RX;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
#endif
return 0;
}
static void
-mvswitch_remove(struct phy_device *pdev)
+mvswitch_detach(struct phy_device *pdev)
{
struct mvswitch_priv *priv = to_mvsw(pdev);
struct net_device *dev = pdev->attached_dev;
- /* restore old netdev ops */
- if (priv->ndo_old && dev)
- dev->netdev_ops = priv->ndo_old;
+ if (!dev)
+ return;
+
dev->phy_ptr = NULL;
- dev->features &= ~NETIF_F_HW_VLAN_RX;
+ dev->eth_mangle_rx = NULL;
+ dev->eth_mangle_tx = NULL;
+ dev->features = priv->orig_features;
+ dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+}
+
+static void
+mvswitch_remove(struct phy_device *pdev)
+{
+ struct mvswitch_priv *priv = to_mvsw(pdev);
+
kfree(priv);
}
.features = PHY_BASIC_FEATURES,
.probe = &mvswitch_probe,
.remove = &mvswitch_remove,
+ .detach = &mvswitch_detach,
.config_init = &mvswitch_config_init,
.config_aneg = &mvswitch_config_aneg,
.read_status = &mvswitch_read_status,