]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - build-utilities/hostap.git/blobdiff - src/drivers/driver_nl80211.c
nl80211: fix warnings
[build-utilities/hostap.git] / src / drivers / driver_nl80211.c
index 52305fce5c94a40cd637e703aa2e0a882889620e..4a04402c20e5533c1ea02c197bb78bb3691f6708 100644 (file)
@@ -1,19 +1,13 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
  * Copyright (c) 2009-2010, Atheros Communications
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -66,6 +60,9 @@
 
 #ifdef ANDROID
 #include "android_drv.h"
+
+static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+                                        size_t buf_len);
 #endif /* ANDROID */
 #ifdef CONFIG_LIBNL20
 /* libnl 2.0 compatibility code */
@@ -286,6 +283,10 @@ struct wpa_driver_nl80211_data {
        int auth_wep_tx_keyidx;
        int auth_local_state_change;
        int auth_p2p;
+#ifdef ANDROID
+       u8 wowlan_triggers;
+       u8 wowlan_enabled;
+#endif
 };
 
 
@@ -310,6 +311,11 @@ static int android_pno_start(struct i802_bss *bss,
                             struct wpa_driver_scan_params *params);
 static int android_pno_stop(struct i802_bss *bss);
 #endif /* ANDROID */
+#ifdef ANDROID_BRCM_P2P_PATCH
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+                                 enum wpa_event_type type,
+                                 const u8 *frame, size_t len);
+#endif /* ANDROID_BRCM_P2P_PATCH */
 
 #ifdef HOSTAPD
 static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
@@ -606,7 +612,7 @@ static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
 {
        struct nl80211_wiphy_data *w = eloop_ctx;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Beacon event message available");
+       wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
 
        nl_recvmsgs(handle, w->nl_cb);
 }
@@ -789,10 +795,28 @@ static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
                   del ? "removed" : "added");
 
        if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
-               if (del)
+               if (del) {
+                       if (drv->if_removed) {
+                               wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+                                          "already set - ignore event");
+                               return;
+                       }
                        drv->if_removed = 1;
-               else
+               } else {
+                       if (if_nametoindex(drv->first_bss.ifname) == 0) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Interface %s "
+                                          "does not exist - ignore "
+                                          "RTM_NEWLINK",
+                                          drv->first_bss.ifname);
+                               return;
+                       }
+                       if (!drv->if_removed) {
+                               wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+                                          "already cleared - ignore event");
+                               return;
+                       }
                        drv->if_removed = 0;
+               }
        }
 
        wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
@@ -909,6 +933,14 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
                                   "event since interface %s is down",
                                   namebuf);
+               } else if (if_nametoindex(drv->first_bss.ifname) == 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+                                  "event since interface %s does not exist",
+                                  drv->first_bss.ifname);
+               } else if (drv->if_removed) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+                                  "event since interface %s is marked "
+                                  "removed", drv->first_bss.ifname);
                } else {
                        wpa_printf(MSG_DEBUG, "nl80211: Interface up");
                        drv->if_disabled = 0;
@@ -1062,6 +1094,20 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
        u16 status;
 
        mgmt = (const struct ieee80211_mgmt *) frame;
+#if (defined (CONFIG_AP) || defined (HOSTAPD) ) && defined (ANDROID_BRCM_P2P_PATCH)
+       if (drv->nlmode == NL80211_IFTYPE_AP || drv->nlmode == NL80211_IFTYPE_P2P_GO) {
+               if (len < 24 + sizeof(mgmt->u.assoc_req)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+                          "frame");
+                       return;
+               }
+               os_memset(&event, 0, sizeof(event));
+               event.assoc_info.freq = drv->assoc_freq;
+               event.assoc_info.req_ies = (u8 *) mgmt->u.assoc_req.variable;
+               event.assoc_info.req_ies_len = len - 24 - sizeof(mgmt->u.assoc_req);
+               event.assoc_info.addr = mgmt->sa;
+       } else {
+#endif
        if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
                wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
                           "frame");
@@ -1095,6 +1141,9 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
        }
 
        event.assoc_info.freq = drv->assoc_freq;
+#if (defined (CONFIG_AP) || defined(HOSTAPD)) && defined (ANDROID_BRCM_P2P_PATCH)
+       }
+#endif
 
        wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
@@ -1151,7 +1200,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
 
 
 static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
-                                 struct nlattr *reason, struct nlattr *addr)
+                                 struct nlattr *reason, struct nlattr *addr,
+                                 struct nlattr *by_ap)
 {
        union wpa_event_data data;
 
@@ -1169,6 +1219,7 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
        os_memset(&data, 0, sizeof(data));
        if (reason)
                data.disassoc_info.reason_code = nla_get_u16(reason);
+       data.disassoc_info.locally_generated = by_ap == NULL;
        wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
 }
 
@@ -1227,6 +1278,14 @@ static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
                event.rx_action.data = &mgmt->u.action.category + 1;
                event.rx_action.len = frame + len - event.rx_action.data;
                wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+#ifdef ANDROID_BRCM_P2P_PATCH
