Commit | Line | Data |
---|---|---|
39192c0b JB |
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 | ||
1acc97b6 | 68 | ieee80211_tx_skb(sdata, skb, 1); |
39192c0b JB |
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 | } | |
c481ec97 S |
87 | |
88 | void ieee80211_chswitch_work(struct work_struct *work) | |
89 | { | |
90 | struct ieee80211_sub_if_data *sdata = | |
46900298 | 91 | container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); |
c481ec97 | 92 | struct ieee80211_bss *bss; |
46900298 | 93 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
c481ec97 S |
94 | |
95 | if (!netif_running(sdata->dev)) | |
96 | return; | |
97 | ||
46900298 | 98 | bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid, |
c481ec97 | 99 | sdata->local->hw.conf.channel->center_freq, |
46900298 | 100 | ifmgd->ssid, ifmgd->ssid_len); |
c481ec97 S |
101 | if (!bss) |
102 | goto exit; | |
103 | ||
104 | sdata->local->oper_channel = sdata->local->csa_channel; | |
00d3f14c | 105 | /* XXX: shouldn't really modify cfg80211-owned data! */ |
c481ec97 | 106 | if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) |
00d3f14c | 107 | bss->cbss.channel = sdata->local->oper_channel; |
c481ec97 S |
108 | |
109 | ieee80211_rx_bss_put(sdata->local, bss); | |
110 | exit: | |
46900298 | 111 | ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; |
c481ec97 S |
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; | |
46900298 | 120 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
c481ec97 | 121 | |
46900298 | 122 | queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); |
c481ec97 S |
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; | |
46900298 | 130 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
c481ec97 S |
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 | ||
46900298 | 137 | if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED) |
c481ec97 S |
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 | ||
46900298 | 146 | if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) |
c481ec97 S |
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) { | |
46900298 | 156 | queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); |
c481ec97 S |
157 | } else { |
158 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | |
159 | IEEE80211_QUEUE_STOP_REASON_CSA); | |
46900298 JB |
160 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; |
161 | mod_timer(&ifmgd->chswitch_timer, | |
00d3f14c JB |
162 | jiffies + |
163 | msecs_to_jiffies(sw_elem->count * | |
164 | bss->cbss.beacon_interval)); | |
c481ec97 S |
165 | } |
166 | } | |
a8302de9 VT |
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 |