wlcore/wl18xx: add hw op for setting Tx HW checksum
authorArik Nemtsov <arik@wizery.com>
Thu, 10 May 2012 09:13:27 +0000 (12:13 +0300)
committerLuciano Coelho <coelho@ti.com>
Tue, 5 Jun 2012 12:55:20 +0000 (15:55 +0300)
Some chip families are capable of checksumming certain classes of Tx
packets in HW. Indicate this fact in the netdev features and perform the
HW checksum by protocol type for the 18xx family.

Fix the location of the skb network header when we move it so we can
rely on it when setting the checksum.

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl18xx/acx.c
drivers/net/wireless/ti/wl18xx/acx.h
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h

index d7dd3def07b59b34fd59b8afb4eba1a686940651..8141447e8f964d2bcf5cc59d80a70ad9610a2b27 100644 (file)
@@ -1288,6 +1288,13 @@ static void wl12xx_get_mac(struct wl1271 *wl)
                wl12xx_get_fuse_mac(wl);
 }
 
+static void wl12xx_set_tx_desc_csum(struct wl1271 *wl,
+                                   struct wl1271_tx_hw_descr *desc,
+                                   struct sk_buff *skb)
+{
+       desc->wl12xx_reserved = 0;
+}
+
 static struct wlcore_ops wl12xx_ops = {
        .identify_chip          = wl12xx_identify_chip,
        .identify_fw            = wl12xx_identify_fw,
@@ -1306,6 +1313,7 @@ static struct wlcore_ops wl12xx_ops = {
        .sta_get_ap_rate_mask   = wl12xx_sta_get_ap_rate_mask,
        .get_pg_ver             = wl12xx_get_pg_ver,
        .get_mac                = wl12xx_get_mac,
+       .set_tx_desc_csum       = wl12xx_set_tx_desc_csum,
 };
 
 static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
index aed8d0dff91b7aedf543ce48d8235880f5a1da50..3379db23a165d1f1256dca926c5049ee1109cd59 100644 (file)
@@ -55,3 +55,29 @@ out:
 
        return ret;
 }
+
+int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
+{
+       struct wl18xx_acx_checksum_state *acx;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "acx checksum state");
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
+
+       ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("failed to set Tx checksum state: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
index c8eebd8500c1b41cb2cde7d46ca4cb10a7c5b20a..544db6ef384127655db581eafb48ed24b9d10c93 100644 (file)
@@ -45,8 +45,24 @@ struct wl18xx_acx_host_config_bitmap {
 
 } __packed;
 
+enum {
+       CHECKSUM_OFFLOAD_DISABLED = 0,
+       CHECKSUM_OFFLOAD_ENABLED  = 1,
+       CHECKSUM_OFFLOAD_FAKE_RX  = 2,
+       CHECKSUM_OFFLOAD_INVALID  = 0xFF
+};
+
+struct wl18xx_acx_checksum_state {
+       struct acx_header header;
+
+        /* enum acx_checksum_state */
+       u8 checksum_state;
+       u8 pad[3];
+} __packed;
+
 int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
                                  u32 sdio_blk_size, u32 extra_mem_blks,
                                  u32 len_field_size);
+int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
 
 #endif /* __WL12XX_ACX_H__ */
index 1e0719c7ccb966e852f08ad2f7bb7fc171f191bc..c47f52c81a72017503872f46d3af1cc165ad3c12 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/ip.h>
 
 #include "../wlcore/wlcore.h"
 #include "../wlcore/debug.h"
@@ -501,9 +502,38 @@ static int wl18xx_hw_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
+       ret = wl18xx_acx_set_checksum_state(wl);
+       if (ret != 0)
+               return ret;
+
        return ret;
 }
 
