diff --git a/drivers/net/wireless/ath/ath10k/core.c
b/drivers/net/wireless/ath/ath10k/core.c
index bcccae1..d31597d 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -31,16 +31,22 @@
 #include "wmi-ops.h"

 unsigned int ath10k_debug_mask;
+bool ath10k_modparam_nohwcrypt;
+bool ath10k_modparam_rawtxrx;
 static bool uart_print;
 static bool skip_otp;

 module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
+module_param_named(nohwcrypt, ath10k_modparam_nohwcrypt, bool, 0644);
+module_param_named(rawtxrx, ath10k_modparam_rawtxrx, bool, 0644);
 module_param(uart_print, bool, 0644);
 module_param(skip_otp, bool, 0644);

 MODULE_PARM_DESC(debug_mask, "Debugging mask");
 MODULE_PARM_DESC(uart_print, "Uart target debugging");
 MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
+MODULE_PARM_DESC(nohwcrypt, "Disable hw crypto globally");
+MODULE_PARM_DESC(rxwtxrx, "Use raw encap/decap mode");

 static const struct ath10k_hw_params ath10k_hw_params_list[] = {
  {
@@ -990,6 +996,12 @@ static int
ath10k_core_init_firmware_features(struct ath10k *ar)
  return -EINVAL;
  }

+ if ((ath10k_modparam_rawtxrx || ath10k_modparam_nohwcrypt) &&
+    !test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) {
+ ath10k_err(ar, "rawtxrx mode supported only in 10.2+ firmware");
+ return -EINVAL;
+ }
+
  /* Backwards compatibility for firmwares without
  * ATH10K_FW_IE_WMI_OP_VERSION.
  */
diff --git a/drivers/net/wireless/ath/ath10k/core.h
b/drivers/net/wireless/ath/ath10k/core.h
index 827b3d7..1e7a5ca 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -66,6 +66,9 @@

 struct ath10k;

+extern bool ath10k_modparam_nohwcrypt;
+extern bool ath10k_modparam_rawtxrx;
+
 enum ath10k_bus {
  ATH10K_BUS_PCI,
 };
@@ -91,6 +94,7 @@ struct ath10k_skb_cb {
  u8 tid;
  u16 freq;
  bool is_offchan;
+ bool nohwcrypt;
  struct ath10k_htt_txbuf *txbuf;
  u32 txbuf_paddr;
  } __packed htt;
@@ -349,6 +353,7 @@ struct ath10k_vif {
  } u;

  bool use_cts_prot;
+ bool nohwcrypt;
  int num_legacy_stations;
  int txpower;
  struct wmi_wmm_params_all_arg wmm_params;
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c
b/drivers/net/wireless/ath/ath10k/htt_rx.c
index b26e32f..29ec1ec 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -1012,8 +1012,7 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,

  /* In most cases this will be true for sniffed frames. It makes sense
  * to deliver them as-is without stripping the crypto param. This would
- * also make sense for software based decryption (which is not
- * implemented in ath10k).
+ * also make sense for software based decryption.
  *
  * If there's no error then the frame is decrypted. At least that is
  * the case for frames that come in via fragmented rx indication.
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c
b/drivers/net/wireless/ath/ath10k/htt_tx.c
index a60ef7d..18446d9 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -508,6 +508,9 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct
sk_buff *msdu)
  prefetch_len);
  skb_cb->htt.txbuf->htc_hdr.flags = 0;

+ if (skb_cb->htt.nohwcrypt)
+ flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
+
  if (!skb_cb->is_protected)
  flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;

diff --git a/drivers/net/wireless/ath/ath10k/hw.h
b/drivers/net/wireless/ath/ath10k/hw.h
index 89e09cb..3d3bb9e 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -293,7 +293,7 @@ enum ath10k_hw_rate_cck {
 #define TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1
 #define TARGET_10X_VOW_CONFIG 0
 #define TARGET_10X_NUM_MSDU_DESC (1024 + 400)
-#define TARGET_10X_MAX_FRAG_ENTRIES 0
+#define TARGET_10X_MAX_FRAG_ENTRIES 10

 /* 10.2 parameters */
 #define TARGET_10_2_DMA_BURST_SIZE 1
diff --git a/drivers/net/wireless/ath/ath10k/mac.c
b/drivers/net/wireless/ath/ath10k/mac.c
index 425dbe2..8f34e79 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -172,6 +172,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
  .key_flags = flags,
  .macaddr = macaddr,
  };
+ int ret;

  lockdep_assert_held(&arvif->ar->conf_mutex);

