diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 897482d..9362c59 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -28,6 +28,7 @@
 #include <linux/rtnetlink.h>
 #include <netpacket/packet.h>
 #include <linux/filter.h>
+#include <linux/errqueue.h>
 #include "nl80211_copy.h"
 
 #include "common.h"
@@ -43,6 +44,26 @@
 #include "rfkill.h"
 #include "driver.h"
 
+#ifndef SO_WIFI_STATUS
+# if defined(__sparc__)
+#  define SO_WIFI_STATUS	0x0025
+# elif defined(__parisc__)
+#  define SO_WIFI_STATUS	0x4022
+# else
+#  define SO_WIFI_STATUS	41
+# endif
+
+# define SCM_WIFI_STATUS	SO_WIFI_STATUS
+#endif
+
+#ifndef SO_EE_ORIGIN_TXSTATUS
+#define SO_EE_ORIGIN_TXSTATUS	4
+#endif
+
+#ifndef PACKET_TX_TIMESTAMP
+#define PACKET_TX_TIMESTAMP	16
+#endif
+
 #ifdef CONFIG_LIBNL20
 /* libnl 2.0 compatibility code */
 #define nl_handle nl_sock
@@ -227,6 +248,7 @@ struct wpa_driver_nl80211_data {
 	unsigned int in_interface_list:1;
 	unsigned int device_ap_sme:1;
 	unsigned int poll_command_supported:1;
+	unsigned int data_tx_status:1;
 
 	u64 remain_on_chan_cookie;
 	u64 send_action_cookie;
@@ -239,9 +261,7 @@ struct wpa_driver_nl80211_data {
 
 	struct i802_bss first_bss;
 
-#ifdef CONFIG_AP
 	int eapol_tx_sock;
-#endif /* CONFIG_AP */
 
 #ifdef HOSTAPD
 	int eapol_sock; /* socket for EAPOL frames */
@@ -1901,6 +1921,7 @@ struct wiphy_info_data {
 	unsigned int error:1;
 	unsigned int device_ap_sme:1;
 	unsigned int poll_command_supported:1;
+	unsigned int data_tx_status:1;
 };
 
 
@@ -2088,6 +2109,15 @@ broken_combination:
 	if (tb[NL80211_ATTR_DEVICE_AP_SME])
 		info->device_ap_sme = 1;
 
+#ifdef NL80211_ATTR_FEATURE_FLAGS
+	if (tb[NL80211_ATTR_FEATURE_FLAGS]) {
+		u32 flags = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]);
+
+		if (flags & NL80211_FEATURE_SK_TX_STATUS)
+			info->data_tx_status = 1;
+	}
+#endif
+
 	return NL_SKIP;
 }
 
@@ -2147,6 +2177,7 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
 
 	drv->device_ap_sme = info.device_ap_sme;
 	drv->poll_command_supported = info.poll_command_supported;
+	drv->data_tx_status = info.data_tx_status;
 
 	return 0;
 }
@@ -2286,6 +2317,66 @@ static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv)
 }
 
 
+static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, void *eloop_ctx,
+						      void *handle)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	u8 data[2048];
+	struct msghdr msg;
+	struct iovec entry;
+	struct {
+		struct cmsghdr cm;
+		char control[512];
+	} control;
+	struct cmsghdr *cmsg;
+	int res, found_ee = 0, found_wifi = 0, acked = 0;
+	union wpa_event_data event;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = &entry;
+	msg.msg_iovlen = 1;
+	entry.iov_base = data;
+	entry.iov_len = sizeof(data);
+	msg.msg_control = &control;
+	msg.msg_controllen = sizeof(control);
+
+	res = recvmsg(sock, &msg, MSG_ERRQUEUE);
+	/* if error or not fitting 802.3 header, return */
+	if (res < 14)
+		return;
+
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+		if (cmsg->cmsg_level == SOL_SOCKET &&
+		    cmsg->cmsg_type == SCM_WIFI_STATUS) {
+			int *ack;
+
+			found_wifi = 1;
+			ack = (void *)CMSG_DATA(cmsg);
+			acked = *ack;
+		}
+
+		if (cmsg->cmsg_level == SOL_PACKET &&
+		    cmsg->cmsg_type == PACKET_TX_TIMESTAMP) {
+			struct sock_extended_err *err =
+				(struct sock_extended_err *)CMSG_DATA(cmsg);
+
+			if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS)
+				found_ee = 1;
+		}
+	}
+
+	if (!found_ee || !found_wifi)
+		return;
+
+	memset(&event, 0, sizeof(event));
+	event.eapol_tx_status.dst = data;
+	event.eapol_tx_status.data = data + 14;
+	event.eapol_tx_status.data_len = res - 14;
+	event.eapol_tx_status.ack = acked;
+	wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
 /**
  * wpa_driver_nl80211_init - Initialize nl80211 driver interface
  * @ctx: context to be used when calling wpa_supplicant functions,
@@ -2336,9 +2427,24 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
 	if (wpa_driver_nl80211_finish_drv_init(drv))
 		goto failed;
 
-#ifdef CONFIG_AP
 	drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
-#endif /* CONFIG_AP */
+	if (drv->eapol_tx_sock < 0)
+		goto failed;
+
+	if (drv->data_tx_status) {
+		int enabled = 1;
+
+		if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
+			       &enabled, sizeof(enabled)) < 0) {
+			wpa_printf(MSG_DEBUG,
+				"nl80211: wifi status sockopt failed\n");
+			drv->data_tx_status = 0;
+		} else {
+			eloop_register_read_sock(drv->eapol_tx_sock,
+				wpa_driver_nl80211_handle_eapol_tx_status,
+				drv, NULL);
+		}
+	}
 
 	if (drv->global) {
 		dl_list_add(&drv->global->interfaces, &drv->list);
@@ -2555,9 +2661,9 @@ static void wpa_driver_nl80211_deinit(void *priv)
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
-#ifdef CONFIG_AP
+	if (drv->data_tx_status)
+		eloop_unregister_read_sock(drv->eapol_tx_sock);
 	close(drv->eapol_tx_sock);
-#endif /* CONFIG_AP */
 
 	if (bss->nl_preq.handle)
 		wpa_driver_nl80211_probe_req_report(bss, 0);
@@ -5140,7 +5246,6 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
 }
 
 
-#ifdef CONFIG_AP
 static int nl80211_send_eapol_data(struct i802_bss *bss,
 				   const u8 *addr, const u8 *data,
 				   size_t data_len)
@@ -5165,7 +5270,6 @@ static int nl80211_send_eapol_data(struct i802_bss *bss,
 		wpa_printf(MSG_ERROR, "nl80211 eapol tx: %s", strerror(errno));
 	return ret;
 }
-#endif /* CONFIG_AP */
 
 
 static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
@@ -5182,10 +5286,8 @@ static int wpa_driver_nl80211_hapd_send_eapol(
 	int res;
 	int qos = flags & WPA_STA_WMM;
 
-#ifdef CONFIG_AP
-	if (drv->device_ap_sme)
+	if (drv->device_ap_sme || drv->data_tx_status)
 		return nl80211_send_eapol_data(bss, addr, data, data_len);
-#endif /* CONFIG_AP */
 
 	len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
 		data_len;
