| /** |
| Copyright (c) 2008 - 2013 Quantenna Communications Inc |
| All Rights Reserved |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
| |
| 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 the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| |
| **/ |
| |
| #ifndef AUTOCONF_INCLUDED |
| #include <linux/config.h> |
| #endif |
| #include <linux/version.h> |
| |
| #include <linux/netdevice.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/dmapool.h> |
| #include <linux/rtnetlink.h> |
| #include <asm/hardware.h> |
| #include <asm/board/board_config.h> |
| |
| #include <qtn/qdrv_sch.h> |
| #include <qtn/topaz_tqe_cpuif.h> |
| #include <qtn/topaz_fwt_db.h> |
| |
| #ifdef CONFIG_QVSP |
| #include "qtn/qvsp.h" |
| #endif |
| |
| #include "qdrv_features.h" |
| #include "qdrv_debug.h" |
| #include "qdrv_mac.h" |
| #include "qdrv_soc.h" |
| #include "qdrv_muc.h" |
| #include "qdrv_hal.h" |
| #include "qdrv_comm.h" |
| #include "qdrv_vap.h" |
| #include "qdrv_wlan.h" |
| |
| extern void indicate_association(void); |
| extern void indicate_disassociation(void); |
| extern unsigned int g_led_assoc_indicate; |
| |
| static bool wps_button_not_initd = 1; |
| static bool igmp_query_timer_not_initd = 1; |
| // TBD - Need to move this to a more suitable place |
| static int vnet_init(struct net_device *dev) |
| { |
| struct qdrv_vap *qv = netdev_priv(dev); |
| struct qdrv_wlan *qw = qv->parent; |
| struct host_ioctl *ioctl; |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n"); |
| |
| ioctl = vnet_alloc_ioctl(qv); |
| if (!ioctl) { |
| DBGPRINTF_E("Failed to allocate message\n"); |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| return -1; |
| } |
| |
| ioctl->ioctl_command = IOCTL_DEV_DEVOPEN; |
| ioctl->ioctl_arg1 = qv->devid; |
| ioctl->ioctl_arg2 = 0; |
| ioctl->ioctl_argp = (u32) NULL; |
| ioctl->ioctl_next = 0; |
| ioctl->ioctl_status = 0; |
| |
| vnet_send_ioctl(qv, ioctl); |
| |
| napi_enable(&qv->napi); |
| napi_schedule(&qv->napi); |
| |
| netif_start_queue(dev); |
| |
| /* Open the device for the 802.11 layer */ |
| ieee80211_open(dev); |
| |
| /* Set up the WPS button IRQ handler */ |
| // TBD - Need to move this to a more suitable place |
| if (wps_button_not_initd) { |
| wps_button_not_initd = 0; |
| qdrv_wps_button_init(dev); |
| } |
| |
| if (igmp_query_timer_not_initd && |
| qv->iv.iv_opmode == IEEE80211_M_HOSTAP) { |
| qdrv_wlan_igmp_query_timer_start(qw); |
| igmp_query_timer_not_initd = 0; |
| } |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| |
| return(0); |
| } |
| |
| static int vnet_stop(struct net_device *dev) |
| { |
| struct qdrv_vap *qv = netdev_priv(dev); |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n"); |
| |
| /* close the device for the 802.11 layer */ |
| ieee80211_stop(dev); |
| |
| napi_disable(&qv->napi); |
| netif_stop_queue(dev); |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| |
| return 0; |
| } |
| |
| static __sram_data struct net_device_ops vnet_device_ops; |
| |
| static void vnet_start(struct net_device * dev) |
| { |
| ether_setup(dev); |
| |
| dev->netdev_ops = &vnet_device_ops; |
| dev->tx_queue_len = 500; |
| } |
| |
| static int qdrv_vap_80211_newstate_callback(struct ieee80211vap *vap, |
| enum ieee80211_state nstate, int arg) |
| { |
| struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv); |
| struct qdrv_wlan *qw = (struct qdrv_wlan *)qv->parent; |
| int error; |
| int stamode; |
| struct ieee80211com *ic = vap->iv_ic; |
| enum ieee80211_state ostate; |
| |
| ostate = vap->iv_state; |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter, caller %p\n", __builtin_return_address(0)); |
| |
| if(vap->iv_state != nstate) |
| { |
| DBGPRINTF(DBG_LL_NOTICE, QDRV_LF_VAP, |
| "New state for \"%s\" %s -> %s\n", |
| vap->iv_dev->name, ieee80211_state_name[vap->iv_state], |
| ieee80211_state_name[nstate]); |
| } |
| |
| stamode = (vap->iv_opmode == IEEE80211_M_STA || |
| vap->iv_opmode == IEEE80211_M_IBSS || |
| vap->iv_opmode == IEEE80211_M_AHDEMO); |
| |
| switch (nstate) { |
| case IEEE80211_S_RUN: |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "IEEE80211_S_RUN\n"); |
| switch (vap->iv_opmode) { |
| |
| case IEEE80211_M_HOSTAP: |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "IEEE80211_M_HOSTAP - Send Beacon\n"); |
| ic->ic_beacon_update(vap); |
| |
| break; |
| case IEEE80211_M_STA: |
| case IEEE80211_M_IBSS: |
| case IEEE80211_M_AHDEMO: |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "IEEE80211_M_STA\n"); |
| indicate_association(); |
| ic->ic_join_bss(vap); |
| SMSTAT(qw, sm_sta_associated); |
| break; |
| default: |
| break; |
| } |
| break; |
| case IEEE80211_S_INIT:// = 0, /* default state */ |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "IEEE80211_S_INIT\n"); |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ic->ic_beacon_stop(vap); |
| } else if (vap->iv_opmode == IEEE80211_M_STA) { |
| /* Pend disassociation with AP before TDLS link return to base channel */ |
| if (!ieee80211_tdls_return_to_base_channel(vap, 1)) |
| return 0; |
| } |
| indicate_disassociation(); |
| break; |
| case IEEE80211_S_SCAN:// = 1, /* scanning */ |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "IEEE80211_S_SCAN\n"); |
| indicate_disassociation(); |
| if (arg == IEEE80211_SCAN_FAIL_TIMEOUT) { |
| if (ostate == IEEE80211_S_AUTH) { |
| SMSTAT(qw, sm_scan_auth_fail_scan_pend); |
| } else if (ostate == IEEE80211_S_ASSOC) { |
| SMSTAT(qw, sm_scan_assoc_fail_scan_pend); |
| } |
| } |
| else if (arg == 0) { |
| SMSTAT(qw, sm_scan_pend); |
| } |
| break; |
| |
| case IEEE80211_S_AUTH:// = 2, /* try to authenticate */ |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "IEEE80211_S_AUTH\n"); |
| indicate_disassociation(); |
| if (stamode) { |
| if (ostate == IEEE80211_S_SCAN) { |
| SMSTAT(qw, sm_auth_pend); |
| } else { |
| SMSTAT(qw, sm_run_deauth_auth_pend); |
| } |
| } |
| break; |
| case IEEE80211_S_ASSOC:// = 3, /* try to assoc */ |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "IEEE80211_S_ASSOC\n"); |
| indicate_disassociation(); |
| if (stamode) { |
| if (ostate == IEEE80211_S_AUTH) { |
| SMSTAT(qw, sm_assoc_pend); |
| } else { |
| SMSTAT(qw, sm_run_disassoc_assoc_pend); |
| } |
| } |
| break; |
| default: |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "<unknown state %d>\n", nstate); |
| indicate_disassociation(); |
| break; |
| } |
| |
| /* Pend disassociation before tearing down all of TDLS links */ |
| if (ieee80211_tdls_pend_disassociation(vap, nstate, arg)) |
| return 0; |
| |
| /* Invoke the parent method to complete the work.*/ |
| error = (*qv->qv_newstate)(vap, nstate, arg); |
| |
| return error; |
| } |
| |
| int qdrv_vap_wds_mode(struct qdrv_vap *qv) |
| { |
| return(qv->iv.iv_flags_ext & IEEE80211_FEXT_WDS ? 1 : 0); |
| } |
| |
| #ifdef CONFIG_QVSP |
| static int qdrv_qvsp_ioctl(void *qw_, uint32_t param, uint32_t value) |
| { |
| #if TOPAZ_QTM |
| struct qdrv_wlan *qw = qw_; |
| |
| switch (param) { |
| case QVSP_CFG_FAT_MIN_CHECK_INTV: |
| qw->vsp_check_intvl = value / MSEC_PER_SEC; |
| break; |
| case QVSP_CFG_ENABLED: |
| if (value) { |
| /* Give stats part 2 check interval to warm up */ |
| qw->vsp_enabling = 2; |
| } |
| break; |
| default: |
| break; |
| } |
| #endif |
| |
| return qdrv_hostlink_qvsp(qw_, param, value); |
| } |
| #endif |
| |
| static void qdrv_vap_set_last(struct qdrv_mac *mac) |
| { |
| int i = QDRV_MAX_VAPS; |
| |
| while (i--) { |
| if (mac->vnet[i]) { |
| break; |
| } |
| } |
| |
| mac->vnet_last = i; |
| } |
| |
| int qdrv_vap_vlan2index_sync(struct qdrv_vap *qv, uint16_t mode, uint16_t vid) |
| { |
| uint8_t vap_id = qv->qv_vap_idx; |
| uint8_t last = 0; |
| |
| if (mode == QVLAN_MODE_DYNAMIC) |
| return 0; |
| |
| if (mode != QVLAN_MODE_ACCESS) |
| vid = VLANID_INDEX_INITVAL; |
| |
| qdrv_sch_vlan2index[vap_id] = vid; |
| |
| /* find the last vap bound to a vlan */ |
| for (vap_id = 0; vap_id < ARRAY_SIZE(qdrv_sch_vlan2index); vap_id++) { |
| if (qdrv_sch_vlan2index[vap_id] != VLANID_INDEX_INITVAL) { |
| last = vap_id + 1; |
| } |
| } |
| qdrv_vap_vlan_max = last; |
| |
| #if !defined(CONFIG_TOPAZ_PCIE_HOST) && !defined(CONFIG_TOPAZ_PCIE_TARGET) |
| qdrv_sch_set_vlanpath(); |
| #endif |
| return 0; |
| } |
| |
| #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) |
| int qdrv_get_active_sub_port(const struct net_bridge_port *p, |
| uint32_t *sub_port_bitmap, int size) |
| { |
| struct topaz_fwt_sw_mcast_entry *mcast_entry; |
| int i; |
| struct ieee80211vap *vap; |
| struct ieee80211com *ic; |
| struct ieee80211_node *ni; |
| int skip; |
| |
| if (size != sizeof(mcast_entry->node_bitmap)) { |
| DBGPRINTF_LIMIT_E("bitmap length is invalid - %d/%d\n", |
| size, sizeof(mcast_entry->node_bitmap)); |
| return 0; |
| } |
| |
| /* Use the ipff entry, which has the bit set for each active node */ |
| mcast_entry = fwt_db_get_sw_mcast_ff(); |
| memcpy(sub_port_bitmap, mcast_entry->node_bitmap, size); |
| |
| vap = (struct ieee80211vap *)netdev_priv(p->dev); |
| /* something unexpected */ |
| if (vap->iv_dev != p->dev) { |
| DBGPRINTF_LIMIT_E("%s: net_device mismatch - %p/%p\n", __FUNCTION__, |
| vap->iv_dev, p->dev); |
| return 0; |
| } |
| |
| ic = vap->iv_ic; |
| |
| /* some nodes(sub ports) in 'sub_port_bitmap' may not belong to 'vap' */ |
| for (i = 0; i < QTN_NCIDX_MAX; i++) { |
| if ((sub_port_bitmap[BR_SUBPORT_IDX(i)] & BR_SUBPORT_BITMAP(i)) == 0) |
| continue; |
| |
| ni = ieee80211_find_node_by_idx(ic, vap, i); |
| if (ni == NULL) { |
| /* node not found or doesn't belong to 'vap' */ |
| skip = 1; |
| } else if (ni->ni_node_idx == 0) { |
| /* race condition */ |
| skip = 1; |
| } else { |
| skip = 0; |
| } |
| |
| if (ni != NULL) |
| ieee80211_free_node(ni); |
| |
| if (skip) |
| sub_port_bitmap[BR_SUBPORT_IDX(i)] &= ~BR_SUBPORT_BITMAP(i); |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(mcast_entry->node_bitmap); i++) { |
| if (sub_port_bitmap[i]) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| int qdrv_check_active_sub_port(const struct net_bridge_port *p, |
| const uint32_t sub_port) |
| { |
| struct net_device *dev; |
| struct ieee80211vap *vap; |
| struct ieee80211com *ic; |
| struct ieee80211_node_table *nt; |
| struct ieee80211_node *ni; |
| |
| dev = p->dev; |
| vap = netdev_priv(dev); |
| |
| if (vap->iv_dev != dev) |
| return -1; |
| |
| BUG_ON(QTN_NCIDX_MAX < IEEE80211_NODE_IDX_UNMAP(sub_port)); |
| |
| ic = vap->iv_ic; |
| nt = &ic->ic_sta; |
| |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| ni = ic->ic_node_idx_ni[IEEE80211_NODE_IDX_UNMAP(sub_port)]; |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| |
| return (ni && ieee80211_node_is_running(ni)); |
| } |
| #endif |
| |
| int qdrv_vap_init(struct qdrv_mac *mac, struct host_ioctl_hifinfo *hifinfo, |
| u32 arg1, u32 arg2) |
| { |
| struct qdrv_vap *qv; |
| struct net_device *vdev; |
| int opmode = 0; |
| int vap_idx = 0; |
| int i; |
| struct ieee80211com *ic = NULL; |
| struct ieee80211vap *vap = NULL; |
| struct qdrv_wlan *qw; |
| int stamode; |
| int repeater_mode; |
| unsigned int qv_devid = arg1 & IOCTL_DEVATTACH_DEVID_MASK; |
| unsigned int dev_devid = QDRV_WLANID_FROM_DEVID(qv_devid); |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n"); |
| |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "name : %s\n", hifinfo->hi_name); |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "semmap : %d\n", hifinfo->hi_semmap[0]); |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "mbox : 0x%08x\n", hifinfo->hi_mboxstart); |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "rxfifo : 0x%08x\n", hifinfo->hi_rxfifo); |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "scanirq : 0x%08x\n", hifinfo->hi_scanirq); |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "scanfifo : 0x%08x\n", hifinfo->hi_scanfifo); |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "qv_devid : 0x%08x\n", qv_devid); |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "dev_devid : 0x%08x\n", dev_devid); |
| |
| /* Search for an empty VAP slot */ |
| for (i = 0; i < QDRV_MAX_VAPS; i++) { |
| if (mac->vnet[i] == NULL) { |
| /* Found one */ |
| vap_idx = i; |
| break; |
| } |
| } |
| |
| /* Check if we found one */ |
| if (i == QDRV_MAX_VAPS) { |
| DBGPRINTF_E("No empty VAP slot available for \"%s\"\n", |
| hifinfo->hi_name); |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| return(-1); |
| } |
| |
| vdev = dev_get_by_name(&init_net, hifinfo->hi_name); |
| if (vdev != NULL) { |
| DBGPRINTF_E("The device name \"%s\" already exists\n", |
| hifinfo->hi_name); |
| dev_put(vdev); |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| return(-1); |
| } |
| |
| /* Allocate our device */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) |
| vdev = alloc_netdev(sizeof(struct qdrv_vap), hifinfo->hi_name, NET_NAME_UNKNOWN, vnet_start); |
| #else |
| vdev = alloc_netdev(sizeof(struct qdrv_vap), hifinfo->hi_name, vnet_start); |
| #endif |
| if (vdev == NULL) { |
| DBGPRINTF_E("Unable to allocate device \"%s\"\n", |
| hifinfo->hi_name); |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| return(-1); |
| } |
| |
| opmode = (arg1 >> 24) & 0xF; |
| |
| qv = netdev_priv(vdev); |
| memset(qv, 0, sizeof(struct qdrv_vap)); |
| |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "qv 0x%08x iv 0x%08x dev 0x%08x\n", |
| (unsigned int) qv, (unsigned int) &qv->iv, (unsigned int) vdev); |
| |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "vap_idx %d opmode %d\n", vap_idx, opmode); |
| |
| qv->ndev = vdev; |
| qv->parent = mac->data; |
| qw = (struct qdrv_wlan *)qv->parent; |
| qv->devid = qv_devid; |
| qv->qv_vap_idx = hifinfo->hi_vapid; |
| if (qv->qv_vap_idx >= QTN_MAX_BSS_VAPS) |
| panic("vapid: %u is out of range!\n", qv->qv_vap_idx); |
| vdev->dev_id = dev_devid; |
| |
| vdev->qtn_flags |= QTN_FLAG_WIFI_DEVICE; |
| TAILQ_INIT(&qv->allnodes); |
| |
| vdev->if_port = TOPAZ_TQE_WMAC_PORT; |
| |
| netif_napi_add(vdev, &qv->napi, qdrv_rx_poll, board_napi_budget()); |
| |
| memcpy(vdev->dev_addr, hifinfo->hi_macaddr, IEEE80211_ADDR_LEN); |
| |
| spin_lock_init(&qv->lock); |
| spin_lock_init(&qv->bc_lock); |
| spin_lock_init(&qv->ni_lst_lock); |
| |
| /* Initiate a VAP setup */ |
| if (ieee80211_vap_setup(&((struct qdrv_wlan *) mac->data)->ic, vdev, |
| hifinfo->hi_name, qv->devid, opmode, IEEE80211_NO_STABEACONS) < 0) { |
| DBGPRINTF_E("The 802.11 layer failed to setup the VAP\n"); |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| return -1; |
| } |
| |
| /* Replace newstate function with our own */ |
| qv->qv_newstate = qv->iv.iv_newstate; |
| qv->iv.iv_newstate = qdrv_vap_80211_newstate_callback; |
| |
| /* Take the RTNL lock since register_netdevice() is used instead of */ |
| /* register_netdev() in ieee80211_vap_attach() */ |
| rtnl_lock(); |
| |
| /* Complete the VAP setup */ |
| if (ieee80211_vap_attach(&qv->iv, |
| ieee80211_media_change, ieee80211_media_status) < 0) { |
| DBGPRINTF_E("The 802.11 layer failed to attach the VAP\n"); |
| rtnl_unlock(); |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| return -1; |
| } |
| |
| ic = &((struct qdrv_wlan *) mac->data)->ic; |
| vap = &qv->iv; |
| |
| vap->iv_sta_assoc_limit = QTN_ASSOC_LIMIT; |
| vap->iv_ssid_group = 0; |
| |
| stamode = (vap->iv_opmode == IEEE80211_M_STA || |
| vap->iv_opmode == IEEE80211_M_IBSS || |
| vap->iv_opmode == IEEE80211_M_AHDEMO); |
| |
| repeater_mode = (ic->ic_flags_ext & IEEE80211_FEXT_REPEATER); |
| |
| if (stamode || repeater_mode) { |
| ic->ic_mindwell_active = QDRV_WLAN_STA_MIN_DWELLTIME_ACTIVE; |
| ic->ic_mindwell_passive = QDRV_WLAN_STA_MIN_DWELLTIME_PASSIVE; |
| ic->ic_maxdwell_active = QDRV_WLAN_STA_MAX_DWELLTIME_ACTIVE; |
| ic->ic_maxdwell_passive = QDRV_WLAN_STA_MAX_DWELLTIME_PASSIVE; |
| ic->ic_opmode = IEEE80211_M_STA; |
| QDRV_SET_SM_FLAG(qw->sm_stats, QDRV_WLAN_SM_STATE_STA); |
| |
| if (repeater_mode) |
| ic->ic_roaming = IEEE80211_ROAMING_AUTO; |
| } else { |
| ic->ic_mindwell_active = QDRV_WLAN_AP_MIN_DWELLTIME_ACTIVE; |
| ic->ic_mindwell_passive = QDRV_WLAN_AP_MIN_DWELLTIME_PASSIVE; |
| ic->ic_maxdwell_active = QDRV_WLAN_AP_MAX_DWELLTIME_ACTIVE; |
| ic->ic_maxdwell_passive = QDRV_WLAN_AP_MAX_DWELLTIME_PASSIVE; |
| ic->ic_opmode = IEEE80211_M_HOSTAP; |
| QDRV_SET_SM_FLAG(qw->sm_stats, QDRV_WLAN_SM_STATE_AP); |
| |
| /* Force auto roaming for AP in case it was set to manual when in STA mode */ |
| ic->ic_roaming = IEEE80211_ROAMING_AUTO; |
| } |
| |
| #ifdef QTN_BG_SCAN |
| ic->ic_qtn_bgscan.dwell_msecs_active = QDRV_WLAN_QTN_BGSCAN_DWELLTIME_ACTIVE; |
| ic->ic_qtn_bgscan.dwell_msecs_passive = QDRV_WLAN_QTN_BGSCAN_DWELLTIME_PASSIVE; |
| ic->ic_qtn_bgscan.duration_msecs_active = QDRV_WLAN_QTN_BGSCAN_DURATION_ACTIVE; |
| ic->ic_qtn_bgscan.duration_msecs_passive_fast = QDRV_WLAN_QTN_BGSCAN_DURATION_PASSIVE_FAST; |
| ic->ic_qtn_bgscan.duration_msecs_passive_normal = QDRV_WLAN_QTN_BGSCAN_DURATION_PASSIVE_NORMAL; |
| ic->ic_qtn_bgscan.duration_msecs_passive_slow = QDRV_WLAN_QTN_BGSCAN_DURATION_PASSIVE_SLOW; |
| ic->ic_qtn_bgscan.thrshld_fat_passive_fast = QDRV_WLAN_QTN_BGSCAN_THRESHLD_PASSIVE_FAST; |
| ic->ic_qtn_bgscan.thrshld_fat_passive_normal = QDRV_WLAN_QTN_BGSCAN_THRESHLD_PASSIVE_NORMAL; |
| ic->ic_qtn_bgscan.debug_flags = 0; |
| #endif /* QTN_BG_SCAN */ |
| |
| rtnl_unlock(); |
| |
| /* vlan configuration structure associated with the device */ |
| if (switch_alloc_vlan_dev(TOPAZ_TQE_WMAC_PORT, dev_devid, vdev->ifindex) == NULL) { |
| DBGPRINTF_E("failed to bind vlan dev to VAP\n"); |
| ieee80211_vap_detach(&qv->iv); |
| return -1; |
| } |
| |
| /* Set some debug stuff */ |
| qv->iv.iv_debug |= IEEE80211_MSG_DEBUG | |
| IEEE80211_MSG_INPUT | |
| IEEE80211_MSG_ASSOC | |
| IEEE80211_MSG_AUTH | |
| IEEE80211_MSG_OUTPUT; |
| |
| /* Disable some ... */ |
| qv->iv.iv_debug &= ~IEEE80211_MSG_DEBUG; |
| qv->iv.iv_debug = 0; |
| ((struct net_device_ops *)(vdev->netdev_ops))->ndo_start_xmit = qdrv_tx_hardstart; |
| ((struct net_device_ops *)(vdev->netdev_ops))->ndo_open = vnet_init; |
| ((struct net_device_ops *)(vdev->netdev_ops))->ndo_stop = vnet_stop; |
| |
| TAILQ_INIT(&qv->ni_lncb_lst); |
| |
| #ifdef CONFIG_QVSP |
| if (qw->qvsp == NULL) { |
| qw->qvsp = qvsp_init(&qdrv_qvsp_ioctl, qw, vdev, stamode, |
| ic->ic_vsp_cb_cfg, ic->ic_vsp_cb_strm_ctrl, ic->ic_vsp_cb_strm_ext_throttler, |
| sizeof(struct ieee80211_node), sizeof(struct ieee80211vap)); |
| if (qw->qvsp && qdrv_wlan_vsp_3rdpt_init(qw)) { |
| printk("Could not initialize VSP 3rd party client control\n"); |
| } |
| } else if (vap->iv_opmode == IEEE80211_M_WDS) { |
| qvsp_inactive_flag_set(qw->qvsp, QVSP_INACTIVE_WDS); |
| } |
| if (repeater_mode && vap->iv_opmode == IEEE80211_M_HOSTAP) |
| ic->ic_vsp_change_stamode(ic, 0); |
| #endif |
| |
| qv->iv.iv_vapnode_idx = IEEE80211_NODE_IDX_MAP(hifinfo->hi_vapnode_idx); |
| qdrv_tx_sch_attach(qv); |
| if (ic->ic_rf_chipid == CHIPID_DUAL && vap->iv_opmode != IEEE80211_M_WDS) { |
| /* Disable one-bit dynamic auto-correlation on RFIC5 */ |
| ic->ic_setparam(vap->iv_bss, IEEE80211_PARAM_DYNAMIC_AC, 0, NULL, 0); |
| } |
| /* initial bss node will be created before the qdisc; reinitialize */ |
| if (vap->iv_bss) { |
| qdrv_tx_sch_node_data_init(qdrv_tx_sch_vap_get_qdisc(vdev), |
| qw->tx_sch_shared_data, &vap->iv_bss->ni_tx_sch, 1); |
| } |
| |
| /* |
| * Finally, set vnet pointer. Needs to be done after all init is |
| * complete, or there will be synchronization problem with |
| * qdrv_tx_wake_queue or others. |
| */ |
| mac->vnet[vap_idx] = vdev; |
| qdrv_vap_set_last(mac); |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| |
| return 0; |
| } |
| |
| /* Any resource allocated to VAP cleanup here */ |
| void qdrv_vap_resource_cleanup(struct qdrv_vap *qv) |
| { |
| if (qv->bc_skb != NULL) { |
| dev_kfree_skb_any(qv->bc_skb); |
| qv->bc_skb = NULL; |
| } |
| } |
| |
| static int qdrv_get_hostap_count(struct ieee80211com *ic, struct ieee80211vap *vap) |
| { |
| int count = 0; |
| |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) |
| count++; |
| } |
| |
| return count; |
| } |
| |
| int qdrv_vap_exit(struct qdrv_mac *mac, struct net_device *vdev) |
| { |
| struct qdrv_vap *qv = netdev_priv(vdev); |
| #ifdef CONFIG_QVSP |
| struct qdrv_wlan *qw = (struct qdrv_wlan *)qv->parent; |
| struct ieee80211com *ic = qv->iv.iv_ic; |
| struct ieee80211vap *vap = &qv->iv; |
| int repeater_mode = ic->ic_flags_ext & IEEE80211_FEXT_REPEATER; |
| |
| if (repeater_mode && |
| vap->iv_opmode == IEEE80211_M_HOSTAP && |
| qdrv_get_hostap_count(ic, vap) == 1) |
| ic->ic_vsp_change_stamode(ic, 1); |
| |
| qvsp_exit(&qw->qvsp, vdev); |
| |
| if (qw->qvsp == NULL) { |
| qdrv_wlan_vsp_3rdpt_exit(qw); |
| } |
| #endif |
| |
| rtnl_lock(); |
| ieee80211_vap_detach(&qv->iv); |
| rtnl_unlock(); |
| |
| #ifdef CONFIG_QVSP |
| if (qw->qvsp && (qdrv_wlan_query_wds(ic) == 0) ) { |
| qvsp_inactive_flag_clear(qw->qvsp, QVSP_INACTIVE_WDS); |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| int qdrv_vap_exit_muc_done(struct qdrv_mac *mac, struct net_device *vdev) |
| { |
| struct qdrv_vap *qv = netdev_priv(vdev); |
| int vnet_found = 0; |
| int i; |
| |
| for (i = 0; i < QDRV_MAX_VAPS; i++) { |
| if (mac->vnet[i] == vdev) { |
| mac->vnet[i] = NULL; |
| qdrv_vap_set_last(mac); |
| vnet_found = 1; |
| } |
| } |
| |
| if (!vnet_found) { |
| DBGPRINTF_E("vap %s not found in mac\n", vdev->name); |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| return -ENODEV; |
| } |
| |
| DBGPRINTF(DBG_LL_INFO, QDRV_LF_VAP, |
| "Delete VAP qv 0x%p \"%s\" (%d)\n", qv, vdev->name, i); |
| |
| if (mac->vnet[0] == NULL && igmp_query_timer_not_initd == 0) { |
| struct qdrv_wlan *qw = (struct qdrv_wlan *)qv->parent; |
| qdrv_wlan_igmp_timer_stop(qw); |
| igmp_query_timer_not_initd = 1; |
| } |
| |
| if (mac->vnet[0] == NULL && wps_button_not_initd == 0) { |
| qdrv_wps_button_exit(); |
| wps_button_not_initd = 1; |
| } |
| |
| qdrv_vap_resource_cleanup(qv); |
| |
| qdrv_tx_done_flush_vap(qv); |
| |
| /* release switch vlan */ |
| switch_free_vlan_dev_by_idx(vdev->dev_id); |
| |
| /* Destroy it ... */ |
| rtnl_lock(); |
| ieee80211_vap_detach_late(&qv->iv); |
| rtnl_unlock(); |
| |
| return 0; |
| } |
| |
| int qdrv_exit_all_vaps(struct qdrv_mac *mac) |
| { |
| int i; |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "-->Enter\n"); |
| |
| for (i = 0; i < QDRV_MAX_VAPS; i++) { |
| if (mac->vnet[i]) { |
| qdrv_vap_exit(mac, mac->vnet[i]); |
| } |
| } |
| |
| DBGPRINTF(DBG_LL_ALL, QDRV_LF_TRACE, "<--Exit\n"); |
| |
| return(0); |
| } |
| |