net: usb: aqc111: Implement TX data path
authorDmitry Bezrukov <dmitry.bezrukov@aquantia.com>
Mon, 26 Nov 2018 09:33:16 +0000 (09:33 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 27 Nov 2018 23:46:06 +0000 (15:46 -0800)
Signed-off-by: Dmitry Bezrukov <dmitry.bezrukov@aquantia.com>
Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/usb/aqc111.c
drivers/net/usb/aqc111.h

index 390ed6c..32a2de7 100644 (file)
@@ -222,6 +222,9 @@ static int aqc111_set_mac_addr(struct net_device *net, void *p)
 static const struct net_device_ops aqc111_netdev_ops = {
        .ndo_open               = usbnet_open,
        .ndo_stop               = usbnet_stop,
+       .ndo_start_xmit         = usbnet_start_xmit,
+       .ndo_tx_timeout         = usbnet_tx_timeout,
+       .ndo_get_stats64        = usbnet_get_stats64,
        .ndo_set_mac_address    = aqc111_set_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
 };
@@ -290,8 +293,19 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
                goto out;
 
        ether_addr_copy(dev->net->dev_addr, dev->net->perm_addr);
+
+       /* Set TX needed headroom & tailroom */
+       dev->net->needed_headroom += sizeof(u64);
+       dev->net->needed_tailroom += sizeof(u64);
+
        dev->net->netdev_ops = &aqc111_netdev_ops;
 
+       if (usb_device_no_sg_constraint(dev->udev))
+               dev->can_dma_sg = 1;
+
+       dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
+       dev->net->features |= AQ_SUPPORT_FEATURE;
+
        aqc111_read_fw_version(dev, aqc111_data);
        aqc111_data->autoneg = AUTONEG_ENABLE;
        aqc111_data->advertised_speed = (usb_speed == USB_SPEED_SUPER) ?
@@ -505,6 +519,12 @@ static int aqc111_reset(struct usbnet *dev)
        struct aqc111_data *aqc111_data = dev->driver_priv;
        u8 reg8 = 0;
 
+       if (usb_device_no_sg_constraint(dev->udev))
+               dev->can_dma_sg = 1;
+
+       dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
+       dev->net->features |= AQ_SUPPORT_FEATURE;
+
        /* Power up ethernet PHY */
        aqc111_data->phy_cfg = AQ_PHY_POWER_EN;
        aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
@@ -558,6 +578,55 @@ static int aqc111_stop(struct usbnet *dev)
        return 0;
 }
 
+static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+                                      gfp_t flags)
+{
+       int frame_size = dev->maxpacket;
+       struct sk_buff *new_skb = NULL;
+       u64 *tx_desc_ptr = NULL;
+       int padding_size = 0;
+       int headroom = 0;
+       int tailroom = 0;
+       u64 tx_desc = 0;
+
+       /*Length of actual data*/
+       tx_desc |= skb->len & AQ_TX_DESC_LEN_MASK;
+
+       headroom = (skb->len + sizeof(tx_desc)) % 8;
+       if (headroom != 0)
+               padding_size = 8 - headroom;
+
+       if (((skb->len + sizeof(tx_desc) + padding_size) % frame_size) == 0) {
+               padding_size += 8;
+               tx_desc |= AQ_TX_DESC_DROP_PADD;
+       }
+
+       if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) &&
+           skb_linearize(skb))
+               return NULL;
+
+       headroom = skb_headroom(skb);
+       tailroom = skb_tailroom(skb);
+
+       if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) {
+               new_skb = skb_copy_expand(skb, sizeof(tx_desc),
+                                         padding_size, flags);
+               dev_kfree_skb_any(skb);
+               skb = new_skb;
+               if (!skb)
+                       return NULL;
+       }
+       if (padding_size != 0)
+               skb_put_zero(skb, padding_size);
+       /* Copy TX header */
+       tx_desc_ptr = skb_push(skb, sizeof(tx_desc));
+       *tx_desc_ptr = cpu_to_le64(tx_desc);
+
+       usbnet_set_skb_tx_stats(skb, 1, 0);
+
+       return skb;
+}
+
 static const struct driver_info aqc111_info = {
        .description    = "Aquantia AQtion USB to 5GbE Controller",
        .bind           = aqc111_bind,
@@ -566,6 +635,9 @@ static const struct driver_info aqc111_info = {
        .link_reset     = aqc111_link_reset,
        .reset          = aqc111_reset,
        .stop           = aqc111_stop,
+       .flags          = FLAG_ETHER | FLAG_FRAMING_AX |
+                         FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+       .tx_fixup       = aqc111_tx_fixup,
 };
 
 #define AQC111_USB_ETH_DEV(vid, pid, table) \
index 0c8e1ee..c09f188 100644 (file)
 #define AQ_USB_PHY_SET_TIMEOUT         10000
 #define AQ_USB_SET_TIMEOUT             4000
 
+/* Feature. ********************************************/
+#define AQ_SUPPORT_FEATURE     (NETIF_F_SG)
+#define AQ_SUPPORT_HW_FEATURE  (NETIF_F_SG)
+
 /* SFR Reg. ********************************************/
 
 #define SFR_GENERAL_STATUS             0x03
@@ -149,6 +153,10 @@ struct aqc111_data {
 #define AQ_INT_SPEED_1G                0x0011
 #define AQ_INT_SPEED_100M      0x0013
 
+/* TX Descriptor */
+#define AQ_TX_DESC_LEN_MASK    0x1FFFFF
+#define AQ_TX_DESC_DROP_PADD   BIT(28)
+
 static struct {
        unsigned char ctrl;
        unsigned char timer_l;