| /****************************************************************************** |
| * |
| * This file is provided under a dual license. When you use or |
| * distribute this software, you may choose to be licensed under |
| * version 2 of the GNU General Public License ("GPLv2 License") |
| * or BSD License. |
| * |
| * GPLv2 License |
| * |
| * Copyright(C) 2016 MediaTek Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See http://www.gnu.org/licenses/gpl-2.0.html for more details. |
| * |
| * BSD LICENSE |
| * |
| * Copyright(C) 2016 MediaTek Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of the copyright holder nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| *****************************************************************************/ |
| /* |
| ** Id: @(#) gl_p2p.c@@ |
| */ |
| |
| /*! \file gl_p2p.c |
| * \brief Main routines of Linux driver interface for Wi-Fi Direct |
| * |
| * This file contains the main routines of Linux driver |
| * for MediaTek Inc. 802.11 Wireless LAN Adapters. |
| */ |
| |
| |
| /****************************************************************************** |
| * C O M P I L E R F L A G S |
| ****************************************************************************** |
| */ |
| |
| /****************************************************************************** |
| * E X T E R N A L R E F E R E N C E S |
| ****************************************************************************** |
| */ |
| |
| #include <linux/poll.h> |
| |
| #include <linux/kmod.h> |
| |
| |
| #include "gl_os.h" |
| #include "debug.h" |
| #include "wlan_lib.h" |
| #include "gl_wext.h" |
| |
| /* #include <net/cfg80211.h> */ |
| #include "gl_p2p_ioctl.h" |
| |
| #include "precomp.h" |
| #include "gl_vendor.h" |
| #include "gl_cfg80211.h" |
| |
| /****************************************************************************** |
| * C O N S T A N T S |
| ****************************************************************************** |
| */ |
| #define ARGV_MAX_NUM (4) |
| |
| /*For CFG80211 - wiphy parameters*/ |
| #define MAX_SCAN_LIST_NUM (1) |
| #define MAX_SCAN_IE_LEN (512) |
| |
| #if 0 |
| #define RUNNING_P2P_MODE 0 |
| #define RUNNING_AP_MODE 1 |
| #define RUNNING_DUAL_AP_MODE 2 |
| #endif |
| /****************************************************************************** |
| * D A T A T Y P E S |
| ****************************************************************************** |
| */ |
| |
| /****************************************************************************** |
| * P U B L I C D A T A |
| ****************************************************************************** |
| */ |
| |
| |
| /****************************************************************************** |
| * P R I V A T E D A T A |
| ****************************************************************************** |
| */ |
| |
| struct net_device *g_P2pPrDev; |
| struct wireless_dev *gprP2pWdev; |
| struct wireless_dev *gprP2pRoleWdev[KAL_P2P_NUM]; |
| struct net_device *gPrP2pDev[KAL_P2P_NUM]; |
| |
| #if CFG_ENABLE_WIFI_DIRECT_CFG_80211 |
| #if (CFG_ENABLE_UNIFY_WIPHY == 0) |
| static struct cfg80211_ops mtk_p2p_ops = { |
| #if (CFG_ENABLE_WIFI_DIRECT_CFG_80211 != 0) |
| /* Froyo */ |
| .add_virtual_intf = mtk_p2p_cfg80211_add_iface, |
| .change_virtual_intf = mtk_p2p_cfg80211_change_iface, /* 1 st */ |
| .del_virtual_intf = mtk_p2p_cfg80211_del_iface, |
| .change_bss = mtk_p2p_cfg80211_change_bss, |
| .scan = mtk_p2p_cfg80211_scan, |
| #if KERNEL_VERSION(4, 5, 0) <= CFG80211_VERSION_CODE |
| .abort_scan = mtk_p2p_cfg80211_abort_scan, |
| #endif |
| .remain_on_channel = mtk_p2p_cfg80211_remain_on_channel, |
| .cancel_remain_on_channel = mtk_p2p_cfg80211_cancel_remain_on_channel, |
| .mgmt_tx = mtk_p2p_cfg80211_mgmt_tx, |
| .mgmt_tx_cancel_wait = mtk_p2p_cfg80211_mgmt_tx_cancel_wait, |
| .connect = mtk_p2p_cfg80211_connect, |
| .disconnect = mtk_p2p_cfg80211_disconnect, |
| .deauth = mtk_p2p_cfg80211_deauth, |
| .disassoc = mtk_p2p_cfg80211_disassoc, |
| .start_ap = mtk_p2p_cfg80211_start_ap, |
| .change_beacon = mtk_p2p_cfg80211_change_beacon, |
| .stop_ap = mtk_p2p_cfg80211_stop_ap, |
| .set_wiphy_params = mtk_p2p_cfg80211_set_wiphy_params, |
| .del_station = mtk_p2p_cfg80211_del_station, |
| .set_bitrate_mask = mtk_p2p_cfg80211_set_bitrate_mask, |
| .mgmt_frame_register = mtk_p2p_cfg80211_mgmt_frame_register, |
| .get_station = mtk_p2p_cfg80211_get_station, |
| .add_key = mtk_p2p_cfg80211_add_key, |
| .get_key = mtk_p2p_cfg80211_get_key, |
| .del_key = mtk_p2p_cfg80211_del_key, |
| .set_default_key = mtk_p2p_cfg80211_set_default_key, |
| .set_default_mgmt_key = mtk_p2p_cfg80211_set_mgmt_key, |
| .join_ibss = mtk_p2p_cfg80211_join_ibss, |
| .leave_ibss = mtk_p2p_cfg80211_leave_ibss, |
| .set_tx_power = mtk_p2p_cfg80211_set_txpower, |
| .get_tx_power = mtk_p2p_cfg80211_get_txpower, |
| .set_power_mgmt = mtk_p2p_cfg80211_set_power_mgmt, |
| #if (CFG_SUPPORT_DFS_MASTER == 1) |
| .start_radar_detection = mtk_p2p_cfg80211_start_radar_detection, |
| #if KERNEL_VERSION(3, 13, 0) <= CFG80211_VERSION_CODE |
| .channel_switch = mtk_p2p_cfg80211_channel_switch, |
| #endif |
| #endif |
| #ifdef CONFIG_NL80211_TESTMODE |
| .testmode_cmd = mtk_p2p_cfg80211_testmode_cmd, |
| #endif |
| #endif |
| }; |
| #if KERNEL_VERSION(3, 18, 0) <= CFG80211_VERSION_CODE |
| |
| static const struct wiphy_vendor_command mtk_p2p_vendor_ops[] = { |
| { |
| { |
| .vendor_id = GOOGLE_OUI, |
| .subcmd = WIFI_SUBCMD_GET_CHANNEL_LIST |
| }, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
| | WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = mtk_cfg80211_vendor_get_channel_list, |
| VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA) |
| }, |
| { |
| { |
| .vendor_id = GOOGLE_OUI, |
| .subcmd = WIFI_SUBCMD_SET_COUNTRY_CODE |
| }, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
| | WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = mtk_cfg80211_vendor_set_country_code, |
| VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA) |
| }, |
| #if CFG_SUPPORT_P2P_PREFERRED_FREQ_LIST |
| /* P2P get preferred freq list */ |
| { |
| { |
| .vendor_id = OUI_QCA, |
| .subcmd = NL80211_VENDOR_SUBCMD_GET_PREFER_FREQ_LIST |
| }, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
| | WIPHY_VENDOR_CMD_NEED_NETDEV |
| | WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = mtk_cfg80211_vendor_get_preferred_freq_list, |
| VENDOR_OPS_SET_POLICY(VENDOR_CMD_RAW_DATA) |
| }, |
| #endif /* CFG_SUPPORT_P2P_PREFERRED_FREQ_LIST */ |
| #if CFG_AUTO_CHANNEL_SEL_SUPPORT |
| { |
| { |
| .vendor_id = OUI_QCA, |
| .subcmd = NL80211_VENDOR_SUBCMD_ACS |
| }, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
| | WIPHY_VENDOR_CMD_NEED_NETDEV |
| | WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = mtk_cfg80211_vendor_acs |
| }, |
| #endif |
| { |
| { |
| .vendor_id = OUI_QCA, |
| .subcmd = NL80211_VENDOR_SUBCMD_GET_FEATURES |
| }, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
| | WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = mtk_cfg80211_vendor_get_features |
| }, |
| }; |
| |
| |
| #endif |
| |
| /* There isn't a lot of sense in it, but you can transmit anything you like */ |
| static const struct ieee80211_txrx_stypes |
| mtk_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { |
| [NL80211_IFTYPE_ADHOC] = { |
| .tx = 0xffff, |
| .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
| }, |
| [NL80211_IFTYPE_STATION] = { |
| .tx = 0xffff, |
| .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
| | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
| }, |
| [NL80211_IFTYPE_AP] = { |
| .tx = 0xffff, |
| .rx = BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
| | BIT(IEEE80211_STYPE_ACTION >> 4) |
| }, |
| [NL80211_IFTYPE_AP_VLAN] = { |
| /* copy AP */ |
| .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) |
| }, |
| [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_PROBE_REQ >> 4) |
| | BIT(IEEE80211_STYPE_ACTION >> 4) |
| } |
| }; |
| |
| #endif |
| #endif |
| |
| #if 0 |
| static const struct iw_priv_args rP2PIwPrivTable[] = { |
| { |
| .cmd = IOC_P2P_CFG_DEVICE, |
| .set_args = IW_PRIV_TYPE_BYTE |
| | (__u16) sizeof(struct iw_p2p_cfg_device_type), |
| .get_args = IW_PRIV_TYPE_NONE, |
| .name = "P2P_CFG_DEVICE"} |
| , |
| { |
| .cmd = IOC_P2P_START_STOP_DISCOVERY, |
| .set_args = IW_PRIV_TYPE_BYTE |
| | (__u16) sizeof(struct iw_p2p_req_device_type), |
| .get_args = IW_PRIV_TYPE_NONE, |
| .name = "P2P_DISCOVERY"} |
| , |
| { |
| .cmd = IOC_P2P_DISCOVERY_RESULTS, |
| .set_args = IW_PRIV_TYPE_NONE, |
| .get_args = IW_PRIV_TYPE_NONE, |
| .name = "P2P_RESULT"} |
| , |
| { |
| .cmd = IOC_P2P_WSC_BEACON_PROBE_RSP_IE, |
| .set_args = IW_PRIV_TYPE_BYTE |
| | (__u16) sizeof(struct iw_p2p_hostapd_param), |
| .get_args = IW_PRIV_TYPE_NONE, |
| .name = "P2P_WSC_IE"} |
| , |
| { |
| .cmd = IOC_P2P_CONNECT_DISCONNECT, |
| .set_args = IW_PRIV_TYPE_BYTE |
| | (__u16) sizeof(struct iw_p2p_connect_device), |
| .get_args = IW_PRIV_TYPE_NONE, |
| .name = "P2P_CONNECT"} |
| , |
| { |
| .cmd = IOC_P2P_PASSWORD_READY, |
| .set_args = IW_PRIV_TYPE_BYTE |
| | (__u16) sizeof(struct iw_p2p_password_ready), |
| .get_args = IW_PRIV_TYPE_NONE, |
| .name = "P2P_PASSWD_RDY"} |
| , |
| { |
| .cmd = IOC_P2P_GET_STRUCT, |
| .set_args = IW_PRIV_TYPE_NONE, |
| .get_args = 256, |
| .name = "P2P_GET_STRUCT"} |
| , |
| { |
| .cmd = IOC_P2P_SET_STRUCT, |
| .set_args = 256, |
| .get_args = IW_PRIV_TYPE_NONE, |
| .name = "P2P_SET_STRUCT"} |
| , |
| { |
| .cmd = IOC_P2P_GET_REQ_DEVICE_INFO, |
| .set_args = IW_PRIV_TYPE_NONE, |
| .get_args = IW_PRIV_TYPE_BYTE |
| | (__u16) sizeof(struct iw_p2p_device_req), |
| .name = "P2P_GET_REQDEV"} |
| , |
| { |
| /* SET STRUCT sub-ioctls commands */ |
| .cmd = PRIV_CMD_OID, |
| .set_args = 256, |
| .get_args = IW_PRIV_TYPE_NONE, |
| .name = "set_oid"} |
| , |
| { |
| /* GET STRUCT sub-ioctls commands */ |
| .cmd = PRIV_CMD_OID, |
| .set_args = IW_PRIV_TYPE_NONE, |
| .get_args = 256, |
| .name = "get_oid"} |
| }; |
| |
| const struct iw_handler_def mtk_p2p_wext_handler_def = { |
| .num_standard = (__u16) sizeof(rP2PIwStandardHandler) |
| / sizeof(iw_handler), |
| /* .num_private = (__u16)sizeof(rP2PIwPrivHandler)/sizeof(iw_handler), */ |
| .num_private_args = (__u16) sizeof(rP2PIwPrivTable) |
| / sizeof(struct iw_priv_args), |
| .standard = rP2PIwStandardHandler, |
| /* .private = rP2PIwPrivHandler, */ |
| .private_args = rP2PIwPrivTable, |
| #if CFG_SUPPORT_P2P_RSSI_QUERY |
| .get_wireless_stats = mtk_p2p_wext_get_wireless_stats, |
| #else |
| .get_wireless_stats = NULL, |
| #endif |
| }; |
| #endif |
| |
| #ifdef CONFIG_PM |
| #if KERNEL_VERSION(3, 9, 0) > CFG80211_VERSION_CODE |
| static const struct wiphy_wowlan_support mtk_p2p_wowlan_support = { |
| .flags = WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_ANY, |
| }; |
| #endif |
| #endif |
| |
| static const struct ieee80211_iface_limit mtk_p2p_sta_go_limits[] = { |
| { |
| .max = 3, |
| .types = BIT(NL80211_IFTYPE_STATION), |
| }, |
| |
| { |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_P2P_GO) |
| | BIT(NL80211_IFTYPE_P2P_CLIENT), |
| }, |
| }; |
| |
| #if (CFG_SUPPORT_DFS_MASTER == 1) |
| #if (KERNEL_VERSION(3, 17, 0) > CFG80211_VERSION_CODE) |
| |
| static const struct ieee80211_iface_limit mtk_ap_limits[] = { |
| { |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_AP), |
| }, |
| }; |
| #endif |
| #endif |
| |
| static const struct ieee80211_iface_combination |
| mtk_iface_combinations_sta[] = { |
| { |
| #ifdef CFG_NUM_DIFFERENT_CHANNELS_STA |
| .num_different_channels = CFG_NUM_DIFFERENT_CHANNELS_STA, |
| #else |
| .num_different_channels = 2, |
| #endif /* CFG_NUM_DIFFERENT_CHANNELS_STA */ |
| .max_interfaces = 3, |
| /*.beacon_int_infra_match = true,*/ |
| .limits = mtk_p2p_sta_go_limits, |
| .n_limits = 1, /* include p2p */ |
| }, |
| }; |
| |
| static const struct ieee80211_iface_combination |
| mtk_iface_combinations_p2p[] = { |
| { |
| #if CFG_ENABLE_UNIFY_WIPHY |
| /* The 2 MCC channels case has been verified */ |
| .num_different_channels = 2, |
| #elif defined(CFG_NUM_DIFFERENT_CHANNELS_P2P) |
| .num_different_channels = CFG_NUM_DIFFERENT_CHANNELS_P2P, |
| #else |
| .num_different_channels = 2, |
| #endif /* CFG_NUM_DIFFERENT_CHANNELS_P2P */ |
| .max_interfaces = 3, |
| /*.beacon_int_infra_match = true,*/ |
| .limits = mtk_p2p_sta_go_limits, |
| .n_limits = ARRAY_SIZE(mtk_p2p_sta_go_limits), /* include p2p */ |
| }, |
| #if (CFG_SUPPORT_DFS_MASTER == 1) |
| #if (KERNEL_VERSION(3, 17, 0) > CFG80211_VERSION_CODE) |
| /* ONLY for passing checks in cfg80211_can_use_iftype_chan |
| * before linux-3.17.0 |
| */ |
| { |
| .num_different_channels = 1, |
| .max_interfaces = 1, |
| .limits = mtk_ap_limits, |
| .n_limits = ARRAY_SIZE(mtk_ap_limits), |
| .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | |
| BIT(NL80211_CHAN_WIDTH_20) | |
| BIT(NL80211_CHAN_WIDTH_40) | |
| BIT(NL80211_CHAN_WIDTH_80) | |
| BIT(NL80211_CHAN_WIDTH_80P80), |
| }, |
| #endif |
| #endif |
| }; |
| |
| |
| const struct ieee80211_iface_combination |
| *p_mtk_iface_combinations_sta = mtk_iface_combinations_sta; |
| const int32_t mtk_iface_combinations_sta_num = |
| ARRAY_SIZE(mtk_iface_combinations_sta); |
| |
| const struct ieee80211_iface_combination |
| *p_mtk_iface_combinations_p2p = mtk_iface_combinations_p2p; |
| const int32_t mtk_iface_combinations_p2p_num = |
| ARRAY_SIZE(mtk_iface_combinations_p2p); |
| |
| /****************************************************************************** |
| * M A C R O S |
| ****************************************************************************** |
| */ |
| |
| /****************************************************************************** |
| * F U N C T I O N D E C L A R A T I O N S |
| ****************************************************************************** |
| */ |
| |
| /* Net Device Hooks */ |
| static int p2pOpen(IN struct net_device *prDev); |
| |
| static int p2pStop(IN struct net_device *prDev); |
| |
| static struct net_device_stats *p2pGetStats(IN struct net_device *prDev); |
| |
| static void p2pSetMulticastList(IN struct net_device *prDev); |
| |
| static netdev_tx_t p2pHardStartXmit(IN struct sk_buff *prSkb, |
| IN struct net_device *prDev); |
| |
| static int p2pSetMACAddress(IN struct net_device *prDev, void *addr); |
| |
| static int p2pDoIOCTL(struct net_device *prDev, |
| struct ifreq *prIFReq, |
| int i4Cmd); |
| |
| #if KERNEL_VERSION(5, 15, 0) <= CFG80211_VERSION_CODE |
| static int p2pDoPrivIOCTL(struct net_device *prDev, struct ifreq *prIfReq, |
| void __user *prData, int i4Cmd); |
| #endif |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief A function for prDev->init |
| * |
| * \param[in] prDev Pointer to struct net_device. |
| * |
| * \retval 0 The execution of wlanInit succeeds. |
| * \retval -ENXIO No such device. |
| */ |
| /*---------------------------------------------------------------------------*/ |
| static int p2pInit(struct net_device *prDev) |
| { |
| if (!prDev) |
| return -ENXIO; |
| |
| return 0; /* success */ |
| } /* end of p2pInit() */ |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief A function for prDev->uninit |
| * |
| * \param[in] prDev Pointer to struct net_device. |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static void p2pUninit(IN struct net_device *prDev) |
| { |
| } /* end of p2pUninit() */ |
| |
| const struct net_device_ops p2p_netdev_ops = { |
| .ndo_open = p2pOpen, |
| .ndo_stop = p2pStop, |
| .ndo_set_mac_address = p2pSetMACAddress, |
| .ndo_set_rx_mode = p2pSetMulticastList, |
| .ndo_get_stats = p2pGetStats, |
| .ndo_do_ioctl = p2pDoIOCTL, |
| #if KERNEL_VERSION(5, 15, 0) <= CFG80211_VERSION_CODE |
| .ndo_siocdevprivate = p2pDoPrivIOCTL, |
| #endif |
| .ndo_start_xmit = p2pHardStartXmit, |
| /* .ndo_select_queue = p2pSelectQueue, */ |
| .ndo_select_queue = wlanSelectQueue, |
| .ndo_init = p2pInit, |
| .ndo_uninit = p2pUninit, |
| }; |
| |
| /****************************************************************************** |
| * F U N C T I O N S |
| ****************************************************************************** |
| */ |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief Allocate memory for P2P_INFO, GL_P2P_INFO, P2P_CONNECTION_SETTINGS |
| * P2P_SPECIFIC_BSS_INFO, P2P_FSM_INFO |
| * |
| * \param[in] prGlueInfo Pointer to glue info |
| * |
| * \return TRUE |
| * FALSE |
| */ |
| /*---------------------------------------------------------------------------*/ |
| u_int8_t p2PAllocInfo(IN struct GLUE_INFO *prGlueInfo, IN uint8_t ucIdex) |
| { |
| struct ADAPTER *prAdapter = NULL; |
| struct WIFI_VAR *prWifiVar = NULL; |
| /* UINT_32 u4Idx = 0; */ |
| |
| ASSERT(prGlueInfo); |
| |
| prAdapter = prGlueInfo->prAdapter; |
| prWifiVar = &(prAdapter->rWifiVar); |
| |
| ASSERT(prAdapter); |
| ASSERT(prWifiVar); |
| |
| do { |
| if (prGlueInfo->prP2PInfo[ucIdex] == NULL) { |
| /*alloc memory for p2p info */ |
| prGlueInfo->prP2PInfo[ucIdex] = |
| kalMemAlloc(sizeof(struct GL_P2P_INFO), |
| VIR_MEM_TYPE); |
| |
| if (prGlueInfo->prP2PDevInfo == NULL) { |
| prGlueInfo->prP2PDevInfo = |
| kalMemAlloc( |
| sizeof(struct GL_P2P_DEV_INFO), |
| VIR_MEM_TYPE); |
| if (prGlueInfo->prP2PDevInfo) { |
| kalMemZero(prGlueInfo->prP2PDevInfo, |
| sizeof(struct GL_P2P_DEV_INFO)); |
| } |
| } |
| |
| if (prAdapter->prP2pInfo == NULL) { |
| prAdapter->prP2pInfo = |
| kalMemAlloc(sizeof(struct P2P_INFO), |
| VIR_MEM_TYPE); |
| if (prAdapter->prP2pInfo) { |
| kalMemZero(prAdapter->prP2pInfo, |
| sizeof(struct P2P_INFO)); |
| } |
| } |
| |
| if (prWifiVar->prP2pDevFsmInfo == NULL) { |
| /* Don't only create P2P device for ucIdex 0. |
| * Avoid the exception that mtk_init_ap_role |
| * called without p2p0. |
| */ |
| prWifiVar->prP2pDevFsmInfo = |
| kalMemAlloc( |
| sizeof(struct P2P_DEV_FSM_INFO), |
| VIR_MEM_TYPE); |
| if (prWifiVar->prP2pDevFsmInfo) { |
| kalMemZero(prWifiVar->prP2pDevFsmInfo, |
| sizeof(struct |
| P2P_DEV_FSM_INFO)); |
| } |
| } |
| |
| prWifiVar->prP2PConnSettings[ucIdex] = |
| kalMemAlloc( |
| sizeof(struct P2P_CONNECTION_SETTINGS), |
| VIR_MEM_TYPE); |
| prWifiVar->prP2pSpecificBssInfo[ucIdex] = |
| kalMemAlloc( |
| sizeof(struct P2P_SPECIFIC_BSS_INFO), |
| VIR_MEM_TYPE); |
| #if CFG_ENABLE_PER_STA_STATISTICS_LOG |
| prWifiVar->prP2pQueryStaStatistics[ucIdex] = |
| kalMemAlloc( |
| sizeof(struct PARAM_GET_STA_STATISTICS), |
| VIR_MEM_TYPE); |
| #endif |
| /* TODO: It can be moved |
| * to the interface been created. |
| */ |
| #if 0 |
| for (u4Idx = 0; u4Idx < BSS_P2P_NUM; u4Idx++) { |
| prWifiVar->aprP2pRoleFsmInfo[u4Idx] = |
| kalMemAlloc(sizeof(struct P2P_ROLE_FSM_INFO), |
| VIR_MEM_TYPE); |
| } |
| #endif |
| } else { |
| ASSERT(prAdapter->prP2pInfo != NULL); |
| ASSERT(prWifiVar->prP2PConnSettings[ucIdex] != NULL); |
| /* ASSERT(prWifiVar->prP2pFsmInfo != NULL); */ |
| ASSERT(prWifiVar->prP2pSpecificBssInfo[ucIdex] != NULL); |
| } |
| /*MUST set memory to 0 */ |
| kalMemZero(prGlueInfo->prP2PInfo[ucIdex], |
| sizeof(struct GL_P2P_INFO)); |
| kalMemZero(prWifiVar->prP2PConnSettings[ucIdex], |
| sizeof(struct P2P_CONNECTION_SETTINGS)); |
| /* kalMemZero(prWifiVar->prP2pFsmInfo, sizeof(P2P_FSM_INFO_T)); */ |
| kalMemZero(prWifiVar->prP2pSpecificBssInfo[ucIdex], |
| sizeof(struct P2P_SPECIFIC_BSS_INFO)); |
| #if CFG_ENABLE_PER_STA_STATISTICS_LOG |
| if (prWifiVar->prP2pQueryStaStatistics[ucIdex]) |
| kalMemZero(prWifiVar->prP2pQueryStaStatistics[ucIdex], |
| sizeof(struct PARAM_GET_STA_STATISTICS)); |
| #endif |
| } while (FALSE); |
| |
| if (!prGlueInfo->prP2PDevInfo) |
| DBGLOG(P2P, ERROR, "prP2PDevInfo error\n"); |
| else |
| DBGLOG(P2P, TRACE, "prP2PDevInfo ok\n"); |
| |
| if (!prGlueInfo->prP2PInfo[ucIdex]) |
| DBGLOG(P2P, ERROR, "prP2PInfo error\n"); |
| else |
| DBGLOG(P2P, TRACE, "prP2PInfo ok\n"); |
| |
| |
| |
| /* chk if alloc successful or not */ |
| if (prGlueInfo->prP2PInfo[ucIdex] && |
| prGlueInfo->prP2PDevInfo && |
| prAdapter->prP2pInfo && |
| prWifiVar->prP2PConnSettings[ucIdex] && |
| /* prWifiVar->prP2pFsmInfo && */ |
| prWifiVar->prP2pSpecificBssInfo[ucIdex]) |
| return TRUE; |
| |
| |
| DBGLOG(P2P, ERROR, "[fail!]p2PAllocInfo :fail\n"); |
| |
| if (prWifiVar->prP2pSpecificBssInfo[ucIdex]) { |
| kalMemFree(prWifiVar->prP2pSpecificBssInfo[ucIdex], |
| VIR_MEM_TYPE, |
| sizeof(struct P2P_SPECIFIC_BSS_INFO)); |
| |
| prWifiVar->prP2pSpecificBssInfo[ucIdex] = NULL; |
| } |
| |
| #if CFG_ENABLE_PER_STA_STATISTICS_LOG |
| if (prWifiVar->prP2pQueryStaStatistics[ucIdex]) { |
| kalMemFree(prWifiVar->prP2pQueryStaStatistics[ucIdex], |
| VIR_MEM_TYPE, |
| sizeof(struct PARAM_GET_STA_STATISTICS)); |
| prWifiVar->prP2pQueryStaStatistics[ucIdex] = NULL; |
| } |
| #endif |
| |
| /* if (prWifiVar->prP2pFsmInfo) { */ |
| /* kalMemFree(prWifiVar->prP2pFsmInfo, |
| * VIR_MEM_TYPE, sizeof(P2P_FSM_INFO_T)); |
| */ |
| |
| /* prWifiVar->prP2pFsmInfo = NULL; */ |
| /* } */ |
| if (prWifiVar->prP2PConnSettings[ucIdex]) { |
| kalMemFree(prWifiVar->prP2PConnSettings[ucIdex], |
| VIR_MEM_TYPE, sizeof(struct P2P_CONNECTION_SETTINGS)); |
| |
| prWifiVar->prP2PConnSettings[ucIdex] = NULL; |
| } |
| if (prGlueInfo->prP2PDevInfo) { |
| kalMemFree(prGlueInfo->prP2PDevInfo, |
| VIR_MEM_TYPE, sizeof(struct GL_P2P_DEV_INFO)); |
| |
| prGlueInfo->prP2PDevInfo = NULL; |
| } |
| if (prGlueInfo->prP2PInfo[ucIdex]) { |
| kalMemFree(prGlueInfo->prP2PInfo[ucIdex], |
| VIR_MEM_TYPE, sizeof(struct GL_P2P_INFO)); |
| |
| prGlueInfo->prP2PInfo[ucIdex] = NULL; |
| } |
| if (prAdapter->prP2pInfo) { |
| kalMemFree(prAdapter->prP2pInfo, |
| VIR_MEM_TYPE, sizeof(struct P2P_INFO)); |
| |
| prAdapter->prP2pInfo = NULL; |
| } |
| return FALSE; |
| |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief Free memory for P2P_INFO, GL_P2P_INFO, P2P_CONNECTION_SETTINGS |
| * P2P_SPECIFIC_BSS_INFO, P2P_FSM_INFO |
| * |
| * \param[in] prGlueInfo Pointer to glue info |
| * [in] ucIdx The BSS with the idx will be freed. |
| * "ucIdx == 0xff" will free all BSSs. |
| * Only has meaning for "CFG_ENABLE_UNIFY_WIPHY == 1" |
| * |
| * \return TRUE |
| * FALSE |
| */ |
| /*---------------------------------------------------------------------------*/ |
| u_int8_t p2PFreeInfo(struct GLUE_INFO *prGlueInfo, uint8_t ucIdx) |
| { |
| struct ADAPTER *prAdapter = prGlueInfo->prAdapter; |
| |
| ASSERT(prGlueInfo); |
| ASSERT(prAdapter); |
| |
| if (ucIdx >= KAL_P2P_NUM) { |
| DBGLOG(P2P, ERROR, "ucIdx=%d is invalid\n", ucIdx); |
| return FALSE; |
| } |
| |
| /* Expect that prAdapter->prP2pInfo must be existing. */ |
| if (prAdapter->prP2pInfo == NULL) { |
| DBGLOG(P2P, ERROR, "prAdapter->prP2pInfo is NULL\n"); |
| return FALSE; |
| } |
| |
| /* TODO: how can I sure that the specific P2P device can be freed? |
| * The original check is that prGlueInfo->prAdapter->fgIsP2PRegistered. |
| * For one wiphy feature, this func may be called without |
| * (fgIsP2PRegistered == FALSE) condition. |
| */ |
| |
| if (prGlueInfo->prP2PInfo[ucIdx] != NULL) { |
| kalMemFree(prAdapter->rWifiVar.prP2PConnSettings[ucIdx], |
| VIR_MEM_TYPE, |
| sizeof(struct P2P_CONNECTION_SETTINGS)); |
| prAdapter->rWifiVar.prP2PConnSettings[ucIdx] = NULL; |
| |
| kalMemFree(prAdapter->rWifiVar.prP2pSpecificBssInfo[ucIdx], |
| VIR_MEM_TYPE, |
| sizeof(struct P2P_SPECIFIC_BSS_INFO)); |
| prAdapter->rWifiVar.prP2pSpecificBssInfo[ucIdx] = NULL; |
| |
| #if CFG_ENABLE_PER_STA_STATISTICS_LOG |
| kalMemFree(prAdapter->rWifiVar.prP2pQueryStaStatistics[ucIdx], |
| VIR_MEM_TYPE, |
| sizeof(struct PARAM_GET_STA_STATISTICS)); |
| prAdapter->rWifiVar.prP2pQueryStaStatistics[ucIdx] = NULL; |
| #endif |
| |
| #if (CFG_SUPPORT_DFS_MASTER == 1) |
| if (prGlueInfo->prP2PInfo[ucIdx]->chandef) { |
| if (prGlueInfo->prP2PInfo[ucIdx]->chandef->chan) { |
| cnmMemFree(prGlueInfo->prAdapter, |
| prGlueInfo->prP2PInfo[ucIdx] |
| ->chandef->chan); |
| prGlueInfo->prP2PInfo[ucIdx] |
| ->chandef->chan = NULL; |
| } |
| cnmMemFree(prGlueInfo->prAdapter, |
| prGlueInfo->prP2PInfo[ucIdx]->chandef); |
| prGlueInfo->prP2PInfo[ucIdx]->chandef = NULL; |
| } |
| #endif |
| kalMemFree(prGlueInfo->prP2PInfo[ucIdx], |
| VIR_MEM_TYPE, |
| sizeof(struct GL_P2P_INFO)); |
| prGlueInfo->prP2PInfo[ucIdx] = NULL; |
| |
| prAdapter->prP2pInfo->u4DeviceNum--; |
| } |
| |
| if (prAdapter->prP2pInfo->u4DeviceNum == 0) { |
| /* all prP2PInfo are freed, and free the general part now */ |
| |
| kalMemFree(prAdapter->prP2pInfo, VIR_MEM_TYPE, |
| sizeof(struct P2P_INFO)); |
| prAdapter->prP2pInfo = NULL; |
| |
| if (prGlueInfo->prP2PDevInfo) { |
| kalMemFree(prGlueInfo->prP2PDevInfo, VIR_MEM_TYPE, |
| sizeof(struct GL_P2P_DEV_INFO)); |
| prGlueInfo->prP2PDevInfo = NULL; |
| } |
| if (prAdapter->rWifiVar.prP2pDevFsmInfo) { |
| kalMemFree(prAdapter->rWifiVar.prP2pDevFsmInfo, |
| VIR_MEM_TYPE, sizeof(struct P2P_DEV_FSM_INFO)); |
| prAdapter->rWifiVar.prP2pDevFsmInfo = NULL; |
| } |
| |
| /* Reomve p2p bss scan list */ |
| scanRemoveAllP2pBssDesc(prAdapter); |
| } |
| |
| return TRUE; |
| } |
| |
| u_int8_t p2pNetRegister(struct GLUE_INFO *prGlueInfo, |
| u_int8_t fgIsRtnlLockAcquired) |
| { |
| u_int8_t fgDoRegister = FALSE; |
| u_int8_t fgRollbackRtnlLock = FALSE; |
| u_int8_t ret; |
| |
| GLUE_SPIN_LOCK_DECLARATION(); |
| |
| ASSERT(prGlueInfo); |
| ASSERT(prGlueInfo->prAdapter); |
| |
| GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV); |
| if (prGlueInfo->prAdapter->rP2PNetRegState |
| == ENUM_NET_REG_STATE_UNREGISTERED) { |
| prGlueInfo->prAdapter->rP2PNetRegState = |
| ENUM_NET_REG_STATE_REGISTERING; |
| fgDoRegister = TRUE; |
| } |
| GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV); |
| |
| if (!fgDoRegister) |
| return TRUE; |
| |
| if (fgIsRtnlLockAcquired && rtnl_is_locked()) { |
| fgRollbackRtnlLock = TRUE; |
| rtnl_unlock(); |
| } |
| |
| /* net device initialize */ |
| netif_carrier_off(prGlueInfo->prP2PInfo[0]->prDevHandler); |
| netif_tx_stop_all_queues(prGlueInfo->prP2PInfo[0]->prDevHandler); |
| |
| /* register for net device */ |
| if (register_netdev(prGlueInfo->prP2PInfo[0]->prDevHandler) < 0) { |
| DBGLOG(INIT, WARN, "unable to register netdevice for p2p\n"); |
| /* free dev in glUnregisterP2P() */ |
| /* free_netdev(prGlueInfo->prP2PInfo[0]->prDevHandler); */ |
| ret = FALSE; |
| } else { |
| prGlueInfo->prAdapter->rP2PNetRegState = |
| ENUM_NET_REG_STATE_REGISTERED; |
| gPrP2pDev[0] = prGlueInfo->prP2PInfo[0]->prDevHandler; |
| ret = TRUE; |
| } |
| |
| if (prGlueInfo->prAdapter->prP2pInfo->u4DeviceNum == KAL_P2P_NUM) { |
| /* net device initialize */ |
| netif_carrier_off(prGlueInfo->prP2PInfo[1]->prDevHandler); |
| netif_tx_stop_all_queues( |
| prGlueInfo->prP2PInfo[1]->prDevHandler); |
| |
| /* register for net device */ |
| if (register_netdev( |
| prGlueInfo->prP2PInfo[1]->prDevHandler) < 0) { |
| |
| DBGLOG(INIT, WARN, |
| "unable to register netdevice for p2p\n"); |
| |
| free_netdev(prGlueInfo->prP2PInfo[1]->prDevHandler); |
| |
| ret = FALSE; |
| } else { |
| prGlueInfo->prAdapter->rP2PNetRegState = |
| ENUM_NET_REG_STATE_REGISTERED; |
| gPrP2pDev[1] = prGlueInfo->prP2PInfo[1]->prDevHandler; |
| ret = TRUE; |
| } |
| |
| |
| DBGLOG(P2P, INFO, "P2P 2nd interface work\n"); |
| } |
| if (fgRollbackRtnlLock) |
| rtnl_lock(); |
| |
| return ret; |
| } |
| |
| u_int8_t p2pNetUnregister(struct GLUE_INFO *prGlueInfo, |
| u_int8_t fgIsRtnlLockAcquired) |
| { |
| u_int8_t fgDoUnregister = FALSE; |
| u_int8_t fgRollbackRtnlLock = FALSE; |
| uint8_t ucRoleIdx; |
| struct ADAPTER *prAdapter = NULL; |
| struct NETDEV_PRIVATE_GLUE_INFO *prNetDevPriv = NULL; |
| struct GL_P2P_INFO *prP2PInfo = NULL; |
| struct BSS_INFO *prP2pBssInfo = NULL; |
| int iftype = 0; |
| struct net_device *prRoleDev = NULL; |
| |
| GLUE_SPIN_LOCK_DECLARATION(); |
| |
| prAdapter = prGlueInfo->prAdapter; |
| |
| ASSERT(prGlueInfo); |
| ASSERT(prAdapter); |
| |
| GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV); |
| if (prAdapter->rP2PNetRegState == ENUM_NET_REG_STATE_REGISTERED) { |
| prAdapter->rP2PNetRegState = ENUM_NET_REG_STATE_UNREGISTERING; |
| fgDoUnregister = TRUE; |
| } |
| GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV); |
| |
| if (!fgDoUnregister) |
| return TRUE; |
| |
| if (fgIsRtnlLockAcquired && rtnl_is_locked()) |
| fgRollbackRtnlLock = TRUE; |
| |
| for (ucRoleIdx = 0; ucRoleIdx < KAL_P2P_NUM; ucRoleIdx++) { |
| prP2PInfo = prGlueInfo->prP2PInfo[ucRoleIdx]; |
| if (prP2PInfo == NULL) |
| continue; |
| |
| #if CFG_ENABLE_UNIFY_WIPHY |
| /* don't unregister the dev that share with the AIS */ |
| if (prP2PInfo->prDevHandler == gprWdev->netdev) |
| continue; |
| #endif |
| |
| prRoleDev = prP2PInfo->aprRoleHandler; |
| if (prRoleDev != NULL) { |
| /* info cfg80211 disconnect */ |
| prNetDevPriv = (struct NETDEV_PRIVATE_GLUE_INFO *) |
| netdev_priv(prRoleDev); |
| iftype = prRoleDev->ieee80211_ptr->iftype; |
| prP2pBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, |
| prNetDevPriv->ucBssIdx); |
| |
| /* FIXME: The p2pRoleFsmUninit may call the |
| * cfg80211_disconnected. |
| * p2pRemove()->glUnregisterP2P->p2pRoleFsmUninit(), |
| * it may be too late. |
| */ |
| if ((prP2pBssInfo != NULL) && |
| (prP2pBssInfo->eConnectionState == |
| PARAM_MEDIA_STATE_CONNECTED) && |
| ((iftype == NL80211_IFTYPE_P2P_CLIENT) || |
| (iftype == NL80211_IFTYPE_STATION))) { |
| #if CFG_WPS_DISCONNECT || (KERNEL_VERSION(4, 2, 0) <= CFG80211_VERSION_CODE) |
| cfg80211_disconnected(prRoleDev, 0, NULL, 0, |
| TRUE, GFP_KERNEL); |
| #else |
| cfg80211_disconnected(prRoleDev, 0, NULL, 0, |
| GFP_KERNEL); |
| #endif |
| } |
| |
| if (prRoleDev != prP2PInfo->prDevHandler) { |
| if (netif_carrier_ok(prRoleDev)) |
| netif_carrier_off(prRoleDev); |
| |
| netif_tx_stop_all_queues(prRoleDev); |
| } |
| } |
| |
| if (netif_carrier_ok(prP2PInfo->prDevHandler)) |
| netif_carrier_off(prP2PInfo->prDevHandler); |
| |
| netif_tx_stop_all_queues(prP2PInfo->prDevHandler); |
| |
| if (fgRollbackRtnlLock) |
| rtnl_unlock(); |
| |
| /* Here are the functions which need rtnl_lock */ |
| if ((prRoleDev) && (prP2PInfo->prDevHandler != prRoleDev)) { |
| DBGLOG(INIT, INFO, "unregister p2p[%d]\n", ucRoleIdx); |
| unregister_netdev(prRoleDev); |
| |
| /* This ndev is created in mtk_p2p_cfg80211_add_iface(), |
| * and unregister_netdev will also free the ndev. |
| */ |
| } |
| |
| DBGLOG(INIT, INFO, "unregister p2pdev[%d]\n", ucRoleIdx); |
| unregister_netdev(prP2PInfo->prDevHandler); |
| |
| if (fgRollbackRtnlLock) |
| rtnl_lock(); |
| } |
| |
| prGlueInfo->prAdapter->rP2PNetRegState = |
| ENUM_NET_REG_STATE_UNREGISTERED; |
| |
| return TRUE; |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief Setup the P2P device information |
| * |
| * \param[in] prGlueInfo Pointer to glue info |
| * [in] prP2pWdev Pointer to the wireless device |
| * [in] prP2pDev Pointer to the net device |
| * [in] u4Idx The P2P Role index (max : (KAL_P2P_NUM-1)) |
| * [in] fgIsApMode Indicate that this device is AP Role or not |
| * |
| * \return 0 Success |
| * -1 Failure |
| */ |
| /*---------------------------------------------------------------------------*/ |
| int glSetupP2P(struct GLUE_INFO *prGlueInfo, struct wireless_dev *prP2pWdev, |
| struct net_device *prP2pDev, int u4Idx, u_int8_t fgIsApMode) |
| { |
| struct ADAPTER *prAdapter = NULL; |
| struct GL_P2P_INFO *prP2PInfo = NULL; |
| struct GL_HIF_INFO *prHif = NULL; |
| struct NETDEV_PRIVATE_GLUE_INFO *prNetDevPriv = NULL; |
| struct mt66xx_chip_info *prChipInfo = NULL; |
| |
| DBGLOG(INIT, INFO, "setup the p2p dev\n"); |
| |
| if ((prGlueInfo == NULL) || |
| (prP2pWdev == NULL) || |
| (prP2pWdev->wiphy == NULL) || |
| (prP2pDev == NULL)) { |
| DBGLOG(INIT, ERROR, "parameter is NULL!!\n"); |
| return -1; |
| } |
| |
| prHif = &prGlueInfo->rHifInfo; |
| prAdapter = prGlueInfo->prAdapter; |
| |
| if ((prAdapter == NULL) || |
| (prHif == NULL)) { |
| DBGLOG(INIT, ERROR, "prAdapter/prHif is NULL!!\n"); |
| return -1; |
| } |
| |
| /* FIXME: check KAL_P2P_NUM in trunk? */ |
| if (u4Idx >= KAL_P2P_NUM) { |
| DBGLOG(INIT, ERROR, "u4Idx(%d) is out of range!!\n", u4Idx); |
| return -1; |
| } |
| |
| prChipInfo = prAdapter->chip_info; |
| |
| /*0. allocate p2pinfo */ |
| if (p2PAllocInfo(prGlueInfo, u4Idx) != TRUE) { |
| DBGLOG(INIT, WARN, "Allocate memory for p2p FAILED\n"); |
| ASSERT(0); |
| return -1; |
| } |
| |
| prP2PInfo = prGlueInfo->prP2PInfo[u4Idx]; |
| |
| #if CFG_ENABLE_WIFI_DIRECT_CFG_80211 |
| /* fill wiphy parameters */ |
| |
| prP2PInfo->prWdev = prP2pWdev; |
| |
| if (!prAdapter->fgEnable5GBand) |
| prP2pWdev->wiphy->bands[BAND_5G] = NULL; |
| |
| #endif /* CFG_ENABLE_WIFI_DIRECT_CFG_80211 */ |
| |
| /* setup netdev */ |
| /* Point to shared glue structure */ |
| prNetDevPriv = (struct NETDEV_PRIVATE_GLUE_INFO *) |
| netdev_priv(prP2pDev); |
| prNetDevPriv->prGlueInfo = prGlueInfo; |
| |
| /* set ucIsP2p for P2P function device */ |
| if (fgIsApMode == TRUE) { |
| prP2pWdev->iftype = NL80211_IFTYPE_AP; |
| #if CFG_ENABLE_UNIFY_WIPHY |
| prNetDevPriv->ucIsP2p = FALSE; |
| #endif |
| } else { |
| prP2pWdev->iftype = NL80211_IFTYPE_P2P_CLIENT; |
| #if CFG_ENABLE_UNIFY_WIPHY |
| prNetDevPriv->ucIsP2p = TRUE; |
| #endif |
| } |
| |
| /* register callback functions */ |
| prP2pDev->needed_headroom += |
| NIC_TX_DESC_AND_PADDING_LENGTH + prChipInfo->txd_append_size; |
| prP2pDev->netdev_ops = &p2p_netdev_ops; |
| #ifdef CONFIG_WIRELESS_EXT |
| prP2pDev->wireless_handlers = &wext_handler_def; |
| #endif |
| |
| #if defined(_HIF_SDIO) |
| #if (MTK_WCN_HIF_SDIO == 0) |
| SET_NETDEV_DEV(prP2pDev, &(prHif->func->dev)); |
| #endif |
| #endif |
| |
| #if CFG_ENABLE_WIFI_DIRECT_CFG_80211 |
| prP2pDev->ieee80211_ptr = prP2pWdev; |
| prP2pWdev->netdev = prP2pDev; |
| #endif |
| |
| #if CFG_TCP_IP_CHKSUM_OFFLOAD |
| /* set HW checksum offload */ |
| if (prAdapter->fgIsSupportCsumOffload) { |
| prP2pDev->features = NETIF_F_IP_CSUM | |
| NETIF_F_IPV6_CSUM | |
| NETIF_F_RXCSUM; |
| } |
| #endif /* CFG_TCP_IP_CHKSUM_OFFLOAD */ |
| |
| kalResetStats(prP2pDev); |
| |
| /* finish */ |
| /* bind netdev pointer to netdev index */ |
| prP2PInfo->prDevHandler = prP2pDev; |
| |
| /* XXX: All the P2P/AP devices do p2pDevFsmInit in the original code */ |
| wlanBindBssIdxToNetInterface(prGlueInfo, p2pDevFsmInit(prAdapter), |
| (void *) prP2PInfo->prDevHandler); |
| |
| prP2PInfo->aprRoleHandler = prP2PInfo->prDevHandler; |
| |
| DBGLOG(P2P, INFO, |
| "check prDevHandler = %p, aprRoleHandler = %p\n", |
| prP2PInfo->prDevHandler, prP2PInfo->aprRoleHandler); |
| |
| prNetDevPriv->ucBssIdx = p2pRoleFsmInit(prAdapter, u4Idx); |
| /* Currently wpasupplicant can't support create interface. */ |
| /* so initial the corresponding data structure here. */ |
| wlanBindBssIdxToNetInterface(prGlueInfo, prNetDevPriv->ucBssIdx, |
| (void *) prP2PInfo->aprRoleHandler); |
| |
| /* bind netdev pointer to netdev index */ |
| #if 0 |
| wlanBindNetInterface(prGlueInfo, NET_DEV_P2P_IDX, |
| (void *)prGlueInfo->prP2PInfo->prDevHandler); |
| #endif |
| |
| /* setup running mode */ |
| p2pFuncInitConnectionSettings(prAdapter, |
| prAdapter->rWifiVar.prP2PConnSettings[u4Idx], fgIsApMode); |
| |
| return 0; |
| } |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief Register for cfg80211 for Wi-Fi Direct |
| * |
| * \param[in] prGlueInfo Pointer to glue info |
| * |
| * \return TRUE |
| * FALSE |
| */ |
| /*---------------------------------------------------------------------------*/ |
| u_int8_t glRegisterP2P(struct GLUE_INFO *prGlueInfo, const char *prDevName, |
| const char *prDevName2, uint8_t ucApMode) |
| { |
| struct ADAPTER *prAdapter = NULL; |
| uint8_t rMacAddr[PARAM_MAC_ADDR_LEN]; |
| u_int8_t fgIsApMode = FALSE; |
| uint8_t ucRegisterNum = 1, i = 0; |
| struct wireless_dev *prP2pWdev = NULL; |
| struct net_device *prP2pDev = NULL; |
| struct wiphy *prWiphy = NULL; |
| const char *prSetDevName; |
| #if (CFG_ENABLE_UNIFY_WIPHY == 0) |
| struct GL_HIF_INFO *prHif = NULL; |
| struct device *prDev; |
| #endif |
| |
| ASSERT(prGlueInfo); |
| |
| prAdapter = prGlueInfo->prAdapter; |
| ASSERT(prAdapter); |
| |
| if ((ucApMode == RUNNING_DUAL_AP_MODE) || |
| (ucApMode == RUNNING_P2P_AP_MODE)) { |
| ucRegisterNum = 2; |
| glP2pCreateWirelessDevice(prGlueInfo); |
| } |
| |
| do { |
| if (ucApMode == RUNNING_P2P_AP_MODE) { |
| if (i == 0) { |
| prSetDevName = prDevName; |
| fgIsApMode = FALSE; |
| } else { |
| prSetDevName = prDevName2; |
| fgIsApMode = TRUE; |
| } |
| } else { |
| /* RUNNING_AP_MODE |
| * RUNNING_DUAL_AP_MODE |
| * RUNNING_P2P_MODE |
| */ |
| prSetDevName = prDevName; |
| |
| if (ucApMode == RUNNING_P2P_MODE) |
| fgIsApMode = FALSE; |
| else |
| fgIsApMode = TRUE; |
| } |
| |
| if (!gprP2pRoleWdev[i]) { |
| DBGLOG(P2P, ERROR, "gprP2pRoleWdev[%d] is NULL\n", i); |
| return FALSE; |
| } |
| |
| prP2pWdev = gprP2pRoleWdev[i]; |
| DBGLOG(INIT, INFO, "glRegisterP2P(%d)\n", i); |
| |
| /* Reset prP2pWdev for the issue that the prP2pWdev doesn't |
| * reset when the usb unplug/plug. |
| */ |
| prWiphy = prP2pWdev->wiphy; |
| memset(prP2pWdev, 0, sizeof(struct wireless_dev)); |
| prP2pWdev->wiphy = prWiphy; |
| |
| /* allocate netdev */ |
| #if KERNEL_VERSION(3, 17, 0) <= CFG80211_VERSION_CODE |
| prP2pDev = alloc_netdev_mq( |
| sizeof(struct NETDEV_PRIVATE_GLUE_INFO), |
| prSetDevName, NET_NAME_PREDICTABLE, |
| ether_setup, CFG_MAX_TXQ_NUM); |
| #else |
| prP2pDev = alloc_netdev_mq( |
| sizeof(struct NETDEV_PRIVATE_GLUE_INFO), |
| prSetDevName, |
| ether_setup, CFG_MAX_TXQ_NUM); |
| #endif |
| if (!prP2pDev) { |
| DBGLOG(INIT, WARN, "unable to allocate ndev for p2p\n"); |
| goto err_alloc_netdev; |
| } |
| |
| /* fill hardware address */ |
| COPY_MAC_ADDR(rMacAddr, prAdapter->rMyMacAddr); |
| rMacAddr[0] |= 0x2; |
| /* change to local administrated address */ |
| rMacAddr[0] ^= i << 2; |
| kalMemCopy(prP2pDev->dev_addr, rMacAddr, ETH_ALEN); |
| kalMemCopy(prP2pDev->perm_addr, prP2pDev->dev_addr, ETH_ALEN); |
| |
| if (glSetupP2P(prGlueInfo, prP2pWdev, prP2pDev, i, fgIsApMode) |
| != 0) { |
| DBGLOG(INIT, WARN, "glSetupP2P FAILED\n"); |
| free_netdev(prP2pDev); |
| return FALSE; |
| } |
| |
| #if (CFG_ENABLE_UNIFY_WIPHY == 0) |
| prHif = &prGlueInfo->rHifInfo; |
| glGetHifDev(prHif, &prDev); |
| if (!prDev) |
| DBGLOG(INIT, ERROR, "P2P[%d] parent dev is NULL\n", i); |
| set_wiphy_dev(prWiphy, prDev); |
| #endif |
| |
| i++; |
| /* prP2pInfo is alloc at glSetupP2P()->p2PAllocInfo() */ |
| prAdapter->prP2pInfo->u4DeviceNum++; |
| |
| /* set p2p net device register state */ |
| /* p2pNetRegister() will check prAdapter->rP2PNetRegState. */ |
| prAdapter->rP2PNetRegState = ENUM_NET_REG_STATE_UNREGISTERED; |
| } while (i < ucRegisterNum); |
| |
| return TRUE; |
| #if 0 |
| err_reg_netdev: |
| free_netdev(prGlueInfo->prP2PInfo->prDevHandler); |
| #endif |
| err_alloc_netdev: |
| return FALSE; |
| } /* end of glRegisterP2P() */ |
| |
| #if CFG_ENABLE_UNIFY_WIPHY |
| u_int8_t glP2pCreateWirelessDevice(struct GLUE_INFO *prGlueInfo) |
| { |
| struct wiphy *prWiphy = gprWdev->wiphy; |
| struct wireless_dev *prWdev = NULL; |
| uint8_t i = 0; |
| |
| #if CFG_ENABLE_WIFI_DIRECT_CFG_80211 |
| if (!prWiphy) { |
| DBGLOG(P2P, ERROR, "unable to allocate wiphy for p2p\n"); |
| return FALSE; |
| } |
| |
| for (i = 0 ; i < KAL_P2P_NUM; i++) { |
| if (!gprP2pRoleWdev[i]) |
| break; |
| } |
| |
| if (i >= KAL_P2P_NUM) { |
| DBGLOG(INIT, WARN, "fail to register wiphy to driver\n"); |
| return FALSE; |
| } |
| |
| prWdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); |
| if (!prWdev) { |
| DBGLOG(P2P, ERROR, "allocate p2p wdev fail, no memory\n"); |
| return FALSE; |
| } |
| |
| /* set priv as pointer to glue structure */ |
| prWdev->wiphy = prWiphy; |
| |
| gprP2pRoleWdev[i] = prWdev; |
| DBGLOG(INIT, INFO, "glP2pCreateWirelessDevice (%p)\n", |
| gprP2pRoleWdev[i]->wiphy); |
| |
| return TRUE; |
| #else |
| return FALSE; |
| #endif |
| } |
| #else /* (CFG_ENABLE_UNIFY_WIPHY == 0) */ |
| u_int8_t glP2pCreateWirelessDevice(struct GLUE_INFO *prGlueInfo) |
| { |
| struct wiphy *prWiphy = NULL; |
| struct wireless_dev *prWdev = NULL; |
| uint8_t i = 0; |
| #if CFG_ENABLE_WIFI_DIRECT_CFG_80211 |
| prWdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); |
| if (!prWdev) { |
| DBGLOG(P2P, ERROR, |
| "allocate p2p wireless device fail, no memory\n"); |
| return FALSE; |
| } |
| /* 1. allocate WIPHY */ |
| prWiphy = wiphy_new(&mtk_p2p_ops, sizeof(struct GLUE_INFO *)); |
| if (!prWiphy) { |
| DBGLOG(P2P, ERROR, "unable to allocate wiphy for p2p\n"); |
| goto free_wdev; |
| } |
| |
| prWiphy->interface_modes = BIT(NL80211_IFTYPE_AP) |
| | BIT(NL80211_IFTYPE_P2P_CLIENT) |
| | BIT(NL80211_IFTYPE_P2P_GO) |
| | BIT(NL80211_IFTYPE_STATION); |
| |
| prWiphy->software_iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE); |
| |
| prWiphy->iface_combinations = p_mtk_iface_combinations_p2p; |
| prWiphy->n_iface_combinations = mtk_iface_combinations_p2p_num; |
| |
| prWiphy->bands[KAL_BAND_2GHZ] = &mtk_band_2ghz; |
| prWiphy->bands[KAL_BAND_5GHZ] = &mtk_band_5ghz; |
| |
| prWiphy->mgmt_stypes = mtk_cfg80211_default_mgmt_stypes; |
| prWiphy->max_remain_on_channel_duration = 5000; |
| prWiphy->n_cipher_suites = 5; |
| prWiphy->cipher_suites = mtk_cipher_suites; |
| #if KERNEL_VERSION(3, 14, 0) > CFG80211_VERSION_CODE |
| prWiphy->flags = WIPHY_FLAG_CUSTOM_REGULATORY |
| | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
| | WIPHY_FLAG_HAVE_AP_SME; |
| #else |
| #if (CFG_SUPPORT_DFS_MASTER == 1) |
| prWiphy->flags = WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
| | WIPHY_FLAG_HAVE_AP_SME |
| | WIPHY_FLAG_HAS_CHANNEL_SWITCH; |
| prWiphy->max_num_csa_counters = 2; |
| #else |
| prWiphy->flags = WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
| | WIPHY_FLAG_HAVE_AP_SME; |
| #endif |
| prWiphy->regulatory_flags = REGULATORY_CUSTOM_REG; |
| #endif |
| prWiphy->ap_sme_capa = 1; |
| |
| prWiphy->max_scan_ssids = MAX_SCAN_LIST_NUM; |
| prWiphy->max_scan_ie_len = MAX_SCAN_IE_LEN; |
| prWiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; |
| #if KERNEL_VERSION(3, 18, 0) <= CFG80211_VERSION_CODE |
| prWiphy->vendor_commands = mtk_p2p_vendor_ops; |
| prWiphy->n_vendor_commands = sizeof(mtk_p2p_vendor_ops) |
| / sizeof(struct wiphy_vendor_command); |
| #endif |
| |
| #ifdef CONFIG_PM |
| #if KERNEL_VERSION(3, 9, 0) > CFG80211_VERSION_CODE |
| prWiphy->wowlan = &mtk_p2p_wowlan_support; |
| #endif |
| #endif |
| |
| cfg80211_regd_set_wiphy(prWiphy); |
| |
| /* 2.1 set priv as pointer to glue structure */ |
| *((struct GLUE_INFO **) wiphy_priv(prWiphy)) = prGlueInfo; |
| /* Here are functions which need rtnl_lock */ |
| if (wiphy_register(prWiphy) < 0) { |
| DBGLOG(INIT, WARN, "fail to register wiphy for p2p\n"); |
| goto free_wiphy; |
| } |
| prWdev->wiphy = prWiphy; |
| |
| for (i = 0 ; i < KAL_P2P_NUM; i++) { |
| if (!gprP2pRoleWdev[i]) { |
| gprP2pRoleWdev[i] = prWdev; |
| DBGLOG(INIT, INFO, |
| "glP2pCreateWirelessDevice (%x)\n", |
| gprP2pRoleWdev[i]->wiphy); |
| break; |
| } |
| } |
| |
| if (i == KAL_P2P_NUM) |
| DBGLOG(INIT, WARN, "fail to register wiphy to driver\n"); |
| |
| return TRUE; |
| |
| free_wiphy: |
| wiphy_free(prWiphy); |
| free_wdev: |
| kfree(prWdev); |
| #endif |
| return FALSE; |
| } |
| #endif /* CFG_ENABLE_UNIFY_WIPHY */ |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief Unregister Net Device for Wi-Fi Direct |
| * |
| * \param[in] prGlueInfo Pointer to glue info |
| * [in] ucIdx The BSS with the idx will be freed. |
| * "ucIdx == 0xff" will free all BSSs. |
| * Only has meaning for "CFG_ENABLE_UNIFY_WIPHY == 1" |
| * |
| * \return TRUE |
| * FALSE |
| */ |
| /*---------------------------------------------------------------------------*/ |
| u_int8_t glUnregisterP2P(struct GLUE_INFO *prGlueInfo, uint8_t ucIdx) |
| { |
| uint8_t ucRoleIdx; |
| struct ADAPTER *prAdapter; |
| struct GL_P2P_INFO *prP2PInfo = NULL; |
| int i4Start = 0, i4End = 0; |
| |
| ASSERT(prGlueInfo); |
| |
| if (ucIdx == 0xff) { |
| i4Start = 0; |
| i4End = BSS_P2P_NUM; |
| } else if (ucIdx < BSS_P2P_NUM) { |
| i4Start = ucIdx; |
| i4End = ucIdx + 1; |
| } else { |
| DBGLOG(INIT, WARN, "The ucIdx (%d) is a wrong value\n", ucIdx); |
| return FALSE; |
| } |
| |
| prAdapter = prGlueInfo->prAdapter; |
| |
| /* 4 <1> Uninit P2P dev FSM */ |
| /* Uninit P2P device FSM */ |
| /* only do p2pDevFsmUninit, when unregister all P2P device */ |
| if (ucIdx == 0xff) |
| p2pDevFsmUninit(prAdapter); |
| |
| /* 4 <2> Uninit P2P role FSM */ |
| for (ucRoleIdx = i4Start; ucRoleIdx < i4End; ucRoleIdx++) { |
| if (P2P_ROLE_INDEX_2_ROLE_FSM_INFO(prAdapter, ucRoleIdx)) { |
| /* FIXME: The cfg80211_XXX() is following the |
| * p2pRoleFsmUninit() sub-progress. |
| * ex: The cfg80211_del_sta() is called in the |
| * kalP2PGOStationUpdate(). |
| * But the netdev had be unregistered at |
| * p2pNetUnregister(). EXCEPTION!! |
| */ |
| p2pRoleFsmUninit(prGlueInfo->prAdapter, ucRoleIdx); |
| } |
| } |
| |
| /* 4 <3> Free Wiphy & netdev */ |
| for (ucRoleIdx = i4Start; ucRoleIdx < i4End; ucRoleIdx++) { |
| prP2PInfo = prGlueInfo->prP2PInfo[ucRoleIdx]; |
| |
| if (prP2PInfo == NULL) |
| continue; |
| /* For P2P interfaces, prDevHandler points to the net_device of |
| * p2p0 interface. And aprRoleHandler points to the net_device |
| * of p2p virtual interface (i.e., p2p1) when it was created. |
| * And when p2p virtual interface is deleted, aprRoleHandler |
| * will change to point to prDevHandler. Hence, when |
| * aprRoleHandler & prDevHandler are pointing to different |
| * addresses, it means vif p2p1 exists. Otherwise it means p2p1 |
| * was already deleted. |
| */ |
| if ((prP2PInfo->aprRoleHandler != NULL) && |
| (prP2PInfo->aprRoleHandler != prP2PInfo->prDevHandler)) { |
| /* This device is added by the P2P, and use |
| * ndev->destructor to free. The p2pDevFsmUninit() use |
| * prP2PInfo->aprRoleHandler to do some check. |
| */ |
| prP2PInfo->aprRoleHandler = NULL; |
| DBGLOG(P2P, INFO, "aprRoleHandler idx %d set NULL\n", |
| ucRoleIdx); |
| |
| /* Expect that gprP2pRoleWdev[ucRoleIdx] has been reset |
| * as gprP2pWdev or NULL in p2pNetUnregister |
| * (unregister_netdev). |
| */ |
| } |
| |
| if (prP2PInfo->prDevHandler) { |
| /* don't free the dev that share with the AIS */ |
| if (prP2PInfo->prDevHandler == gprWdev->netdev) |
| gprP2pRoleWdev[ucRoleIdx] = NULL; |
| else |
| free_netdev(prP2PInfo->prDevHandler); |
| prP2PInfo->prDevHandler = NULL; |
| } |
| |
| /* 4 <4> Free P2P internal memory */ |
| if (!p2PFreeInfo(prGlueInfo, ucRoleIdx)) { |
| /* FALSE: (fgIsP2PRegistered!=FALSE)||(ucRoleIdx err) */ |
| DBGLOG(INIT, ERROR, "p2PFreeInfo FAILED\n"); |
| ASSERT(0); |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } /* end of glUnregisterP2P() */ |
| |
| /* Net Device Hooks */ |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief A function for net_device open (ifup) |
| * |
| * \param[in] prDev Pointer to struct net_device. |
| * |
| * \retval 0 The execution succeeds. |
| * \retval < 0 The execution failed. |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static int p2pOpen(IN struct net_device *prDev) |
| { |
| /* P_GLUE_INFO_T prGlueInfo = NULL; */ |
| /* P_ADAPTER_T prAdapter = NULL; */ |
| /* P_MSG_P2P_FUNCTION_SWITCH_T prFuncSwitch; */ |
| |
| ASSERT(prDev); |
| |
| #if 0 /* Move after device name set. (mtk_p2p_set_local_dev_info) */ |
| prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev)); |
| ASSERT(prGlueInfo); |
| |
| prAdapter = prGlueInfo->prAdapter; |
| ASSERT(prAdapter); |
| |
| /* 1. switch P2P-FSM on */ |
| /* 1.1 allocate for message */ |
| prFuncSwitch = (P_MSG_P2P_FUNCTION_SWITCH_T) cnmMemAlloc(prAdapter, |
| RAM_TYPE_MSG, sizeof(MSG_P2P_FUNCTION_SWITCH_T)); |
| |
| if (!prFuncSwitch) { |
| ASSERT(0); /* Can't trigger P2P FSM */ |
| return -ENOMEM; |
| } |
| |
| /* 1.2 fill message */ |
| prFuncSwitch->rMsgHdr.eMsgId = MID_MNY_P2P_FUN_SWITCH; |
| prFuncSwitch->fgIsFuncOn = TRUE; |
| |
| /* 1.3 send message */ |
| mboxSendMsg(prAdapter, |
| MBOX_ID_0, |
| (struct MSG_HDR *) prFuncSwitch, |
| MSG_SEND_METHOD_BUF); |
| #endif |
| |
| /* 2. carrier on & start TX queue */ |
| /*DFS todo 20161220_DFS*/ |
| #if (CFG_SUPPORT_DFS_MASTER == 1) |
| if (prDev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) { |
| /*netif_carrier_on(prDev);*/ |
| netif_tx_start_all_queues(prDev); |
| } |
| #else |
| /*netif_carrier_on(prDev);*/ |
| netif_tx_start_all_queues(prDev); |
| #endif |
| |
| return 0; /* success */ |
| } /* end of p2pOpen() */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief A function for net_device stop (ifdown) |
| * |
| * \param[in] prDev Pointer to struct net_device. |
| * |
| * \retval 0 The execution succeeds. |
| * \retval < 0 The execution failed. |
| */ |
| /*----------------------------------------------------------------------------*/ |
| static int p2pStop(IN struct net_device *prDev) |
| { |
| struct GLUE_INFO *prGlueInfo = NULL; |
| struct ADAPTER *prAdapter = NULL; |
| struct GL_P2P_DEV_INFO *prP2pGlueDevInfo = NULL; |
| /* P_MSG_P2P_FUNCTION_SWITCH_T prFuncSwitch; */ |
| |
| GLUE_SPIN_LOCK_DECLARATION(); |
| |
| ASSERT(prDev); |
| |
| prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev)); |
| ASSERT(prGlueInfo); |
| |
| prAdapter = prGlueInfo->prAdapter; |
| ASSERT(prAdapter); |
| |
| /* XXX: The p2pStop may be triggered after the wlanRemove. */ |
| /* And prGlueInfo->prP2PDevInfo is freed in p2PFreeInfo. */ |
| if (!prAdapter->fgIsP2PRegistered) |
| return -EFAULT; |
| |
| prP2pGlueDevInfo = prGlueInfo->prP2PDevInfo; |
| ASSERT(prP2pGlueDevInfo); |
| |
| /* 0. Do the scan done and set parameter to abort if the scan pending */ |
| /*DBGLOG(INIT, INFO, "p2pStop and ucRoleIdx = %u\n", ucRoleIdx);*/ |
| |
| GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV); |
| if ((prP2pGlueDevInfo->prScanRequest != NULL) && |
| (prP2pGlueDevInfo->prScanRequest->wdev == prDev->ieee80211_ptr)) { |
| DBGLOG(INIT, INFO, "p2pStop and abort scan!!\n"); |
| kalCfg80211ScanDone(prP2pGlueDevInfo->prScanRequest, TRUE); |
| prP2pGlueDevInfo->prScanRequest = NULL; |
| } |
| GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV); |
| |
| /* 1. stop TX queue */ |
| netif_tx_stop_all_queues(prDev); |
| #if 0 |
| /* 2. switch P2P-FSM off */ |
| /* 2.1 allocate for message */ |
| prFuncSwitch = (P_MSG_P2P_FUNCTION_SWITCH_T) cnmMemAlloc(prAdapter, |
| RAM_TYPE_MSG, sizeof(MSG_P2P_FUNCTION_SWITCH_T)); |
| |
| if (!prFuncSwitch) { |
| ASSERT(0); /* Can't trigger P2P FSM */ |
| return -ENOMEM; |
| } |
| |
| /* 2.2 fill message */ |
| prFuncSwitch->rMsgHdr.eMsgId = MID_MNY_P2P_FUN_SWITCH; |
| prFuncSwitch->fgIsFuncOn = FALSE; |
| |
| /* 2.3 send message */ |
| mboxSendMsg(prAdapter, |
| MBOX_ID_0, |
| (struct MSG_HDR *) prFuncSwitch, |
| MSG_SEND_METHOD_BUF); |
| #endif |
| /* 3. stop queue and turn off carrier */ |
| /* TH3 multiple P2P */ |
| /*prGlueInfo->prP2PInfo[0]->eState = PARAM_MEDIA_STATE_DISCONNECTED;*/ |
| |
| netif_tx_stop_all_queues(prDev); |
| if (netif_carrier_ok(prDev)) |
| netif_carrier_off(prDev); |
| |
| return 0; |
| } /* end of p2pStop() */ |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief A method of struct net_device, |
| * to get the network interface statistical |
| * information. |
| * |
| * Whenever an application needs to get statistics for the interface, |
| * this method is called. |
| * This happens, for example, when ifconfig or netstat -i is run. |
| * |
| * \param[in] prDev Pointer to struct net_device. |
| * |
| * \return net_device_stats buffer pointer. |
| */ |
| /*---------------------------------------------------------------------------*/ |
| struct net_device_stats *p2pGetStats(IN struct net_device *prDev) |
| { |
| return (struct net_device_stats *)kalGetStats(prDev); |
| } /* end of p2pGetStats() */ |
| |
| static void p2pSetMulticastList(IN struct net_device *prDev) |
| { |
| struct GLUE_INFO *prGlueInfo = (struct GLUE_INFO *) NULL; |
| |
| prGlueInfo = (prDev != NULL) |
| ? *((struct GLUE_INFO **) netdev_priv(prDev)) |
| : NULL; |
| |
| ASSERT(prDev); |
| ASSERT(prGlueInfo); |
| if (!prDev || !prGlueInfo) { |
| DBGLOG(INIT, WARN, |
| " abnormal dev or skb: prDev(0x%p), prGlueInfo(0x%p)\n", |
| prDev, prGlueInfo); |
| return; |
| } |
| |
| g_P2pPrDev = prDev; |
| |
| /* 4 Mark HALT, notify main thread to finish current job */ |
| set_bit(GLUE_FLAG_SUB_MOD_MULTICAST_BIT, &prGlueInfo->ulFlag); |
| /* wake up main thread */ |
| wake_up_interruptible(&prGlueInfo->waitq); |
| |
| } /* p2pSetMulticastList */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function is to set multicast list and set rx mode. |
| * |
| * \param[in] prDev Pointer to struct net_device |
| * |
| * \return (none) |
| */ |
| /*----------------------------------------------------------------------------*/ |
| void mtk_p2p_wext_set_Multicastlist(struct GLUE_INFO *prGlueInfo) |
| { |
| uint32_t u4SetInfoLen = 0; |
| uint32_t u4McCount; |
| struct net_device *prDev = g_P2pPrDev; |
| |
| prGlueInfo = (prDev != NULL) |
| ? *((struct GLUE_INFO **) netdev_priv(prDev)) |
| : NULL; |
| |
| ASSERT(prDev); |
| ASSERT(prGlueInfo); |
| if (!prDev || !prGlueInfo || !prGlueInfo->prP2PDevInfo) { |
| DBGLOG(INIT, WARN, |
| " abnormal dev or skb: prDev(0x%p), prGlueInfo(0x%p)\n", |
| prDev, prGlueInfo); |
| return; |
| } |
| |
| if (prDev->flags & IFF_PROMISC) |
| prGlueInfo->prP2PDevInfo->u4PacketFilter |
| |= PARAM_PACKET_FILTER_PROMISCUOUS; |
| |
| if (prDev->flags & IFF_BROADCAST) |
| prGlueInfo->prP2PDevInfo->u4PacketFilter |
| |= PARAM_PACKET_FILTER_BROADCAST; |
| u4McCount = netdev_mc_count(prDev); |
| |
| if (prDev->flags & IFF_MULTICAST) { |
| if ((prDev->flags & IFF_ALLMULTI) |
| || (u4McCount > MAX_NUM_GROUP_ADDR)) |
| prGlueInfo->prP2PDevInfo->u4PacketFilter |
| |= PARAM_PACKET_FILTER_ALL_MULTICAST; |
| else |
| prGlueInfo->prP2PDevInfo->u4PacketFilter |
| |= PARAM_PACKET_FILTER_MULTICAST; |
| } |
| |
| if (prGlueInfo->prP2PDevInfo->u4PacketFilter |
| & PARAM_PACKET_FILTER_MULTICAST) { |
| /* Prepare multicast address list */ |
| struct netdev_hw_addr *ha; |
| uint32_t i = 0; |
| |
| /* Avoid race condition with kernel net subsystem */ |
| netif_addr_lock_bh(prDev); |
| |
| netdev_for_each_mc_addr(ha, prDev) { |
| /* If ha is null, it will break the loop. */ |
| /* Check mc count before accessing to ha to |
| * prevent from kernel crash. |
| */ |
| if (i == u4McCount || !ha) |
| break; |
| if (i < MAX_NUM_GROUP_ADDR) { |
| COPY_MAC_ADDR( |
| &(prGlueInfo->prP2PDevInfo |
| ->aucMCAddrList[i]), |
| GET_ADDR(ha)); |
| i++; |
| } |
| } |
| |
| netif_addr_unlock_bh(prDev); |
| |
| DBGLOG(P2P, TRACE, "SEt Multicast Address List\n"); |
| |
| if (i >= MAX_NUM_GROUP_ADDR) |
| return; |
| wlanoidSetP2PMulticastList(prGlueInfo->prAdapter, |
| &(prGlueInfo->prP2PDevInfo->aucMCAddrList[0]), |
| (i * ETH_ALEN), &u4SetInfoLen); |
| |
| } |
| |
| } /* end of p2pSetMulticastList() */ |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief This function is TX entry point of NET DEVICE. |
| * |
| * \param[in] prSkb Pointer of the sk_buff to be sent |
| * \param[in] prDev Pointer to struct net_device |
| * |
| * \retval NETDEV_TX_OK - on success. |
| * \retval NETDEV_TX_BUSY - on failure, packet will be discarded by upper layer. |
| */ |
| /*---------------------------------------------------------------------------*/ |
| netdev_tx_t p2pHardStartXmit(IN struct sk_buff *prSkb, |
| IN struct net_device *prDev) |
| { |
| struct NETDEV_PRIVATE_GLUE_INFO *prNetDevPrivate = |
| (struct NETDEV_PRIVATE_GLUE_INFO *) NULL; |
| struct GLUE_INFO *prGlueInfo = NULL; |
| uint8_t ucBssIndex; |
| struct BSS_INFO *prP2pBssInfo = NULL; |
| |
| ASSERT(prSkb); |
| ASSERT(prDev); |
| |
| prNetDevPrivate = (struct NETDEV_PRIVATE_GLUE_INFO *) |
| netdev_priv(prDev); |
| prGlueInfo = prNetDevPrivate->prGlueInfo; |
| ucBssIndex = prNetDevPrivate->ucBssIdx; |
| |
| kalResetPacket(prGlueInfo, (void *) prSkb); |
| |
| kalHardStartXmit(prSkb, prDev, prGlueInfo, ucBssIndex); |
| prP2pBssInfo = GET_BSS_INFO_BY_INDEX(prGlueInfo->prAdapter, ucBssIndex); |
| if ((prP2pBssInfo->eConnectionState == PARAM_MEDIA_STATE_CONNECTED) || |
| (prP2pBssInfo->rStaRecOfClientList.u4NumElem > 0)) |
| kalPerMonStart(prGlueInfo); |
| |
| return NETDEV_TX_OK; |
| } /* end of p2pHardStartXmit() */ |
| |
| /*----------------------------------------------------------------------------*/ |
| /*! |
| * \brief A method of struct net_device, a primary SOCKET interface to configure |
| * the interface lively. Handle an ioctl call on one of our devices. |
| * Everything Linux ioctl specific is done here. |
| * Then we pass the contents |
| * of the ifr->data to the request message handler. |
| * |
| * \param[in] prDev Linux kernel netdevice |
| * |
| * \param[in] prIFReq Our private ioctl request structure, |
| * typed for the generic struct ifreq |
| * so we can use ptr to function |
| * |
| * \param[in] cmd Command ID |
| * |
| * \retval WLAN_STATUS_SUCCESS The IOCTL command is executed successfully. |
| * \retval OTHER The execution of IOCTL command is failed. |
| */ |
| /*----------------------------------------------------------------------------*/ |
| |
| int p2pDoIOCTL(struct net_device *prDev, struct ifreq *prIfReq, int i4Cmd) |
| { |
| struct GLUE_INFO *prGlueInfo = NULL; |
| int ret = 0; |
| /* char *prExtraBuf = NULL; */ |
| /* UINT_32 u4ExtraSize = 0; */ |
| /* struct iwreq *prIwReq = (struct iwreq *)prIfReq; */ |
| /* struct iw_request_info rIwReqInfo; */ |
| |
| ASSERT(prDev && prIfReq); |
| |
| prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev)); |
| if (!prGlueInfo) { |
| DBGLOG(P2P, ERROR, "prGlueInfo is NULL\n"); |
| return -EFAULT; |
| } |
| |
| if (prGlueInfo->u4ReadyFlag == 0) { |
| DBGLOG(P2P, ERROR, "Adapter is not ready\n"); |
| return -EINVAL; |
| } |
| |
| if (i4Cmd == SIOCGIWPRIV) { |
| ret = wext_support_ioctl(prDev, prIfReq, i4Cmd); |
| } else if ((i4Cmd >= SIOCIWFIRSTPRIV) && (i4Cmd < SIOCIWLASTPRIV)) { |
| /* 0x8BE0 ~ 0x8BFF, private ioctl region */ |
| ret = priv_support_ioctl(prDev, prIfReq, i4Cmd); |
| } else if (i4Cmd == SIOCDEVPRIVATE + 1) { |
| #ifdef CFG_ANDROID_AOSP_PRIV_CMD |
| ret = android_private_support_driver_cmd(prDev, prIfReq, i4Cmd); |
| #else |
| ret = priv_support_driver_cmd(prDev, prIfReq, i4Cmd); |
| #endif /* CFG_ANDROID_AOSP_PRIV_CMD */ |
| } else { |
| DBGLOG(INIT, WARN, "Unexpected ioctl command: 0x%04x\n", i4Cmd); |
| ret = -1; |
| } |
| |
| #if 0 |
| /* fill rIwReqInfo */ |
| rIwReqInfo.cmd = (__u16) i4Cmd; |
| rIwReqInfo.flags = 0; |
| |
| switch (i4Cmd) { |
| case SIOCSIWENCODEEXT: |
| /* Set Encryption Material after 4-way handshaking is done */ |
| if (prIwReq->u.encoding.pointer) { |
| u4ExtraSize = prIwReq->u.encoding.length; |
| prExtraBuf = kalMemAlloc(u4ExtraSize, VIR_MEM_TYPE); |
| |
| if (!prExtraBuf) { |
| ret = -ENOMEM; |
| break; |
| } |
| |
| if (copy_from_user(prExtraBuf, |
| prIwReq->u.encoding.pointer, |
| prIwReq->u.encoding.length)) |
| ret = -EFAULT; |
| } else if (prIwReq->u.encoding.length != 0) { |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (ret == 0) |
| ret = mtk_p2p_wext_set_key(prDev, |
| &rIwReqInfo, |
| &(prIwReq->u), prExtraBuf); |
| |
| kalMemFree(prExtraBuf, VIR_MEM_TYPE, u4ExtraSize); |
| prExtraBuf = NULL; |
| break; |
| |
| case SIOCSIWMLME: |
| /* IW_MLME_DISASSOC used for disconnection */ |
| if (prIwReq->u.data.length != sizeof(struct iw_mlme)) { |
| DBGLOG(INIT, INFO, |
| "MLME buffer strange:%d\n", |
| prIwReq->u.data.length); |
| ret = -EINVAL; |
| break; |
| } |
| |
| if (!prIwReq->u.data.pointer) { |
| ret = -EINVAL; |
| break; |
| } |
| |
| prExtraBuf = kalMemAlloc(sizeof(struct iw_mlme), VIR_MEM_TYPE); |
| if (!prExtraBuf) { |
| ret = -ENOMEM; |
| break; |
| } |
| |
| if (copy_from_user(prExtraBuf, |
| prIwReq->u.data.pointer, sizeof(struct iw_mlme))) |
| ret = -EFAULT; |
| else |
| ret = mtk_p2p_wext_mlme_handler(prDev, |
| &rIwReqInfo, &(prIwReq->u), prExtraBuf); |
| |
| kalMemFree(prExtraBuf, VIR_MEM_TYPE, sizeof(struct iw_mlme)); |
| prExtraBuf = NULL; |
| break; |
| |
| case SIOCGIWPRIV: |
| /* This ioctl is used to list all IW privilege ioctls */ |
| ret = mtk_p2p_wext_get_priv(prDev, |
| &rIwReqInfo, &(prIwReq->u), NULL); |
| break; |
| |
| case SIOCGIWSCAN: |
| ret = mtk_p2p_wext_discovery_results(prDev, |
| &rIwReqInfo, &(prIwReq->u), NULL); |
| break; |
| |
| case SIOCSIWAUTH: |
| ret = mtk_p2p_wext_set_auth(prDev, |
| &rIwReqInfo, &(prIwReq->u), NULL); |
| break; |
| |
| case IOC_P2P_CFG_DEVICE: |
| case IOC_P2P_PROVISION_COMPLETE: |
| case IOC_P2P_START_STOP_DISCOVERY: |
| case IOC_P2P_DISCOVERY_RESULTS: |
| case IOC_P2P_WSC_BEACON_PROBE_RSP_IE: |
| case IOC_P2P_CONNECT_DISCONNECT: |
| case IOC_P2P_PASSWORD_READY: |
| case IOC_P2P_GET_STRUCT: |
| case IOC_P2P_SET_STRUCT: |
| case IOC_P2P_GET_REQ_DEVICE_INFO: |
| #if 0 |
| ret = rP2PIwPrivHandler[i4Cmd - SIOCIWFIRSTPRIV](prDev, |
| &rIwReqInfo, |
| &(prIwReq->u), |
| (char *)&(prIwReq->u)); |
| #endif |
| break; |
| #if CFG_SUPPORT_P2P_RSSI_QUERY |
| case SIOCGIWSTATS: |
| ret = mtk_p2p_wext_get_rssi(prDev, |
| &rIwReqInfo, &(prIwReq->u), NULL); |
| break; |
| #endif |
| default: |
| ret = -ENOTTY; |
| } |
| #endif /* 0 */ |
| |
| return ret; |
| } /* end of p2pDoIOCTL() */ |
| |
| |
| #if KERNEL_VERSION(5, 15, 0) <= CFG80211_VERSION_CODE |
| int p2pDoPrivIOCTL(struct net_device *prDev, struct ifreq *prIfReq, |
| void __user *prData, int i4Cmd) { |
| return p2pDoIOCTL(prDev, prIfReq, i4Cmd); |
| } |
| #endif |
| |
| /*---------------------------------------------------------------------------*/ |
| /*! |
| * \brief To override p2p interface address |
| * |
| * \param[in] prDev Net device requested. |
| * \param[in] addr Pointer to address |
| * |
| * \retval 0 For success. |
| * \retval -E2BIG For user's buffer size is too small. |
| * \retval -EFAULT For fail. |
| * |
| */ |
| /*---------------------------------------------------------------------------*/ |
| int p2pSetMACAddress(IN struct net_device *prDev, void *addr) |
| { |
| struct ADAPTER *prAdapter = NULL; |
| struct GLUE_INFO *prGlueInfo = NULL; |
| struct sockaddr *sa = NULL; |
| struct BSS_INFO *prBssInfo = NULL; |
| struct BSS_INFO *prDevBssInfo = NULL; |
| uint8_t ucRoleIdx = 0, ucBssIdx = 0; |
| struct GL_P2P_INFO *prP2pInfo = NULL; |
| |
| ASSERT(prDev); |
| |
| prGlueInfo = *((struct GLUE_INFO **) netdev_priv(prDev)); |
| ASSERT(prGlueInfo); |
| |
| prAdapter = prGlueInfo->prAdapter; |
| ASSERT(prAdapter); |
| |
| if (!prDev || !addr) { |
| DBGLOG(INIT, ERROR, "Set macaddr with ndev(%d) and addr(%d)\n", |
| (prDev == NULL) ? 0 : 1, (addr == NULL) ? 0 : 1); |
| return WLAN_STATUS_INVALID_DATA; |
| } |
| |
| if (mtk_Netdev_To_RoleIdx(prGlueInfo, prDev, &ucRoleIdx) != 0) { |
| DBGLOG(INIT, ERROR, "can't find the matched dev"); |
| return WLAN_STATUS_INVALID_DATA; |
| } |
| |
| if (p2pFuncRoleToBssIdx(prGlueInfo->prAdapter, |
| ucRoleIdx, &ucBssIdx) != WLAN_STATUS_SUCCESS) { |
| DBGLOG(INIT, ERROR, "can't find the matched bss"); |
| return WLAN_STATUS_INVALID_DATA; |
| } |
| |
| prBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, ucBssIdx); |
| if (!prBssInfo) { |
| DBGLOG(INIT, ERROR, "bss is not active\n"); |
| return WLAN_STATUS_INVALID_DATA; |
| } |
| |
| prDevBssInfo = GET_BSS_INFO_BY_INDEX(prAdapter, |
| P2P_DEV_BSS_INDEX); |
| if (!prDevBssInfo) { |
| DBGLOG(INIT, ERROR, "dev bss is not active\n"); |
| return WLAN_STATUS_INVALID_DATA; |
| } |
| |
| prP2pInfo = prGlueInfo->prP2PInfo[0]; |
| if (!prP2pInfo) { |
| DBGLOG(INIT, ERROR, "p2p info is null\n"); |
| return WLAN_STATUS_INVALID_DATA; |
| } |
| |
| sa = (struct sockaddr *)addr; |
| |
| COPY_MAC_ADDR(prBssInfo->aucOwnMacAddr, sa->sa_data); |
| COPY_MAC_ADDR(prDev->dev_addr, sa->sa_data); |
| |
| if ((prP2pInfo->prDevHandler == prDev) |
| && mtk_IsP2PNetDevice(prGlueInfo, prDev)) { |
| COPY_MAC_ADDR(prAdapter->rWifiVar.aucDeviceAddress, |
| sa->sa_data); |
| COPY_MAC_ADDR(prDevBssInfo->aucOwnMacAddr, sa->sa_data); |
| DBGLOG(INIT, INFO, |
| "[%d][%d] Set random macaddr to " MACSTR ".\n", |
| ucBssIdx, |
| prDevBssInfo->ucBssIndex, |
| MAC2STR(prDevBssInfo->aucOwnMacAddr)); |
| } else { |
| DBGLOG(INIT, INFO, |
| "[%d] Set random macaddr to " MACSTR ".\n", |
| ucBssIdx, |
| MAC2STR(prBssInfo->aucOwnMacAddr)); |
| } |
| |
| return WLAN_STATUS_SUCCESS; |
| } |
| |