@@ -184,6 +185,9 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
  arg.key_cipher = WMI_CIPHER_TKIP;
  arg.key_txmic_len = 8;
  arg.key_rxmic_len = 8;
+ if (ath10k_modparam_rawtxrx)
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+
  break;
  case WLAN_CIPHER_SUITE_WEP40:
  case WLAN_CIPHER_SUITE_WEP104:
@@ -197,12 +201,24 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
  return -EOPNOTSUPP;
  }

+ if (ath10k_modparam_rawtxrx) {
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
+ }
+
+ if (arvif->nohwcrypt)
+ cmd = DISABLE_KEY;
+
  if (cmd == DISABLE_KEY) {
  arg.key_cipher = WMI_CIPHER_NONE;
  arg.key_data = NULL;
  }

- return ath10k_wmi_vdev_install_key(arvif->ar, &arg);
+ ret = ath10k_wmi_vdev_install_key(arvif->ar, &arg);
+
+ if (arvif->nohwcrypt && !ret)
+ return -EOPNOTSUPP;
+ return ret;
 }

 static int ath10k_install_key(struct ath10k_vif *arvif,
@@ -3192,9 +3208,24 @@ ath10k_tx_h_get_txmode(struct ath10k *ar,
struct ieee80211_vif *vif,
  if (ieee80211_is_data_present(fc) && sta && sta->tdls)
  return ATH10K_HW_TXRX_ETHERNET;

+ if (ath10k_modparam_rawtxrx)
+ return ATH10K_HW_TXRX_RAW;
+
  return ATH10K_HW_TXRX_NATIVE_WIFI;
 }

+static bool ath10k_tx_h_use_hwcrypto(struct ieee80211_vif *vif,
+     struct sk_buff *skb) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ const u32 mask = IEEE80211_TX_INTFL_DONT_ENCRYPT |
+ IEEE80211_TX_CTL_INJECTED;
+ if ((info->flags & mask) == mask)
+ return false;
+ if (vif)
+ return !ath10k_vif_to_arvif(vif)->nohwcrypt;
+ return true;
+}
+
 /* HTT Tx uses Native Wifi tx mode which expects 802.11 frames without QoS
  * Control in the header.
  */
@@ -3640,6 +3671,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
  ATH10K_SKB_CB(skb)->htt.is_offchan = false;
  ATH10K_SKB_CB(skb)->htt.freq = 0;
  ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr);
+ ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb);
  ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif);
  ATH10K_SKB_CB(skb)->txmode = ath10k_tx_h_get_txmode(ar, vif, sta, skb);
  ATH10K_SKB_CB(skb)->is_protected = ieee80211_has_protected(fc);
@@ -3655,9 +3687,8 @@ static void ath10k_tx(struct ieee80211_hw *hw,
  ath10k_tx_h_8023(skb);
  break;
  case ATH10K_HW_TXRX_RAW:
- /* FIXME: Packet injection isn't implemented. It should be
- * doable with firmware 10.2 on qca988x.
- */
+ if (ath10k_modparam_rawtxrx)
+ break;
  WARN_ON_ONCE(1);
  ieee80211_free_txskb(hw, skb);
  return;
@@ -4154,6 +4185,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
  int bit;
  int i;
  u32 vdev_param;
+ u32 param_value;

  vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;

@@ -4257,6 +4289,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
  }
  }

+ if (ath10k_modparam_nohwcrypt || vif->sw_crypto)
+ arvif->nohwcrypt = true;
+
+ if (arvif->nohwcrypt && !ath10k_modparam_rawtxrx) {
+ ath10k_warn(ar, "rawtxrx module param needed for sw crypto\n");
+ goto err;
+ }
+
  ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev create %d (add interface)
type %d subtype %d bcnmode %s\n",
    arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype,
    arvif->beacon_buf ? "single-buf" : "per-skb");
@@ -4285,8 +4325,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
  arvif->def_wep_key_idx = -1;

  vdev_param = ar->wmi.vdev_param->tx_encap_type;
+ if (ath10k_modparam_rawtxrx)
+ param_value = ATH10K_HW_TXRX_RAW;
+ else
+ param_value = ATH10K_HW_TXRX_NATIVE_WIFI;
+
  ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
- ATH10K_HW_TXRX_NATIVE_WIFI);
+ param_value);
+
  /* 10.X firmware does not support this VDEV parameter. Do not warn */
  if (ret && ret != -EOPNOTSUPP) {
  ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n",
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c
b/drivers/net/wireless/ath/ath10k/wmi.c
index ebaa096..d7e557b 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -507,7 +507,7 @@ static struct wmi_vdev_param_map wmi_10x_vdev_param_map = {
  .txbf = WMI_VDEV_PARAM_UNSUPPORTED,
  .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED,
  .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED,
- .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED,
+ .tx_encap_type = WMI_10X_VDEV_PARAM_TX_ENCAP_TYPE,
  .ap_detect_out_of_sync_sleeping_sta_time_secs =
  WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
 };
@@ -3891,7 +3891,9 @@ static struct sk_buff
*ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
  config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
  config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI);
  config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI);
