static int rtl_op_change_interface( struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype new_type, bool p2p ) { struct rtl_priv *rtlpriv = rtl_priv( hw ); int ret; rtl_op_remove_interface( hw, vif ); vif->type = new_type; vif->p2p = p2p; ret = rtl_op_add_interface( hw, vif ); RT_TRACE( rtlpriv, COMP_MAC80211, DBG_LOUD, "p2p %x\n", p2p ); return ret; }
static int rtl_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); int err = 0; if (mac->vif) { RT_TRACE(COMP_ERR, DBG_WARNING, ("vif has been set!! mac->vif = 0x%p\n", mac->vif)); return -EOPNOTSUPP; } /*This flag is not defined before kernel 3.4*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; #endif rtl_ips_nic_on(hw); mutex_lock(&rtlpriv->locks.conf_mutex); /*<delete in kernel start>*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) switch (ieee80211_vif_type_p2p(vif)) { case NL80211_IFTYPE_P2P_CLIENT: mac->p2p = P2P_ROLE_CLIENT; /*fall through*/ #else /*<delete in kernel end>*/ switch (vif->type) { /*<delete in kernel start>*/ #endif /*<delete in kernel end>*/ case NL80211_IFTYPE_STATION: if (mac->beacon_enabled == 1) { RT_TRACE(COMP_MAC80211, DBG_LOUD, ("NL80211_IFTYPE_STATION \n")); mac->beacon_enabled = 0; rtlpriv->cfg->ops->update_interrupt_mask(hw, 0, rtlpriv->cfg->maps[RTL_IBSS_INT_MASKS]); } break; case NL80211_IFTYPE_ADHOC: RT_TRACE(COMP_MAC80211, DBG_LOUD, ("NL80211_IFTYPE_ADHOC \n")); mac->link_state = MAC80211_LINKED; rtlpriv->cfg->ops->set_bcn_reg(hw); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) mac->basic_rates = 0xfff; else mac->basic_rates = 0xff0; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE, (u8 *) (&mac->basic_rates)); break; /*<delete in kernel start>*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) case NL80211_IFTYPE_P2P_GO: mac->p2p = P2P_ROLE_GO; /*fall through*/ #endif /*<delete in kernel end>*/ case NL80211_IFTYPE_AP: RT_TRACE(COMP_MAC80211, DBG_LOUD, ("NL80211_IFTYPE_AP \n")); mac->link_state = MAC80211_LINKED; rtlpriv->cfg->ops->set_bcn_reg(hw); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) mac->basic_rates = 0xfff; else mac->basic_rates = 0xff0; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE, (u8 *) (&mac->basic_rates)); break; case NL80211_IFTYPE_MESH_POINT: RT_TRACE(COMP_MAC80211, DBG_LOUD, ("NL80211_IFTYPE_MESH_POINT \n")); mac->link_state = MAC80211_LINKED; rtlpriv->cfg->ops->set_bcn_reg(hw); if (rtlpriv->rtlhal.current_bandtype == BAND_ON_2_4G) mac->basic_rates = 0xfff; else mac->basic_rates = 0xff0; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE, (u8 *) (&mac->basic_rates)); break; default: RT_TRACE(COMP_ERR, DBG_EMERG, ("operation mode %d is not support!\n", vif->type)); err = -EOPNOTSUPP; goto out; } #ifdef VIF_TODO if (!rtl_set_vif_info(hw, vif)) goto out; #endif if (mac->p2p) { RT_TRACE(COMP_MAC80211, DBG_LOUD, ("p2p role %x \n",vif->type)); mac->basic_rates = 0xff0;/*disable cck rate for p2p*/ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE, (u8 *) (&mac->basic_rates)); } mac->vif = vif; mac->opmode = vif->type; rtlpriv->cfg->ops->set_network_type(hw, vif->type); memcpy(mac->mac_addr, vif->addr, ETH_ALEN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr); out: mutex_unlock(&rtlpriv->locks.conf_mutex); return err; } static void rtl_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); mutex_lock(&rtlpriv->locks.conf_mutex); /* Free beacon resources */ if ((vif->type == NL80211_IFTYPE_AP) || (vif->type == NL80211_IFTYPE_ADHOC) || (vif->type == NL80211_IFTYPE_MESH_POINT)) { if (mac->beacon_enabled == 1) { mac->beacon_enabled = 0; rtlpriv->cfg->ops->update_interrupt_mask(hw, 0, rtlpriv->cfg->maps[RTL_IBSS_INT_MASKS]); } } /* *Note: We assume NL80211_IFTYPE_UNSPECIFIED as *NO LINK for our hardware. */ mac->p2p = 0; mac->vif = NULL; mac->link_state = MAC80211_NOLINK; memset(mac->bssid, 0, 6); mac->vendor = PEER_UNKNOWN; mac->opmode = NL80211_IFTYPE_UNSPECIFIED; rtlpriv->cfg->ops->set_network_type(hw, mac->opmode); mutex_unlock(&rtlpriv->locks.conf_mutex); } /*<delete in kernel start>*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) /*<delete in kernel end>*/ static int rtl_op_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype new_type, bool p2p) { struct rtl_priv *rtlpriv = rtl_priv(hw); int ret; rtl_op_remove_interface(hw, vif); vif->type = new_type; vif->p2p = p2p; ret = rtl_op_add_interface(hw, vif); RT_TRACE(COMP_MAC80211, DBG_LOUD, (" p2p %x\n",p2p)); return ret; } /*<delete in kernel start>*/ #endif /*<delete in kernel end>*/ static int rtl_op_config(struct ieee80211_hw *hw, u32 changed) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct ieee80211_conf *conf = &hw->conf; if (mac->skip_scan) return 1; mutex_lock(&rtlpriv->locks.conf_mutex); if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { /* BIT(2) */ RT_TRACE(COMP_MAC80211, DBG_LOUD, ("IEEE80211_CONF_CHANGE_LISTEN_INTERVAL\n")); } /*For IPS */ if (changed & IEEE80211_CONF_CHANGE_IDLE) { if (hw->conf.flags & IEEE80211_CONF_IDLE) rtl_ips_nic_off(hw); else rtl_ips_nic_on(hw); } else { /* *although rfoff may not cause by ips, but we will *check the reason in set_rf_power_state function */ if (unlikely(ppsc->rfpwr_state == ERFOFF)) rtl_ips_nic_on(hw); } /*For LPS */ if (changed & IEEE80211_CONF_CHANGE_PS) { cancel_delayed_work(&rtlpriv->works.ps_work); cancel_delayed_work(&rtlpriv->works.ps_rfon_wq); if (conf->flags & IEEE80211_CONF_PS) { rtlpriv->psc.sw_ps_enabled = true; /* sleep here is must, or we may recv the beacon and * cause mac80211 into wrong ps state, this will cause * power save nullfunc send fail, and further cause * pkt loss, So sleep must quickly but not immediatly * because that will cause nullfunc send by mac80211 * fail, and cause pkt loss, we have tested that 5mA * is worked very well */ if (!rtlpriv->psc.multi_buffered) queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_work, MSECS(5)); } else { rtl_swlps_rf_awake(hw); rtlpriv->psc.sw_ps_enabled = false; } } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { RT_TRACE(COMP_MAC80211, DBG_LOUD, ("IEEE80211_CONF_CHANGE_RETRY_LIMITS %x\n", hw->conf.long_frame_max_tx_count)); mac->retry_long = hw->conf.long_frame_max_tx_count; mac->retry_short = hw->conf.long_frame_max_tx_count; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT, (u8 *) (&hw->conf.long_frame_max_tx_count)); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)) struct ieee80211_channel *channel = hw->conf.chandef.chan; enum nl80211_channel_type channel_type = cfg80211_get_chandef_type(&(hw->conf.chandef)); #else struct ieee80211_channel *channel = hw->conf.channel; enum nl80211_channel_type channel_type = hw->conf.channel_type; #endif u8 wide_chan = (u8) channel->hw_value; if (mac->act_scanning) mac->n_channels++; if (rtlpriv->dm.supp_phymode_switch && mac->link_state < MAC80211_LINKED && !mac->act_scanning) { if (rtlpriv->cfg->ops->check_switch_to_dmdp) rtlpriv->cfg->ops->check_switch_to_dmdp(hw); } /* *because we should back channel to *current_network.chan in in scanning, *So if set_chan == current_network.chan *we should set it. *because mac80211 tell us wrong bw40 *info for cisco1253 bw20, so we modify *it here based on UPPER & LOWER */ switch (channel_type) { case NL80211_CHAN_HT20: case NL80211_CHAN_NO_HT: /* SC */ mac->cur_40_prime_sc = PRIME_CHNL_OFFSET_DONT_CARE; rtlphy->current_chan_bw = HT_CHANNEL_WIDTH_20; mac->bw_40 = false; break; case NL80211_CHAN_HT40MINUS: /* SC */ mac->cur_40_prime_sc = PRIME_CHNL_OFFSET_UPPER; rtlphy->current_chan_bw = HT_CHANNEL_WIDTH_20_40; mac->bw_40 = true; /*wide channel */ wide_chan -= 2; break; case NL80211_CHAN_HT40PLUS: /* SC */ mac->cur_40_prime_sc = PRIME_CHNL_OFFSET_LOWER; rtlphy->current_chan_bw = HT_CHANNEL_WIDTH_20_40; mac->bw_40 = true; /*wide channel */ wide_chan += 2; break; default: mac->bw_40 = false; RT_TRACE(COMP_ERR, DBG_EMERG, ("switch case not processed \n")); break; } if (wide_chan <= 0) wide_chan = 1; /* in scanning, when before we offchannel we may send a ps=1 * null to AP, and then we may send a ps = 0 null to AP quickly, * but first null have cause AP's put lots of packet to hw tx * buffer, these packet must be tx before off channel so we must * delay more time to let AP flush these packets before * offchannel, or dis-association or delete BA will happen by AP */ if (rtlpriv->mac80211.offchan_deley) { rtlpriv->mac80211.offchan_deley = false; mdelay(50); } rtlphy->current_channel = wide_chan; rtlpriv->cfg->ops->switch_channel(hw); rtlpriv->cfg->ops->set_channel_access(hw); rtlpriv->cfg->ops->set_bw_mode(hw, channel_type); } mutex_unlock(&rtlpriv->locks.conf_mutex); return 0; } static void rtl_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, u64 multicast) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); *new_flags &= RTL_SUPPORTED_FILTERS; if (0 == changed_flags) return; /*TODO: we disable broadcase now, so enable here */ if (changed_flags & FIF_ALLMULTI) { if (*new_flags & FIF_ALLMULTI) { mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AM] | rtlpriv->cfg->maps[MAC_RCR_AB]; RT_TRACE(COMP_MAC80211, DBG_LOUD, ("Enable receive multicast frame.\n")); } else { mac->rx_conf &= ~(rtlpriv->cfg->maps[MAC_RCR_AM] | rtlpriv->cfg->maps[MAC_RCR_AB]); RT_TRACE(COMP_MAC80211, DBG_LOUD, ("Disable receive multicast frame.\n")); } } if (changed_flags & FIF_FCSFAIL) { if (*new_flags & FIF_FCSFAIL) { mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACRC32]; RT_TRACE(COMP_MAC80211, DBG_LOUD, ("Enable receive FCS error frame.\n")); } else { mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACRC32]; RT_TRACE(COMP_MAC80211, DBG_LOUD, ("Disable receive FCS error frame.\n")); } } /* if ssid not set to hw don't check bssid * here just used for linked scanning, & linked * and nolink check bssid is set in set network_type */ if ((changed_flags & FIF_BCN_PRBRESP_PROMISC) && (mac->link_state >= MAC80211_LINKED)) { if (mac->opmode != NL80211_IFTYPE_AP && mac->opmode != NL80211_IFTYPE_MESH_POINT) { if (*new_flags & FIF_BCN_PRBRESP_PROMISC) { rtlpriv->cfg->ops->set_chk_bssid(hw, false); } else { rtlpriv->cfg->ops->set_chk_bssid(hw, true); } } } if (changed_flags & FIF_CONTROL) { if (*new_flags & FIF_CONTROL) { mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACF]; RT_TRACE(COMP_MAC80211, DBG_LOUD, ("Enable receive control frame.\n")); } else { mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACF]; RT_TRACE(COMP_MAC80211, DBG_LOUD, ("Disable receive control frame.\n")); } } if (changed_flags & FIF_OTHER_BSS) { if (*new_flags & FIF_OTHER_BSS) { mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AAP]; RT_TRACE(COMP_MAC80211, DBG_LOUD, ("Enable receive other BSS's frame.\n")); } else { mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_AAP]; RT_TRACE(COMP_MAC80211, DBG_LOUD, ("Disable receive other BSS's frame.\n")); } } } static int rtl_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal= rtl_hal(rtl_priv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_sta_info *sta_entry; if (sta) { sta_entry = (struct rtl_sta_info *) sta->drv_priv; spin_lock_bh(&rtlpriv->locks.entry_list_lock); list_add_tail(&sta_entry->list, &rtlpriv->entry_list); spin_unlock_bh(&rtlpriv->locks.entry_list_lock); if (rtlhal->current_bandtype == BAND_ON_2_4G) { sta_entry->wireless_mode = WIRELESS_MODE_G; if (sta->supp_rates[0] <= 0xf) sta_entry->wireless_mode = WIRELESS_MODE_B; if (sta->ht_cap.ht_supported == true) sta_entry->wireless_mode = WIRELESS_MODE_N_24G; if (vif->type == NL80211_IFTYPE_ADHOC) sta_entry->wireless_mode = WIRELESS_MODE_G; } else if (rtlhal->current_bandtype == BAND_ON_5G) { sta_entry->wireless_mode = WIRELESS_MODE_A; if (sta->ht_cap.ht_supported == true) sta_entry->wireless_mode = WIRELESS_MODE_N_24G; if (vif->type == NL80211_IFTYPE_ADHOC) sta_entry->wireless_mode = WIRELESS_MODE_A; } /*disable cck rate for p2p*/ if (mac->p2p) sta->supp_rates[0] &= 0xfffffff0; memcpy(sta_entry->mac_addr, sta->addr, ETH_ALEN); RT_TRACE(COMP_MAC80211, DBG_DMESG, ("Add sta addr is %pM\n",sta->addr)); rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0); } return 0; } static int rtl_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_sta_info *sta_entry; if (sta) { RT_TRACE(COMP_MAC80211, DBG_DMESG, ("Remove sta addr is %pM\n",sta->addr)); sta_entry = (struct rtl_sta_info *) sta->drv_priv; sta_entry->wireless_mode = 0; sta_entry->ratr_index = 0; spin_lock_bh(&rtlpriv->locks.entry_list_lock); list_del(&sta_entry->list); spin_unlock_bh(&rtlpriv->locks.entry_list_lock); } return 0; } static int _rtl_get_hal_qnum(u16 queue) { int qnum; switch (queue) { case 0: qnum = AC3_VO; break; case 1: qnum = AC2_VI; break; case 2: qnum = AC0_BE; break; case 3: qnum = AC1_BK; break; default: qnum = AC0_BE; break; } return qnum; }