aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwmc3200wifi/cfg80211.c
diff options
context:
space:
mode:
authorZhu Yi <yi.zhu@intel.com>2009-05-21 09:20:45 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-05-22 14:06:02 -0400
commitbb9f8692f5043efef0dcef048cdd1db68299c2cb (patch)
treede0eade3ea6e40341727789a3a91e5c506b68759 /drivers/net/wireless/iwmc3200wifi/cfg80211.c
parente31a16d6f64ef0e324c6f54d5112703c3f13a9c4 (diff)
iwmc3200wifi: Add new Intel Wireless Multicomm 802.11 driver
This driver supports Intel's full MAC wireless multicomm 802.11 hardware. Although the hardware is a 802.11agn device, we currently only support 802.11ag, in managed and ad-hoc mode (no AP mode for now). Signed-off-by: Zhu Yi <yi.zhu@intel.com> Signed-off-by: Samuel Ortiz <samuel.ortiz@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi/cfg80211.c')
-rw-r--r--drivers/net/wireless/iwmc3200wifi/cfg80211.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
new file mode 100644
index 000000000000..3256ad2c96ce
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -0,0 +1,409 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
5 * Samuel Ortiz <samuel.ortiz@intel.com>
6 * Zhu Yi <yi.zhu@intel.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version
10 * 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
21 *
22 */
23
24#include <linux/kernel.h>
25#include <linux/netdevice.h>
26#include <linux/wireless.h>
27#include <linux/ieee80211.h>
28#include <net/cfg80211.h>
29
30#include "iwm.h"
31#include "commands.h"
32#include "cfg80211.h"
33#include "debug.h"
34
35#define RATETAB_ENT(_rate, _rateid, _flags) \
36 { \
37 .bitrate = (_rate), \
38 .hw_value = (_rateid), \
39 .flags = (_flags), \
40 }
41
42#define CHAN2G(_channel, _freq, _flags) { \
43 .band = IEEE80211_BAND_2GHZ, \
44 .center_freq = (_freq), \
45 .hw_value = (_channel), \
46 .flags = (_flags), \
47 .max_antenna_gain = 0, \
48 .max_power = 30, \
49}
50
51#define CHAN5G(_channel, _flags) { \
52 .band = IEEE80211_BAND_5GHZ, \
53 .center_freq = 5000 + (5 * (_channel)), \
54 .hw_value = (_channel), \
55 .flags = (_flags), \
56 .max_antenna_gain = 0, \
57 .max_power = 30, \
58}
59
60static struct ieee80211_rate iwm_rates[] = {
61 RATETAB_ENT(10, 0x1, 0),
62 RATETAB_ENT(20, 0x2, 0),
63 RATETAB_ENT(55, 0x4, 0),
64 RATETAB_ENT(110, 0x8, 0),
65 RATETAB_ENT(60, 0x10, 0),
66 RATETAB_ENT(90, 0x20, 0),
67 RATETAB_ENT(120, 0x40, 0),
68 RATETAB_ENT(180, 0x80, 0),
69 RATETAB_ENT(240, 0x100, 0),
70 RATETAB_ENT(360, 0x200, 0),
71 RATETAB_ENT(480, 0x400, 0),
72 RATETAB_ENT(540, 0x800, 0),
73};
74
75#define iwm_a_rates (iwm_rates + 4)
76#define iwm_a_rates_size 8
77#define iwm_g_rates (iwm_rates + 0)
78#define iwm_g_rates_size 12
79
80static struct ieee80211_channel iwm_2ghz_channels[] = {
81 CHAN2G(1, 2412, 0),
82 CHAN2G(2, 2417, 0),
83 CHAN2G(3, 2422, 0),
84 CHAN2G(4, 2427, 0),
85 CHAN2G(5, 2432, 0),
86 CHAN2G(6, 2437, 0),
87 CHAN2G(7, 2442, 0),
88 CHAN2G(8, 2447, 0),
89 CHAN2G(9, 2452, 0),
90 CHAN2G(10, 2457, 0),
91 CHAN2G(11, 2462, 0),
92 CHAN2G(12, 2467, 0),
93 CHAN2G(13, 2472, 0),
94 CHAN2G(14, 2484, 0),
95};
96
97static struct ieee80211_channel iwm_5ghz_a_channels[] = {
98 CHAN5G(34, 0), CHAN5G(36, 0),
99 CHAN5G(38, 0), CHAN5G(40, 0),
100 CHAN5G(42, 0), CHAN5G(44, 0),
101 CHAN5G(46, 0), CHAN5G(48, 0),
102 CHAN5G(52, 0), CHAN5G(56, 0),
103 CHAN5G(60, 0), CHAN5G(64, 0),
104 CHAN5G(100, 0), CHAN5G(104, 0),
105 CHAN5G(108, 0), CHAN5G(112, 0),
106 CHAN5G(116, 0), CHAN5G(120, 0),
107 CHAN5G(124, 0), CHAN5G(128, 0),
108 CHAN5G(132, 0), CHAN5G(136, 0),
109 CHAN5G(140, 0), CHAN5G(149, 0),
110 CHAN5G(153, 0), CHAN5G(157, 0),
111 CHAN5G(161, 0), CHAN5G(165, 0),
112 CHAN5G(184, 0), CHAN5G(188, 0),
113 CHAN5G(192, 0), CHAN5G(196, 0),
114 CHAN5G(200, 0), CHAN5G(204, 0),
115 CHAN5G(208, 0), CHAN5G(212, 0),
116 CHAN5G(216, 0),
117};
118
119static struct ieee80211_supported_band iwm_band_2ghz = {
120 .channels = iwm_2ghz_channels,
121 .n_channels = ARRAY_SIZE(iwm_2ghz_channels),
122 .bitrates = iwm_g_rates,
123 .n_bitrates = iwm_g_rates_size,
124};
125
126static struct ieee80211_supported_band iwm_band_5ghz = {
127 .channels = iwm_5ghz_a_channels,
128 .n_channels = ARRAY_SIZE(iwm_5ghz_a_channels),
129 .bitrates = iwm_a_rates,
130 .n_bitrates = iwm_a_rates_size,
131};
132
133int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
134{
135 struct wiphy *wiphy = iwm_to_wiphy(iwm);
136 struct iwm_bss_info *bss, *next;
137 struct iwm_umac_notif_bss_info *umac_bss;
138 struct ieee80211_mgmt *mgmt;
139 struct ieee80211_channel *channel;
140 struct ieee80211_supported_band *band;
141 s32 signal;
142 int freq;
143
144 list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
145 umac_bss = bss->bss;
146 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
147
148 if (umac_bss->band == UMAC_BAND_2GHZ)
149 band = wiphy->bands[IEEE80211_BAND_2GHZ];
150 else if (umac_bss->band == UMAC_BAND_5GHZ)
151 band = wiphy->bands[IEEE80211_BAND_5GHZ];
152 else {
153 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
154 return -EINVAL;
155 }
156
157 freq = ieee80211_channel_to_frequency(umac_bss->channel);
158 channel = ieee80211_get_channel(wiphy, freq);
159 signal = umac_bss->rssi * 100;
160
161 if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
162 le16_to_cpu(umac_bss->frame_len),
163 signal, GFP_KERNEL))
164 return -EINVAL;
165 }
166
167 return 0;
168}
169
170static int iwm_cfg80211_change_iface(struct wiphy *wiphy, int ifindex,
171 enum nl80211_iftype type, u32 *flags,
172 struct vif_params *params)
173{
174 struct net_device *ndev;
175 struct wireless_dev *wdev;
176 struct iwm_priv *iwm;
177 u32 old_mode;
178
179 /* we're under RTNL */
180 ndev = __dev_get_by_index(&init_net, ifindex);
181 if (!ndev)
182 return -ENODEV;
183
184 wdev = ndev->ieee80211_ptr;
185 iwm = ndev_to_iwm(ndev);
186 old_mode = iwm->conf.mode;
187
188 switch (type) {
189 case NL80211_IFTYPE_STATION:
190 iwm->conf.mode = UMAC_MODE_BSS;
191 break;
192 case NL80211_IFTYPE_ADHOC:
193 iwm->conf.mode = UMAC_MODE_IBSS;
194 break;
195 default:
196 return -EOPNOTSUPP;
197 }
198
199 wdev->iftype = type;
200
201 if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
202 return 0;
203
204 iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
205
206 if (iwm->umac_profile_active) {
207 int ret = iwm_invalidate_mlme_profile(iwm);
208 if (ret < 0)
209 IWM_ERR(iwm, "Couldn't invalidate profile\n");
210 }
211
212 return 0;
213}
214
215static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
216 struct cfg80211_scan_request *request)
217{
218 struct iwm_priv *iwm = ndev_to_iwm(ndev);
219 int ret;
220
221 if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
222 IWM_ERR(iwm, "Scan while device is not ready\n");
223 return -EIO;
224 }
225
226 if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
227 IWM_ERR(iwm, "Scanning already\n");
228 return -EAGAIN;
229 }
230
231 if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
232 IWM_ERR(iwm, "Scanning being aborted\n");
233 return -EAGAIN;
234 }
235
236 set_bit(IWM_STATUS_SCANNING, &iwm->status);
237
238 ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
239 if (ret) {
240 clear_bit(IWM_STATUS_SCANNING, &iwm->status);
241 return ret;
242 }
243
244 iwm->scan_request = request;
245 return 0;
246}
247
248static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
249{
250 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
251
252 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
253 (iwm->conf.rts_threshold != wiphy->rts_threshold)) {
254 int ret;
255
256 iwm->conf.rts_threshold = wiphy->rts_threshold;
257
258 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
259 CFG_RTS_THRESHOLD,
260 iwm->conf.rts_threshold);
261 if (ret < 0)
262 return ret;
263 }
264
265 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
266 (iwm->conf.frag_threshold != wiphy->frag_threshold)) {
267 int ret;
268
269 iwm->conf.frag_threshold = wiphy->frag_threshold;
270
271 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
272 CFG_FRAG_THRESHOLD,
273 iwm->conf.frag_threshold);
274 if (ret < 0)
275 return ret;
276 }
277
278 return 0;
279}
280
281static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
282 struct cfg80211_ibss_params *params)
283{
284 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
285 struct ieee80211_channel *chan = params->channel;
286 struct cfg80211_bss *bss;
287
288 if (!test_bit(IWM_STATUS_READY, &iwm->status))
289 return -EIO;
290
291 /* UMAC doesn't support creating IBSS network with specified bssid.
292 * This should be removed after we have join only mode supported. */
293 if (params->bssid)
294 return -EOPNOTSUPP;
295
296 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
297 params->ssid, params->ssid_len);
298 if (!bss) {
299 iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
300 schedule_timeout_interruptible(2 * HZ);
301 bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
302 params->ssid, params->ssid_len);
303 }
304 /* IBSS join only mode is not supported by UMAC ATM */
305 if (bss) {
306 cfg80211_put_bss(bss);
307 return -EOPNOTSUPP;
308 }
309
310 iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
311 iwm->umac_profile->ibss.band = chan->band;
312 iwm->umac_profile->ibss.channel = iwm->channel;
313 iwm->umac_profile->ssid.ssid_len = params->ssid_len;
314 memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
315
316 if (params->bssid)
317 memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
318
319 return iwm_send_mlme_profile(iwm);
320}
321
322static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
323{
324 struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
325
326 if (iwm->umac_profile_active)
327 return iwm_invalidate_mlme_profile(iwm);
328
329 return 0;
330}
331
332static struct cfg80211_ops iwm_cfg80211_ops = {
333 .change_virtual_intf = iwm_cfg80211_change_iface,
334 .scan = iwm_cfg80211_scan,
335 .set_wiphy_params = iwm_cfg80211_set_wiphy_params,
336 .join_ibss = iwm_cfg80211_join_ibss,
337 .leave_ibss = iwm_cfg80211_leave_ibss,
338};
339
340struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
341{
342 int ret = 0;
343 struct wireless_dev *wdev;
344
345 /*
346 * We're trying to have the following memory
347 * layout:
348 *
349 * +-------------------------+
350 * | struct wiphy |
351 * +-------------------------+
352 * | struct iwm_priv |
353 * +-------------------------+
354 * | bus private data |
355 * | (e.g. iwm_priv_sdio) |
356 * +-------------------------+
357 *
358 */
359
360 wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
361 if (!wdev) {
362 dev_err(dev, "Couldn't allocate wireless device\n");
363 return ERR_PTR(-ENOMEM);
364 }
365
366 wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
367 sizeof(struct iwm_priv) + sizeof_bus);
368 if (!wdev->wiphy) {
369 dev_err(dev, "Couldn't allocate wiphy device\n");
370 ret = -ENOMEM;
371 goto out_err_new;
372 }
373
374 set_wiphy_dev(wdev->wiphy, dev);
375 wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
376 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
377 BIT(NL80211_IFTYPE_ADHOC);
378 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
379 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
380 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
381
382 ret = wiphy_register(wdev->wiphy);
383 if (ret < 0) {
384 dev_err(dev, "Couldn't register wiphy device\n");
385 goto out_err_register;
386 }
387
388 return wdev;
389
390 out_err_register:
391 wiphy_free(wdev->wiphy);
392
393 out_err_new:
394 kfree(wdev);
395
396 return ERR_PTR(ret);
397}
398
399void iwm_wdev_free(struct iwm_priv *iwm)
400{
401 struct wireless_dev *wdev = iwm_to_wdev(iwm);
402
403 if (!wdev)
404 return;
405
406 wiphy_unregister(wdev->wiphy);
407 wiphy_free(wdev->wiphy);
408 kfree(wdev);
409}