예제 #1
0
/**
 * 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;
    }
}
예제 #2
0
/*
 * 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;

}
예제 #3
0
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;
}
예제 #4
0
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;

        }
    }
}
예제 #5
0
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);
    }

}
예제 #6
0
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);
}
예제 #7
0
bool wlan_is_connected(wlan_if_t vaphandle)
{
    return (ieee80211_vap_ready_is_set(vaphandle));
}
예제 #8
0
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;
}