Implementions the functions for creating, mananging and deleting various type of WLAN interfaces. Signed-off-by: Gokul Sivakumar --- .../net/wireless/infineon/inffmac/interface.c | 523 ++++++++++++++++++ .../net/wireless/infineon/inffmac/interface.h | 80 +++ 2 files changed, 603 insertions(+) create mode 100644 drivers/net/wireless/infineon/inffmac/interface.c create mode 100644 drivers/net/wireless/infineon/inffmac/interface.h diff --git a/drivers/net/wireless/infineon/inffmac/interface.c b/drivers/net/wireless/infineon/inffmac/interface.c new file mode 100644 index 000000000000..9fa4a78dd000 --- /dev/null +++ b/drivers/net/wireless/infineon/inffmac/interface.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Copyright (c) 2025, Infineon Technologies AG, or an affiliate of Infineon Technologies AG. + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "defs.h" +#include "chanspec.h" +#include "hw_ids.h" +#include "core.h" +#include "debug.h" +#include "tracepoint.h" +#include "fwil_types.h" +#include "p2p.h" +#include "btcoex.h" +#include "pno.h" +#include "fwsignal.h" +#include "cfg80211.h" +#include "feature.h" +#include "fwil.h" +#include "proto.h" +#include "vendor.h" +#include "vendor_inf.h" +#include "bus.h" +#include "common.h" +#include "he.h" +#include "eht.h" +#include "twt.h" +#include "offload.h" +#include "pmsr.h" + +bool inff_is_apmode_operating(struct wiphy *wiphy) +{ + struct inff_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct inff_cfg80211_vif *vif; + bool ret = false; + + list_for_each_entry(vif, &cfg->vif_list, list) { + if (inff_is_apmode(vif) && + test_bit(INFF_VIF_STATUS_AP_CREATED, &vif->sme_state)) + ret = true; + } + + return ret; +} + +bool inff_is_apmode(struct inff_cfg80211_vif *vif) +{ + enum nl80211_iftype iftype; + + iftype = vif->wdev.iftype; + return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; +} + +bool inff_is_ibssmode(struct inff_cfg80211_vif *vif) +{ + return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; +} + +bool check_vif_up(struct inff_cfg80211_vif *vif) +{ + if (!test_bit(INFF_VIF_STATUS_READY, &vif->sme_state)) { + inff_dbg(INFO, "device is not ready : status (%lu)\n", + vif->sme_state); + return false; + } + return true; +} + +enum nl80211_iftype inff_cfg80211_get_iftype(struct inff_if *ifp) +{ + struct wireless_dev *wdev = &ifp->vif->wdev; + + return wdev->iftype; +} + +static int inff_get_first_free_bsscfgidx(struct inff_pub *drvr) +{ + int bsscfgidx; + + for (bsscfgidx = 0; bsscfgidx < INFF_MAX_IFS; bsscfgidx++) { + /* bsscfgidx 1 is reserved for legacy P2P */ + if (bsscfgidx == 1) + continue; + if (!drvr->iflist[bsscfgidx]) + return bsscfgidx; + } + + return -ENOMEM; +} + +static void inff_set_vif_sta_macaddr(struct inff_if *ifp, u8 *mac_addr) +{ + u8 mac_idx = ifp->drvr->sta_mac_idx; + + /* set difference MAC address with locally administered bit */ + memcpy(mac_addr, ifp->mac_addr, ETH_ALEN); + mac_addr[0] |= 0x02; + mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0; + mac_idx++; + mac_idx = mac_idx % 2; + ifp->drvr->sta_mac_idx = mac_idx; +} + +static int inff_cfg80211_request_sta_if(struct inff_if *ifp, u8 *macaddr) +{ + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; + int err; + + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_STA | + WL_INTERFACE_MAC_USE; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN); + else + inff_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); + + err = inff_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + inff_dbg(INFO, "failed to create interface(v1), err=%d\n", + err); + } else { + inff_dbg(INFO, "interface created(v1)\n"); + return 0; + } + + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN); + else + inff_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = inff_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + inff_dbg(INFO, "failed to create interface(v2), err=%d\n", + err); + } else { + inff_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = inff_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + inff_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN); + else + inff_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = inff_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + inff_dbg(INFO, "interface created(v3)\n"); + break; + default: + inff_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + inff_info("station interface creation failed (%d)\n", + err); + return -EIO; + } + + return 0; +} + +static int inff_cfg80211_request_ap_if(struct inff_if *ifp) +{ + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; + struct inff_pub *drvr = ifp->drvr; + struct inff_mbss_ssid_le mbss_ssid_le; + int bsscfgidx; + int err; + + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_AP | + WL_INTERFACE_MAC_USE; + + inff_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); + + err = inff_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + inff_dbg(INFO, "failed to create interface(v1), err=%d\n", + err); + } else { + inff_dbg(INFO, "interface created(v1)\n"); + return 0; + } + + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_AP; + + inff_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = inff_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + inff_dbg(INFO, "failed to create interface(v2), err=%d\n", + err); + } else { + inff_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = inff_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + inff_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_AP; + inff_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = inff_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + inff_dbg(INFO, "interface created(v3)\n"); + break; + default: + inff_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + inff_info("Does not support interface_create (%d)\n", + err); + memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); + bsscfgidx = inff_get_first_free_bsscfgidx(ifp->drvr); + if (bsscfgidx < 0) + return bsscfgidx; + + mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); + mbss_ssid_le.SSID_len = cpu_to_le32(5); + sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx); + + err = inff_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, + sizeof(mbss_ssid_le)); + + if (err < 0) + iphy_err(drvr, "setting ssid failed %d\n", err); + } + + return err; +} + +/** + * inff_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 or STA device. + * @type: interface type. + * + * Return: pointer to new vif on success, ERR_PTR(-errno) if not + */ +struct wireless_dev *inff_apsta_add_vif(struct wiphy *wiphy, const char *name, + struct vif_params *params, + enum nl80211_iftype type) +{ + struct inff_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct inff_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct inff_pub *drvr = cfg->pub; + struct inff_cfg80211_vif *vif; + int err; + + if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP) + return ERR_PTR(-EINVAL); + + if (inff_cfg80211_vif_event_armed(cfg)) + return ERR_PTR(-EBUSY); + + inff_dbg(INFO, "Adding vif \"%s\"\n", name); + + vif = inff_alloc_vif(cfg, type); + if (IS_ERR(vif)) + return (struct wireless_dev *)vif; + + inff_cfg80211_arm_vif_event(cfg, vif); + + if (type == NL80211_IFTYPE_STATION) + err = inff_cfg80211_request_sta_if(ifp, params->macaddr); + else + err = inff_cfg80211_request_ap_if(ifp); + if (err) { + inff_cfg80211_arm_vif_event(cfg, NULL); + goto fail; + } + + /* wait for firmware event */ + err = inff_cfg80211_wait_vif_event(cfg, INFF_E_IF_ADD, + INFF_VIF_EVENT_TIMEOUT); + inff_cfg80211_arm_vif_event(cfg, NULL); + if (!err) { + iphy_err(drvr, "timeout occurred\n"); + err = -EIO; + goto fail; + } + + /* interface created in firmware */ + ifp = vif->ifp; + if (!ifp) { + iphy_err(drvr, "no if pointer provided\n"); + err = -ENOENT; + goto fail; + } + + strscpy(ifp->ndev->name, name, sizeof(ifp->ndev->name)); + err = inff_net_attach(ifp, true); + if (err) { + iphy_err(drvr, "Registering netdevice failed\n"); + free_netdev(ifp->ndev); + goto fail; + } + + return &ifp->vif->wdev; + +fail: + inff_free_vif(vif); + return ERR_PTR(err); +} + +/** + * inff_mon_add_vif() - create monitor mode virtual interface + * + * @wiphy: wiphy device of new interface. + * @name: name of the new interface. + * + * Return: pointer to new vif on success, ERR_PTR(-errno) if not + */ +struct wireless_dev *inff_mon_add_vif(struct wiphy *wiphy, const char *name) +{ + struct inff_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct inff_cfg80211_vif *vif; + struct net_device *ndev; + struct inff_if *ifp; + int err; + + if (cfg->pub->mon_if) { + err = -EEXIST; + goto err_out; + } + + vif = inff_alloc_vif(cfg, NL80211_IFTYPE_MONITOR); + if (IS_ERR(vif)) { + err = PTR_ERR(vif); + goto err_out; + } + + ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, ether_setup); + if (!ndev) { + err = -ENOMEM; + goto err_free_vif; + } + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + ndev->ieee80211_ptr = &vif->wdev; + ndev->needs_free_netdev = true; + ndev->priv_destructor = inff_cfg80211_free_netdev; + SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); + + ifp = netdev_priv(ndev); + ifp->vif = vif; + ifp->ndev = ndev; + ifp->drvr = cfg->pub; + + vif->ifp = ifp; + vif->wdev.netdev = ndev; + + err = inff_net_mon_attach(ifp); + if (err) { + inff_err("Failed to attach %s device\n", ndev->name); + free_netdev(ndev); + goto err_free_vif; + } + + cfg->pub->mon_if = ifp; + + return &vif->wdev; + +err_free_vif: + inff_free_vif(vif); +err_out: + return ERR_PTR(err); +} + +int inff_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct inff_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = wdev->netdev; + + ndev->netdev_ops->ndo_stop(ndev); + + inff_net_detach(ndev, true); + + cfg->pub->mon_if = NULL; + + return 0; +} + +int inff_cfg80211_del_apsta_iface(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct inff_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = wdev->netdev; + struct inff_if *ifp = netdev_priv(ndev); + struct inff_pub *drvr = cfg->pub; + int ret; + int err; + + inff_cfg80211_arm_vif_event(cfg, ifp->vif); + + err = inff_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0); + if (err) { + iphy_err(drvr, "interface_remove failed %d\n", err); + goto err_unarm; + } + + /* wait for firmware event */ + ret = inff_cfg80211_wait_vif_event(cfg, INFF_E_IF_DEL, INFF_VIF_EVENT_TIMEOUT); + if (!ret) { + iphy_err(drvr, "timeout occurred\n"); + err = -EIO; + goto err_unarm; + } + + inff_remove_interface(ifp, true); + +err_unarm: + inff_cfg80211_arm_vif_event(cfg, NULL); + return err; +} + +struct inff_cfg80211_vif *inff_alloc_vif(struct inff_cfg80211_info *cfg, + enum nl80211_iftype type) +{ + struct inff_cfg80211_vif *vif; + + inff_dbg(TRACE, "allocating virtual interface (size=%zu)\n", + sizeof(*vif)); + vif = kzalloc(sizeof(*vif), GFP_KERNEL); + if (!vif) + return ERR_PTR(-ENOMEM); + + vif->wdev.wiphy = cfg->wiphy; + vif->wdev.iftype = type; + + inff_init_prof(&vif->profile); + init_completion(&vif->mgmt_tx); + list_add_tail(&vif->list, &cfg->vif_list); + return vif; +} + +void inff_free_vif(struct inff_cfg80211_vif *vif) +{ + list_del(&vif->list); + kfree(vif); +} diff --git a/drivers/net/wireless/infineon/inffmac/interface.h b/drivers/net/wireless/infineon/inffmac/interface.h new file mode 100644 index 000000000000..3d35a67666f8 --- /dev/null +++ b/drivers/net/wireless/infineon/inffmac/interface.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Copyright (c) 2025, Infineon Technologies AG, or an affiliate of Infineon Technologies AG. + * All rights reserved. + */ + +#ifndef INFF_INTERFACE_H +#define INFF_INTERFACE_H + +#define WLC_E_IF_ROLE_STA 0 /* Infra STA */ +#define WLC_E_IF_ROLE_AP 1 /* Access Point */ +#define WLC_E_IF_ROLE_WLAN_SENSE 10 /* WLAN Sensing interface */ + +#define WL_INTERFACE_CREATE_VER_1 1 +#define WL_INTERFACE_CREATE_VER_2 2 +#define WL_INTERFACE_CREATE_VER_3 3 +#define WL_INTERFACE_CREATE_VER_MAX WL_INTERFACE_CREATE_VER_3 + +#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_v1 { + u16 ver; /* structure version */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v2 { + u16 ver; /* structure version */ + u8 pad1[2]; + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 iftype; /* type of interface created */ + u8 pad2; + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v3 { + u16 ver; /* structure version */ + u16 len; /* length of structure + data */ + u16 fixed_len; /* length of structure */ + u8 iftype; /* type of interface created */ + u8 wlc_index; /* optional for wlc index */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 bssid[ETH_ALEN]; /* optional for BSSID */ + u8 if_index; /* interface index request */ + u8 pad[3]; + u8 data[]; /* Optional for specific data */ +}; + +#define WL_IOV_OP_BSSCFG_DISABLE 0 +#define WL_IOV_OP_BSSCFG_ENABLE 1 +#define WL_IOV_OP_MANUAL_STA_BSSCFG_CREATE 2 +#define WL_IOV_OP_MANUAL_AP_BSSCFG_CREATE 3 + +bool check_vif_up(struct inff_cfg80211_vif *vif); +int inff_cfg80211_del_apsta_iface(struct wiphy *wiphy, struct wireless_dev *wdev); +struct wireless_dev *inff_mon_add_vif(struct wiphy *wiphy, const char *name); +int inff_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); + +struct wireless_dev *inff_apsta_add_vif(struct wiphy *wiphy, const char *name, + struct vif_params *params, + enum nl80211_iftype type); +enum nl80211_iftype inff_cfg80211_get_iftype(struct inff_if *ifp); + +struct inff_cfg80211_vif *inff_alloc_vif(struct inff_cfg80211_info *cfg, enum nl80211_iftype type); +void inff_free_vif(struct inff_cfg80211_vif *vif); +bool inff_is_apmode_operating(struct wiphy *wiphy); + +bool inff_is_apmode(struct inff_cfg80211_vif *vif); +bool inff_is_ibssmode(struct inff_cfg80211_vif *vif); + +#endif /* INFF_INTERFACE_H */ -- 2.25.1