+       } else if (stype == WLAN_FC_STYPE_ASSOC_REQ) {
+               mlme_event_assoc(drv, frame, len);
+       } else if (stype == WLAN_FC_STYPE_DISASSOC) {
+               mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, frame, len);
+       } else if (stype == WLAN_FC_STYPE_DEAUTH) {
+               mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, frame, len);
+#endif /* ANDROID_BRCM_P2P_PATCH */
        } else {
                event.rx_mgmt.frame = frame;
                event.rx_mgmt.frame_len = len;
@@ -1309,6 +1368,13 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
                reason_code = le_to_host16(mgmt->u.deauth.reason_code);
 
        if (type == EVENT_DISASSOC) {
+               event.disassoc_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
+#ifdef ANDROID_BRCM_P2P_PATCH
+               if (is_ap_interface(drv->nlmode)) {
+                       event.disassoc_info.addr = mgmt->sa;
+               } else
+#endif /* ANDROID_BRCM_P2P_PATCH */
                event.disassoc_info.addr = bssid;
                event.disassoc_info.reason_code = reason_code;
                if (frame + len > mgmt->u.disassoc.variable) {
@@ -1317,6 +1383,13 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
                                mgmt->u.disassoc.variable;
                }
        } else {
+               event.deauth_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
+#ifdef ANDROID_BRCM_P2P_PATCH
+               if (is_ap_interface(drv->nlmode)) {
+                       event.deauth_info.addr = mgmt->sa;
+               } else
+#endif /* ANDROID_BRCM_P2P_PATCH */
                event.deauth_info.addr = bssid;
                event.deauth_info.reason_code = reason_code;
                if (frame + len > mgmt->u.deauth.variable) {
@@ -1827,6 +1900,10 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
                wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
                           "event: RSSI low");
                ed.signal_change.above_threshold = 0;
+       } else if (event == NL80211_CQM_RSSI_BEACON_LOSS) {
+               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+                          "event: beacon loss!");
+               wpa_supplicant_event(drv->ctx, EVENT_START_ROAMING, &ed);
        } else
                return;
 
@@ -1849,6 +1926,25 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void nl80211_roaming_support_event(struct wpa_driver_nl80211_data *drv,
+                                         struct nlattr *tb[])
+{
+       int enabled;
+       enum wpa_event_type event;
+
+       enabled = (tb[NL80211_ATTR_ROAMING_DISABLED] == NULL);
+
+       if (enabled)
+               event = EVENT_ROAMING_ENABLED;
+       else
+               event = EVENT_ROAMING_DISABLED;
+
+       wpa_printf(MSG_DEBUG, "nl80211: roaming %s",
+                  enabled ? "enabled" : "disabled");
+
+       wpa_supplicant_event(drv->ctx, event, NULL);
+}
+
 static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
                                      struct nlattr **tb)
 {
@@ -2083,7 +2179,8 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
                break;
        case NL80211_CMD_DISCONNECT:
                mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
-                                     tb[NL80211_ATTR_MAC]);
+                                     tb[NL80211_ATTR_MAC],
+                                     tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
                break;
        case NL80211_CMD_MICHAEL_MIC_FAILURE:
                mlme_event_michael_mic_failure(drv, tb);
@@ -2100,6 +2197,9 @@ static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
        case NL80211_CMD_NOTIFY_CQM:
                nl80211_cqm_event(drv, tb);
                break;
+       case NL80211_CMD_ROAMING_SUPPORT:
+               nl80211_roaming_support_event(drv, tb);
+               break;
        case NL80211_CMD_REG_CHANGE:
                wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
                wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
@@ -2269,6 +2369,7 @@ struct wiphy_info_data {
        unsigned int device_ap_sme:1;
        unsigned int poll_command_supported:1;
        unsigned int data_tx_status:1;
+       unsigned int monitor_supported:1;
 };
 
 
@@ -2340,6 +2441,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                        case NL80211_IFTYPE_P2P_CLIENT:
                                p2p_client_supported = 1;
                                break;
+                       case NL80211_IFTYPE_MONITOR:
+                               info->monitor_supported = 1;
+                               break;
                        }
                }
        }
@@ -2441,6 +2545,12 @@ broken_combination:
        if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
                capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
 
+       if (tb[NL80211_ATTR_FEATURE_FLAGS]) {
+               int features = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]);
+               if (features & NL80211_FEATURE_SCHED_SCAN_INTERVALS)
+                       capa->sched_scan_intervals_supported = 1;
+       }
+
        if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
                capa->max_remain_on_chan =
                        nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
@@ -2560,6 +2670,20 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
         */
        drv->use_monitor = !info.poll_command_supported;
 
+       if (drv->device_ap_sme && drv->use_monitor) {
+               /*
+                * Non-mac80211 drivers may not support monitor interface.
+                * Make sure we do not get stuck with incorrect capability here
+                * by explicitly testing this.
+                */
+               if (!info.monitor_supported) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
+                                  "with device_ap_sme since no monitor mode "
+                                  "support detected");
+                       drv->use_monitor = 0;
+               }
+       }
+
        /*
         * If we aren't going to use monitor interfaces, but the
         * driver doesn't support data TX status, we won't get TX
@@ -3145,6 +3269,28 @@ out_err:
 }
 
 
+static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
+{
+       if (nl80211_alloc_mgmt_handle(bss))
+               return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+                  "handle %p (device SME)", bss->nl_mgmt);
+
+       if (nl80211_register_frame(bss, bss->nl_mgmt,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_ACTION << 4),
+                                  NULL, 0) < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
+       nl_destroy_handles(&bss->nl_mgmt);
+       return -1;
+}
+
+
 static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
 {
        if (bss->nl_mgmt == NULL)
@@ -3408,6 +3554,8 @@ static int wpa_driver_nl80211_scan(void *priv,
        }
 
        if (params->p2p_probe) {
+               wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
                /*
                 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
                 * by masking out everything else apart from the OFDM rates 6,
@@ -3481,12 +3629,16 @@ nla_put_failure:
  * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
  * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
  * @params: Scan parameters
- * @interval: Interval between scan cycles in milliseconds
+ * @long_interval: interval between scan cycles after end of short cycles
+ * @short_interval: interval between initial short scan cycles
+ * @num_short_intervals: number of interval short scan intervals
  * Returns: 0 on success, -1 on failure or if not supported
  */
 static int wpa_driver_nl80211_sched_scan(void *priv,
                                         struct wpa_driver_scan_params *params,
-                                        u32 interval)
+                                        u32 long_interval,
+                                        u32 short_interval,
+                                        u8 num_short_intervals)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -3518,7 +3670,17 @@ static int wpa_driver_nl80211_sched_scan(void *priv,
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
+       NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, long_interval);
+
+       if (drv->capa.sched_scan_intervals_supported) {
+               NLA_PUT_U32(msg,
+                           NL80211_ATTR_SCHED_SCAN_SHORT_INTERVAL,
+                           short_interval);
+
+               NLA_PUT_U8(msg,
+                          NL80211_ATTR_SCHED_SCAN_NUM_SHORT_INTERVALS,
+                          num_short_intervals);
+       }
 
        if (drv->num_filter_ssids &&
            (int) drv->num_filter_ssids <= drv->capa.max_match_sets) {
@@ -3583,8 +3745,9 @@ static int wpa_driver_nl80211_sched_scan(void *priv,
                goto nla_put_failure;
        }
 
-       wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
-                  "scan interval %d msec", ret, interval);
+       wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) "
+                  "intervals: short=%d ms long=%d ms num_short_intervals=%d"
+                  , ret, short_interval, long_interval, num_short_intervals);
 
 nla_put_failure:
        nlmsg_free(ssids);
