brcmfmac: add support for P2P listen mode.
[deliverable/linux.git] / drivers / net / wireless / brcm80211 / brcmfmac / wl_cfg80211.c
index d47b274018ba259d82e630045a7f9bc31d57ec61..6ab6397219a55b3a796036dbd1a9f163c1078cfd 100644 (file)
@@ -26,6 +26,7 @@
 #include <brcmu_wifi.h>
 #include "dhd.h"
 #include "dhd_dbg.h"
+#include "p2p.h"
 #include "wl_cfg80211.h"
 #include "fwil.h"
 
 #define BRCMF_PNO_SCAN_COMPLETE                1
 #define BRCMF_PNO_SCAN_INCOMPLETE      0
 
-#define BRCMF_IFACE_MAX_CNT            2
+#define BRCMF_IFACE_MAX_CNT            3
 
-#define TLV_LEN_OFF                    1       /* length offset */
-#define TLV_HDR_LEN                    2       /* header length */
-#define TLV_BODY_OFF                   2       /* body offset */
-#define TLV_OUI_LEN                    3       /* oui id length */
 #define WPA_OUI                                "\x00\x50\xF2"  /* WPA OUI */
 #define WPA_OUI_TYPE                   1
 #define RSN_OUI                                "\x00\x0F\xAC"  /* RSN OUI */
@@ -76,9 +73,7 @@
 #define VNDR_IE_PKTFLAG_OFFSET         8
 #define VNDR_IE_VSIE_OFFSET            12
 #define VNDR_IE_HDR_SIZE               12
-#define VNDR_IE_BEACON_FLAG            0x1
-#define VNDR_IE_PRBRSP_FLAG            0x2
-#define MAX_VNDR_IE_NUMBER             5
+#define VNDR_IE_PARSE_LIMIT            5
 
 #define        DOT11_MGMT_HDR_LEN              24      /* d11 management header len */
 #define        DOT11_BCN_PRB_FIXED_LEN         12      /* beacon/probe fixed length */
@@ -271,13 +266,6 @@ static const u32 __wl_cipher_suites[] = {
        WLAN_CIPHER_SUITE_AES_CMAC,
 };
 
