void ieee80211_chswitch_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); struct ieee80211_bss *bss; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (!netif_running(sdata->dev)) return; bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid, sdata->local->hw.conf.channel->center_freq, ifmgd->ssid, ifmgd->ssid_len); if (!bss) goto exit; sdata->local->oper_channel = sdata->local->csa_channel; /* XXX: shouldn't really modify cfg80211-owned data! */ if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) bss->cbss.channel = sdata->local->oper_channel; ieee80211_rx_bss_put(sdata->local, bss); exit: ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_QUEUE_STOP_REASON_CSA); }
static int ieee80211_ioctl_siwpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *wrq, char *extra) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_conf *conf = &local->hw.conf; int ret = 0, timeout = 0; bool ps; if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EINVAL; if (wrq->disabled) { ps = false; timeout = 0; goto set; } switch (wrq->flags & IW_POWER_MODE) { case IW_POWER_ON: /* If not specified */ case IW_POWER_MODE: /* If set all mask */ case IW_POWER_ALL_R: /* If explicitely state all */ ps = true; break; default: /* Otherwise we ignore */ break; } if (wrq->flags & IW_POWER_TIMEOUT) timeout = wrq->value / 1000; set: if (ps == local->powersave && timeout == local->dynamic_ps_timeout) return ret; local->powersave = ps; local->dynamic_ps_timeout = timeout; if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) && local->dynamic_ps_timeout > 0) mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(local->dynamic_ps_timeout)); else { if (local->powersave) conf->flags |= IEEE80211_CONF_PS; else conf->flags &= ~IEEE80211_CONF_PS; } ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } return ret; }
static int ieee80211_ioctl_siwtxpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *data, char *extra) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); bool need_reconfig = 0; int new_power_level; if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL; if (data->txpower.flags & IW_TXPOW_RANGE) return -EINVAL; if (data->txpower.fixed) { new_power_level = data->txpower.value; } else { /* * Automatic power level. Use maximum power for the current * channel. Should be part of rate control. */ struct ieee80211_channel* chan = local->hw.conf.channel; if (!chan) return -EINVAL; new_power_level = chan->max_power; } if (local->hw.conf.power_level != new_power_level) { local->hw.conf.power_level = new_power_level; need_reconfig = 1; } if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) { local->hw.conf.radio_enabled = !(data->txpower.disabled); need_reconfig = 1; ieee80211_led_radio(local, local->hw.conf.radio_enabled); } if (need_reconfig) { ieee80211_hw_config(local); /* The return value of hw_config is not of big interest here, * as it doesn't say that it failed because of _this_ config * change or something else. Ignore it. */ } return 0; }
void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, u16 capab_info, u8 *pwr_constr_elem, u8 pwr_constr_elem_len) { struct ieee80211_conf *conf = &sdata->local->hw.conf; if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT)) return; /* Power constraint IE length should be 1 octet */ if (pwr_constr_elem_len != 1) return; if ((*pwr_constr_elem <= conf->channel->max_power) && (*pwr_constr_elem != sdata->local->power_constr_level)) { sdata->local->power_constr_level = *pwr_constr_elem; ieee80211_hw_config(sdata->local, 0); } }
int ieee80211_set_freq(struct ieee80211_local *local, int freqMHz) { int set = 0; int ret = -EINVAL; enum ieee80211_band band; struct ieee80211_supported_band *sband; int i; for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { sband = local->hw.wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) { struct ieee80211_channel *chan = &sband->channels[i]; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; if (chan->center_freq == freqMHz) { set = 1; local->oper_channel = chan; break; } } if (set) break; } if (set) { if (local->sta_sw_scanning) ret = 0; else ret = ieee80211_hw_config(local); rate_control_clear(local); } return ret; }
static int ieee80211_ioctl_siwretry(struct net_device *dev, struct iw_request_info *info, struct iw_param *retry, char *extra) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); if (retry->disabled || (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) return -EINVAL; if (retry->flags & IW_RETRY_MAX) { local->hw.conf.long_frame_max_tx_count = retry->value; } else if (retry->flags & IW_RETRY_MIN) { local->hw.conf.short_frame_max_tx_count = retry->value; } else { local->hw.conf.long_frame_max_tx_count = retry->value; local->hw.conf.short_frame_max_tx_count = retry->value; } ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS); return 0; }
static int ieee80211_ioctl_siwtxpower(struct net_device *dev, struct iw_request_info *info, union iwreq_data *data, char *extra) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_channel* chan = local->hw.conf.channel; u32 reconf_flags = 0; int new_power_level; if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL; if (data->txpower.flags & IW_TXPOW_RANGE) return -EINVAL; if (!chan) return -EINVAL; if (data->txpower.fixed) new_power_level = min(data->txpower.value, chan->max_power); else /* Automatic power level setting */ new_power_level = chan->max_power; if (local->hw.conf.power_level != new_power_level) { local->hw.conf.power_level = new_power_level; reconf_flags |= IEEE80211_CONF_CHANGE_POWER; } if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) { local->hw.conf.radio_enabled = !(data->txpower.disabled); reconf_flags |= IEEE80211_CONF_CHANGE_RADIO_ENABLED; ieee80211_led_radio(local, local->hw.conf.radio_enabled); } if (reconf_flags) ieee80211_hw_config(local, reconf_flags); return 0; }
static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, const u8 *bssid, const int beacon_int, struct ieee80211_channel *chan, const u32 basic_rates, const u16 capability, u64 tsf) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; int rates, i; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; u8 *pos; struct ieee80211_supported_band *sband; struct cfg80211_bss *bss; u32 bss_change; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; enum nl80211_channel_type channel_type; lockdep_assert_held(&ifibss->mtx); /* Reset own TSF to allow time synchronization work. */ drv_reset_tsf(local, sdata); skb = ifibss->skb; RCU_INIT_POINTER(ifibss->presp, NULL); synchronize_rcu(); skb->data = skb->head; skb->len = 0; skb_reset_tail_pointer(skb); skb_reserve(skb, sdata->local->hw.extra_tx_headroom); if (memcmp(ifibss->bssid, bssid, ETH_ALEN)) sta_info_flush(sdata->local, sdata); /* if merging, indicate to driver that we leave the old IBSS */ if (sdata->vif.bss_conf.ibss_joined) { sdata->vif.bss_conf.ibss_joined = false; netif_carrier_off(sdata->dev); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS); } memcpy(ifibss->bssid, bssid, ETH_ALEN); sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; channel_type = ifibss->channel_type; if (channel_type > NL80211_CHAN_HT20 && !cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) channel_type = NL80211_CHAN_HT20; if (!ieee80211_set_channel_type(local, sdata, channel_type)) { /* can only fail due to HT40+/- mismatch */ channel_type = NL80211_CHAN_HT20; WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_HT20)); } ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); sband = local->hw.wiphy->bands[chan->band]; /* build supported rates array */ pos = supp_rates; for (i = 0; i < sband->n_bitrates; i++) { int rate = sband->bitrates[i].bitrate; u8 basic = 0; if (basic_rates & BIT(i)) basic = 0x80; *pos++ = basic | (u8) (rate / 5); } /* Build IBSS probe response */ mgmt = (void *) skb_put(skb, 24 + sizeof(mgmt->u.beacon)); memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); memset(mgmt->da, 0xff, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int); mgmt->u.beacon.timestamp = cpu_to_le64(tsf); mgmt->u.beacon.capab_info = cpu_to_le16(capability); pos = skb_put(skb, 2 + ifibss->ssid_len); *pos++ = WLAN_EID_SSID; *pos++ = ifibss->ssid_len; memcpy(pos, ifibss->ssid, ifibss->ssid_len); rates = sband->n_bitrates; if (rates > 8) rates = 8; pos = skb_put(skb, 2 + rates); *pos++ = WLAN_EID_SUPP_RATES; *pos++ = rates; memcpy(pos, supp_rates, rates); if (sband->band == IEEE80211_BAND_2GHZ) { pos = skb_put(skb, 2 + 1); *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; *pos++ = ieee80211_frequency_to_channel(chan->center_freq); } pos = skb_put(skb, 2 + 2); *pos++ = WLAN_EID_IBSS_PARAMS; *pos++ = 2; /* FIX: set ATIM window based on scan results */ *pos++ = 0; *pos++ = 0; if (sband->n_bitrates > 8) { rates = sband->n_bitrates - 8; pos = skb_put(skb, 2 + rates); *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = rates; memcpy(pos, &supp_rates[8], rates); } if (ifibss->ie_len) memcpy(skb_put(skb, ifibss->ie_len), ifibss->ie, ifibss->ie_len); /* add HT capability and information IEs */ if (channel_type && sband->ht_cap.ht_supported) { pos = skb_put(skb, 4 + sizeof(struct ieee80211_ht_cap) + sizeof(struct ieee80211_ht_info)); pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); pos = ieee80211_ie_build_ht_info(pos, &sband->ht_cap, chan, channel_type); } if (local->hw.queues >= 4) { pos = skb_put(skb, 9); *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = 7; /* len */ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ *pos++ = 0x50; *pos++ = 0xf2; *pos++ = 2; /* WME */ *pos++ = 0; /* WME info */ *pos++ = 1; /* WME ver */ *pos++ = 0; /* U-APSD no in use */ } RCU_INIT_POINTER(ifibss->presp, skb); sdata->vif.bss_conf.beacon_int = beacon_int; sdata->vif.bss_conf.basic_rates = basic_rates; bss_change = BSS_CHANGED_BEACON_INT; bss_change |= ieee80211_reset_erp_info(sdata); bss_change |= BSS_CHANGED_BSSID; bss_change |= BSS_CHANGED_BEACON; bss_change |= BSS_CHANGED_BEACON_ENABLED; bss_change |= BSS_CHANGED_BASIC_RATES; bss_change |= BSS_CHANGED_HT; bss_change |= BSS_CHANGED_IBSS; sdata->vif.bss_conf.ibss_joined = true; ieee80211_bss_info_change_notify(sdata, bss_change); ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates); ifibss->state = IEEE80211_IBSS_MLME_JOINED; mod_timer(&ifibss->timer, round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); bss = cfg80211_inform_bss_frame(local->hw.wiphy, local->hw.conf.channel, mgmt, skb->len, 0, GFP_KERNEL); cfg80211_put_bss(bss); netif_carrier_on(sdata->dev); cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); }