@@ -5080,24 +5243,32 @@ static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv,
 
 static int wpa_driver_nl80211_send_frame(struct i802_bss *bss,
                                         const void *data, size_t len,
-                                        int encrypt, int noack)
+                                        int encrypt, int noack,
+                                        unsigned int freq, int no_cck,
+                                        int offchanok, unsigned int wait_time)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
        u64 cookie;
 
+       if (freq == 0)
+               freq = bss->freq;
+
        if (drv->use_monitor)
                return wpa_driver_nl80211_send_mntr(drv, data, len,
                                                    encrypt, noack);
 
-       return nl80211_send_frame_cmd(bss, bss->freq, 0, data, len,
-                                     &cookie, 0, noack, 0);
+       return nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
+                                     &cookie, no_cck, noack, offchanok);
 }
 
 
-static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
-                                       size_t data_len, int noack)
+static int wpa_driver_nl80211_send_mlme_freq(struct i802_bss *bss,
+                                            const u8 *data,
+                                            size_t data_len, int noack,
+                                            unsigned int freq, int no_cck,
+                                            int offchanok,
+                                            unsigned int wait_time)
 {
-       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct ieee80211_mgmt *mgmt;
        int encrypt = 1;
@@ -5114,16 +5285,33 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
                 * but it works due to the single-threaded nature
                 * of wpa_supplicant.
                 */
-               return nl80211_send_frame_cmd(bss, drv->last_mgmt_freq, 0,
+               if (freq == 0)
+                       freq = drv->last_mgmt_freq;
+               return nl80211_send_frame_cmd(bss, freq, 0,
                                              data, data_len, NULL, 1, noack,
                                              1);
        }
 
-       if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
+#ifdef ANDROID_BRCM_P2P_PATCH
+       if (is_ap_interface(drv->nlmode)) {
+               wpa_printf(MSG_DEBUG, "%s: Sending frame on bss freq %d "
+                          "using nl80211_send_frame_cmd", __func__,
+                          bss->freq);
                return nl80211_send_frame_cmd(bss, bss->freq, 0,
-                                             data, data_len, NULL,
-                                             0, noack, 0);
+                                             data, data_len,
+                                             &drv->send_action_cookie, 0,
+                                             noack, 0);
+       }
+#else /* ANDROID_BRCM_P2P_PATCH */
+       if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
+               if (freq == 0)
+                       freq = bss->freq;
+               return nl80211_send_frame_cmd(bss, freq, 0,
+                                             data, data_len,
+                                             &drv->send_action_cookie,
+                                             no_cck, noack, offchanok);
        }
+#endif /* ANDROID_BRCM_P2P_PATCH */
 
        if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
            WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
@@ -5139,8 +5327,20 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
                        encrypt = 0;
        }
 
+       wpa_printf(MSG_DEBUG, "%s: Sending frame using monitor interface/"
+                  "l2 socket", __func__);
        return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
-                                            noack);
+                                            noack, freq, no_cck, offchanok,
+                                            wait_time);
+}
+
+
+static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
+                                       size_t data_len, int noack)
+{
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_send_mlme_freq(bss, data, data_len, noack,
+                                                0, 0, 0, 0);
 }
 
 
@@ -5331,6 +5531,9 @@ static int wpa_driver_nl80211_set_ap(void *priv,
                                params->short_slot_time, params->ht_opmode,
                                params->isolate, params->basic_rates);
        }
+#if defined(ANDROID_BRCM_P2P_PATCH) && defined(HOSTAPD)
+       wpa_driver_nl80211_probe_req_report(priv, 1);
+#endif
        return ret;
  nla_put_failure:
        nlmsg_free(msg);
@@ -5368,8 +5571,15 @@ static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
                                    NL80211_CHAN_HT40PLUS);
                        break;
                default:
+#ifdef ANDROID_BRCM_P2P_PATCH
+                       /* Should be changed to HT20 as a default value because
+                        * P2P firmware does not support 11n for BCM4329 */
+                       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+                                   NL80211_CHAN_NO_HT);
+#else /* ANDROID_BRCM_P2P_PATCH */
                        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
                                    NL80211_CHAN_HT20);
+#endif /* ANDROID_BRCM_P2P_PATCH */
                        break;
                }
        }
