/** * requestor ready to enter network sleep * @param vap : vap handle * @param requestor_id : id of requestor * @return EOK if success and error code if failed. */ int ieee80211_power_arbiter_enter_nwsleep(struct ieee80211vap *vap, u_int32_t requestor_id) { ieee80211_power_t power_handle = vap->iv_power; ieee80211_vap_event evt; IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s - requestor_id = 0x%08x\n", __func__, requestor_id); if ( ! ieee80211_power_arbiter_enabled()) { return __ieee80211_power_enter_nwsleep(vap); } else { struct ieee80211_power *pm_priv = vap->iv_power; struct ieee80211_power_arbiter_info *pm_arbiter = &pm_priv->pm_arbiter; if ( ! ieee80211_pwr_arbiter_requestor_id_enabled(pm_arbiter, requestor_id)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, "%s: Requestor ID is disabled 0x%x \n",__func__, requestor_id); return EINVAL; } if ( ! ieee80211_pwr_arbiter_TDLS_active(pm_arbiter)) { return __ieee80211_power_enter_nwsleep(vap); } /* * ignore any requests while the vap is not ready. */ spin_lock(&power_handle->pm_state_lock); if (!ieee80211_vap_ready_is_set(vap)) { spin_unlock(&power_handle->pm_state_lock); IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s vap is not ready \n", __func__); return EINVAL; } /* Determine if all TDLS direct connections are ready to sleep */ ieee80211_pwr_arbiter_set_sleep(vap, requestor_id); power_handle->pm_sleep_count++; if (ieee80211_pwr_arbiter_can_sleep(vap, power_handle)) { /* if this is the first request put the vap to network sleep */ if (power_handle->pm_nwsleep_event_sent == 0) { spin_unlock(&power_handle->pm_state_lock); IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s \n", __func__); evt.type = IEEE80211_VAP_NETWORK_SLEEP; ieee80211_vap_deliver_event(vap, &evt); if (!ieee80211_resmgr_exists(vap->iv_ic)) { vap->iv_ic->ic_pwrsave_set_state(vap->iv_ic, IEEE80211_PWRSAVE_NETWORK_SLEEP); } power_handle->pm_nwsleep_event_sent = 1; goto done; } } spin_unlock(&power_handle->pm_state_lock); done: return EOK; } }
/* * allows multiple modules call this API. * to exit network sleep. */ static int __ieee80211_power_exit_nwsleep(struct ieee80211vap *vap) { ieee80211_power_t power_handle = vap->iv_power; ieee80211_vap_event evt; IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s sleep_count %d \n", __func__,power_handle->pm_sleep_count ); /* * ignore any requests while the vap is not ready. */ spin_lock(&power_handle->pm_state_lock); if (!ieee80211_vap_ready_is_set(vap)) { spin_unlock(&power_handle->pm_state_lock); IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s vap is not ready \n", __func__); return EINVAL; } power_handle->pm_sleep_count--; /* if this is the first request put the vap to network sleep */ if (power_handle->pm_sleep_count == 0) { power_handle->pm_nwsleep_event_sent = 0; spin_unlock(&power_handle->pm_state_lock); IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s \n", __func__); evt.type = IEEE80211_VAP_ACTIVE; ieee80211_vap_deliver_event(vap, &evt); if (!ieee80211_resmgr_exists(vap->iv_ic)) { vap->iv_ic->ic_pwrsave_set_state(vap->iv_ic, IEEE80211_PWRSAVE_AWAKE); } } else { spin_unlock(&power_handle->pm_state_lock); } return EOK; }
static bool ieee80211_pwrsave_smps_check(ieee80211_pwrsave_smps_t smps) { u_int16_t i, throughput = 0, rssi; struct ieee80211vap *vap = smps->ips_vap; struct ieee80211com *ic = vap->iv_ic; if (!smps->ips_connected || !ieee80211_vap_ready_is_set(vap) || (vap->iv_opmode != IEEE80211_M_STA) || (!ieee80211node_has_flag(vap->iv_bss, IEEE80211_NODE_HT))) { return FALSE; } /* SM power save check. * SM Power save is enabled if, * - There is no data traffic or * - Throughput is less than threshold and RSSI is greater than threshold. */ throughput = vap->iv_txrxbytes / (IEEE80211_PWRSAVE_TIMER_INTERVAL * 125); // in Mbps rssi = ic->ic_node_getrssi(vap->iv_bss, -1, IEEE80211_RSSI_BEACON); vap->iv_txrxbytes = 0; // Clear byte counter smps->ips_smpsDataHistory[smps->ips_smpsCurrDataIndex++] = throughput; smps->ips_smpsCurrDataIndex = smps->ips_smpsCurrDataIndex % IEEE80211_SMPS_DATAHIST_NUM; /* We calculate average throughput over the past samples */ throughput = 0; for (i = 0; i < IEEE80211_SMPS_DATAHIST_NUM; i++) { throughput += smps->ips_smpsDataHistory[i]; } throughput /= IEEE80211_SMPS_DATAHIST_NUM; /* * We make the thresholds slightly different for SM power save enable & disable to get * over the ping-pong effect when calculated throughput is close to the threshold value. * SMPS Enable Threshold = Registry Value * SMPS Disable Threshold = Registry Value + IEEE80211_SMPS_THRESH_DIFF */ if (!throughput || ((throughput < vap->iv_smps_datathresh) && (rssi > vap->iv_smps_rssithresh))) { /* Receive criteria met, do SM power save. */ ieee80211_pwrsave_smps_event(smps, IEEE80211_SMPS_ENABLE); } else if ((throughput > (vap->iv_smps_datathresh + IEEE80211_SMPS_THRESH_DIFF)) || (rssi < vap->iv_smps_rssithresh)) { ieee80211_pwrsave_smps_event(smps, IEEE80211_SMPS_DISABLE); } return TRUE; }
static void ieee80211_vap_iter_ready_vaps(void *arg, wlan_if_t vap) { struct ieee80211_iter_vaps_ready_arg *params = (struct ieee80211_iter_vaps_ready_arg *) arg; if (ieee80211_vap_ready_is_set(vap)) { switch(ieee80211vap_get_opmode(vap)) { case IEEE80211_M_HOSTAP: case IEEE80211_M_BTAMP: params->num_ap_vaps_ready++; break; case IEEE80211_M_IBSS: params->num_ibss_vaps_ready++; break; case IEEE80211_M_STA: params->num_sta_vaps_ready++; break; default: break; } } }
void ieee80211_recv_beacon_ibss(struct ieee80211_node *ni, wbuf_t wbuf, int subtype, struct ieee80211_rx_status *rs, ieee80211_scan_entry_t scan_entry) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; u_int16_t capinfo; int ibssmerge = 0; struct ieee80211_channelswitch_ie *chanie = NULL; struct ieee80211_extendedchannelswitch_ie *echanie = NULL; struct ieee80211_frame *wh; u_int64_t tsf; u_int8_t *quiet_elm = NULL; bool free_node = FALSE; #if ATH_SUPPORT_IBSS_DFS struct ieee80211_ibssdfs_ie *ibssdfsie = NULL; #endif /* ATH_SUPPORT_IBSS_DFS */ wh = (struct ieee80211_frame *)wbuf_header(wbuf); capinfo = ieee80211_scan_entry_capinfo(scan_entry); if (!(capinfo & IEEE80211_CAPINFO_IBSS)) return; OS_MEMCPY((u_int8_t *)&tsf, ieee80211_scan_entry_tsf(scan_entry), sizeof(tsf)); if (ni != ni->ni_bss_node) { OS_MEMCPY(ni->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp)); /* update activity indication */ ni->ni_beacon_rstamp = OS_GET_TIMESTAMP(); ni->ni_probe_ticks = 0; } /* * Check BBSID before updating our beacon configuration to make * sure the received beacon really belongs to our Adhoc network. */ if ((subtype == IEEE80211_FC0_SUBTYPE_BEACON) && ieee80211_vap_ready_is_set(vap) && (IEEE80211_ADDR_EQ(wh->i_addr3, ieee80211_node_get_bssid(vap->iv_bss)))) { /* * if the neighbor is not in the list, add it to the list. */ if (ni == ni->ni_bss_node) { ni = ieee80211_add_neighbor(ni, scan_entry); if (ni == NULL) { return; } OS_MEMCPY(ni->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp)); free_node = TRUE; } ni->ni_rssi = rs->rs_rssi; /* * record tsf of last beacon into bss node */ OS_MEMCPY(ni->ni_bss_node->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp)); /* * record absolute time of last beacon */ vap->iv_last_beacon_time = OS_GET_TIMESTAMP(); ic->ic_beacon_update(ni, rs->rs_rssi); #if ATH_SUPPORT_IBSS_WMM /* * check for WMM parameters */ if ((ieee80211_scan_entry_wmeparam_ie(scan_entry) != NULL) || (ieee80211_scan_entry_wmeinfo_ie(scan_entry) != NULL)) { /* Node is WMM-capable if WME IE (either subtype) is present */ ni->ni_ext_caps |= IEEE80211_NODE_C_QOS; /* QOS-enable */ ieee80211node_set_flag(ni, IEEE80211_NODE_QOS); } else { /* If WME IE not present node is not WMM capable */ ni->ni_ext_caps &= ~IEEE80211_NODE_C_QOS; ieee80211node_clear_flag(ni, IEEE80211_NODE_QOS); } #endif #if ATH_SUPPORT_IBSS_DFS if (ic->ic_curchan->ic_flagext & IEEE80211_CHAN_DFS) { //check and see if we need to update other info for ibss_dfsie ibssdfsie = (struct ieee80211_ibssdfs_ie *)ieee80211_scan_entry_ibssdfs_ie(scan_entry); if(ibssdfsie) { if (ieee80211_check_and_update_ibss_dfs(vap, ibssdfsie)) { ieee80211_ibss_beacon_update_start(ic); } } /* update info from DFS owner */ if(ibssdfsie){ if(IEEE80211_ADDR_EQ(wh->i_addr2, ibssdfsie->owner)) { if(OS_MEMCMP(ibssdfsie, &vap->iv_ibssdfs_ie_data, MIN_IBSS_DFS_IE_CONTENT_SIZE)) { if(le64_to_cpu(tsf) >= rs->rs_tstamp.tsf){ IEEE80211_ADDR_COPY(vap->iv_ibssdfs_ie_data.owner, ibssdfsie->owner); vap->iv_ibssdfs_ie_data.rec_interval = ibssdfsie->rec_interval; ieee80211_ibss_beacon_update_start(ic); } } } } /* check if owner and it is not transmitting ibssIE */ else if(IEEE80211_ADDR_EQ(wh->i_addr2, vap->iv_ibssdfs_ie_data.owner)){ IEEE80211_ADDR_COPY(vap->iv_ibssdfs_ie_data.owner, vap->iv_myaddr); vap->iv_ibssdfs_state = IEEE80211_IBSSDFS_OWNER; ieee80211_ibss_beacon_update_start(ic); } } #endif /* ATH_SUPPORT_IBSS_DFS */ /* * check for spectrum management */ if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) { chanie = (struct ieee80211_channelswitch_ie *) ieee80211_scan_entry_csa(scan_entry); #if ATH_SUPPORT_IBSS_DFS if(chanie) { OS_MEMCPY(&vap->iv_channelswitch_ie_data, chanie, sizeof(struct ieee80211_channelswitch_ie)); ieee80211_ibss_beacon_update_start(ic); } #endif /* ATH_SUPPORT_IBSS_DFS */ echanie = (struct ieee80211_extendedchannelswitch_ie *) ieee80211_scan_entry_xcsa(scan_entry); if ((!chanie) && (!echanie)) { quiet_elm = ieee80211_scan_entry_quiet(scan_entry); } } } /* * Handle ibss merge as needed; check the tsf on the * frame before attempting the merge. The 802.11 spec * says the station should change its bssid to match * the oldest station with the same ssid, where oldest * is determined by the tsf. Note that hardware * reconfiguration happens through callback to * ath as the state machine will go from * RUN -> RUN when this happens. */ if (ieee80211_vap_ready_is_set(vap) && (le64_to_cpu(tsf) >= rs->rs_tstamp.tsf)) { ibssmerge = ieee80211_ibss_merge(ni,scan_entry); } if (ibssmerge) { ieee80211_mlme_adhoc_merge_start(ni); } /* * RNWF-specific: indicate to NDIS about the potential association. * It should be done after IBSS merge, which is called from ath_beacon_update(). */ if (IEEE80211_ADDR_EQ(wh->i_addr3, ieee80211_node_get_bssid(ni))) { /* Indicate node joined IBSS */ if ((capinfo & IEEE80211_CAPINFO_RADIOMEAS) && ieee80211_vap_rrm_is_set(vap)) { if(ni->ni_assoc_state != IEEE80211_NODE_ADHOC_STATE_AUTH_ASSOC) ieee80211_set_node_rrm(ni,TRUE); } else { ieee80211_set_node_rrm(ni,FALSE); } ieee80211_mlme_adhoc_join_indication(ni, wbuf); /* notify mlme of beacon reception */ ieee80211_mlme_join_complete_adhoc(ni); } if (ibssmerge) { ieee80211_mlme_adhoc_merge_completion(ni); } if (quiet_elm) { ic->ic_set_quiet(ni, quiet_elm); } if (free_node == TRUE) { ieee80211_free_node(ni); } }
void ieee80211_vap_iter_beacon_miss(void *arg, wlan_if_t vap) { systime_t tstamp; systime_t last_link_time; systime_t last_traffic_time; struct ieee80211com *ic = vap->iv_ic; struct ieee80211_mlme_priv *mlme_priv = vap->iv_mlme_priv; ieee80211_mlme_event event; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: %s iv_bmiss_count=%d reset=%d max=%d arg=%08p swbmiss=%d\n", __func__, (arg != NULL) ? "SW" : "HW", vap->iv_bmiss_count, vap->iv_bmiss_count_for_reset, vap->iv_bmiss_count_max, arg, mlme_sta_swbmiss_active(vap)); /* * Our handling is only meaningful for stations that are * associated; any other conditions else will be handled * through different means (e.g. the tx timeout on mgt frames). */ if ((vap->iv_opmode != IEEE80211_M_STA) || !ieee80211_vap_ready_is_set(vap)) { mlme_sta_swbmiss_timer_print_status(vap); /* print the status of sw bmiss */ return; } /* * ignore HW beacon miss if SW beacon miss is enabled. * and completely rely on SW beacon miss. */ if ((arg == NULL) && mlme_sta_swbmiss_active(vap)) { return; } /* * WAR for excessive beacon miss problem on SoC. * Consider a beacon miss only when we have two consecutive * beacon misses and there are no rx activities in between. * * Count beacon misses only if we gave the AP a chance by sending a * directed Probe Request. * * Don't do anything if we are scanning a foreign channel. * Trying to transmit a frame (Probe Request) during a channel change * (which includes a channel reset) can cause a NMI due to invalid HW * addresses. * Trying to transmit a Probe Request while in a foreign channel * wouldn't do us any good either. * * Query current time only after retrieving LastLinkTime. This avoids * possible negative values if this routine is preempted by reception of * a beacon or directed frame which would update the fields used to * calculate LastLinkTime. */ last_traffic_time = ieee80211_get_directed_frame_timestamp(vap); last_link_time = (vap->iv_last_beacon_time > last_traffic_time) ? vap->iv_last_beacon_time : last_traffic_time; tstamp = OS_GET_TIMESTAMP(); { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%d.%03d | %s: count=%d probe=%d beacon:%lums directed:%lums data:%lums ap_frame:%lums traffic_ind:%lums\n", ((u_int32_t) CONVERT_SYSTEM_TIME_TO_MS(tstamp)) / 1000, ((u_int32_t) CONVERT_SYSTEM_TIME_TO_MS(tstamp)) % 1000, __func__, vap->iv_bmiss_count, ieee80211_scan_can_transmit(ic->ic_scanner), (u_int32_t) CONVERT_SYSTEM_TIME_TO_MS(tstamp - vap->iv_last_beacon_time), (u_int32_t) CONVERT_SYSTEM_TIME_TO_MS(tstamp - last_traffic_time), (u_int32_t) CONVERT_SYSTEM_TIME_TO_MS(tstamp - vap->iv_lastdata), (u_int32_t) CONVERT_SYSTEM_TIME_TO_MS(tstamp - vap->iv_last_ap_frame), (u_int32_t) CONVERT_SYSTEM_TIME_TO_MS(tstamp - vap->iv_last_traffic_indication)); } /* * Do not count beacon misses received when we're off-channel, or * within IEEE80211_MINIMUM_BMISS_TIME ms of the last valid beacon. */ if ((! ieee80211_scan_in_home_channel(ic->ic_scanner)) || (CONVERT_SYSTEM_TIME_TO_MS(tstamp - last_link_time) < IEEE80211_MINIMUM_BMISS_TIME)) { mlme_sta_swbmiss_timer_start(vap); /* restart beacon miss timer */ return; } vap->iv_bmiss_count++; event.u.event_bmiss.cur_bmiss_count = vap->iv_bmiss_count; event.u.event_bmiss.max_bmiss_count = vap->iv_bmiss_count_for_reset; event.type = IEEE80211_MLME_EVENT_BEACON_MISS; ieee80211_mlme_deliver_event(mlme_priv,&event); if (vap->iv_bmiss_count < vap->iv_bmiss_count_for_reset) { #ifdef ATH_SWRETRY /* Turn off the sw retry mechanism until we receive * any data frame or probe response for the BSS we are * associated to. */ ic->ic_set_swretrystate(vap->iv_bss, FALSE); #endif /* * It is possible that the hardware gets into * deaf mode. Reset the hardware to see if it can recover * from the condition. */ /* indicate device error */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: Beacon miss, do internal reset!!\n", __func__); IEEE80211_DELIVER_EVENT_DEVICE_ERROR(vap); mlme_sta_swbmiss_timer_start(vap); /* restart beacon miss timer */ return; } /* max bmiss count reached */ vap->iv_bmiss_count = 0; /* reset bmiss counter */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, "%s: Beacon miss, will indicate to OS!!\n", __func__); /* indicate beacon miss */ IEEE80211_DELIVER_EVENT_BEACON_MISS(vap); }
bool wlan_is_connected(wlan_if_t vaphandle) { return (ieee80211_vap_ready_is_set(vaphandle)); }
int ieee80211_recv_probereq(struct ieee80211_node *ni, wbuf_t wbuf, int subtype) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; u_int8_t *frm, *efrm; u_int8_t *ssid, *rates, *ven; #if ATH_SUPPORT_AP_WDS_COMBO if (vap->iv_opmode == IEEE80211_M_STA || !ieee80211_vap_ready_is_set(vap) || vap->iv_no_beacon) { #else if (vap->iv_opmode == IEEE80211_M_STA || !ieee80211_vap_ready_is_set(vap)) { #endif vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ return -EINVAL; } wh = (struct ieee80211_frame *) wbuf_header(wbuf); frm = (u_int8_t *)&wh[1]; efrm = wbuf_header(wbuf) + wbuf_get_pktlen(wbuf); /*zhaoyang1 add start for probe request REQUIREMENTS-340*/ if (vap->iv_probe_request) { switch (vap->iv_probe_request) { case IEEE80211_BROADCAST_PROBE: if (IEEE80211_IS_BROADCAST(wh->i_addr1)) { vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ return -EINVAL; } break; case IEEE80211_ALL_PROBE: vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ return -EINVAL; default: printk("Probe_req value is wrong, iv_probe_request = %d\n", vap->iv_probe_request); break; } } /*zhaoyang1 add end*/ if (IEEE80211_IS_MULTICAST(wh->i_addr2)) { /* frame must be directed */ vap->iv_stats.is_rx_mgtdiscard++; /* XXX stat */ return -EINVAL; } /* * prreq frame format * [tlv] ssid * [tlv] supported rates * [tlv] extended supported rates * [tlv] Atheros Advanced Capabilities */ ssid = rates = NULL; while (((frm+1) < efrm) && (frm + frm[1] + 1 < efrm)) { switch (*frm) { case IEEE80211_ELEMID_SSID: ssid = frm; break; case IEEE80211_ELEMID_RATES: rates = frm; break; case IEEE80211_ELEMID_VENDOR: if (vap->iv_venie && vap->iv_venie->ven_oui_set) { ven = frm; if (ven[2] == vap->iv_venie->ven_oui[0] && ven[3] == vap->iv_venie->ven_oui[1] && ven[4] == vap->iv_venie->ven_oui[2]) { vap->iv_venie->ven_ie_len = MIN(ven[1] + 2, IEEE80211_MAX_IE_LEN); OS_MEMCPY(vap->iv_venie->ven_ie, ven, vap->iv_venie->ven_ie_len); } } break; } frm += frm[1] + 2; } if (frm > efrm) { return -EINVAL; } IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); IEEE80211_VERIFY_SSID(vap->iv_bss, ssid); if (IEEE80211_VAP_IS_HIDESSID_ENABLED(vap) && (ssid[1] == 0)) { IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, wh, ieee80211_mgt_subtype_name[ subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], "%s", "no ssid with ssid suppression enabled"); vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/ return -EINVAL; } /* * Skip Probe Requests received while the scan algorithm is setting a new * channel, or while in a foreign channel. * Trying to transmit a frame (Probe Response) during a channel change * (which includes a channel reset) can cause a NMI due to invalid HW * addresses. * Trying to transmit the Probe Response while in a foreign channel * wouldn't do us any good either. */ if (ieee80211_scan_can_transmit(ic->ic_scanner)) ieee80211_send_proberesp(ni, wh->i_addr2, NULL,0); return 0; }