brcmfmac: add creating station interface support
authorWright Feng <wright.feng@cypress.com>
Thu, 29 Sep 2022 05:06:11 +0000 (00:06 -0500)
committerKalle Valo <kvalo@kernel.org>
Wed, 5 Oct 2022 07:41:44 +0000 (10:41 +0300)
With RSDB device, it is able to control two station interfaces
concurrently. So we add creating station interface support and
allow user to create it via cfg80211.

Signed-off-by: Wright Feng <wright.feng@cypress.com>
Signed-off-by: Chi-hsien Lin <chi-hsien.lin@infineon.com>
Signed-off-by: Ian Lin <ian.lin@infineon.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20220929050614.31518-2-ian.lin@infineon.com
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h

index 362d8fd0af4e3e1f185e2314985e424203d6ce7d..6817e745434abec59647db9d3ecdc94759f4c072 100644 (file)
@@ -264,6 +264,19 @@ struct parsed_vndr_ies {
        struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
 };
 
+#define WL_INTERFACE_MAC_DONT_USE      0x0
+#define WL_INTERFACE_MAC_USE           0x2
+
+#define WL_INTERFACE_CREATE_STA                0x0
+#define WL_INTERFACE_CREATE_AP         0x1
+
+struct wl_interface_create {
+       u16     ver;
+       u32     flags;
+       u8      mac_addr[ETH_ALEN];
+       u8      pad[13];
+};
+
 static u8 nl80211_band_to_fwil(enum nl80211_band band)
 {
        switch (band) {
@@ -551,6 +564,42 @@ static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)
        return -ENOMEM;
 }
 
+static void brcmf_set_sta_iface_macaddr(struct brcmf_if *ifp,
+                                       struct wl_interface_create *iface)
+{
+       u8 mac_idx = ifp->drvr->sta_mac_idx;
+
+       /* set difference MAC address with locally administered bit */
+       iface->flags |= WL_INTERFACE_MAC_USE;
+       memcpy(iface->mac_addr, ifp->mac_addr, ETH_ALEN);
+       iface->mac_addr[0] |= 0x02;
+       iface->mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0;
+       mac_idx++;
+       mac_idx = mac_idx % 2;
+       ifp->drvr->sta_mac_idx = mac_idx;
+}
+
+static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr)
+{
+       struct wl_interface_create iface;
+       int err;
+
+       memset(&iface, 0, sizeof(iface));
+
+       iface.ver = 0;
+       iface.flags = WL_INTERFACE_CREATE_STA;
+       if (!is_zero_ether_addr(macaddr)) {
+               /* set MAC address in cfg80211 params */
+               memcpy(iface.mac_addr, macaddr, ETH_ALEN);
+       } else {
+               brcmf_set_sta_iface_macaddr(ifp, &iface);
+       }
+
+       err = brcmf_fil_iovar_data_get(ifp, "interface_create", &iface,
+                                      sizeof(iface));
+       return err;
+}
+
 static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
 {
        struct brcmf_pub *drvr = ifp->drvr;
@@ -576,15 +625,17 @@ static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
 }
 
 /**
- * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
+ * brcmf_apsta_add_vif() - create a new AP or STA virtual interface
  *
  * @wiphy: wiphy device of new interface.
  * @name: name of the new interface.
- * @params: contains mac address for AP device.
+ * @params: contains mac address for AP or STA device.
+ * @type: interface type.
  */
 static
-struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
-                                     struct vif_params *params)
+struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name,
+                                        struct vif_params *params,
+                                        enum nl80211_iftype type)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
@@ -592,18 +643,24 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
        struct brcmf_cfg80211_vif *vif;
        int err;
 
+       if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP)
+               return ERR_PTR(-EINVAL);
+
        if (brcmf_cfg80211_vif_event_armed(cfg))
                return ERR_PTR(-EBUSY);
 
        brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
 
-       vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP);
+       vif = brcmf_alloc_vif(cfg, type);
        if (IS_ERR(vif))
                return (struct wireless_dev *)vif;
 
        brcmf_cfg80211_arm_vif_event(cfg, vif);
 
-       err = brcmf_cfg80211_request_ap_if(ifp);
+       if (type == NL80211_IFTYPE_STATION)
+               err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr);
+       else
+               err = brcmf_cfg80211_request_ap_if(ifp);
        if (err) {
                brcmf_cfg80211_arm_vif_event(cfg, NULL);
                goto fail;
@@ -750,15 +807,15 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
        }
        switch (type) {
        case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_WDS:
        case NL80211_IFTYPE_MESH_POINT:
                return ERR_PTR(-EOPNOTSUPP);
        case NL80211_IFTYPE_MONITOR:
                return brcmf_mon_add_vif(wiphy, name);
+       case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_AP:
-               wdev = brcmf_ap_add_vif(wiphy, name, params);
+               wdev = brcmf_apsta_add_vif(wiphy, name, params, type);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_P2P_GO:
@@ -878,8 +935,8 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
        return err;
 }
 
-static int brcmf_cfg80211_del_ap_iface(struct wiphy *wiphy,
-                                      struct wireless_dev *wdev)
+static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy,
+                                         struct wireless_dev *wdev)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct net_device *ndev = wdev->netdev;
@@ -936,15 +993,15 @@ 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_VLAN:
        case NL80211_IFTYPE_WDS:
        case NL80211_IFTYPE_MESH_POINT:
                return -EOPNOTSUPP;
        case NL80211_IFTYPE_MONITOR:
                return brcmf_mon_del_vif(wiphy, wdev);
+       case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_AP:
-               return brcmf_cfg80211_del_ap_iface(wiphy, wdev);
+               return brcmf_cfg80211_del_apsta_iface(wiphy, wdev);
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_P2P_DEVICE:
index 340346c122d3278db69524ca237fd90dbe480a1a..2e71b5c2a9751d5b04e6c55a84761cf915d72cd0 100644 (file)
@@ -136,6 +136,7 @@ struct brcmf_pub {
        struct work_struct bus_reset;
 
        u8 clmver[BRCMF_DCMD_SMLEN];
+       u8 sta_mac_idx;
 };
 
 /* forward declarations */