- config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE);
+ config.rx_decap_mode = __cpu_to_le32(ATH10K_HW_TXRX_NATIVE_WIFI);
+ if (ath10k_modparam_rawtxrx)
+ config.rx_decap_mode = __cpu_to_le32(ATH10K_HW_TXRX_RAW);

  config.scan_max_pending_reqs =
  __cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index f8d6813..b41c71c 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -711,6 +711,7 @@ struct cfg80211_acl_data {
  * @p2p_opp_ps: P2P opportunistic PS
  * @acl: ACL configuration used by the drivers which has support for
  * MAC address based access control
+ * @sw_crypto: use software crypto engine.*
  */
 struct cfg80211_ap_settings {
  struct cfg80211_chan_def chandef;
@@ -729,6 +730,7 @@ struct cfg80211_ap_settings {
  u8 p2p_ctwindow;
  bool p2p_opp_ps;
  const struct cfg80211_acl_data *acl;
+ int sw_crypto;
 };

 /**
@@ -1210,6 +1212,7 @@ struct mpath_info {
  * @ap_isolate: do not forward packets between connected stations
  * @ht_opmode: HT Operation mode
  * (u16 = opmode, -1 = do not change)
+ * @sw_crypto: Use software crypto instead of hardware.
  * @p2p_ctwindow: P2P CT Window (-1 = no change)
  * @p2p_opp_ps: P2P opportunistic PS (-1 = no change)
  */
@@ -1221,6 +1224,7 @@ struct bss_parameters {
  u8 basic_rates_len;
  int ap_isolate;
  int ht_opmode;
+ int sw_crypto;
  s8 p2p_ctwindow, p2p_opp_ps;
 };

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 3314298..5f45d02 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1344,6 +1344,8 @@ enum ieee80211_vif_flags {
  * @csa_active: marks whether a channel switch is going on. Internally it is
  * write-protected by sdata_lock and local->mtx so holding either is fine
  * for read access.
+ * @sw_crypto: indicates frames on the BSS should be encrypted by software
+ * and bypass hardware crypto engine.
  * @driver_flags: flags/capabilities the driver has for this interface,
  * these need to be set (or cleared) when the interface is added
  * or, if supported by the driver, the interface type is changed
@@ -1368,6 +1370,7 @@ struct ieee80211_vif {
  u8 addr[ETH_ALEN];
  bool p2p;
  bool csa_active;
+ bool sw_crypto;

  u8 cab_queue;
  u8 hw_queue[IEEE80211_NUM_ACS];
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 241220c..cdfa8a8 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1761,6 +1761,9 @@ enum nl80211_commands {
  * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
  *      is operating in an indoor environment.
  *
+ * @NL80211_ATTR_SW_CRYPTO: use software crypto instead of hardware crypto for
+ * the BSS.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2130,6 +2133,8 @@ enum nl80211_attrs {

  NL80211_ATTR_REG_INDOOR,

+ NL80211_ATTR_SW_CRYPTO,
+
  /* add attributes here, update the policy in nl80211.c */

  __NL80211_ATTR_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 72a0178..a50c9226 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1889,6 +1889,13 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
  sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
  }

+ if (params->sw_crypto >= 0) {
+ if (params->sw_crypto)
+ sdata->vif.sw_crypto = true;
+ else
+ sdata->vif.sw_crypto = false;
+ }
+
  if (params->ht_opmode >= 0) {
  sdata->vif.bss_conf.ht_operation_mode =
  (u16) params->ht_opmode;
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 9d63d93..b56f31a 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -192,7 +192,9 @@ static int tkip_encrypt_skb(struct
ieee80211_tx_data *tx, struct sk_buff *skb)

  if (info->control.hw_key &&
     !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
-    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
+    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
+    !(info->control.hw_key->flags &
+      IEEE80211_KEY_FLAG_RESERVE_TAILROOM)) {
  /* hwaccel - with no need for software-generated IV */
  return 0;
  }
@@ -200,7 +202,8 @@ static int tkip_encrypt_skb(struct
ieee80211_tx_data *tx, struct sk_buff *skb)
  hdrlen = ieee80211_hdrlen(hdr->frame_control);
  len = skb->len - hdrlen;

- if (info->control.hw_key)
+ if (info->control.hw_key && !(info->control.hw_key->flags &
+      IEEE80211_KEY_FLAG_RESERVE_TAILROOM))
  tail = 0;
  else
  tail = IEEE80211_TKIP_ICV_LEN;
@@ -227,8 +230,12 @@ static int tkip_encrypt_skb(struct
ieee80211_tx_data *tx, struct sk_buff *skb)
  spin_unlock(&key->u.tkip.txlock);

  /* hwaccel - with software IV */
- if (info->control.hw_key)
+ if (info->control.hw_key) {
+ if (info->control.hw_key->flags &
+    IEEE80211_KEY_FLAG_RESERVE_TAILROOM)
+ skb_put(skb, tail);
  return 0;
+ }

  /* Add room for ICV */
  skb_put(skb, IEEE80211_TKIP_ICV_LEN);
@@ -411,6 +418,8 @@ static int ccmp_encrypt_skb(struct
ieee80211_tx_data *tx, struct sk_buff *skb,
  if (info->control.hw_key &&
     !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
     !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
+    !(info->control.hw_key->flags &
+       IEEE80211_KEY_FLAG_RESERVE_TAILROOM) &&
     !((info->control.hw_key->flags &
        IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
       ieee80211_is_mgmt(hdr->frame_control))) {
@@ -424,7 +433,8 @@ static int ccmp_encrypt_skb(struct
ieee80211_tx_data *tx, struct sk_buff *skb,
  hdrlen = ieee80211_hdrlen(hdr->frame_control);
  len = skb->len - hdrlen;

- if (info->control.hw_key)
+ if (info->control.hw_key && !(info->control.hw_key->flags &
+      IEEE80211_KEY_FLAG_RESERVE_TAILROOM))
  tail = 0;
  else
  tail = mic_len;
@@ -456,8 +466,12 @@ static int ccmp_encrypt_skb(struct
ieee80211_tx_data *tx, struct sk_buff *skb,
  ccmp_pn2hdr(pos, pn, key->conf.keyidx);

  /* hwaccel - with software CCMP header */
- if (info->control.hw_key)
+ if (info->control.hw_key) {
+ if (info->control.hw_key->flags &
+    IEEE80211_KEY_FLAG_RESERVE_TAILROOM)
+ skb_put(skb, tail);
  return 0;
+ }

  pos += IEEE80211_CCMP_HDR_LEN;
  ccmp_special_blocks(skb, pn, b_0, aad);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 8a33bba..c2dea0e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4851,6 +4851,7 @@ static int nl80211_set_bss(struct sk_buff *skb,
struct genl_info *info)
  params.use_short_preamble = -1;
  params.use_short_slot_time = -1;
  params.ap_isolate = -1;
+ params.sw_crypto = -1;
  params.ht_opmode = -1;
  params.p2p_ctwindow = -1;
  params.p2p_opp_ps = -1;
@@ -4901,6 +4902,10 @@ static int nl80211_set_bss(struct sk_buff *skb,
struct genl_info *info)
     !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
  return -EINVAL;
  }
+ if (info->attrs[NL80211_ATTR_SW_CRYPTO]) {
+ params.sw_crypto =
+    !!nla_get_u8(info->attrs[NL80211_ATTR_SW_CRYPTO]);
+ }

  if (!rdev->ops->change_bss)
  return -EOPNOTSUPP;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index af3617c..c411a20 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -986,6 +986,7 @@ TRACE_EVENT(rdev_change_bss,
  __field(int, use_short_preamble)
  __field(int, use_short_slot_time)
  __field(int, ap_isolate)
+ __field(int, sw_crypto)
  __field(int, ht_opmode)
  ),
  TP_fast_assign(
@@ -995,14 +996,16 @@ TRACE_EVENT(rdev_change_bss,
  __entry->use_short_preamble = params->use_short_preamble;
  __entry->use_short_slot_time = params->use_short_slot_time;
  __entry->ap_isolate = params->ap_isolate;
+ __entry->sw_crypto = params->sw_crypto;
  __entry->ht_opmode = params->ht_opmode;
  ),
  TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", use cts prot: %d, "
   "use short preamble: %d, use short slot time: %d, "
-  "ap isolate: %d, ht opmode: %d",
+  "ap isolate: %d, sw_crypto: %d, ht opmode: %d",
   WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->use_cts_prot,
   __entry->use_short_preamble, __entry->use_short_slot_time,
-  __entry->ap_isolate, __entry->ht_opmode)
+  __entry->ap_isolate, __entry->sw_crypto,
+  __entry->ht_opmode)
 );

 TRACE_EVENT(rdev_set_txq_params,