-/* tag_ID/length/value_buffer tuple */
-struct brcmf_tlv {
-       u8 id;
-       u8 len;
-       u8 data[1];
-};
-
 /* Vendor specific ie. id = 221, oui and type defines exact ie */
 struct brcmf_vs_tlv {
        u8 id;
@@ -294,7 +282,7 @@ struct parsed_vndr_ie_info {
 
 struct parsed_vndr_ies {
        u32 count;
-       struct parsed_vndr_ie_info ie_info[MAX_VNDR_IE_NUMBER];
+       struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
 };
 
 /* Quarter dBm units to mW
@@ -381,7 +369,7 @@ static u8 brcmf_mw_to_qdbm(u16 mw)
        return qdbm;
 }
 
-static u16 channel_to_chanspec(struct ieee80211_channel *ch)
+u16 channel_to_chanspec(struct ieee80211_channel *ch)
 {
        u16 chanspec;
 
@@ -431,6 +419,55 @@ send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
        return err;
 }
 
+static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
+                                                    const char *name,
+                                                    enum nl80211_iftype type,
+                                                    u32 *flags,
+                                                    struct vif_params *params)
+{
+       brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
+       switch (type) {
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_MESH_POINT:
+               return ERR_PTR(-EOPNOTSUPP);
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_P2P_GO:
+               return brcmf_p2p_add_vif(wiphy, name, type, flags, params);
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_P2P_DEVICE:
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+}
+
+static
+int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_MESH_POINT:
+               return -EOPNOTSUPP;
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_P2P_GO:
+               return brcmf_p2p_del_vif(wiphy, wdev);
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_P2P_DEVICE:
+       default:
+               return -EINVAL;
+       }
+       return -EOPNOTSUPP;
+}
+
 static s32
 brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
                         enum nl80211_iftype type, u32 *flags,
@@ -696,11 +733,12 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
        s32 err;
        u32 passive_scan;
        struct brcmf_scan_results *results;
+       struct escan_info *escan = &cfg->escan_info;
 
        brcmf_dbg(SCAN, "Enter\n");
-       cfg->escan_info.ndev = ndev;
-       cfg->escan_info.wiphy = wiphy;
-       cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
+       escan->ndev = ndev;
+       escan->wiphy = wiphy;
+       escan->escan_state = WL_ESCAN_STATE_SCANNING;
        passive_scan = cfg->active_scan ? 0 : 1;
        err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN,
                                    passive_scan);
@@ -714,7 +752,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
        results->count = 0;
        results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
 
-       err = brcmf_run_escan(cfg, ndev, request, WL_ESCAN_ACTION_START);
+       err = escan->run(cfg, ndev, request, WL_ESCAN_ACTION_START);
        if (err)
                brcmf_set_mpc(ndev, 1);
        return err;
@@ -769,6 +807,11 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
        cfg->scan_request = request;
        set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
        if (escan_req) {
+               cfg->escan_info.run = brcmf_run_escan;
+               err = brcmf_p2p_scan_prep(wiphy, request);
+               if (err)
+                       goto scan_out;
+
                err = brcmf_do_escan(cfg, wiphy, ndev, request);
                if (err)
                        goto scan_out;
@@ -2186,7 +2229,7 @@ static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
  * triples, returning a pointer to the substring whose first element
  * matches tag
  */
-static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
+struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
 {
        struct brcmf_tlv *elt;
        int totlen;
@@ -3227,7 +3270,7 @@ brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
                          parsed_info->vndrie.oui[2],
                          parsed_info->vndrie.oui_type);
 
-               if (vndr_ies->count >= MAX_VNDR_IE_NUMBER)
+               if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
                        break;
 next:
                remaining_len -= (ie->len + TLV_HDR_LEN);
@@ -3261,7 +3304,6 @@ brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
        return ie_len + VNDR_IE_HDR_SIZE;
 }
 
-static
 s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
                          const u8 *vndr_ie_buf, u32 vndr_ie_len)
 {
@@ -3295,12 +3337,12 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
        curr_ie_buf = iovar_ie_buf;
        if (ifp->vif->mode == WL_MODE_AP) {
                switch (pktflag) {
-               case VNDR_IE_PRBRSP_FLAG:
+               case BRCMF_VNDR_IE_PRBRSP_FLAG:
                        mgmt_ie_buf = saved_ie->probe_res_ie;
                        mgmt_ie_len = &saved_ie->probe_res_ie_len;
                        mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
                        break;
-               case VNDR_IE_BEACON_FLAG:
+               case BRCMF_VNDR_IE_BEACON_FLAG:
                        mgmt_ie_buf = saved_ie->beacon_ie;
                        mgmt_ie_len = &saved_ie->beacon_ie_len;
                        mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
@@ -3311,9 +3353,22 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
                        goto exit;
                }
        } else {
-               err = -EPERM;
-               brcmf_err("not suitable type\n");
-               goto exit;
+               switch (pktflag) {
+               case BRCMF_VNDR_IE_PRBREQ_FLAG:
+                       mgmt_ie_buf = saved_ie->probe_req_ie;
+                       mgmt_ie_len = &saved_ie->probe_req_ie_len;
+                       mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
+                       break;
+               case BRCMF_VNDR_IE_PRBRSP_FLAG:
+                       mgmt_ie_buf = saved_ie->probe_res_ie;
+                       mgmt_ie_len = &saved_ie->probe_res_ie_len;
+                       mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
+                       break;
+               default:
+                       err = -EPERM;
+                       brcmf_err("not suitable type\n");
+                       goto exit;
+               }
        }
 
        if (vndr_ie_len > mgmt_ie_buf_len) {
@@ -3508,7 +3563,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
        }
        /* Set Beacon IEs to FW */
        err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev),
-                                   VNDR_IE_BEACON_FLAG,
+                                   BRCMF_VNDR_IE_BEACON_FLAG,
                                    settings->beacon.tail,
                                    settings->beacon.tail_len);
        if (err)
@@ -3518,7 +3573,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
 
        /* Set Probe Response IEs to FW */
        err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev),