+static void wl18xx_set_tx_desc_csum(struct wl1271 *wl,
+                                   struct wl1271_tx_hw_descr *desc,
+                                   struct sk_buff *skb)
+{
+       u32 ip_hdr_offset;
+       struct iphdr *ip_hdr;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL) {
+               desc->wl18xx_checksum_data = 0;
+               return;
+       }
+
+       ip_hdr_offset = skb_network_header(skb) - skb_mac_header(skb);
+       if (WARN_ON(ip_hdr_offset >= (1<<7))) {
+               desc->wl18xx_checksum_data = 0;
+               return;
+       }
+
+       desc->wl18xx_checksum_data = ip_hdr_offset << 1;
+
+       /* FW is interested only in the LSB of the protocol  TCP=0 UDP=1 */
+       ip_hdr = (void *)skb_network_header(skb);
+       desc->wl18xx_checksum_data |= (ip_hdr->protocol & 0x01);
+}
+
 static struct wlcore_ops wl18xx_ops = {
        .identify_chip  = wl18xx_identify_chip,
        .boot           = wl18xx_boot,
@@ -517,6 +547,7 @@ static struct wlcore_ops wl18xx_ops = {
        .tx_immediate_compl = wl18xx_tx_immediate_completion,
        .tx_delayed_compl = NULL,
        .hw_init        = wl18xx_hw_init,
+       .set_tx_desc_csum = wl18xx_set_tx_desc_csum,
 };
 
 int __devinit wl18xx_probe(struct platform_device *pdev)
index 2d1302d23ac87946ea5c5f7b7cb9f779b4f62ff2..ff0c5ba8e8ff2459e293cb5d580b357e6ce2f52e 100644 (file)
@@ -1245,6 +1245,7 @@ enum {
        ACX_CONFIG_HANGOVER              = 0x0042,
        ACX_FEATURE_CFG                  = 0x0043,
        ACX_PROTECTION_CFG               = 0x0044,
+       ACX_CHECKSUM_CONFIG              = 0x0045,
 };
 
 
index 9384b4d56c24a9dc1386732c86d1ac74ca75832b..4573249ea89f5d60d40c881748c5974b1fb961f9 100644 (file)
@@ -119,4 +119,15 @@ static inline int wlcore_identify_fw(struct wl1271 *wl)
        return 0;
 }
 
+static inline void
+wlcore_hw_set_tx_desc_csum(struct wl1271 *wl,
+                          struct wl1271_tx_hw_descr *desc,
+                          struct sk_buff *skb)
+{
+       if (!wl->ops->set_tx_desc_csum)
+               BUG_ON(1);
+
+       wl->ops->set_tx_desc_csum(wl, desc, skb);
+}
+
 #endif
index b4e02ef22fc51b703ef1b6d7e13209e24f8e43fe..44e4e7bb29bd2f8adfb2f9b5cbf42ca55f2d9732 100644 (file)
@@ -270,6 +270,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        if (extra) {
                int hdrlen = ieee80211_hdrlen(frame_control);
                memmove(frame_start, hdr, hdrlen);
+               skb_set_network_header(skb, skb_network_offset(skb) + extra);
        }
 
        /* configure packet life time */
@@ -332,9 +333,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
            ieee80211_has_protected(frame_control))
                tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
 
-       desc->reserved = 0;
        desc->tx_attr = cpu_to_le16(tx_attr);
 
+       wlcore_hw_set_tx_desc_csum(wl, desc, skb);
        wlcore_hw_set_tx_desc_data_len(wl, desc, skb);
 }
 
index 220727421fa269943a46ebe8cfa939ead67147c0..e24c436bf65fa2e0b6c59a7abd926ba58cdb2b48 100644 (file)
@@ -130,7 +130,16 @@ struct wl1271_tx_hw_descr {
        u8 tid;
        /* host link ID (HLID) */
        u8 hlid;
-       u8 reserved;
+
+       union {
+               u8 wl12xx_reserved;
+
+               /*
+                * bit 0   -> 0 = udp, 1 = tcp
+                * bit 1:7 -> IP header offset
+                */
+               u8 wl18xx_checksum_data;
+       } __packed;
 } __packed;
 
 enum wl1271_tx_hw_res_status {
index 0b3f0b586f4bb534802b80ebf6dcb1f97e15c8a6..c062d304ab0d760c6f377875f9fa968e7760ad6e 100644 (file)
@@ -61,6 +61,9 @@ struct wlcore_ops {
                                    struct wl12xx_vif *wlvif);
        s8 (*get_pg_ver)(struct wl1271 *wl);
        void (*get_mac)(struct wl1271 *wl);
+       void (*set_tx_desc_csum)(struct wl1271 *wl,
+                                struct wl1271_tx_hw_descr *desc,
+                                struct sk_buff *skb);
 };
 
 enum wlcore_partitions {