@@ -6066,6 +6276,10 @@ static int nl80211_setup_ap(struct i802_bss *bss)
                if (nl80211_mgmt_subscribe_ap(bss))
                        return -1;
 
+       if (drv->device_ap_sme && !drv->use_monitor)
+               if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
+                       return -1;
+
        if (!drv->device_ap_sme && drv->use_monitor &&
            nl80211_create_monitor_interface(drv) &&
            !drv->device_ap_sme)
@@ -6086,9 +6300,11 @@ static void nl80211_teardown_ap(struct i802_bss *bss)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (drv->device_ap_sme)
+       if (drv->device_ap_sme) {
                wpa_driver_nl80211_probe_req_report(bss, 0);
-       else if (drv->use_monitor)
+               if (!drv->use_monitor)
+                       nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+       } else if (drv->use_monitor)
                nl80211_remove_monitor_interface(drv);
        else
                nl80211_mgmt_unsubscribe(bss, "AP teardown");
@@ -6179,7 +6395,8 @@ static int wpa_driver_nl80211_hapd_send_eapol(
        pos += 2;
        memcpy(pos, data, data_len);
 
-       res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0);
+       res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
+                                           0, 0, 0, 0);
        if (res < 0) {
                wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
                           "failed: %d (%s)",
@@ -6344,6 +6561,20 @@ retry:
        if (ret)
                goto nla_put_failure;
 
+       if (params->bssid && params->fixed_bssid) {
+               wpa_printf(MSG_DEBUG, "  * BSSID=" MACSTR,
+                          MAC2STR(params->bssid));
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+       }
+
+       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+           params->key_mgmt_suite == KEY_MGMT_PSK ||
+           params->key_mgmt_suite == KEY_MGMT_802_1X_SHA256 ||
+           params->key_mgmt_suite == KEY_MGMT_PSK_SHA256) {
+               wpa_printf(MSG_DEBUG, "  * control port");
+               NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+       }
+
        if (params->wpa_ie) {
                wpa_hexdump(MSG_DEBUG,
                            "  * Extra IEs for Beacon/Probe Response frames",
@@ -6565,6 +6796,16 @@ skip_auth_type:
                NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
        }
 
+       if (params->disable_ht)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+       if (params->htcaps && params->htcaps_mask) {
+               int sz = sizeof(struct ieee80211_ht_capabilities);
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+                       params->htcaps_mask);
+       }
+
        ret = nl80211_set_conn_keys(params, msg);
        if (ret)
                goto nla_put_failure;
@@ -6712,6 +6953,16 @@ static int wpa_driver_nl80211_associate(
                        params->prev_bssid);
        }
 
+       if (params->disable_ht)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+
+       if (params->htcaps && params->htcaps_mask) {
+               int sz = sizeof(struct ieee80211_ht_capabilities);
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+                       params->htcaps_mask);
+       }
+
        if (params->p2p)
                wpa_printf(MSG_DEBUG, "  * P2P group");
 
@@ -7046,6 +7297,7 @@ static int i802_flush(void *priv)
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       int res;
 
        msg = nlmsg_alloc();
        if (!msg)
@@ -7059,7 +7311,12 @@ static int i802_flush(void *priv)
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
                    if_nametoindex(bss->ifname));
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
+       res = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (res) {
+               wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+                          "(%s)", res, strerror(-res));
+       }
+       return res;
  nla_put_failure:
        nlmsg_free(msg);
        return -ENOBUFS;
@@ -7719,10 +7976,15 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
                new_bss->ifindex = ifidx;
                new_bss->drv = drv;
                new_bss->next = drv->first_bss.next;
+               new_bss->freq = drv->first_bss.freq;
                drv->first_bss.next = new_bss;
                if (drv_priv)
                        *drv_priv = new_bss;
                nl80211_init_bss(new_bss);
+
+               /* Subscribe management frames for this WPA_IF_AP_BSS */
+               if (nl80211_setup_ap(new_bss))
+                       return -1;
        }
 #endif /* HOSTAPD */
 
@@ -7774,6 +8036,8 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
                        if (tbss->next == bss) {
                                tbss->next = bss->next;
+                               /* Unsubscribe management frames */
+                               nl80211_teardown_ap(bss);
                                nl80211_destroy_bss(bss);
                                os_free(bss);
                                bss = NULL;
@@ -7822,9 +8086,11 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss,
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+#ifndef ANDROID_BRCM_P2P_PATCH
        if (wait)
                NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
-       if (offchanok)
+#endif /* ANDROID_BRCM_P2P_PATCH */
+       if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
                NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
        if (no_cck)
                NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
@@ -7869,7 +8135,8 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
        struct ieee80211_hdr *hdr;
 
        wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
-                  "wait=%d ms no_cck=%d)", drv->ifindex, wait_time, no_cck);
+                  "freq=%u MHz wait=%d ms no_cck=%d)",
+                  drv->ifindex, freq, wait_time, no_cck);
 
        buf = os_zalloc(24 + data_len);
        if (buf == NULL)
@@ -7883,8 +8150,10 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
        os_memcpy(hdr->addr3, bssid, ETH_ALEN);
 
        if (is_ap_interface(drv->nlmode))
-               ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len,
-                                                  0);
+               ret = wpa_driver_nl80211_send_mlme_freq(priv, buf,
+                                                       24 + data_len,
+                                                       0, freq, no_cck, 1,
+                                                       wait_time);
        else
                ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
                                             24 + data_len,