-                                   VNDR_IE_PRBRSP_FLAG,
+                                   BRCMF_VNDR_IE_PRBRSP_FLAG,
                                    settings->beacon.proberesp_ies,
                                    settings->beacon.proberesp_ies_len);
        if (err)
@@ -3546,6 +3601,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
        if (err < 0) {
                brcmf_err("BRCMF_C_UP error (%d)\n", err);
                goto exit;
+               brcmf_fil_iovar_int_set(ifp, "apsta", 0);
        }
 
        memset(&join_params, 0, sizeof(join_params));
@@ -3623,7 +3679,153 @@ brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
        return err;
 }
 
+
+static void
+brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
+                                  struct wireless_dev *wdev,
+                                  u16 frame_type, bool reg)
+{
+       struct brcmf_if *ifp = netdev_priv(wdev->netdev);
+       struct brcmf_cfg80211_vif *vif = ifp->vif;
+       u16 mgmt_type;
+
+       brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
+
+       mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
+       if (reg)
+               vif->mgmt_rx_reg |= BIT(mgmt_type);
+       else
+               vif->mgmt_rx_reg |= ~BIT(mgmt_type);
+}
+
+
+static int
+brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                      struct ieee80211_channel *chan, bool offchan,
+                      unsigned int wait, const u8 *buf, size_t len,
+                      bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+{
+       struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+       const struct ieee80211_mgmt *mgmt;
+       struct brcmf_cfg80211_vif *vif;
+       s32 err = 0;
+       s32 ie_offset;
+       s32 ie_len;
+
+       brcmf_dbg(TRACE, "Enter\n");
+
+       *cookie = 0;
+
+       mgmt = (const struct ieee80211_mgmt *)buf;
+
+       if (ieee80211_is_mgmt(mgmt->frame_control)) {
+               if (ieee80211_is_probe_resp(mgmt->frame_control)) {
+                       /* Right now the only reason to get a probe response */
+                       /* is for p2p listen response from wpa_supplicant.   */
+                       /* Unfortunately the wpa_supplicant sends it on the  */
+                       /* primary ndev, while dongle wants it on the p2p    */
+                       /* vif. Since this is only reason for a probe        */
+                       /* response to be sent, the vif is taken from cfg.   */
+                       /* If ever desired to send proberesp for non p2p     */
+                       /* response then data should be checked for          */
+                       /* "DIRECT-". Note in future supplicant will take    */
+                       /* dedicated p2p wdev to do this and then this 'hack'*/
+                       /* is not needed anymore.                            */
+                       ie_offset =  DOT11_MGMT_HDR_LEN +
+                                    DOT11_BCN_PRB_FIXED_LEN;
+                       ie_len = len - ie_offset;
+
+                       vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+                       if (vif == NULL) {
+                               brcmf_err("No p2p device available for probe response\n");
+                               err = -ENODEV;
+                               goto exit;
+                       }
+                       err = brcmf_vif_set_mgmt_ie(vif,
+                                                   BRCMF_VNDR_IE_PRBRSP_FLAG,
+                                                   &buf[ie_offset],
+                                                   ie_len);
+                       cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+                                               GFP_KERNEL);
+                       goto exit;
+               }
+       }
+       brcmf_dbg(TRACE, "Unhandled, is_mgmt %d, fc=%04x!!!!!\n",
+                 ieee80211_is_mgmt(mgmt->frame_control), mgmt->frame_control);
+exit:
+       return err;
+}
+
+
+static int
+brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+                                       struct wireless_dev *wdev,
+                                       u64 cookie)
+{
+       struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+       struct brcmf_cfg80211_vif *vif;
+       int err = 0;
+
+       brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
+
+       vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+       if (vif == NULL) {
+               brcmf_err("No p2p device available for probe response\n");
+               err = -ENODEV;
+               goto exit;
+       }
+       brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+exit:
+       return err;
+}
+
+static s32 brcmf_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
+                                            const struct brcmf_event_msg *e,
+                                            void *data)
+{
+       struct wireless_dev *wdev;
+       struct brcmf_cfg80211_vif *vif = ifp->vif;
+       struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
+       u16 chanspec = be16_to_cpu(rxframe->chanspec);
+       u8 *mgmt_frame;
+       u32 mgmt_frame_len;
+       s32 freq;
+       u16 mgmt_type;
+
+       brcmf_dbg(INFO,
+                 "Enter: event %d reason %d\n", e->event_code, e->reason);
+
+       /* Firmware sends us two proberesponses for each idx one. At the */
+       /* moment only bsscfgidx 0 is passed up to supplicant            */
+       if (e->bsscfgidx)
+               return 0;
+
+       /* Check if wpa_supplicant has registered for this frame */
+       brcmf_dbg(INFO, "vif->mgmt_rx_reg %04x\n", vif->mgmt_rx_reg);
+       mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4;
+       if ((vif->mgmt_rx_reg & BIT(mgmt_type)) == 0)
+               return 0;
+
+       mgmt_frame = (u8 *)(rxframe + 1);
+       mgmt_frame_len = e->datalen - sizeof(*rxframe);
+       freq = ieee80211_channel_to_frequency(CHSPEC_CHANNEL(chanspec),
+                                             CHSPEC_IS2G(chanspec) ?
+                                             IEEE80211_BAND_2GHZ :
+                                             IEEE80211_BAND_5GHZ);
+       wdev = ifp->ndev->ieee80211_ptr;
+       cfg80211_rx_mgmt(wdev, freq, 0, mgmt_frame, mgmt_frame_len, GFP_ATOMIC);
+
+       brcmf_dbg(INFO,
+                 "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
+                 mgmt_frame_len, e->datalen, chanspec, freq);
+
+       return 0;
+}
+
+
 static struct cfg80211_ops wl_cfg80211_ops = {
+       .add_virtual_intf = brcmf_cfg80211_add_iface,
+       .del_virtual_intf = brcmf_cfg80211_del_iface,
        .change_virtual_intf = brcmf_cfg80211_change_iface,
        .scan = brcmf_cfg80211_scan,
        .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
@@ -3650,25 +3852,39 @@ static struct cfg80211_ops wl_cfg80211_ops = {
        .del_station = brcmf_cfg80211_del_station,
        .sched_scan_start = brcmf_cfg80211_sched_scan_start,
        .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
+       .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
+       .mgmt_tx = brcmf_cfg80211_mgmt_tx,
+       .remain_on_channel = brcmf_p2p_remain_on_channel,
+       .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
 #ifdef CONFIG_NL80211_TESTMODE
        .testmode_cmd = brcmf_cfg80211_testmode
 #endif
 };
 
-static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
+static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type)
 {
-       s32 err = 0;
-
-       switch (mode) {
-       case WL_MODE_BSS:
-               return NL80211_IFTYPE_STATION;
-       case WL_MODE_IBSS:
-               return NL80211_IFTYPE_ADHOC;
+       switch (type) {
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_MESH_POINT:
+               return -ENOTSUPP;
+       case NL80211_IFTYPE_ADHOC:
+               return WL_MODE_IBSS;
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+               return WL_MODE_BSS;
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+               return WL_MODE_AP;
+       case NL80211_IFTYPE_P2P_DEVICE:
+               return WL_MODE_P2P;
+       case NL80211_IFTYPE_UNSPECIFIED:
        default:
-               return NL80211_IFTYPE_UNSPECIFIED;
+               break;
        }
 
-       return err;
+       return -EINVAL;
 }
 
 static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
@@ -3680,6 +3896,52 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
        wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
 }
 
