aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorHante Meuleman <meuleman@broadcom.com>2014-12-03 15:05:33 -0500
committerJohn W. Linville <linville@tuxdriver.com>2014-12-04 11:35:03 -0500
commita44aa4001a86d46f936ca449e5d6c268446bfae2 (patch)
tree606c87444a9650569da88a1de0a8f25a043cd8b2 /drivers/net
parentc4034f43e65466ba020a7bec5038640143465c7c (diff)
brcmfmac: add multiple BSS support.
This patch adds support for multiple BSS interfaces (AP). In total three AP configurations can be created. In order to use multiple BSS firmware needs to support it. Reviewed-by: Arend Van Spriel <arend@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Signed-off-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c245
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/core.c39
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/core.h3
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/feature.c23
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/feature.h1
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fweh.c6
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h6
8 files changed, 272 insertions, 53 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
index 0ea312546635..3aecc5f48719 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -520,6 +520,95 @@ brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
520 ADDR_INDIRECT); 520 ADDR_INDIRECT);
521} 521}
522 522
523static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
524{
525 struct brcmf_mbss_ssid_le mbss_ssid_le;
526 int bsscfgidx;
527 int err;
528
529 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
530 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
531 if (bsscfgidx < 0)
532 return bsscfgidx;
533
534 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
535 mbss_ssid_le.SSID_len = cpu_to_le32(5);
536 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
537
538 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
539 sizeof(mbss_ssid_le));
540 if (err < 0)
541 brcmf_err("setting ssid failed %d\n", err);
542
543 return err;
544}
545
546/**
547 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
548 *
549 * @wiphy: wiphy device of new interface.
550 * @name: name of the new interface.
551 * @flags: not used.
552 * @params: contains mac address for AP device.
553 */
554static
555struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
556 u32 *flags, struct vif_params *params)
557{
558 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
559 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
560 struct brcmf_cfg80211_vif *vif;
561 int err;
562
563 if (brcmf_cfg80211_vif_event_armed(cfg))
564 return ERR_PTR(-EBUSY);
565
566 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
567
568 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
569 if (IS_ERR(vif))
570 return (struct wireless_dev *)vif;
571
572 brcmf_cfg80211_arm_vif_event(cfg, vif);
573
574 err = brcmf_cfg80211_request_ap_if(ifp);
575 if (err) {
576 brcmf_cfg80211_arm_vif_event(cfg, NULL);
577 goto fail;
578 }
579
580 /* wait for firmware event */
581 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
582 msecs_to_jiffies(1500));
583 brcmf_cfg80211_arm_vif_event(cfg, NULL);
584 if (!err) {
585 brcmf_err("timeout occurred\n");
586 err = -EIO;
587 goto fail;
588 }
589
590 /* interface created in firmware */
591 ifp = vif->ifp;
592 if (!ifp) {
593 brcmf_err("no if pointer provided\n");
594 err = -ENOENT;
595 goto fail;
596 }
597
598 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
599 err = brcmf_net_attach(ifp, true);
600 if (err) {
601 brcmf_err("Registering netdevice failed\n");
602 goto fail;
603 }
604
605 return &ifp->vif->wdev;
606
607fail:
608 brcmf_free_vif(vif);
609 return ERR_PTR(err);
610}
611
523static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) 612static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
524{ 613{
525 enum nl80211_iftype iftype; 614 enum nl80211_iftype iftype;
@@ -545,12 +634,16 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
545 switch (type) { 634 switch (type) {
546 case NL80211_IFTYPE_ADHOC: 635 case NL80211_IFTYPE_ADHOC:
547 case NL80211_IFTYPE_STATION: 636 case NL80211_IFTYPE_STATION:
548 case NL80211_IFTYPE_AP:
549 case NL80211_IFTYPE_AP_VLAN: 637 case NL80211_IFTYPE_AP_VLAN:
550 case NL80211_IFTYPE_WDS: 638 case NL80211_IFTYPE_WDS:
551 case NL80211_IFTYPE_MONITOR: 639 case NL80211_IFTYPE_MONITOR:
552 case NL80211_IFTYPE_MESH_POINT: 640 case NL80211_IFTYPE_MESH_POINT:
553 return ERR_PTR(-EOPNOTSUPP); 641 return ERR_PTR(-EOPNOTSUPP);
642 case NL80211_IFTYPE_AP:
643 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
644 if (!IS_ERR(wdev))
645 brcmf_cfg80211_update_proto_addr_mode(wdev);
646 return wdev;
554 case NL80211_IFTYPE_P2P_CLIENT: 647 case NL80211_IFTYPE_P2P_CLIENT:
555 case NL80211_IFTYPE_P2P_GO: 648 case NL80211_IFTYPE_P2P_GO:
556 case NL80211_IFTYPE_P2P_DEVICE: 649 case NL80211_IFTYPE_P2P_DEVICE:
@@ -3361,11 +3454,10 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3361} 3454}
3362 3455
3363static s32 3456static s32
3364brcmf_configure_wpaie(struct net_device *ndev, 3457brcmf_configure_wpaie(struct brcmf_if *ifp,
3365 const struct brcmf_vs_tlv *wpa_ie, 3458 const struct brcmf_vs_tlv *wpa_ie,
3366 bool is_rsn_ie) 3459 bool is_rsn_ie)
3367{ 3460{
3368 struct brcmf_if *ifp = netdev_priv(ndev);
3369 u32 auth = 0; /* d11 open authentication */ 3461 u32 auth = 0; /* d11 open authentication */
3370 u16 count; 3462 u16 count;
3371 s32 err = 0; 3463 s32 err = 0;
@@ -3840,6 +3932,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3840 enum nl80211_iftype dev_role; 3932 enum nl80211_iftype dev_role;
3841 struct brcmf_fil_bss_enable_le bss_enable; 3933 struct brcmf_fil_bss_enable_le bss_enable;
3842 u16 chanspec; 3934 u16 chanspec;
3935 bool mbss;
3843 3936
3844 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", 3937 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
3845 settings->chandef.chan->hw_value, 3938 settings->chandef.chan->hw_value,
@@ -3850,6 +3943,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3850 settings->inactivity_timeout); 3943 settings->inactivity_timeout);
3851 3944
3852 dev_role = ifp->vif->wdev.iftype; 3945 dev_role = ifp->vif->wdev.iftype;
3946 mbss = ifp->vif->mbss;
3853 3947
3854 memset(&ssid_le, 0, sizeof(ssid_le)); 3948 memset(&ssid_le, 0, sizeof(ssid_le));
3855 if (settings->ssid == NULL || settings->ssid_len == 0) { 3949 if (settings->ssid == NULL || settings->ssid_len == 0) {
@@ -3869,8 +3963,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3869 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); 3963 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
3870 } 3964 }
3871 3965
3872 brcmf_set_mpc(ifp, 0); 3966 if (!mbss) {
3873 brcmf_configure_arp_offload(ifp, false); 3967 brcmf_set_mpc(ifp, 0);
3968 brcmf_configure_arp_offload(ifp, false);
3969 }
3874 3970
3875 /* find the RSN_IE */ 3971 /* find the RSN_IE */
3876 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, 3972 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
@@ -3884,13 +3980,16 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3884 brcmf_dbg(TRACE, "WPA(2) IE is found\n"); 3980 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
3885 if (wpa_ie != NULL) { 3981 if (wpa_ie != NULL) {
3886 /* WPA IE */ 3982 /* WPA IE */
3887 err = brcmf_configure_wpaie(ndev, wpa_ie, false); 3983 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
3888 if (err < 0) 3984 if (err < 0)
3889 goto exit; 3985 goto exit;
3890 } else { 3986 } else {
3987 struct brcmf_vs_tlv *tmp_ie;
3988
3989 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
3990
3891 /* RSN IE */ 3991 /* RSN IE */
3892 err = brcmf_configure_wpaie(ndev, 3992 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
3893 (struct brcmf_vs_tlv *)rsn_ie, true);
3894 if (err < 0) 3993 if (err < 0)
3895 goto exit; 3994 goto exit;
3896 } 3995 }
@@ -3901,45 +4000,53 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3901 4000
3902 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); 4001 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
3903 4002
3904 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); 4003 if (!mbss) {
3905 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); 4004 chanspec = chandef_to_chanspec(&cfg->d11inf,
3906 if (err < 0) { 4005 &settings->chandef);
3907 brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); 4006 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
3908 goto exit;
3909 }
3910
3911 if (settings->beacon_interval) {
3912 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
3913 settings->beacon_interval);
3914 if (err < 0) { 4007 if (err < 0) {
3915 brcmf_err("Beacon Interval Set Error, %d\n", err); 4008 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4009 chanspec, err);
3916 goto exit; 4010 goto exit;
3917 } 4011 }
3918 } 4012
3919 if (settings->dtim_period) { 4013 if (settings->beacon_interval) {
3920 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, 4014 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
3921 settings->dtim_period); 4015 settings->beacon_interval);
3922 if (err < 0) { 4016 if (err < 0) {
3923 brcmf_err("DTIM Interval Set Error, %d\n", err); 4017 brcmf_err("Beacon Interval Set Error, %d\n",
3924 goto exit; 4018 err);
4019 goto exit;
4020 }
4021 }
4022 if (settings->dtim_period) {
4023 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4024 settings->dtim_period);
4025 if (err < 0) {
4026 brcmf_err("DTIM Interval Set Error, %d\n", err);
4027 goto exit;
4028 }
3925 } 4029 }
3926 }
3927 4030
3928 if (dev_role == NL80211_IFTYPE_AP) { 4031 if (dev_role == NL80211_IFTYPE_AP) {
3929 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); 4032 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4033 if (err < 0) {
4034 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4035 goto exit;
4036 }
4037 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4038 }
4039
4040 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
3930 if (err < 0) { 4041 if (err < 0) {
3931 brcmf_err("BRCMF_C_DOWN error %d\n", err); 4042 brcmf_err("SET INFRA error %d\n", err);
3932 goto exit; 4043 goto exit;
3933 } 4044 }
3934 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
3935 }
3936
3937 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
3938 if (err < 0) {
3939 brcmf_err("SET INFRA error %d\n", err);
3940 goto exit;
3941 } 4045 }
3942 if (dev_role == NL80211_IFTYPE_AP) { 4046 if (dev_role == NL80211_IFTYPE_AP) {
4047 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4048 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4049
3943 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1); 4050 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
3944 if (err < 0) { 4051 if (err < 0) {
3945 brcmf_err("setting AP mode failed %d\n", err); 4052 brcmf_err("setting AP mode failed %d\n", err);
@@ -3984,7 +4091,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3984 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); 4091 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
3985 4092
3986exit: 4093exit:
3987 if (err) { 4094 if ((err) && (!mbss)) {
3988 brcmf_set_mpc(ifp, 1); 4095 brcmf_set_mpc(ifp, 1);
3989 brcmf_configure_arp_offload(ifp, true); 4096 brcmf_configure_arp_offload(ifp, true);
3990 } 4097 }
@@ -4005,20 +4112,31 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4005 /* first to make sure they get processed by fw. */ 4112 /* first to make sure they get processed by fw. */
4006 msleep(400); 4113 msleep(400);
4007 4114
4115 if (ifp->vif->mbss) {
4116 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4117 return err;
4118 }
4119
4008 memset(&join_params, 0, sizeof(join_params)); 4120 memset(&join_params, 0, sizeof(join_params));
4009 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, 4121 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4010 &join_params, sizeof(join_params)); 4122 &join_params, sizeof(join_params));
4011 if (err < 0) 4123 if (err < 0)
4012 brcmf_err("SET SSID error (%d)\n", err); 4124 brcmf_err("SET SSID error (%d)\n", err);
4013 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); 4125 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4014 if (err < 0) 4126 if (err < 0)
4015 brcmf_err("BRCMF_C_UP error %d\n", err); 4127 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4016 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); 4128 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4017 if (err < 0) 4129 if (err < 0)
4018 brcmf_err("setting AP mode failed %d\n", err); 4130 brcmf_err("setting AP mode failed %d\n", err);
4019 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); 4131 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4020 if (err < 0) 4132 if (err < 0)
4021 brcmf_err("setting INFRA mode failed %d\n", err); 4133 brcmf_err("setting INFRA mode failed %d\n", err);
4134 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4135 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
4136 /* Bring device back up so it can be used again */
4137 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4138 if (err < 0)
4139 brcmf_err("BRCMF_C_UP error %d\n", err);
4022 } else { 4140 } else {
4023 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); 4141 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4024 bss_enable.enable = cpu_to_le32(0); 4142 bss_enable.enable = cpu_to_le32(0);
@@ -4370,7 +4488,9 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
4370 enum nl80211_iftype type, 4488 enum nl80211_iftype type,
4371 bool pm_block) 4489 bool pm_block)
4372{ 4490{
4491 struct brcmf_cfg80211_vif *vif_walk;
4373 struct brcmf_cfg80211_vif *vif; 4492 struct brcmf_cfg80211_vif *vif;
4493 bool mbss;
4374 4494
4375 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", 4495 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
4376 sizeof(*vif)); 4496 sizeof(*vif));
@@ -4386,6 +4506,17 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
4386 4506
4387 brcmf_init_prof(&vif->profile); 4507 brcmf_init_prof(&vif->profile);
4388 4508
4509 if (type == NL80211_IFTYPE_AP) {
4510 mbss = false;
4511 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4512 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4513 mbss = true;
4514 break;
4515 }
4516 }
4517 vif->mbss = mbss;
4518 }
4519
4389 list_add_tail(&vif->list, &cfg->vif_list); 4520 list_add_tail(&vif->list, &cfg->vif_list);
4390 return vif; 4521 return vif;
4391} 4522}
@@ -4628,6 +4759,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
4628 struct net_device *ndev, 4759 struct net_device *ndev,
4629 const struct brcmf_event_msg *e, void *data) 4760 const struct brcmf_event_msg *e, void *data)
4630{ 4761{
4762 struct brcmf_if *ifp = netdev_priv(ndev);
4631 static int generation; 4763 static int generation;
4632 u32 event = e->event_code; 4764 u32 event = e->event_code;
4633 u32 reason = e->reason; 4765 u32 reason = e->reason;
@@ -4638,6 +4770,8 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
4638 ndev != cfg_to_ndev(cfg)) { 4770 ndev != cfg_to_ndev(cfg)) {
4639 brcmf_dbg(CONN, "AP mode link down\n"); 4771 brcmf_dbg(CONN, "AP mode link down\n");
4640 complete(&cfg->vif_disabled); 4772 complete(&cfg->vif_disabled);
4773 if (ifp->vif->mbss)
4774 brcmf_remove_interface(ifp->drvr, ifp->bssidx);
4641 return 0; 4775 return 0;
4642 } 4776 }
4643 4777
@@ -5429,7 +5563,28 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy)
5429 return 0; 5563 return 0;
5430} 5564}
5431 5565
5432static const struct ieee80211_iface_limit brcmf_iface_limits[] = { 5566static const struct ieee80211_iface_limit brcmf_iface_limits_mbss[] = {
5567 {
5568 .max = 1,
5569 .types = BIT(NL80211_IFTYPE_STATION) |
5570 BIT(NL80211_IFTYPE_ADHOC)
5571 },
5572 {
5573 .max = 4,
5574 .types = BIT(NL80211_IFTYPE_AP)
5575 },
5576 {
5577 .max = 1,
5578 .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5579 BIT(NL80211_IFTYPE_P2P_GO)
5580 },
5581 {
5582 .max = 1,
5583 .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
5584 }
5585};
5586
5587static const struct ieee80211_iface_limit brcmf_iface_limits_sbss[] = {
5433 { 5588 {
5434 .max = 2, 5589 .max = 2,
5435 .types = BIT(NL80211_IFTYPE_STATION) | 5590 .types = BIT(NL80211_IFTYPE_STATION) |
@@ -5450,8 +5605,8 @@ static struct ieee80211_iface_combination brcmf_iface_combos[] = {
5450 { 5605 {
5451 .max_interfaces = BRCMF_IFACE_MAX_CNT, 5606 .max_interfaces = BRCMF_IFACE_MAX_CNT,
5452 .num_different_channels = 1, 5607 .num_different_channels = 1,
5453 .n_limits = ARRAY_SIZE(brcmf_iface_limits), 5608 .n_limits = ARRAY_SIZE(brcmf_iface_limits_sbss),
5454 .limits = brcmf_iface_limits 5609 .limits = brcmf_iface_limits_sbss,
5455 } 5610 }
5456}; 5611};
5457 5612
@@ -5527,6 +5682,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
5527 ifc_combo = brcmf_iface_combos[0]; 5682 ifc_combo = brcmf_iface_combos[0];
5528 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) 5683 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5529 ifc_combo.num_different_channels = 2; 5684 ifc_combo.num_different_channels = 2;
5685 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
5686 ifc_combo.n_limits = ARRAY_SIZE(brcmf_iface_limits_mbss),
5687 ifc_combo.limits = brcmf_iface_limits_mbss;
5688 }
5530 wiphy->iface_combinations = kmemdup(&ifc_combo, 5689 wiphy->iface_combinations = kmemdup(&ifc_combo,
5531 sizeof(ifc_combo), 5690 sizeof(ifc_combo),
5532 GFP_KERNEL); 5691 GFP_KERNEL);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
index 2a5b22cb3fef..9e98b8d52757 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
@@ -183,6 +183,7 @@ struct vif_saved_ie {
183 * @pm_block: power-management blocked. 183 * @pm_block: power-management blocked.
184 * @list: linked list. 184 * @list: linked list.
185 * @mgmt_rx_reg: registered rx mgmt frame types. 185 * @mgmt_rx_reg: registered rx mgmt frame types.
186 * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
186 */ 187 */
187struct brcmf_cfg80211_vif { 188struct brcmf_cfg80211_vif {
188 struct brcmf_if *ifp; 189 struct brcmf_if *ifp;
@@ -194,6 +195,7 @@ struct brcmf_cfg80211_vif {
194 struct vif_saved_ie saved_ie; 195 struct vif_saved_ie saved_ie;
195 struct list_head list; 196 struct list_head list;
196 u16 mgmt_rx_reg; 197 u16 mgmt_rx_reg;
198 bool mbss;
197}; 199};
198 200
199/* association inform */ 201/* association inform */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c
index f407665cb1ea..effe6d7831d9 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
@@ -836,7 +836,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
836 return ifp; 836 return ifp;
837} 837}
838 838
839void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) 839static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
840{ 840{
841 struct brcmf_if *ifp; 841 struct brcmf_if *ifp;
842 842
@@ -869,6 +869,38 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
869 } 869 }
870} 870}
871 871
872void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx)
873{
874 if (drvr->iflist[bssidx]) {
875 brcmf_fws_del_interface(drvr->iflist[bssidx]);
876 brcmf_del_if(drvr, bssidx);
877 }
878}
879
880int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
881{
882 int ifidx;
883 int bsscfgidx;
884 bool available;
885 int highest;
886
887 available = false;
888 bsscfgidx = 2;
889 highest = 2;
890 for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
891 if (drvr->iflist[ifidx]) {
892 if (drvr->iflist[ifidx]->bssidx == bsscfgidx)
893 bsscfgidx = highest + 1;
894 else if (drvr->iflist[ifidx]->bssidx > highest)
895 highest = drvr->iflist[ifidx]->bssidx;
896 } else {
897 available = true;
898 }
899 }
900
901 return available ? bsscfgidx : -ENOMEM;
902}
903
872int brcmf_attach(struct device *dev) 904int brcmf_attach(struct device *dev)
873{ 905{
874 struct brcmf_pub *drvr = NULL; 906 struct brcmf_pub *drvr = NULL;
@@ -1033,10 +1065,7 @@ void brcmf_detach(struct device *dev)
1033 1065
1034 /* make sure primary interface removed last */ 1066 /* make sure primary interface removed last */
1035 for (i = BRCMF_MAX_IFS-1; i > -1; i--) 1067 for (i = BRCMF_MAX_IFS-1; i > -1; i--)
1036 if (drvr->iflist[i]) { 1068 brcmf_remove_interface(drvr, i);
1037 brcmf_fws_del_interface(drvr->iflist[i]);
1038 brcmf_del_if(drvr, i);
1039 }
1040 1069
1041 brcmf_cfg80211_detach(drvr->config); 1070 brcmf_cfg80211_detach(drvr->config);
1042 1071
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h
index 98228e922d3a..23f74b139cc8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
@@ -175,7 +175,8 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
175int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); 175int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
176struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, 176struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
177 char *name, u8 *mac_addr); 177 char *name, u8 *mac_addr);
178void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); 178void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx);
179int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
179void brcmf_txflowblock_if(struct brcmf_if *ifp, 180void brcmf_txflowblock_if(struct brcmf_if *ifp,
180 enum brcmf_netif_stop_reason reason, bool state); 181 enum brcmf_netif_stop_reason reason, bool state);
181void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, 182void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
index 931f68aefaa4..defb7a44e0bc 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
@@ -97,6 +97,28 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
97 } 97 }
98} 98}
99 99
100/**
101 * brcmf_feat_iovar_int_set() - determine feature through iovar set.
102 *
103 * @ifp: interface to query.
104 * @id: feature id.
105 * @name: iovar name.
106 */
107static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp,
108 enum brcmf_feat_id id, char *name, u32 val)
109{
110 int err;
111
112 err = brcmf_fil_iovar_int_set(ifp, name, val);
113 if (err == 0) {
114 brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
115 ifp->drvr->feat_flags |= BIT(id);
116 } else {
117 brcmf_dbg(TRACE, "%s feature check failed: %d\n",
118 brcmf_feat_names[id], err);
119 }
120}
121
100void brcmf_feat_attach(struct brcmf_pub *drvr) 122void brcmf_feat_attach(struct brcmf_pub *drvr)
101{ 123{
102 struct brcmf_if *ifp = drvr->iflist[0]; 124 struct brcmf_if *ifp = drvr->iflist[0];
@@ -104,6 +126,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
104 brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); 126 brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
105 if (drvr->bus_if->wowl_supported) 127 if (drvr->bus_if->wowl_supported)
106 brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); 128 brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
129 brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
107 130
108 /* set chip related quirks */ 131 /* set chip related quirks */
109 switch (drvr->bus_if->chip) { 132 switch (drvr->bus_if->chip) {
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
index b9a796d0a44d..f5832e077bb7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
@@ -22,6 +22,7 @@
22 * MCHAN: multi-channel for concurrent P2P. 22 * MCHAN: multi-channel for concurrent P2P.
23 */ 23 */
24#define BRCMF_FEAT_LIST \ 24#define BRCMF_FEAT_LIST \
25 BRCMF_FEAT_DEF(MBSS) \
25 BRCMF_FEAT_DEF(MCHAN) \ 26 BRCMF_FEAT_DEF(MCHAN) \
26 BRCMF_FEAT_DEF(WOWL) 27 BRCMF_FEAT_DEF(WOWL)
27/* 28/*
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
index 7338b335e153..ec62492ffa69 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
@@ -221,10 +221,8 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
221 221
222 err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); 222 err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
223 223
224 if (ifp && ifevent->action == BRCMF_E_IF_DEL) { 224 if (ifp && ifevent->action == BRCMF_E_IF_DEL)
225 brcmf_fws_del_interface(ifp); 225 brcmf_remove_interface(drvr, ifevent->bssidx);
226 brcmf_del_if(drvr, ifevent->bssidx);
227 }
228} 226}
229 227
230/** 228/**
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
index ba64b292f7a5..50891c02c4c1 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
@@ -519,4 +519,10 @@ struct brcmf_fil_wowl_pattern_le {
519 /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */ 519 /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */
520}; 520};
521 521
522struct brcmf_mbss_ssid_le {
523 __le32 bsscfgidx;
524 __le32 SSID_len;
525 unsigned char SSID[32];
526};
527
522#endif /* FWIL_TYPES_H_ */ 528#endif /* FWIL_TYPES_H_ */