@@ -8005,8 +8274,24 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
+       if (drv->nlmode != NL80211_IFTYPE_STATION) {
+               wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+                          "allowed in station mode (iftype=%d)",
+                          drv->nlmode);
+               return -1;
+       }
+
        if (!report) {
-               if (bss->nl_preq) {
+               if (bss->nl_preq && drv->device_ap_sme &&
+                   is_ap_interface(drv->nlmode)) {
+                       /*
+                        * Do not disable Probe Request reporting that was
+                        * enabled in nl80211_setup_ap().
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+                                  "Probe Request reporting nl_preq=%p while "
+                                  "in AP mode", bss->nl_preq);
+               } else if (bss->nl_preq) {
                        wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
                                   "reporting nl_preq=%p", bss->nl_preq);
                        eloop_unregister_read_sock(
@@ -8034,6 +8319,36 @@ static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
                                   NULL, 0) < 0)
                goto out_err;
 
+#ifdef ANDROID_BRCM_P2P_PATCH
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_ASSOC_REQ << 4),
+                                  NULL, 0) < 0) {
+               goto out_err;
+       }
+
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_REASSOC_REQ << 4),
+                                  NULL, 0) < 0) {
+               goto out_err;
+       }
+
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_DISASSOC << 4),
+                                  NULL, 0) < 0) {
+               goto out_err;
+       }
+
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_DEAUTH << 4),
+                                  NULL, 0) < 0) {
+               goto out_err;
+       }
+#endif /* ANDROID_BRCM_P2P_PATCH */
+
        eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq),
                                 wpa_driver_nl80211_event_receive, bss->nl_cb,
                                 bss->nl_preq);
@@ -8105,6 +8420,14 @@ static int wpa_driver_nl80211_deinit_ap(void *priv)
        return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
 }
 
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+               return -1;
+       return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
 
 static void wpa_driver_nl80211_resume(void *priv)
 {
@@ -8257,7 +8580,8 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
                              int encrypt)
 {
        struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0);
+       return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
+                                            0, 0, 0, 0);
 }
 
 
@@ -8387,6 +8711,324 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
        return -ENOBUFS;
 }
 
