/* default RSSI threshold for scan results if none specified. */
 #define NL80211_SCAN_RSSI_THOLD_OFF            -300
 
+#define NL80211_CQM_TXE_MAX_INTVL              1800
+
 /**
  * enum nl80211_iftype - (virtual) interface types
  *
  * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
  * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
  *     consecutive packets were not acknowledged by the peer
+ * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures
+ *     during the given %NL80211_ATTR_CQM_TXE_INTVL before an
+ *     %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and
+ *     %NL80211_ATTR_CQM_TXE_PKTS is generated.
+ * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given
+ *     %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is
+ *     checked.
+ * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic
+ *     interval in which %NL80211_ATTR_CQM_TXE_PKTS and
+ *     %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
+ *     %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
        NL80211_ATTR_CQM_RSSI_HYST,
        NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
        NL80211_ATTR_CQM_PKT_LOSS_EVENT,
+       NL80211_ATTR_CQM_TXE_RATE,
+       NL80211_ATTR_CQM_TXE_PKTS,
+       NL80211_ATTR_CQM_TXE_INTVL,
 
        /* keep last */
        __NL80211_ATTR_CQM_AFTER_LAST,
 
  * @set_power_mgmt: Configure WLAN power management. A timeout value of -1
  *     allows the driver to adjust the dynamic ps timeout value.
  * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
+ * @set_cqm_txe_config: Configure connection quality monitor TX error
+ *     thresholds.
  * @sched_scan_start: Tell the driver to start a scheduled scan.
  * @sched_scan_stop: Tell the driver to stop an ongoing scheduled
  *     scan.  The driver_initiated flag specifies whether the driver
                                       struct net_device *dev,
                                       s32 rssi_thold, u32 rssi_hyst);
 
+       int     (*set_cqm_txe_config)(struct wiphy *wiphy,
+                                     struct net_device *dev,
+                                     u32 rate, u32 pkts, u32 intvl);
+
        void    (*mgmt_frame_register)(struct wiphy *wiphy,
                                       struct wireless_dev *wdev,
                                       u16 frame_type, bool reg);
 void cfg80211_cqm_pktloss_notify(struct net_device *dev,
                                 const u8 *peer, u32 num_packets, gfp_t gfp);
 
+/**
+ * cfg80211_cqm_txe_notify - TX error rate event
+ * @dev: network device
+ * @peer: peer's MAC address
+ * @num_packets: how many packets were lost
+ * @rate: % of packets which failed transmission
+ * @intvl: interval (in s) over which the TX failure threshold was breached.
+ * @gfp: context flags
+ *
+ * Notify userspace when configured % TX failures over number of packets in a
+ * given interval is exceeded.
+ */
+void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
+                            u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
+
 /**
  * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
  * @dev: network device
 
 }
 EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
 
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+                            const u8 *peer, u32 num_packets,
+                            u32 rate, u32 intvl, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       nl80211_send_cqm_txe_notify(rdev, dev, peer, num_packets,
+                                   rate, intvl, gfp);
+}
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+
 void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
                               const u8 *replay_ctr, gfp_t gfp)
 {
 
        [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
        [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
        [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+       [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
+       [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
+       [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
 };
 
+static int nl80211_set_cqm_txe(struct genl_info *info,
+                               u32 rate, u32 pkts, u32 intvl)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev;
+       struct net_device *dev = info->user_ptr[1];
+
+       if ((rate < 0 || rate > 100) ||
+           (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL))
+               return -EINVAL;
+
+       wdev = dev->ieee80211_ptr;
+
+       if (!rdev->ops->set_cqm_txe_config)
+               return -EOPNOTSUPP;
+
+       if (wdev->iftype != NL80211_IFTYPE_STATION &&
+           wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
+               return -EOPNOTSUPP;
+
+       return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev,
+                                            rate, pkts, intvl);
+}
+
 static int nl80211_set_cqm_rssi(struct genl_info *info,
                                s32 threshold, u32 hysteresis)
 {
                threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
                hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
                err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
+       } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
+                  attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
+                  attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
+               u32 rate, pkts, intvl;
+               rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
+               pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
+               intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
+               err = nl80211_set_cqm_txe(info, rate, pkts, intvl);
        } else
                err = -EINVAL;
 
        nlmsg_free(msg);
 }
 
+void
+nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
+                           struct net_device *netdev, const u8 *peer,
+                           u32 num_packets, u32 rate, u32 intvl, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       struct nlattr *pinfoattr;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
+               goto nla_put_failure;
+
+       pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
+       if (!pinfoattr)
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, pinfoattr);
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
 void
 nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
                                struct net_device *netdev, const u8 *peer,
 
                                struct net_device *netdev, const u8 *peer,
                                u32 num_packets, gfp_t gfp);
 
+void
+nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
+                           struct net_device *netdev, const u8 *peer,
+                           u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
+
 void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
                              struct net_device *netdev, const u8 *bssid,
                              const u8 *replay_ctr, gfp_t gfp);