+static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_STATION) |
+                        BIT(NL80211_IFTYPE_ADHOC) |
+                        BIT(NL80211_IFTYPE_AP)
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                        BIT(NL80211_IFTYPE_P2P_GO)
+       },
+};
+static const struct ieee80211_iface_combination brcmf_iface_combos[] = {
+       {
+                .max_interfaces = BRCMF_IFACE_MAX_CNT - 1,
+                .num_different_channels = 1, /* no multi-channel for now */
+                .n_limits = ARRAY_SIZE(brcmf_iface_limits),
+                .limits = brcmf_iface_limits
+       }
+};
+
+static const struct ieee80211_txrx_stypes
+brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
+       [NL80211_IFTYPE_STATION] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_CLIENT] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_GO] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+                     BIT(IEEE80211_STYPE_AUTH >> 4) |
+                     BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+                     BIT(IEEE80211_STYPE_ACTION >> 4)
+       }
+};
+
 static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
 {
        struct wiphy *wiphy;
@@ -3692,10 +3954,15 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
        }
        set_wiphy_dev(wiphy, phydev);
        wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+       wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
        wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
        wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                                 BIT(NL80211_IFTYPE_ADHOC) |
-                                BIT(NL80211_IFTYPE_AP);
+                                BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                BIT(NL80211_IFTYPE_P2P_GO);
+       wiphy->iface_combinations = brcmf_iface_combos;
+       wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
        wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
        wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a;  /* Set
                                                * it as 11a by default.
@@ -3707,10 +3974,10 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
        wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
        wiphy->cipher_suites = __wl_cipher_suites;
        wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
-       wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;    /* enable power
-                                                                * save mode
-                                                                * by default
-                                                                */
+       wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
+                       WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+       wiphy->mgmt_stypes = brcmf_txrx_stypes;
+       wiphy->max_remain_on_channel_duration = 5000;
        brcmf_wiphy_pno_params(wiphy);
        err = wiphy_register(wiphy);
        if (err < 0) {
@@ -3721,31 +3988,25 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
        return wiphy;
 }
 