+#ifdef ANDROID
+
+#define MAX_PATTERN_SIZE        256
+#define MAX_MASK_SIZE           (MAX_PATTERN_SIZE/8)
+
+/* Describes a single RX filter configuration */
+struct rx_filter {
+       /* name - A human recongmizable name for the filter */
+       char *name;
+
+       /* get_pattern_handler - A handler which enables the user to configure
+        * the pattern dynamically (For example filter according to the HW addr).
+        * If NULL the static pattern configured will be used.
+        * buf - the pattern will be copied to buf
+        * buflen - the size of buf
+        * arg - A generic input argumet which can be passed to the handler
+        */
+       int (* get_pattern_handler) (u8 *buf, int buflen, void* arg);
+
+       /* pattern - A static pattern to filter
+        * This array contains the bytes of the pattern. The mask field
+        * indicates which bytes should be used in the filter and which
+        * can be discarded
+        */
+       u8 pattern[MAX_PATTERN_SIZE];
+
+       /* pattern_len - The number of bytes used in pattern */
+       u8 pattern_len;
+
+       /* mask - A bit mask indicating which bytes in pattern should be
+        * used for filtering. Each bit here corresponds to a byte in pattern
+        */
+       u8 mask[MAX_MASK_SIZE];
+
+       /* mask_len - The number of bytes used in mask */
+       u8 mask_len;
+};
+
+static u8 *nl80211_rx_filter_get_pattern(struct rx_filter *filter, void *arg)
+{
+       if (filter->get_pattern_handler) {
+               if (filter->get_pattern_handler(filter->pattern,
+                                               filter->pattern_len, arg)) {
+                       return NULL;
+               }
+       }
+
+       return filter->pattern;
+}
+
+static int
+nl80211_self_filter_get_pattern_handler(u8 *buf, int buflen, void *arg)
+{
+       int ret;
+       struct i802_bss *bss = (struct i802_bss *)arg;
+
+       ret = linux_get_ifhwaddr(bss->drv->global->ioctl_sock,
+                                bss->ifname, buf);
+       if (ret) {
+               wpa_printf(MSG_ERROR, "Failed to get own HW addr (%d)", ret);
+               return -1;
+       }
+       return 0;
+}
+
+static struct rx_filter rx_filters[] = {
+       /* ID 0 */
+       {.name = "self",
+        .pattern = {},
+        .pattern_len = 6,
+        .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) },
+        .mask_len = 1,
+        .get_pattern_handler = nl80211_self_filter_get_pattern_handler,
+       },
+
+       /* ID 1 */
+       {.name = "bcast",
+        .pattern = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
+        .pattern_len = 6,
+        .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) },
+        .mask_len = 1,
+       },
+
+       /* ID 2 */
+       {.name = "ipv4mc",
+        .pattern = {0x01,0x00,0x5E},
+        .pattern_len = 3,
+        .mask = { BIT(0) | BIT(1) | BIT(2) },
+        .mask_len = 1,
+       },
+
+       /* ID 3 */
+       {.name = "ipv6mc",
+        .pattern = {0x33,0x33},
+        .pattern_len = 2,
+        .mask = { BIT(0) | BIT(1) },
+        .mask_len = 1,
+       },
+
+       /* ID 4 */
+       {.name = "dhcp",
+        .pattern = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0   , 0   ,
+                    0   , 0   , 0   , 0   , 0   , 0   , 0x45, 0   ,
+                    0   , 0   , 0   , 0   , 0   , 0   , 0   , 0x11,
+                    0   , 0   , 0   , 0   , 0   , 0   , 0   , 0   ,
+                    0   , 0   , 0   , 0   , 0x00, 0x44},
+        .pattern_len = 38,
+        .mask = { BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5),
+                  BIT(6),                               /* OCTET 2 */
+                  BIT(7),                               /* OCTET 3 */
+                  0,                                    /* OCTET 4 */
+                  BIT(4) | BIT(5) },                    /* OCTET 5 */
+        .mask_len = 5,
+       },
+
+       /* ID 5 */
+       {.name = "arp",
+        .pattern = {0   , 0   , 0   , 0   , 0   , 0   , 0   , 0   ,
+                    0   , 0   , 0   , 0   , 0x08, 0x06},
+        .pattern_len = 14,
+        .mask = { 0,                                    /* OCTET 1 */
+                  BIT(4) | BIT(5) },                    /* OCTET 2 */
+        .mask_len = 2,
+       },
+
+       /* ID 6 */
+       {.name = "ssdp",
+        .pattern = {0x01, 0x00, 0x5E, 0   , 0   , 0   , 0   , 0   ,
+                    0   , 0   , 0   , 0   , 0   , 0   , 0x45, 0   ,
+                    0   , 0   , 0   , 0   , 0   , 0   , 0   , 0x11,
+                    0   , 0   , 0   , 0   , 0   , 0   , 0xEF, 0xFF,
+                    0xFF, 0xFA, 0   , 0   , 0x07, 0x6C},
+        .pattern_len = 38,
+        .mask = { BIT(0) | BIT(1) | BIT(2),             /* OCTET 1 */
+                  BIT(6),                               /* OCTET 2 */
+                  BIT(7),                               /* OCTET 3 */
+                  BIT(6) | BIT(7),                      /* OCTET 4 */
+                  BIT(0) | BIT(1) | BIT(4) | BIT(5) },  /* OCTET 5 */
+        .mask_len = 5,
+       },
+};
+
+#define NR_RX_FILTERS  (int)(sizeof(rx_filters) / sizeof(struct rx_filter))
+
+static int nl80211_set_wowlan_triggers(struct i802_bss *bss, int enable)
+{
+       struct nl_msg *msg, *pats = NULL;
+       struct nlattr *wowtrig, *pat;
+       int i, ret = -1;
+       int filters;
+
+       bss->drv->wowlan_enabled = !!enable;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, bss->drv->global->nl80211_id, 0,
+                   0, NL80211_CMD_SET_WOWLAN, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->drv->first_bss.ifindex);
+       wowtrig = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+
+       if (!wowtrig) {
+               ret = -ENOBUFS;
+               goto nla_put_failure;
+       }
+
+       if (!enable) {
+               NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+       } else {
+               pats = nlmsg_alloc();
+               if (!pats) {
+                       ret = -ENOMEM;
+                       goto nla_put_failure;
+               }
+
+               /*
+                * In GB filters 0 and 1 are always set but in ICS they
+                * were completely removed. Add filter 0 (unicast) by default
+                * so unicast traffic won't be dropped in any case.
+                */
+
+               filters = bss->drv->wowlan_triggers |= 1;
+
+               for (i = 0; i < NR_RX_FILTERS; i++) {
+                       struct rx_filter *rx_filter = &rx_filters[i];
+                       int patnr = 1;
+                       u8 *pattern;
+
+                       if (!(filters & (1 << i)))
+                               continue;
+
+                       pattern = nl80211_rx_filter_get_pattern(rx_filter, bss);
+                       if (!pattern)
+                               continue;
+
+                       pat = nla_nest_start(pats, patnr++);
+                       NLA_PUT(pats, NL80211_WOWLAN_PKTPAT_MASK,
+                               rx_filter->mask_len,
+                               rx_filter->mask);
+
+                       NLA_PUT(pats, NL80211_WOWLAN_PKTPAT_PATTERN,
+                               rx_filter->pattern_len,
+                               pattern);
+
+                       nla_nest_end(pats, pat);
+               }
+       }
+
+       if (pats)
+               nla_put_nested(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, pats);
+
+       nla_nest_end(msg, wowtrig);
+
+       ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+
+       if (ret < 0)
+               wpa_printf(MSG_ERROR, "Failed to set WoWLAN trigger:%d\n", ret);
+
+       if (pats)
+               nlmsg_free(pats);
+
+       return 0;
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+static int nl80211_toggle_wowlan_trigger(struct i802_bss *bss, int nr,
+                                        int enabled)
+{
+       if (nr >= NR_RX_FILTERS) {
+               wpa_printf(MSG_ERROR, "Unknown filter: %d\n", nr);
+               return -1;
+       }
+
+       if (enabled)
+               bss->drv->wowlan_triggers |= 1 << nr;
+       else
+               bss->drv->wowlan_triggers &= ~(1 << nr);
+
+       if (bss->drv->wowlan_enabled)
+               nl80211_set_wowlan_triggers(bss, 1);
+
+       return 0;
+}
+
+static int nl80211_parse_wowlan_trigger_nr(char *s)
+{
+       long i;
+       char *endp;
+
+       i = strtol(s, &endp, 10);
+
+       if(endp == s)
+               return -1;
+       return i;
+}
+
+static int nl80211_toggle_dropbcast(int enable)
+{
+       char filename[90];
+       int rv;
+       FILE *f;
+
+       snprintf(filename, sizeof(filename) - 1,
+                "/sys/bus/platform/devices/wl12xx/drop_bcast");
+       f = fopen(filename, "w");
+       if (!f) {
+               wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+                          filename, strerror(errno));
+               return -1;
+       }
+
+       rv = fprintf(f, "%d", enable);
+       fclose(f);
+       if (rv < 1) {
+               wpa_printf(MSG_DEBUG, "Could not write to file %s: %s",
+                          filename, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int nl80211_dropbcast_get(char *buf, size_t buf_len)
+{
+       char filename[90], value[10], *pos;
+       int f, rv;
+
+       snprintf(filename, sizeof(filename) - 1,
+                "/sys/bus/platform/devices/wl12xx/drop_bcast");
+       f = open(filename, O_RDONLY);
+       if (f < 0) {
+               wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+                          filename, strerror(errno));
+               return -1;
+       }
+
+       rv = read(f, value, sizeof(value) - 1);
+       close(f);
+       if (rv < 0) {
+               wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+                          filename, strerror(errno));
+               return -1;
+       }
+
+       value[rv] = '\0';
+       pos = os_strchr(value, '\n');
+       if (pos)
+               *pos = '\0';
+
+       return snprintf(buf, buf_len, "Drop bcast = %s\n", value);
+}
+
+#endif /* ANDROID */
 
 static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
 {
@@ -8534,6 +9176,15 @@ nla_put_failure:
 static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
                                     int ctwindow)
 {
+#ifdef ANDROID_BRCM_P2P_PATCH
+       char buf[MAX_DRV_CMD_SIZE];
+
+       memset(buf, 0, sizeof(buf));
+       wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+       os_snprintf(buf, sizeof(buf), "P2P_SET_PS %d %d %d", legacy_ps, opp_ps,
+                   ctwindow);
+       return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1);
+#else /* ANDROID_BRCM_P2P_PATCH */
        struct i802_bss *bss = priv;
 
        wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
