1 /* 2 * spectrum management 3 * 4 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> 5 * Copyright 2002-2005, Instant802 Networks, Inc. 6 * Copyright 2005-2006, Devicescape Software, Inc. 7 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 8 * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 9 * Copyright 2007-2008, Intel Corporation 10 * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License version 2 as 14 * published by the Free Software Foundation. 15 */ 16 17 #include <linux/ieee80211.h> 18 #include <net/wireless.h> 19 #include <net/mac80211.h> 20 #include "ieee80211_i.h" 21 #include "sta_info.h" 22 #include "wme.h" 23 24 static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata, 25 struct ieee80211_msrment_ie *request_ie, 26 const u8 *da, const u8 *bssid, 27 u8 dialog_token) 28 { 29 struct ieee80211_local *local = sdata->local; 30 struct sk_buff *skb; 31 struct ieee80211_mgmt *msr_report; 32 33 skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + 34 sizeof(struct ieee80211_msrment_ie)); 35 36 if (!skb) { 37 printk(KERN_ERR "%s: failed to allocate buffer for " 38 "measurement report frame\n", sdata->dev->name); 39 return; 40 } 41 42 skb_reserve(skb, local->hw.extra_tx_headroom); 43 msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); 44 memset(msr_report, 0, 24); 45 memcpy(msr_report->da, da, ETH_ALEN); 46 memcpy(msr_report->sa, sdata->dev->dev_addr, ETH_ALEN); 47 memcpy(msr_report->bssid, bssid, ETH_ALEN); 48 msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 49 IEEE80211_STYPE_ACTION); 50 51 skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement)); 52 msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; 53 msr_report->u.action.u.measurement.action_code = 54 WLAN_ACTION_SPCT_MSR_RPRT; 55 msr_report->u.action.u.measurement.dialog_token = dialog_token; 56 57 msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT; 58 msr_report->u.action.u.measurement.length = 59 sizeof(struct ieee80211_msrment_ie); 60 61 memset(&msr_report->u.action.u.measurement.msr_elem, 0, 62 sizeof(struct ieee80211_msrment_ie)); 63 msr_report->u.action.u.measurement.msr_elem.token = request_ie->token; 64 msr_report->u.action.u.measurement.msr_elem.mode |= 65 IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED; 66 msr_report->u.action.u.measurement.msr_elem.type = request_ie->type; 67 68 ieee80211_tx_skb(sdata, skb, 1); 69 } 70 71 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 72 struct ieee80211_mgmt *mgmt, 73 size_t len) 74 { 75 /* 76 * Ignoring measurement request is spec violation. 77 * Mandatory measurements must be reported optional 78 * measurements might be refused or reported incapable 79 * For now just refuse 80 * TODO: Answer basic measurement as unmeasured 81 */ 82 ieee80211_send_refuse_measurement_request(sdata, 83 &mgmt->u.action.u.measurement.msr_elem, 84 mgmt->sa, mgmt->bssid, 85 mgmt->u.action.u.measurement.dialog_token); 86 } 87 88 void ieee80211_chswitch_work(struct work_struct *work) 89 { 90 struct ieee80211_sub_if_data *sdata = 91 container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); 92 struct ieee80211_bss *bss; 93 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 94 95 if (!netif_running(sdata->dev)) 96 return; 97 98 bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid, 99 sdata->local->hw.conf.channel->center_freq, 100 ifmgd->ssid, ifmgd->ssid_len); 101 if (!bss) 102 goto exit; 103 104 sdata->local->oper_channel = sdata->local->csa_channel; 105 /* XXX: shouldn't really modify cfg80211-owned data! */ 106 if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) 107 bss->cbss.channel = sdata->local->oper_channel; 108 109 ieee80211_rx_bss_put(sdata->local, bss); 110 exit: 111 ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; 112 ieee80211_wake_queues_by_reason(&sdata->local->hw, 113 IEEE80211_QUEUE_STOP_REASON_CSA); 114 } 115 116 void ieee80211_chswitch_timer(unsigned long data) 117 { 118 struct ieee80211_sub_if_data *sdata = 119 (struct ieee80211_sub_if_data *) data; 120 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 121 122 queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); 123 } 124 125 void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata, 126 struct ieee80211_channel_sw_ie *sw_elem, 127 struct ieee80211_bss *bss) 128 { 129 struct ieee80211_channel *new_ch; 130 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 131 int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num); 132 133 /* FIXME: Handle ADHOC later */ 134 if (sdata->vif.type != NL80211_IFTYPE_STATION) 135 return; 136 137 if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED) 138 return; 139 140 if (sdata->local->sw_scanning || sdata->local->hw_scanning) 141 return; 142 143 /* Disregard subsequent beacons if we are already running a timer 144 processing a CSA */ 145 146 if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) 147 return; 148 149 new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); 150 if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) 151 return; 152 153 sdata->local->csa_channel = new_ch; 154 155 if (sw_elem->count <= 1) { 156 queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); 157 } else { 158 ieee80211_stop_queues_by_reason(&sdata->local->hw, 159 IEEE80211_QUEUE_STOP_REASON_CSA); 160 ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; 161 mod_timer(&ifmgd->chswitch_timer, 162 jiffies + 163 msecs_to_jiffies(sw_elem->count * 164 bss->cbss.beacon_interval)); 165 } 166 } 167 168 void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, 169 u16 capab_info, u8 *pwr_constr_elem, 170 u8 pwr_constr_elem_len) 171 { 172 struct ieee80211_conf *conf = &sdata->local->hw.conf; 173 174 if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT)) 175 return; 176 177 /* Power constraint IE length should be 1 octet */ 178 if (pwr_constr_elem_len != 1) 179 return; 180 181 if ((*pwr_constr_elem <= conf->channel->max_power) && 182 (*pwr_constr_elem != sdata->local->power_constr_level)) { 183 sdata->local->power_constr_level = *pwr_constr_elem; 184 ieee80211_hw_config(sdata->local, 0); 185 } 186 } 187 188