-static
 struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
-                                          struct net_device *netdev,
-                                          s32 mode, bool pm_block)
+                                          enum nl80211_iftype type,
+                                          bool pm_block)
 {
        struct brcmf_cfg80211_vif *vif;
 
        if (cfg->vif_cnt == BRCMF_IFACE_MAX_CNT)
                return ERR_PTR(-ENOSPC);
 
+       brcmf_dbg(TRACE, "allocating virtual interface (size=%d)\n",
+                 sizeof(*vif));
        vif = kzalloc(sizeof(*vif), GFP_KERNEL);
        if (!vif)
                return ERR_PTR(-ENOMEM);
 
        vif->wdev.wiphy = cfg->wiphy;
-       vif->wdev.netdev = netdev;
-       vif->wdev.iftype = brcmf_mode_to_nl80211_iftype(mode);
-
-       if (netdev) {
-               vif->ifp = netdev_priv(netdev);
-               netdev->ieee80211_ptr = &vif->wdev;
-               SET_NETDEV_DEV(netdev, wiphy_dev(cfg->wiphy));
-       }
+       vif->wdev.iftype = type;
 
-       vif->mode = mode;
+       vif->mode = brcmf_nl80211_iftype_to_mode(type);
        vif->pm_block = pm_block;
        vif->roam_off = -1;
 
@@ -3756,7 +4017,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
        return vif;
 }
 
-static void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
+void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
 {
        struct brcmf_cfg80211_info *cfg;
        struct wiphy *wiphy;
@@ -4103,6 +4364,57 @@ brcmf_notify_mic_status(struct brcmf_if *ifp,
        return 0;
 }
 
+static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
+                                 const struct brcmf_event_msg *e, void *data)
+{
+       struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+       struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
+       struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+       struct brcmf_cfg80211_vif *vif;
+
+       brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
+                 ifevent->action, ifevent->flags, ifevent->ifidx,
+                 ifevent->bssidx);
+
+
+       mutex_lock(&event->vif_event_lock);
+       event->action = ifevent->action;
+       vif = event->vif;
+
+       switch (ifevent->action) {
+       case BRCMF_E_IF_ADD:
+               /* waiting process may have timed out */
+               if (!cfg->vif_event.vif)
+                       return -EBADF;
+
+               ifp->vif = vif;
+               vif->ifp = ifp;
+               vif->wdev.netdev = ifp->ndev;
+               ifp->ndev->ieee80211_ptr = &vif->wdev;
+               SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
+               mutex_unlock(&event->vif_event_lock);
+               wake_up(&event->vif_wq);
+
+               /* waiting process need to set the netdev name */
+               wait_for_completion(&event->vif_complete);
+               return brcmf_net_attach(ifp);
+
+       case BRCMF_E_IF_DEL:
+               ifp->vif = NULL;
+               brcmf_free_vif(vif);
+               mutex_unlock(&event->vif_event_lock);
+               /* event may not be upon user request */
+               if (brcmf_cfg80211_vif_event_armed(cfg))
+                       wake_up(&event->vif_wq);
+               return 0;
+
+       default:
+               mutex_unlock(&event->vif_event_lock);
+               break;
+       }
+       return -EINVAL;
+}
+
 static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
 {
        conf->frag_threshold = (u32)-1;
@@ -4134,6 +4446,12 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
                            brcmf_notify_connect_status);
        brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
                            brcmf_notify_sched_scan_results);