@@ -8548,6 +9199,7 @@ static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
                return -1; /* Not yet supported */
 
        return nl80211_set_power_save(bss, legacy_ps);
+#endif /* ANDROID_BRCM_P2P_PATCH */
 }
 
 
@@ -8660,6 +9312,64 @@ static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
 }
 
 
+#define WPA_PS_ENABLED         0
+#define WPA_PS_DISABLED                1
+
+static int wpa_driver_set_power_save(void *priv, int state)
+{
+       return nl80211_set_power_save(priv, state == WPA_PS_ENABLED);
+}
+
+
+static int get_power_mode_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       int *state = arg;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb[NL80211_ATTR_PS_STATE])
+               return NL_SKIP;
+
+       if (state) {
+               int s = (int) nla_get_u32(tb[NL80211_ATTR_PS_STATE]);
+               wpa_printf(MSG_DEBUG, "nl80211: Get power mode = %d", s);
+               *state = (s == NL80211_PS_ENABLED) ?
+                       WPA_PS_ENABLED : WPA_PS_DISABLED;
+       }
+
+       return NL_SKIP;
+}
+
+
+static int wpa_driver_get_power_save(void *priv, int *state)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret = -1;
+       enum nl80211_ps_state ps_state;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_POWER_SAVE);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+       ret = send_and_recv_msgs(drv, msg, get_power_mode_handler, state);
+       msg = NULL;
+       if (ret < 0)
+               wpa_printf(MSG_ERROR, "nl80211: Get power mode fail: %d", ret);
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
 static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -8770,6 +9480,194 @@ static int android_pno_stop(struct i802_bss *bss)
        return android_priv_cmd(bss, "PNOFORCE 0");
 }
 