+       brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
+                           brcmf_notify_vif_event);
+       brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
+                           brcmf_notify_rx_mgmt_p2p_probereq);
+       brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
+                           brcmf_p2p_notify_listen_complete);
 }
 
 static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
@@ -4200,6 +4518,13 @@ static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
        brcmf_deinit_priv_mem(cfg);
 }
 
+static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
+{
+       init_waitqueue_head(&event->vif_wq);
+       init_completion(&event->vif_complete);
+       mutex_init(&event->vif_event_lock);
+}
+
 struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
                                                  struct device *busdev)
 {
@@ -4223,25 +4548,33 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
        cfg = wiphy_priv(wiphy);
        cfg->wiphy = wiphy;
        cfg->pub = drvr;
+       init_vif_event(&cfg->vif_event);
        INIT_LIST_HEAD(&cfg->vif_list);
 
-       vif = brcmf_alloc_vif(cfg, ndev, WL_MODE_BSS, false);
+       vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
        if (IS_ERR(vif)) {
                wiphy_free(wiphy);
                return NULL;
        }
 
+       vif->ifp = ifp;
+       vif->wdev.netdev = ndev;
+       ndev->ieee80211_ptr = &vif->wdev;
+       SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
+
        err = wl_init_priv(cfg);
        if (err) {
                brcmf_err("Failed to init iwm_priv (%d)\n", err);
                goto cfg80211_attach_out;
        }
+       brcmf_p2p_attach(cfg);
 
        ifp->vif = vif;
        return cfg;
 
 cfg80211_attach_out:
        brcmf_free_vif(vif);
+       wiphy_free(wiphy);
        return NULL;
 }
 
@@ -4480,3 +4813,61 @@ s32 brcmf_cfg80211_down(struct net_device *ndev)
        return err;
 }
 
+u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state)
+{
+       struct brcmf_cfg80211_vif *vif;
+       bool result = 0;
+
+       list_for_each_entry(vif, &cfg->vif_list, list) {
+               if (test_bit(state, &vif->sme_state))
+                       result++;
+       }
+       return result;
+}
+
+static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
+                                   u8 action)
+{
+       u8 evt_action;
+
+       mutex_lock(&event->vif_event_lock);
+       evt_action = event->action;
+       mutex_unlock(&event->vif_event_lock);
+       return evt_action == action;
+}
+
+void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
+                                 struct brcmf_cfg80211_vif *vif)
+{
+       struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+
+       mutex_lock(&event->vif_event_lock);
+       event->vif = vif;
+       event->action = 0;
+       mutex_unlock(&event->vif_event_lock);
+}
+
+bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
+{
+       struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+       bool armed;
+
+       mutex_lock(&event->vif_event_lock);
+       armed = event->vif != NULL;
+       mutex_unlock(&event->vif_event_lock);
+
+       return armed;
+}
+int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
+                                         u8 action, ulong timeout)
+{
+       struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
+
+       return wait_event_timeout(event->vif_wq,
+                                 vif_event_equals(event, action), timeout);
+}
+
+void brcmf_cfg80211_vif_complete(struct brcmf_cfg80211_info *cfg)
+{
+       complete(&cfg->vif_event.vif_complete);
+}
This page took 0.034031 seconds and 5 git commands to generate.