+
+static int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+                                        size_t buf_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ifreq ifr;
+       android_wifi_priv_cmd priv_cmd;
+       int ret = 0;
+
+       if (os_strcasecmp(cmd, "STOP") == 0) {
+               linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
+               wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
+       } else if (os_strcasecmp(cmd, "START") == 0) {
+               linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+               wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
+       } else if (os_strcasecmp(cmd, "MACADDR") == 0) {
+               u8 macaddr[ETH_ALEN] = {};
+
+               ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                                        macaddr);
+               if (!ret)
+                       ret = os_snprintf(buf, buf_len,
+                                         "Macaddr = " MACSTR "\n",
+                                         MAC2STR(macaddr));
+       } else if (os_strcasecmp(cmd, "RELOAD") == 0) {
+               wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+       } else if (os_strncasecmp(cmd, "POWERMODE ", 10) == 0) {
+               int state = atoi(cmd + 10);
+               ret = wpa_driver_set_power_save(priv, state);
+               if (ret < 0)
+                       wpa_driver_send_hang_msg(drv);
+               else
+                       drv_errors = 0;
+       } else if (os_strncasecmp(cmd, "GETPOWER", 8) == 0) {
+               int state = -1;
+               ret = wpa_driver_get_power_save(priv, &state);
+               if (!ret && (state != -1))
+                       ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n",
+                                         state);
+       } else if( os_strncasecmp(cmd, "RXFILTER-ADD ", 13) == 0 ) {
+               int i = nl80211_parse_wowlan_trigger_nr(cmd + 13);
+               if(i < 0)
+                       return i;
+               return nl80211_toggle_wowlan_trigger(bss, i, 1);
+       } else if( os_strncasecmp(cmd, "RXFILTER-REMOVE ", 16) == 0 ) {
+               int i = nl80211_parse_wowlan_trigger_nr(cmd + 16);
+               if(i < 0)
+                       return i;
+               return nl80211_toggle_wowlan_trigger(bss, i, 0);
+       } else if (os_strcasecmp(cmd, "RXFILTER-START") == 0) {
+               return nl80211_set_wowlan_triggers(bss, 1);
+       } else if (os_strcasecmp(cmd, "RXFILTER-STOP") == 0) {
+               return nl80211_set_wowlan_triggers(bss, 0);
+       } else if (os_strncasecmp(cmd, "DROPBCAST", 9) == 0) {
+               char *value = cmd + 10;
+
+               if (!os_strcasecmp(value, "ENABLE") ||
+                   !os_strcasecmp(value, "1")) {
+                       ret = nl80211_toggle_dropbcast(1);
+               } else if (!os_strcasecmp(value, "DISABLE") ||
+                          !os_strcasecmp(value, "0")) {
+                       ret = nl80211_toggle_dropbcast(0);
+               } else if (!os_strcasecmp(value, "GET") ||
+                          !os_strlen(value)) {
+                       ret = nl80211_dropbcast_get(buf, buf_len);
+               } else {
+                       wpa_printf(MSG_ERROR,
+                                  "Invalid parameter for DROPBCAST: %s",
+                                  value);
+                       ret = -1;
+               }
+       } else if( os_strcasecmp(cmd, "LINKSPEED") == 0 ) {
+               struct wpa_signal_info sig;
+               int linkspeed;
+
+               if (!drv->associated)
+                       return -1;
+
+               ret = nl80211_get_link_signal(drv, &sig);
+               if (ret == 0) {
+                       linkspeed = sig.current_txrate;
+                       ret = os_snprintf(buf, buf_len, "LinkSpeed %d\n",
+                                         linkspeed);
+               }
+       } else if (os_strcasecmp(cmd, "RSSI") == 0 ||
+                  os_strcasecmp(cmd, "RSSI-APPROX") == 0) {
+               struct wpa_signal_info sig;
+               int rssi;
+
+               if (!drv->associated)
+                       return -1;
+
+               ret = nl80211_get_link_signal(drv, &sig);
+               if (ret == 0) {
+                       rssi = sig.current_signal;
+                       ret = os_snprintf(buf, buf_len, "%s rssi %d\n",
+                                         drv->ssid, rssi);
+               }
+       } else {
+               wpa_printf(MSG_ERROR, "Unsupported command: %s", cmd);
+               ret = -1;
+       }
+
+       return ret;
+}
+
+#ifdef ANDROID_BRCM_P2P_PATCH
+
+static int wpa_driver_set_p2p_noa(void *priv, u8 count, int start,
+                                 int duration)
+{
+       char buf[MAX_DRV_CMD_SIZE];
+
+       memset(buf, 0, sizeof(buf));
+       wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+       os_snprintf(buf, sizeof(buf), "P2P_SET_NOA %d %d %d", count, start,
+                   duration);
+       return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1);
+}
+
+
+static int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len)
+{
+       char rbuf[MAX_DRV_CMD_SIZE];
+       char *cmd = "P2P_GET_NOA";
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+       os_memset(buf, 0, len);
+       ret = wpa_driver_nl80211_driver_cmd(priv, cmd, rbuf, sizeof(rbuf));
+       if (ret <= 0)
+               return 0;
+       ret >>= 1;
+       if (ret > (int)len)
+               ret = (int)len;
+       hexstr2bin(rbuf, buf, ret);
+       return ret;
+}
+
+
+static int wpa_driver_set_ap_wps_p2p_ie(void *priv,
+                                       const struct wpabuf *beacon,
+                                       const struct wpabuf *proberesp,
+                                       const struct wpabuf *assocresp)
+{
+       char buf[MAX_WPSP2PIE_CMD_SIZE];
+       struct wpabuf *ap_wps_p2p_ie = NULL;
+       char *_cmd = "SET_AP_WPS_P2P_IE";
+       char *pbuf;
+       int ret = 0;
+       int i;
+       struct cmd_desc {
+               int cmd;
+               const struct wpabuf *src;
+       } cmd_arr[] = {
+               {0x1, beacon},
+               {0x2, proberesp},
+               {0x4, assocresp},
+               {-1, NULL}
+       };
+
+       wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+       for (i = 0; cmd_arr[i].cmd != -1; i++) {
+               os_memset(buf, 0, sizeof(buf));
+               pbuf = buf;
+               pbuf += sprintf(pbuf, "%s %d", _cmd, cmd_arr[i].cmd);
+               *pbuf++ = '\0';
+               ap_wps_p2p_ie = cmd_arr[i].src ?
+                       wpabuf_dup(cmd_arr[i].src) : NULL;
+               if (ap_wps_p2p_ie) {
+                       os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie),
+                                 wpabuf_len(ap_wps_p2p_ie));
+                       ret = wpa_driver_nl80211_driver_cmd(
+                               priv, buf, buf,
+                               os_strlen(_cmd) + 3 +
+                               wpabuf_len(ap_wps_p2p_ie));
+                       wpabuf_free(ap_wps_p2p_ie);
+                       if (ret < 0)
+                               break;
+               }
+       }
+
+       return ret;
+}
+
+#endif /* ANDROID_BRCM_P2P_PATCH */
+
 #endif /* ANDROID */
 
 
@@ -8830,6 +9728,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        wpa_driver_nl80211_cancel_remain_on_channel,
        .probe_req_report = wpa_driver_nl80211_probe_req_report,
        .deinit_ap = wpa_driver_nl80211_deinit_ap,
+       .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
        .resume = wpa_driver_nl80211_resume,
        .send_ft_action = nl80211_send_ft_action,
        .signal_monitor = nl80211_signal_monitor,
@@ -8848,4 +9747,12 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .send_tdls_mgmt = nl80211_send_tdls_mgmt,
        .tdls_oper = nl80211_tdls_oper,
 #endif /* CONFIG_TDLS */
+#ifdef ANDROID_BRCM_P2P_PATCH
+       .get_noa = wpa_driver_get_p2p_noa,
+       .set_noa = wpa_driver_set_p2p_noa,
+       .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
+#endif /* ANDROID_BRCM_P2P_PATCH */
+#ifdef ANDROID
+       .driver_cmd = wpa_driver_nl80211_driver_cmd,
+#endif /* ANDROID */
 };