aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
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
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')
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile2
-rw-r--r--drivers/net/wireless/iwmc3200wifi/Kconfig23
-rw-r--r--drivers/net/wireless/iwmc3200wifi/Makefile5
-rw-r--r--drivers/net/wireless/iwmc3200wifi/bus.h57
-rw-r--r--drivers/net/wireless/iwmc3200wifi/cfg80211.c409
-rw-r--r--drivers/net/wireless/iwmc3200wifi/cfg80211.h31
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.c920
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.h419
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debug.h124
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debugfs.c453
-rw-r--r--drivers/net/wireless/iwmc3200wifi/eeprom.c187
-rw-r--r--drivers/net/wireless/iwmc3200wifi/eeprom.h114
-rw-r--r--drivers/net/wireless/iwmc3200wifi/fw.c388
-rw-r--r--drivers/net/wireless/iwmc3200wifi/fw.h100
-rw-r--r--drivers/net/wireless/iwmc3200wifi/hal.c464
-rw-r--r--drivers/net/wireless/iwmc3200wifi/hal.h236
-rw-r--r--drivers/net/wireless/iwmc3200wifi/iwm.h350
-rw-r--r--drivers/net/wireless/iwmc3200wifi/lmac.h457
-rw-r--r--drivers/net/wireless/iwmc3200wifi/main.c680
-rw-r--r--drivers/net/wireless/iwmc3200wifi/netdev.c172
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rfkill.c88
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rx.c1431
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rx.h60
-rw-r--r--drivers/net/wireless/iwmc3200wifi/sdio.c516
-rw-r--r--drivers/net/wireless/iwmc3200wifi/sdio.h67
-rw-r--r--drivers/net/wireless/iwmc3200wifi/tx.c492
-rw-r--r--drivers/net/wireless/iwmc3200wifi/umac.h744
-rw-r--r--drivers/net/wireless/iwmc3200wifi/wext.c723
29 files changed, 9713 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 91be3e7bf133..a67d29290ba0 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -501,5 +501,6 @@ source "drivers/net/wireless/zd1211rw/Kconfig"
501source "drivers/net/wireless/rt2x00/Kconfig" 501source "drivers/net/wireless/rt2x00/Kconfig"
502source "drivers/net/wireless/orinoco/Kconfig" 502source "drivers/net/wireless/orinoco/Kconfig"
503source "drivers/net/wireless/wl12xx/Kconfig" 503source "drivers/net/wireless/wl12xx/Kconfig"
504source "drivers/net/wireless/iwmc3200wifi/Kconfig"
504 505
505endmenu 506endmenu
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index f2b1861e6bcb..7a4647e78fd3 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -60,3 +60,5 @@ obj-$(CONFIG_ATH_COMMON) += ath/
60obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o 60obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
61 61
62obj-$(CONFIG_WL12XX) += wl12xx/ 62obj-$(CONFIG_WL12XX) += wl12xx/
63
64obj-$(CONFIG_IWM) += iwmc3200wifi/
diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig
new file mode 100644
index 000000000000..ae84ddabcc1c
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/Kconfig
@@ -0,0 +1,23 @@
1config IWM
2 tristate "Intel Wireless Multicomm 3200 WiFi driver"
3 depends on MMC && WLAN_80211 && EXPERIMENTAL
4 select LIB80211
5 select FW_LOADER
6 select RFKILL
7
8config IWM_DEBUG
9 bool "Enable full debugging output in iwmc3200wifi"
10 depends on IWM && DEBUG_FS
11 ---help---
12 This option will enable debug tracing and setting for iwm
13
14 You can set the debug level and module through debugfs. By
15 default all modules are set to the IWL_DL_ERR level.
16 To see the list of debug modules and levels, see iwm/debug.h
17
18 For example, if you want the full MLME debug output:
19 echo 0xff > /debug/iwm/phyN/debug/mlme
20
21 Or, if you want the full debug, for all modules:
22 echo 0xff > /debug/iwm/phyN/debug/level
23 echo 0xff > /debug/iwm/phyN/debug/modules
diff --git a/drivers/net/wireless/iwmc3200wifi/Makefile b/drivers/net/wireless/iwmc3200wifi/Makefile
new file mode 100644
index 000000000000..7cb415e5c11b
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/Makefile
@@ -0,0 +1,5 @@
1obj-$(CONFIG_IWM) := iwmc3200wifi.o
2iwmc3200wifi-objs += main.o netdev.o rx.o tx.o sdio.o hal.o fw.o
3iwmc3200wifi-objs += commands.o wext.o cfg80211.o eeprom.o rfkill.o
4
5iwmc3200wifi-$(CONFIG_IWM_DEBUG) += debugfs.o
diff --git a/drivers/net/wireless/iwmc3200wifi/bus.h b/drivers/net/wireless/iwmc3200wifi/bus.h
new file mode 100644
index 000000000000..836663eec257
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/bus.h
@@ -0,0 +1,57 @@
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#ifndef __IWM_BUS_H__
25#define __IWM_BUS_H__
26
27#include "iwm.h"
28
29struct iwm_if_ops {
30 int (*enable)(struct iwm_priv *iwm);
31 int (*disable)(struct iwm_priv *iwm);
32 int (*send_chunk)(struct iwm_priv *iwm, u8* buf, int count);
33
34 int (*debugfs_init)(struct iwm_priv *iwm, struct dentry *parent_dir);
35 void (*debugfs_exit)(struct iwm_priv *iwm);
36
37 const char *umac_name;
38 const char *calib_lmac_name;
39 const char *lmac_name;
40};
41
42static inline int iwm_bus_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
43{
44 return iwm->bus_ops->send_chunk(iwm, buf, count);
45}
46
47static inline int iwm_bus_enable(struct iwm_priv *iwm)
48{
49 return iwm->bus_ops->enable(iwm);
50}
51
52static inline int iwm_bus_disable(struct iwm_priv *iwm)
53{
54 return iwm->bus_ops->disable(iwm);
55}
56
57#endif
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}
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.h b/drivers/net/wireless/iwmc3200wifi/cfg80211.h
new file mode 100644
index 000000000000..56a34145acbf
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.h
@@ -0,0 +1,31 @@
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#ifndef __IWM_CFG80211_H__
25#define __IWM_CFG80211_H__
26
27int iwm_cfg80211_inform_bss(struct iwm_priv *iwm);
28struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev);
29void iwm_wdev_free(struct iwm_priv *iwm);
30
31#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
new file mode 100644
index 000000000000..834a7f544e5d
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -0,0 +1,920 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#include <linux/kernel.h>
40#include <linux/wireless.h>
41#include <linux/etherdevice.h>
42#include <linux/ieee80211.h>
43
44#include "iwm.h"
45#include "bus.h"
46#include "hal.h"
47#include "umac.h"
48#include "commands.h"
49#include "debug.h"
50
51static int iwm_send_lmac_ptrough_cmd(struct iwm_priv *iwm,
52 u8 lmac_cmd_id,
53 const void *lmac_payload,
54 u16 lmac_payload_size,
55 u8 resp)
56{
57 struct iwm_udma_wifi_cmd udma_cmd = UDMA_LMAC_INIT;
58 struct iwm_umac_cmd umac_cmd;
59 struct iwm_lmac_cmd lmac_cmd;
60
61 lmac_cmd.id = lmac_cmd_id;
62
63 umac_cmd.id = UMAC_CMD_OPCODE_WIFI_PASS_THROUGH;
64 umac_cmd.resp = resp;
65
66 return iwm_hal_send_host_cmd(iwm, &udma_cmd, &umac_cmd, &lmac_cmd,
67 lmac_payload, lmac_payload_size);
68}
69
70int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
71 bool resp)
72{
73 struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
74 struct iwm_umac_cmd umac_cmd;
75
76 umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER;
77 umac_cmd.resp = resp;
78
79 return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
80 payload, payload_size);
81}
82
83static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] =
84{
85 {4, 3, 0, COEX_UNASSOC_IDLE_FLAGS},
86 {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
87 {4, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
88 {4, 3, 0, COEX_CALIBRATION_FLAGS},
89 {4, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
90 {4, 3, 0, COEX_CONNECTION_ESTAB_FLAGS},
91 {4, 3, 0, COEX_ASSOCIATED_IDLE_FLAGS},
92 {4, 3, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
93 {4, 3, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
94 {4, 3, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
95 {6, 3, 0, COEX_XOR_RF_ON_FLAGS},
96 {4, 3, 0, COEX_RF_OFF_FLAGS},
97 {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
98 {4, 3, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
99 {4, 3, 0, COEX_RSRVD1_FLAGS},
100 {4, 3, 0, COEX_RSRVD2_FLAGS}
101};
102
103static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] =
104{
105 {1, 1, 0, COEX_UNASSOC_IDLE_FLAGS},
106 {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
107 {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
108 {5, 5, 0, COEX_CALIBRATION_FLAGS},
109 {4, 4, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
110 {5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS},
111 {4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS},
112 {4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
113 {4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
114 {4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
115 {1, 1, 0, COEX_RF_ON_FLAGS},
116 {1, 1, 0, COEX_RF_OFF_FLAGS},
117 {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
118 {5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
119 {1, 1, 0, COEX_RSRVD1_FLAGS},
120 {1, 1, 0, COEX_RSRVD2_FLAGS}
121};
122
123int iwm_send_prio_table(struct iwm_priv *iwm)
124{
125 struct iwm_coex_prio_table_cmd coex_table_cmd;
126 u32 coex_enabled, mode_enabled;
127
128 memset(&coex_table_cmd, 0, sizeof(struct iwm_coex_prio_table_cmd));
129
130 coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK;
131
132 switch (iwm->conf.coexist_mode) {
133 case COEX_MODE_XOR:
134 case COEX_MODE_CM:
135 coex_enabled = 1;
136 break;
137 default:
138 coex_enabled = 0;
139 break;
140 }
141
142 switch (iwm->conf.mode) {
143 case UMAC_MODE_BSS:
144 case UMAC_MODE_IBSS:
145 mode_enabled = 1;
146 break;
147 default:
148 mode_enabled = 0;
149 break;
150 }
151
152 if (coex_enabled && mode_enabled) {
153 coex_table_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK |
154 COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK |
155 COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK;
156
157 switch (iwm->conf.coexist_mode) {
158 case COEX_MODE_XOR:
159 memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl,
160 sizeof(iwm_sta_xor_prio_tbl));
161 break;
162 case COEX_MODE_CM:
163 memcpy(coex_table_cmd.sta_prio, iwm_sta_cm_prio_tbl,
164 sizeof(iwm_sta_cm_prio_tbl));
165 break;
166 default:
167 IWM_ERR(iwm, "Invalid coex_mode 0x%x\n",
168 iwm->conf.coexist_mode);
169 break;
170 }
171 } else
172 IWM_WARN(iwm, "coexistense disabled\n");
173
174 return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD,
175 &coex_table_cmd,
176 sizeof(struct iwm_coex_prio_table_cmd), 1);
177}
178
179int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
180{
181 struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd;
182
183 memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd));
184
185 cal_cfg_cmd.ucode_cfg.init.enable = cpu_to_le32(calib_requested);
186 cal_cfg_cmd.ucode_cfg.init.start = cpu_to_le32(calib_requested);
187 cal_cfg_cmd.ucode_cfg.init.send_res = cpu_to_le32(calib_requested);
188 cal_cfg_cmd.ucode_cfg.flags =
189 cpu_to_le32(CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK);
190
191 return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd,
192 sizeof(struct iwm_lmac_cal_cfg_cmd), 1);
193}
194
195int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
196{
197 struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd;
198
199 memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd));
200
201 cal_cfg_cmd.ucode_cfg.periodic.enable = cpu_to_le32(calib_requested);
202 cal_cfg_cmd.ucode_cfg.periodic.start = cpu_to_le32(calib_requested);
203
204 return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd,
205 sizeof(struct iwm_lmac_cal_cfg_cmd), 0);
206}
207
208int iwm_store_rxiq_calib_result(struct iwm_priv *iwm)
209{
210 struct iwm_calib_rxiq *rxiq;
211 u8 *eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
212 int grplen = sizeof(struct iwm_calib_rxiq_group);
213
214 rxiq = kzalloc(sizeof(struct iwm_calib_rxiq), GFP_KERNEL);
215 if (!rxiq) {
216 IWM_ERR(iwm, "Couldn't alloc memory for RX IQ\n");
217 return -ENOMEM;
218 }
219
220 eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
221 if (IS_ERR(eeprom_rxiq)) {
222 IWM_ERR(iwm, "Couldn't access EEPROM RX IQ entry\n");
223 return PTR_ERR(eeprom_rxiq);
224 }
225
226 iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].buf = (u8 *)rxiq;
227 iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].size = sizeof(*rxiq);
228
229 rxiq->hdr.opcode = SHILOH_PHY_CALIBRATE_RX_IQ_CMD;
230 rxiq->hdr.first_grp = 0;
231 rxiq->hdr.grp_num = 1;
232 rxiq->hdr.all_data_valid = 1;
233
234 memcpy(&rxiq->group[0], eeprom_rxiq, 4 * grplen);
235 memcpy(&rxiq->group[4], eeprom_rxiq + 6 * grplen, grplen);
236
237 return 0;
238}
239
240int iwm_send_calib_results(struct iwm_priv *iwm)
241{
242 int i, ret = 0;
243
244 for (i = PHY_CALIBRATE_OPCODES_NUM; i < CALIBRATION_CMD_NUM; i++) {
245 if (test_bit(i - PHY_CALIBRATE_OPCODES_NUM,
246 &iwm->calib_done_map)) {
247 IWM_DBG_CMD(iwm, DBG,
248 "Send calibration %d result\n", i);
249 ret |= iwm_send_lmac_ptrough_cmd(iwm,
250 REPLY_PHY_CALIBRATION_CMD,
251 iwm->calib_res[i].buf,
252 iwm->calib_res[i].size, 0);
253
254 kfree(iwm->calib_res[i].buf);
255 iwm->calib_res[i].buf = NULL;
256 iwm->calib_res[i].size = 0;
257 }
258 }
259
260 return ret;
261}
262
263int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp)
264{
265 struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
266 struct iwm_umac_cmd umac_cmd;
267 struct iwm_umac_cmd_reset reset;
268
269 reset.flags = reset_flags;
270
271 umac_cmd.id = UMAC_CMD_OPCODE_RESET;
272 umac_cmd.resp = resp;
273
274 return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &reset,
275 sizeof(struct iwm_umac_cmd_reset));
276}
277
278int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value)
279{
280 struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
281 struct iwm_umac_cmd umac_cmd;
282 struct iwm_umac_cmd_set_param_fix param;
283
284 if ((tbl != UMAC_PARAM_TBL_CFG_FIX) &&
285 (tbl != UMAC_PARAM_TBL_FA_CFG_FIX))
286 return -EINVAL;
287
288 umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_FIX;
289 umac_cmd.resp = 0;
290
291 param.tbl = cpu_to_le16(tbl);
292 param.key = cpu_to_le16(key);
293 param.value = cpu_to_le32(value);
294
295 return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &param,
296 sizeof(struct iwm_umac_cmd_set_param_fix));
297}
298
299int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
300 void *payload, u16 payload_size)
301{
302 struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
303 struct iwm_umac_cmd umac_cmd;
304 struct iwm_umac_cmd_set_param_var *param_hdr;
305 u8 *param;
306 int ret;
307
308 param = kzalloc(payload_size +
309 sizeof(struct iwm_umac_cmd_set_param_var), GFP_KERNEL);
310 if (!param) {
311 IWM_ERR(iwm, "Couldn't allocate param\n");
312 return -ENOMEM;
313 }
314
315 param_hdr = (struct iwm_umac_cmd_set_param_var *)param;
316
317 umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_VAR;
318 umac_cmd.resp = 0;
319
320 param_hdr->tbl = cpu_to_le16(UMAC_PARAM_TBL_CFG_VAR);
321 param_hdr->key = cpu_to_le16(key);
322 param_hdr->len = cpu_to_le16(payload_size);
323 memcpy(param + sizeof(struct iwm_umac_cmd_set_param_var),
324 payload, payload_size);
325
326 ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, param,
327 sizeof(struct iwm_umac_cmd_set_param_var) +
328 payload_size);
329 kfree(param);
330
331 return ret;
332}
333
334int iwm_send_umac_config(struct iwm_priv *iwm,
335 __le32 reset_flags)
336{
337 int ret;
338
339 /* Use UMAC default values */
340 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
341 CFG_POWER_INDEX, iwm->conf.power_index);
342 if (ret < 0)
343 return ret;
344
345 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
346 CFG_FRAG_THRESHOLD,
347 iwm->conf.frag_threshold);
348 if (ret < 0)
349 return ret;
350
351 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
352 CFG_RTS_THRESHOLD,
353 iwm->conf.rts_threshold);
354 if (ret < 0)
355 return ret;
356
357 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
358 CFG_CTS_TO_SELF, iwm->conf.cts_to_self);
359 if (ret < 0)
360 return ret;
361
362 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
363 CFG_COEX_MODE, iwm->conf.coexist_mode);
364 if (ret < 0)
365 return ret;
366
367 /*
368 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
369 CFG_ASSOCIATION_TIMEOUT,
370 iwm->conf.assoc_timeout);
371 if (ret < 0)
372 return ret;
373
374 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
375 CFG_ROAM_TIMEOUT,
376 iwm->conf.roam_timeout);
377 if (ret < 0)
378 return ret;
379
380 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
381 CFG_WIRELESS_MODE,
382 WIRELESS_MODE_11A | WIRELESS_MODE_11G);
383 if (ret < 0)
384 return ret;
385 */
386
387 ret = iwm_umac_set_config_var(iwm, CFG_NET_ADDR,
388 iwm_to_ndev(iwm)->dev_addr, ETH_ALEN);
389 if (ret < 0)
390 return ret;
391
392 /* UMAC PM static configurations */
393 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
394 CFG_PM_LEGACY_RX_TIMEOUT, 0x12C);
395 if (ret < 0)
396 return ret;
397
398 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
399 CFG_PM_LEGACY_TX_TIMEOUT, 0x15E);
400 if (ret < 0)
401 return ret;
402
403 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
404 CFG_PM_CTRL_FLAGS, 0x30001);
405 if (ret < 0)
406 return ret;
407
408 ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
409 CFG_PM_KEEP_ALIVE_IN_BEACONS, 0x80);
410 if (ret < 0)
411 return ret;
412
413 /* reset UMAC */
414 ret = iwm_send_umac_reset(iwm, reset_flags, 1);
415 if (ret < 0)
416 return ret;
417
418 ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC,
419 WAIT_NOTIF_TIMEOUT);
420 if (ret) {
421 IWM_ERR(iwm, "Wait for UMAC RESET timeout\n");
422 return ret;
423 }
424
425 return ret;
426}
427
428int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id)
429{
430 struct iwm_udma_wifi_cmd udma_cmd;
431 struct iwm_umac_cmd umac_cmd;
432 struct iwm_tx_info *tx_info = skb_to_tx_info(skb);
433
434 udma_cmd.eop = 1; /* always set eop for non-concatenated Tx */
435 udma_cmd.credit_group = pool_id;
436 udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid;
437 udma_cmd.lmac_offset = 0;
438
439 umac_cmd.id = REPLY_TX;
440 umac_cmd.color = tx_info->color;
441 umac_cmd.resp = 0;
442
443 return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
444 skb->data, skb->len);
445}
446
447static int iwm_target_read(struct iwm_priv *iwm, __le32 address,
448 u8 *response, u32 resp_size)
449{
450 struct iwm_udma_nonwifi_cmd target_cmd;
451 struct iwm_nonwifi_cmd *cmd;
452 u16 seq_num;
453 int ret = 0;
454
455 target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ;
456 target_cmd.addr = address;
457 target_cmd.op1_sz = cpu_to_le32(resp_size);
458 target_cmd.op2 = 0;
459 target_cmd.handle_by_hw = 0;
460 target_cmd.resp = 1;
461 target_cmd.eop = 1;
462
463 ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
464 if (ret < 0)
465 IWM_ERR(iwm, "Couldn't send READ command\n");
466
467 /* When succeding, the send_target routine returns the seq number */
468 seq_num = ret;
469
470 ret = wait_event_interruptible_timeout(iwm->nonwifi_queue,
471 (cmd = iwm_get_pending_nonwifi_cmd(iwm, seq_num,
472 UMAC_HDI_OUT_OPCODE_READ)) != NULL,
473 2 * HZ);
474
475 if (!ret) {
476 IWM_ERR(iwm, "Didn't receive a target READ answer\n");
477 return ret;
478 }
479
480 memcpy(response, cmd->buf.hdr + sizeof(struct iwm_udma_in_hdr),
481 resp_size);
482
483 kfree(cmd);
484
485 return ret;
486}
487
488int iwm_read_mac(struct iwm_priv *iwm, u8 *mac)
489{
490 int ret;
491 u8 mac_align[ALIGN(ETH_ALEN, 8)];
492
493 ret = iwm_target_read(iwm, cpu_to_le32(WICO_MAC_ADDRESS_ADDR),
494 mac_align, sizeof(mac_align));
495 if (ret < 0)
496 return ret;
497
498 if (is_valid_ether_addr(mac_align))
499 memcpy(mac, mac_align, ETH_ALEN);
500 else {
501 IWM_ERR(iwm, "Invalid EEPROM MAC\n");
502 memcpy(mac, iwm->conf.mac_addr, ETH_ALEN);
503 get_random_bytes(&mac[3], 3);
504 }
505
506 return 0;
507}
508
509int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx)
510{
511 struct iwm_umac_tx_key_id tx_key_id;
512
513 if (!iwm->default_key || !iwm->default_key->in_use)
514 return -EINVAL;
515
516 tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID;
517 tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) -
518 sizeof(struct iwm_umac_wifi_if));
519
520 tx_key_id.key_idx = key_idx;
521
522 return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1);
523}
524
525static int iwm_check_profile(struct iwm_priv *iwm)
526{
527 if (!iwm->umac_profile_active)
528 return -EAGAIN;
529
530 if (iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
531 iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104 &&
532 iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_TKIP &&
533 iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_CCMP) {
534 IWM_ERR(iwm, "Wrong unicast cipher: 0x%x\n",
535 iwm->umac_profile->sec.ucast_cipher);
536 return -EAGAIN;
537 }
538
539 if (iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
540 iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_104 &&
541 iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_TKIP &&
542 iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_CCMP) {
543 IWM_ERR(iwm, "Wrong multicast cipher: 0x%x\n",
544 iwm->umac_profile->sec.mcast_cipher);
545 return -EAGAIN;
546 }
547
548 if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 ||
549 iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) &&
550 (iwm->umac_profile->sec.ucast_cipher !=
551 iwm->umac_profile->sec.mcast_cipher)) {
552 IWM_ERR(iwm, "Unicast and multicast ciphers differ for WEP\n");
553 }
554
555 return 0;
556}
557
558int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
559 struct iwm_key *key)
560{
561 int ret;
562 u8 cmd[64], *sta_addr, *key_data, key_len;
563 s8 key_idx;
564 u16 cmd_size = 0;
565 struct iwm_umac_key_hdr *key_hdr = &key->hdr;
566 struct iwm_umac_key_wep40 *wep40 = (struct iwm_umac_key_wep40 *)cmd;
567 struct iwm_umac_key_wep104 *wep104 = (struct iwm_umac_key_wep104 *)cmd;
568 struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd;
569 struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd;
570
571 if (set_tx_key)
572 iwm->default_key = key;
573
574 /*
575 * We check if our current profile is valid.
576 * If not, we dont push the key, we just cache them,
577 * so that with the next siwsessid call, the keys
578 * will be actually pushed.
579 */
580 if (!remove) {
581 ret = iwm_check_profile(iwm);
582 if (ret < 0)
583 return ret;
584 }
585
586 sta_addr = key->hdr.mac;
587 key_data = key->key;
588 key_len = key->key_len;
589 key_idx = key->hdr.key_idx;
590
591 if (!remove) {
592 IWM_DBG_WEXT(iwm, DBG, "key_idx:%d set tx key:%d\n",
593 key_idx, set_tx_key);
594 IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len);
595 IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n",
596 key_hdr->mac, key_hdr->key_idx, key_hdr->multicast);
597
598 IWM_DBG_WEXT(iwm, DBG, "profile: mcast:0x%x, ucast:0x%x\n",
599 iwm->umac_profile->sec.mcast_cipher,
600 iwm->umac_profile->sec.ucast_cipher);
601 IWM_DBG_WEXT(iwm, DBG, "profile: auth_type:0x%x, flags:0x%x\n",
602 iwm->umac_profile->sec.auth_type,
603 iwm->umac_profile->sec.flags);
604
605 switch (key->alg) {
606 case UMAC_CIPHER_TYPE_WEP_40:
607 wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY;
608 wep40->hdr.buf_size =
609 cpu_to_le16(sizeof(struct iwm_umac_key_wep40) -
610 sizeof(struct iwm_umac_wifi_if));
611
612 memcpy(&wep40->key_hdr, key_hdr,
613 sizeof(struct iwm_umac_key_hdr));
614 memcpy(wep40->key, key_data, key_len);
615 wep40->static_key = 1;
616
617 cmd_size = sizeof(struct iwm_umac_key_wep40);
618 break;
619
620 case UMAC_CIPHER_TYPE_WEP_104:
621 wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY;
622 wep104->hdr.buf_size =
623 cpu_to_le16(sizeof(struct iwm_umac_key_wep104) -
624 sizeof(struct iwm_umac_wifi_if));
625
626 memcpy(&wep104->key_hdr, key_hdr,
627 sizeof(struct iwm_umac_key_hdr));
628 memcpy(wep104->key, key_data, key_len);
629 wep104->static_key = 1;
630
631 cmd_size = sizeof(struct iwm_umac_key_wep104);
632 break;
633
634 case UMAC_CIPHER_TYPE_CCMP:
635 key_hdr->key_idx++;
636 ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY;
637 ccmp->hdr.buf_size =
638 cpu_to_le16(sizeof(struct iwm_umac_key_ccmp) -
639 sizeof(struct iwm_umac_wifi_if));
640
641 memcpy(&ccmp->key_hdr, key_hdr,
642 sizeof(struct iwm_umac_key_hdr));
643
644 memcpy(ccmp->key, key_data, key_len);
645
646 if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
647 memcpy(ccmp->iv_count, key->rx_seq, 6);
648
649 cmd_size = sizeof(struct iwm_umac_key_ccmp);
650 break;
651
652 case UMAC_CIPHER_TYPE_TKIP:
653 key_hdr->key_idx++;
654 tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY;
655 tkip->hdr.buf_size =
656 cpu_to_le16(sizeof(struct iwm_umac_key_tkip) -
657 sizeof(struct iwm_umac_wifi_if));
658
659 memcpy(&tkip->key_hdr, key_hdr,
660 sizeof(struct iwm_umac_key_hdr));
661
662 memcpy(tkip->tkip_key, key_data, IWM_TKIP_KEY_SIZE);
663 memcpy(tkip->mic_tx_key, key_data + IWM_TKIP_KEY_SIZE,
664 IWM_TKIP_MIC_SIZE);
665 memcpy(tkip->mic_rx_key,
666 key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE,
667 IWM_TKIP_MIC_SIZE);
668
669 if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
670 memcpy(ccmp->iv_count, key->rx_seq, 6);
671
672 cmd_size = sizeof(struct iwm_umac_key_tkip);
673 break;
674
675 default:
676 return -ENOTSUPP;
677 }
678
679 if ((key->alg == UMAC_CIPHER_TYPE_CCMP) ||
680 (key->alg == UMAC_CIPHER_TYPE_TKIP))
681 /*
682 * UGLY_UGLY_UGLY
683 * Copied HACK from the MWG driver.
684 * Without it, the key is set before the second
685 * EAPOL frame is sent, and the latter is thus
686 * encrypted.
687 */
688 schedule_timeout_interruptible(usecs_to_jiffies(300));
689
690 ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1);
691 if (ret < 0)
692 goto err;
693
694 /*
695 * We need a default key only if it is set and
696 * if we're doing WEP.
697 */
698 if (iwm->default_key == key &&
699 ((key->alg == UMAC_CIPHER_TYPE_WEP_40) ||
700 (key->alg == UMAC_CIPHER_TYPE_WEP_104))) {
701 ret = iwm_set_tx_key(iwm, key_idx);
702 if (ret < 0)
703 goto err;
704 }
705 } else {
706 struct iwm_umac_key_remove key_remove;
707
708 key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY;
709 key_remove.hdr.buf_size =
710 cpu_to_le16(sizeof(struct iwm_umac_key_remove) -
711 sizeof(struct iwm_umac_wifi_if));
712 memcpy(&key_remove.key_hdr, key_hdr,
713 sizeof(struct iwm_umac_key_hdr));
714
715 ret = iwm_send_wifi_if_cmd(iwm, &key_remove,
716 sizeof(struct iwm_umac_key_remove),
717 1);
718 if (ret < 0)
719 return ret;
720
721 iwm->keys[key_idx].in_use = 0;
722 }
723
724 return 0;
725
726 err:
727 kfree(key);
728 return ret;
729}
730
731
732int iwm_send_mlme_profile(struct iwm_priv *iwm)
733{
734 int ret, i;
735 struct iwm_umac_profile profile;
736
737 memcpy(&profile, iwm->umac_profile, sizeof(profile));
738
739 profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE;
740 profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) -
741 sizeof(struct iwm_umac_wifi_if));
742
743 ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1);
744 if (ret < 0) {
745 IWM_ERR(iwm, "Send profile command failed\n");
746 return ret;
747 }
748
749 /* Wait for the profile to be active */
750 ret = wait_event_interruptible_timeout(iwm->mlme_queue,
751 iwm->umac_profile_active == 1,
752 3 * HZ);
753 if (!ret)
754 return -EBUSY;
755
756
757 for (i = 0; i < IWM_NUM_KEYS; i++)
758 if (iwm->keys[i].in_use) {
759 int default_key = 0;
760 struct iwm_key *key = &iwm->keys[i];
761
762 if (key == iwm->default_key)
763 default_key = 1;
764
765 /* Wait for the profile before sending the keys */
766 wait_event_interruptible_timeout(iwm->mlme_queue,
767 (test_bit(IWM_STATUS_ASSOCIATING, &iwm->status) ||
768 test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)),
769 3 * HZ);
770
771 ret = iwm_set_key(iwm, 0, default_key, key);
772 if (ret < 0)
773 return ret;
774 }
775
776 return 0;
777}
778
779int iwm_invalidate_mlme_profile(struct iwm_priv *iwm)
780{
781 int ret;
782 struct iwm_umac_invalidate_profile invalid;
783
784 invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE;
785 invalid.hdr.buf_size =
786 cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) -
787 sizeof(struct iwm_umac_wifi_if));
788
789 invalid.reason = WLAN_REASON_UNSPECIFIED;
790
791 ret = iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1);
792 if (ret < 0)
793 return ret;
794
795 ret = wait_event_interruptible_timeout(iwm->mlme_queue,
796 (iwm->umac_profile_active == 0),
797 2 * HZ);
798 if (!ret)
799 return -EBUSY;
800
801 return 0;
802}
803
804int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags)
805{
806 struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
807 struct iwm_umac_cmd umac_cmd;
808 struct iwm_umac_cmd_stats_req stats_req;
809
810 stats_req.flags = cpu_to_le32(flags);
811
812 umac_cmd.id = UMAC_CMD_OPCODE_STATISTIC_REQUEST;
813 umac_cmd.resp = 0;
814
815 return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stats_req,
816 sizeof(struct iwm_umac_cmd_stats_req));
817}
818
819int iwm_send_umac_channel_list(struct iwm_priv *iwm)
820{
821 struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
822 struct iwm_umac_cmd umac_cmd;
823 struct iwm_umac_cmd_get_channel_list *ch_list;
824 int size = sizeof(struct iwm_umac_cmd_get_channel_list) +
825 sizeof(struct iwm_umac_channel_info) * 4;
826 int ret;
827
828 ch_list = kzalloc(size, GFP_KERNEL);
829 if (!ch_list) {
830 IWM_ERR(iwm, "Couldn't allocate channel list cmd\n");
831 return -ENOMEM;
832 }
833
834 ch_list->ch[0].band = UMAC_BAND_2GHZ;
835 ch_list->ch[0].type = UMAC_CHANNEL_WIDTH_20MHZ;
836 ch_list->ch[0].flags = UMAC_CHANNEL_FLAG_VALID;
837
838 ch_list->ch[1].band = UMAC_BAND_5GHZ;
839 ch_list->ch[1].type = UMAC_CHANNEL_WIDTH_20MHZ;
840 ch_list->ch[1].flags = UMAC_CHANNEL_FLAG_VALID;
841
842 ch_list->ch[2].band = UMAC_BAND_2GHZ;
843 ch_list->ch[2].type = UMAC_CHANNEL_WIDTH_20MHZ;
844 ch_list->ch[2].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS;
845
846 ch_list->ch[3].band = UMAC_BAND_5GHZ;
847 ch_list->ch[3].type = UMAC_CHANNEL_WIDTH_20MHZ;
848 ch_list->ch[3].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS;
849
850 ch_list->count = cpu_to_le16(4);
851
852 umac_cmd.id = UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST;
853 umac_cmd.resp = 1;
854
855 ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ch_list, size);
856
857 kfree(ch_list);
858
859 return ret;
860}
861
862int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
863 int ssid_num)
864{
865 struct iwm_umac_cmd_scan_request req;
866 int i, ret;
867
868 memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request));
869
870 req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST;
871 req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request)
872 - sizeof(struct iwm_umac_wifi_if));
873 req.type = UMAC_WIFI_IF_SCAN_TYPE_USER;
874 req.timeout = 2;
875 req.seq_num = iwm->scan_id;
876 req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX);
877
878 for (i = 0; i < req.ssid_num; i++) {
879 memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len);
880 req.ssids[i].ssid_len = ssids[i].ssid_len;
881 }
882
883 ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0);
884 if (ret < 0) {
885 IWM_ERR(iwm, "Couldn't send scan request\n");
886 return ret;
887 }
888
889 iwm->scan_id = iwm->scan_id++ % IWM_SCAN_ID_MAX;
890
891 return 0;
892}
893
894int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len)
895{
896 struct cfg80211_ssid one_ssid;
897
898 if (test_and_set_bit(IWM_STATUS_SCANNING, &iwm->status))
899 return 0;
900
901 one_ssid.ssid_len = min(ssid_len, IEEE80211_MAX_SSID_LEN);
902 memcpy(&one_ssid.ssid, ssid, one_ssid.ssid_len);
903
904 return iwm_scan_ssids(iwm, &one_ssid, 1);
905}
906
907int iwm_target_reset(struct iwm_priv *iwm)
908{
909 struct iwm_udma_nonwifi_cmd target_cmd;
910
911 target_cmd.opcode = UMAC_HDI_OUT_OPCODE_REBOOT;
912 target_cmd.addr = 0;
913 target_cmd.op1_sz = 0;
914 target_cmd.op2 = 0;
915 target_cmd.handle_by_hw = 0;
916 target_cmd.resp = 0;
917 target_cmd.eop = 1;
918
919 return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
920}
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h
new file mode 100644
index 000000000000..36b13a130595
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/commands.h
@@ -0,0 +1,419 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef __IWM_COMMANDS_H__
40#define __IWM_COMMANDS_H__
41
42#include <linux/ieee80211.h>
43
44#define IWM_BARKER_REBOOT_NOTIFICATION 0xF
45#define IWM_ACK_BARKER_NOTIFICATION 0x10
46
47/* UMAC commands */
48#define UMAC_RST_CTRL_FLG_LARC_CLK_EN 0x0001
49#define UMAC_RST_CTRL_FLG_LARC_RESET 0x0002
50#define UMAC_RST_CTRL_FLG_FUNC_RESET 0x0004
51#define UMAC_RST_CTRL_FLG_DEV_RESET 0x0008
52#define UMAC_RST_CTRL_FLG_WIFI_CORE_EN 0x0010
53#define UMAC_RST_CTRL_FLG_WIFI_LINK_EN 0x0040
54#define UMAC_RST_CTRL_FLG_WIFI_MLME_EN 0x0080
55#define UMAC_RST_CTRL_FLG_NVM_RELOAD 0x0100
56
57struct iwm_umac_cmd_reset {
58 __le32 flags;
59} __attribute__ ((packed));
60
61#define UMAC_PARAM_TBL_ORD_FIX 0x0
62#define UMAC_PARAM_TBL_ORD_VAR 0x1
63#define UMAC_PARAM_TBL_CFG_FIX 0x2
64#define UMAC_PARAM_TBL_CFG_VAR 0x3
65#define UMAC_PARAM_TBL_BSS_TRK 0x4
66#define UMAC_PARAM_TBL_FA_CFG_FIX 0x5
67#define UMAC_PARAM_TBL_STA 0x6
68#define UMAC_PARAM_TBL_CHN 0x7
69#define UMAC_PARAM_TBL_STATISTICS 0x8
70
71/* fast access table */
72enum {
73 CFG_FRAG_THRESHOLD = 0,
74 CFG_FRAME_RETRY_LIMIT,
75 CFG_OS_QUEUE_UTIL_TH,
76 CFG_RX_FILTER,
77 /* <-- LAST --> */
78 FAST_ACCESS_CFG_TBL_FIX_LAST
79};
80
81/* fixed size table */
82enum {
83 CFG_POWER_INDEX = 0,
84 CFG_PM_LEGACY_RX_TIMEOUT,
85 CFG_PM_LEGACY_TX_TIMEOUT,
86 CFG_PM_CTRL_FLAGS,
87 CFG_PM_KEEP_ALIVE_IN_BEACONS,
88 CFG_BT_ON_THRESHOLD,
89 CFG_RTS_THRESHOLD,
90 CFG_CTS_TO_SELF,
91 CFG_COEX_MODE,
92 CFG_WIRELESS_MODE,
93 CFG_ASSOCIATION_TIMEOUT,
94 CFG_ROAM_TIMEOUT,
95 CFG_CAPABILITY_SUPPORTED_RATES,
96 CFG_SCAN_ALLOWED_UNASSOC_FLAGS,
97 CFG_SCAN_ALLOWED_MAIN_ASSOC_FLAGS,
98 CFG_SCAN_ALLOWED_PAN_ASSOC_FLAGS,
99 CFG_SCAN_INTERNAL_PERIODIC_ENABLED,
100 CFG_SCAN_IMM_INTERNAL_PERIODIC_SCAN_ON_INIT,
101 CFG_SCAN_DEFAULT_PERIODIC_FREQ_SEC,
102 CFG_SCAN_NUM_PASSIVE_CHAN_PER_PARTIAL_SCAN,
103 CFG_TLC_SUPPORTED_TX_HT_RATES,
104 CFG_TLC_SUPPORTED_TX_RATES,
105 CFG_TLC_VALID_ANTENNA,
106 CFG_TLC_SPATIAL_STREAM_SUPPORTED,
107 CFG_TLC_RETRY_PER_RATE,
108 CFG_TLC_RETRY_PER_HT_RATE,
109 CFG_TLC_FIXED_RATE,
110 CFG_TLC_FIXED_RATE_FLAGS,
111 CFG_TLC_CONTROL_FLAGS,
112 CFG_TLC_SR_MIN_FAIL,
113 CFG_TLC_SR_MIN_PASS,
114 CFG_TLC_HT_STAY_IN_COL_PASS_THRESH,
115 CFG_TLC_HT_STAY_IN_COL_FAIL_THRESH,
116 CFG_TLC_LEGACY_STAY_IN_COL_PASS_THRESH,
117 CFG_TLC_LEGACY_STAY_IN_COL_FAIL_THRESH,
118 CFG_TLC_HT_FLUSH_STATS_PACKETS,
119 CFG_TLC_LEGACY_FLUSH_STATS_PACKETS,
120 CFG_TLC_LEGACY_FLUSH_STATS_MS,
121 CFG_TLC_HT_FLUSH_STATS_MS,
122 CFG_TLC_STAY_IN_COL_TIME_OUT,
123 CFG_TLC_AGG_SHORT_LIM,
124 CFG_TLC_AGG_LONG_LIM,
125 CFG_TLC_HT_SR_NO_DECREASE,
126 CFG_TLC_LEGACY_SR_NO_DECREASE,
127 CFG_TLC_SR_FORCE_DECREASE,
128 CFG_TLC_SR_ALLOW_INCREASE,
129 CFG_TLC_AGG_SET_LONG,
130 CFG_TLC_AUTO_AGGREGATION,
131 CFG_TLC_AGG_THRESHOLD,
132 CFG_TLC_TID_LOAD_THRESHOLD,
133 CFG_TLC_BLOCK_ACK_TIMEOUT,
134 CFG_TLC_NO_BA_COUNTED_AS_ONE,
135 CFG_TLC_NUM_BA_STREAMS_ALLOWED,
136 CFG_TLC_NUM_BA_STREAMS_PRESENT,
137 CFG_TLC_RENEW_ADDBA_DELAY,
138 CFG_TLC_NUM_OF_MULTISEC_TO_COUN_LOAD,
139 CFG_TLC_IS_STABLE_IN_HT,
140 CFG_RLC_CHAIN_CTRL,
141 CFG_TRK_TABLE_OP_MODE,
142 CFG_TRK_TABLE_RSSI_THRESHOLD,
143 CFG_TX_PWR_TARGET, /* Used By xVT */
144 CFG_TX_PWR_LIMIT_USR,
145 CFG_TX_PWR_LIMIT_BSS, /* 11d limit */
146 CFG_TX_PWR_LIMIT_BSS_CONSTRAINT, /* 11h constraint */
147 CFG_TX_PWR_MODE,
148 CFG_MLME_DBG_NOTIF_BLOCK,
149 CFG_BT_OFF_BECONS_INTERVALS,
150 CFG_BT_FRAG_DURATION,
151
152 /* <-- LAST --> */
153 CFG_TBL_FIX_LAST
154};
155
156/* variable size table */
157enum {
158 CFG_NET_ADDR = 0,
159 CFG_PROFILE,
160 /* <-- LAST --> */
161 CFG_TBL_VAR_LAST
162};
163
164struct iwm_umac_cmd_set_param_fix {
165 __le16 tbl;
166 __le16 key;
167 __le32 value;
168} __attribute__ ((packed));
169
170struct iwm_umac_cmd_set_param_var {
171 __le16 tbl;
172 __le16 key;
173 __le16 len;
174 __le16 reserved;
175} __attribute__ ((packed));
176
177struct iwm_umac_cmd_get_param {
178 __le16 tbl;
179 __le16 key;
180} __attribute__ ((packed));
181
182struct iwm_umac_cmd_get_param_resp {
183 __le16 tbl;
184 __le16 key;
185 __le16 len;
186 __le16 reserved;
187} __attribute__ ((packed));
188
189struct iwm_umac_cmd_eeprom_proxy_hdr {
190 __le32 type;
191 __le32 offset;
192 __le32 len;
193} __attribute__ ((packed));
194
195struct iwm_umac_cmd_eeprom_proxy {
196 struct iwm_umac_cmd_eeprom_proxy_hdr hdr;
197 u8 buf[0];
198} __attribute__ ((packed));
199
200#define IWM_UMAC_CMD_EEPROM_TYPE_READ 0x1
201#define IWM_UMAC_CMD_EEPROM_TYPE_WRITE 0x2
202
203#define UMAC_CHANNEL_FLAG_VALID BIT(0)
204#define UMAC_CHANNEL_FLAG_IBSS BIT(1)
205#define UMAC_CHANNEL_FLAG_ACTIVE BIT(3)
206#define UMAC_CHANNEL_FLAG_RADAR BIT(4)
207#define UMAC_CHANNEL_FLAG_DFS BIT(7)
208
209struct iwm_umac_channel_info {
210 u8 band;
211 u8 type;
212 u8 reserved;
213 u8 flags;
214 __le32 channels_mask;
215} __attribute__ ((packed));
216
217struct iwm_umac_cmd_get_channel_list {
218 __le16 count;
219 __le16 reserved;
220 struct iwm_umac_channel_info ch[0];
221} __attribute__ ((packed));
222
223
224/* UMAC WiFi interface commands */
225
226/* Coexistence mode */
227#define COEX_MODE_SA 0x1
228#define COEX_MODE_XOR 0x2
229#define COEX_MODE_CM 0x3
230#define COEX_MODE_MAX 0x4
231
232/* Wireless mode */
233#define WIRELESS_MODE_11A 0x1
234#define WIRELESS_MODE_11G 0x2
235
236#define UMAC_PROFILE_EX_IE_REQUIRED 0x1
237#define UMAC_PROFILE_QOS_ALLOWED 0x2
238
239/* Scanning */
240#define UMAC_WIFI_IF_PROBE_OPTION_MAX 10
241
242#define UMAC_WIFI_IF_SCAN_TYPE_USER 0x0
243#define UMAC_WIFI_IF_SCAN_TYPE_UMAC_RESERVED 0x1
244#define UMAC_WIFI_IF_SCAN_TYPE_HOST_PERIODIC 0x2
245#define UMAC_WIFI_IF_SCAN_TYPE_MAX 0x3
246
247struct iwm_umac_ssid {
248 u8 ssid_len;
249 u8 ssid[IEEE80211_MAX_SSID_LEN];
250 u8 reserved[3];
251} __attribute__ ((packed));
252
253struct iwm_umac_cmd_scan_request {
254 struct iwm_umac_wifi_if hdr;
255 __le32 type; /* UMAC_WIFI_IF_SCAN_TYPE_* */
256 u8 ssid_num;
257 u8 seq_num;
258 u8 timeout; /* In seconds */
259 u8 reserved;
260 struct iwm_umac_ssid ssids[UMAC_WIFI_IF_PROBE_OPTION_MAX];
261} __attribute__ ((packed));
262
263#define UMAC_CIPHER_TYPE_NONE 0xFF
264#define UMAC_CIPHER_TYPE_USE_GROUPCAST 0x00
265#define UMAC_CIPHER_TYPE_WEP_40 0x01
266#define UMAC_CIPHER_TYPE_WEP_104 0x02
267#define UMAC_CIPHER_TYPE_TKIP 0x04
268#define UMAC_CIPHER_TYPE_CCMP 0x08
269
270/* Supported authentication types - bitmap */
271#define UMAC_AUTH_TYPE_OPEN 0x00
272#define UMAC_AUTH_TYPE_LEGACY_PSK 0x01
273#define UMAC_AUTH_TYPE_8021X 0x02
274#define UMAC_AUTH_TYPE_RSNA_PSK 0x04
275
276/* iwm_umac_security.flag is WPA supported -- bits[0:0] */
277#define UMAC_SEC_FLG_WPA_ON_POS 0
278#define UMAC_SEC_FLG_WPA_ON_SEED 1
279#define UMAC_SEC_FLG_WPA_ON_MSK (UMAC_SEC_FLG_WPA_ON_SEED << \
280 UMAC_SEC_FLG_WPA_ON_POS)
281
282/* iwm_umac_security.flag is WPA2 supported -- bits [1:1] */
283#define UMAC_SEC_FLG_RSNA_ON_POS 1
284#define UMAC_SEC_FLG_RSNA_ON_SEED 1
285#define UMAC_SEC_FLG_RSNA_ON_MSK (UMAC_SEC_FLG_RSNA_ON_SEED << \
286 UMAC_SEC_FLG_RSNA_ON_POS)
287
288/* iwm_umac_security.flag is WSC mode on -- bits [2:2] */
289#define UMAC_SEC_FLG_WSC_ON_POS 2
290#define UMAC_SEC_FLG_WSC_ON_SEED 1
291
292/* Legacy profile can use only WEP40 and WEP104 for encryption and
293 * OPEN or PSK for authentication */
294#define UMAC_SEC_FLG_LEGACY_PROFILE 0
295
296struct iwm_umac_security {
297 u8 auth_type;
298 u8 ucast_cipher;
299 u8 mcast_cipher;
300 u8 flags;
301} __attribute__ ((packed));
302
303struct iwm_umac_ibss {
304 u8 beacon_interval; /* in millisecond */
305 u8 atim; /* in millisecond */
306 s8 join_only;
307 u8 band;
308 u8 channel;
309 u8 reserved[3];
310} __attribute__ ((packed));
311
312#define UMAC_MODE_BSS 0
313#define UMAC_MODE_IBSS 1
314
315#define UMAC_BSSID_MAX 4
316
317struct iwm_umac_profile {
318 struct iwm_umac_wifi_if hdr;
319 __le32 mode;
320 struct iwm_umac_ssid ssid;
321 u8 bssid[UMAC_BSSID_MAX][ETH_ALEN];
322 struct iwm_umac_security sec;
323 struct iwm_umac_ibss ibss;
324 __le32 channel_2ghz;
325 __le32 channel_5ghz;
326 __le16 flags;
327 u8 wireless_mode;
328 u8 bss_num;
329} __attribute__ ((packed));
330
331struct iwm_umac_invalidate_profile {
332 struct iwm_umac_wifi_if hdr;
333 u8 reason;
334 u8 reserved[3];
335} __attribute__ ((packed));
336
337/* Encryption key commands */
338struct iwm_umac_key_wep40 {
339 struct iwm_umac_wifi_if hdr;
340 struct iwm_umac_key_hdr key_hdr;
341 u8 key[WLAN_KEY_LEN_WEP40];
342 u8 static_key;
343 u8 reserved[2];
344} __attribute__ ((packed));
345
346struct iwm_umac_key_wep104 {
347 struct iwm_umac_wifi_if hdr;
348 struct iwm_umac_key_hdr key_hdr;
349 u8 key[WLAN_KEY_LEN_WEP104];
350 u8 static_key;
351 u8 reserved[2];
352} __attribute__ ((packed));
353
354#define IWM_TKIP_KEY_SIZE 16
355#define IWM_TKIP_MIC_SIZE 8
356struct iwm_umac_key_tkip {
357 struct iwm_umac_wifi_if hdr;
358 struct iwm_umac_key_hdr key_hdr;
359 u8 iv_count[6];
360 u8 reserved[2];
361 u8 tkip_key[IWM_TKIP_KEY_SIZE];
362 u8 mic_rx_key[IWM_TKIP_MIC_SIZE];
363 u8 mic_tx_key[IWM_TKIP_MIC_SIZE];
364} __attribute__ ((packed));
365
366struct iwm_umac_key_ccmp {
367 struct iwm_umac_wifi_if hdr;
368 struct iwm_umac_key_hdr key_hdr;
369 u8 iv_count[6];
370 u8 reserved[2];
371 u8 key[WLAN_KEY_LEN_CCMP];
372} __attribute__ ((packed));
373
374struct iwm_umac_key_remove {
375 struct iwm_umac_wifi_if hdr;
376 struct iwm_umac_key_hdr key_hdr;
377} __attribute__ ((packed));
378
379struct iwm_umac_tx_key_id {
380 struct iwm_umac_wifi_if hdr;
381 u8 key_idx;
382 u8 reserved[3];
383} __attribute__ ((packed));
384
385struct iwm_umac_cmd_stats_req {
386 __le32 flags;
387} __attribute__ ((packed));
388
389/* LMAC commands */
390int iwm_read_mac(struct iwm_priv *iwm, u8 *mac);
391int iwm_send_prio_table(struct iwm_priv *iwm);
392int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
393int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
394int iwm_send_calib_results(struct iwm_priv *iwm);
395int iwm_store_rxiq_calib_result(struct iwm_priv *iwm);
396
397/* UMAC commands */
398int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
399 bool resp);
400int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp);
401int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value);
402int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
403 void *payload, u16 payload_size);
404int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags);
405int iwm_send_mlme_profile(struct iwm_priv *iwm);
406int iwm_invalidate_mlme_profile(struct iwm_priv *iwm);
407int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id);
408int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx);
409int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
410 struct iwm_key *key);
411int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags);
412int iwm_send_umac_channel_list(struct iwm_priv *iwm);
413int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
414 int ssid_num);
415int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len);
416
417/* UDMA commands */
418int iwm_target_reset(struct iwm_priv *iwm);
419#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/debug.h b/drivers/net/wireless/iwmc3200wifi/debug.h
new file mode 100644
index 000000000000..8fbb42d9c21f
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/debug.h
@@ -0,0 +1,124 @@
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#ifndef __IWM_DEBUG_H__
25#define __IWM_DEBUG_H__
26
27#define IWM_ERR(p, f, a...) dev_err(iwm_to_dev(p), f, ## a)
28#define IWM_WARN(p, f, a...) dev_warn(iwm_to_dev(p), f, ## a)
29#define IWM_INFO(p, f, a...) dev_info(iwm_to_dev(p), f, ## a)
30#define IWM_CRIT(p, f, a...) dev_crit(iwm_to_dev(p), f, ## a)
31
32#ifdef CONFIG_IWM_DEBUG
33
34#define IWM_DEBUG_MODULE(i, level, module, f, a...) \
35do { \
36 if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\
37 dev_printk(KERN_INFO, (iwm_to_dev(i)), \
38 "%s " f, __func__ , ## a); \
39} while (0)
40
41#define IWM_HEXDUMP(i, level, module, pref, buf, len) \
42do { \
43 if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\
44 print_hex_dump(KERN_INFO, pref, DUMP_PREFIX_OFFSET, \
45 16, 1, buf, len, 1); \
46} while (0)
47
48#else
49
50#define IWM_DEBUG_MODULE(i, level, module, f, a...)
51#define IWM_HEXDUMP(i, level, module, pref, buf, len)
52
53#endif /* CONFIG_IWM_DEBUG */
54
55/* Debug modules */
56enum iwm_debug_module_id {
57 IWM_DM_BOOT = 0,
58 IWM_DM_FW,
59 IWM_DM_SDIO,
60 IWM_DM_NTF,
61 IWM_DM_RX,
62 IWM_DM_TX,
63 IWM_DM_MLME,
64 IWM_DM_CMD,
65 IWM_DM_WEXT,
66 __IWM_DM_NR,
67};
68#define IWM_DM_DEFAULT 0
69
70#define IWM_DBG_BOOT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, BOOT, f, ## a)
71#define IWM_DBG_FW(i, l, f, a...) IWM_DEBUG_MODULE(i, l, FW, f, ## a)
72#define IWM_DBG_SDIO(i, l, f, a...) IWM_DEBUG_MODULE(i, l, SDIO, f, ## a)
73#define IWM_DBG_NTF(i, l, f, a...) IWM_DEBUG_MODULE(i, l, NTF, f, ## a)
74#define IWM_DBG_RX(i, l, f, a...) IWM_DEBUG_MODULE(i, l, RX, f, ## a)
75#define IWM_DBG_TX(i, l, f, a...) IWM_DEBUG_MODULE(i, l, TX, f, ## a)
76#define IWM_DBG_MLME(i, l, f, a...) IWM_DEBUG_MODULE(i, l, MLME, f, ## a)
77#define IWM_DBG_CMD(i, l, f, a...) IWM_DEBUG_MODULE(i, l, CMD, f, ## a)
78#define IWM_DBG_WEXT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, WEXT, f, ## a)
79
80/* Debug levels */
81enum iwm_debug_level {
82 IWM_DL_NONE = 0,
83 IWM_DL_ERR,
84 IWM_DL_WARN,
85 IWM_DL_INFO,
86 IWM_DL_DBG,
87};
88#define IWM_DL_DEFAULT IWM_DL_ERR
89
90struct iwm_debugfs {
91 struct iwm_priv *iwm;
92 struct dentry *rootdir;
93 struct dentry *devdir;
94 struct dentry *dbgdir;
95 struct dentry *txdir;
96 struct dentry *rxdir;
97 struct dentry *busdir;
98
99 u32 dbg_level;
100 struct dentry *dbg_level_dentry;
101
102 unsigned long dbg_modules;
103 struct dentry *dbg_modules_dentry;
104
105 u8 dbg_module[__IWM_DM_NR];
106 struct dentry *dbg_module_dentries[__IWM_DM_NR];
107
108 struct dentry *txq_dentry;
109 struct dentry *tx_credit_dentry;
110 struct dentry *rx_ticket_dentry;
111};
112
113#ifdef CONFIG_IWM_DEBUG
114int iwm_debugfs_init(struct iwm_priv *iwm);
115void iwm_debugfs_exit(struct iwm_priv *iwm);
116#else
117static inline int iwm_debugfs_init(struct iwm_priv *iwm)
118{
119 return 0;
120}
121static inline void iwm_debugfs_exit(struct iwm_priv *iwm) {}
122#endif
123
124#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c
new file mode 100644
index 000000000000..0fa7b9150d58
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c
@@ -0,0 +1,453 @@
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/bitops.h>
26#include <linux/debugfs.h>
27
28#include "iwm.h"
29#include "bus.h"
30#include "rx.h"
31#include "debug.h"
32
33static struct {
34 u8 id;
35 char *name;
36} iwm_debug_module[__IWM_DM_NR] = {
37 {IWM_DM_BOOT, "boot"},
38 {IWM_DM_FW, "fw"},
39 {IWM_DM_SDIO, "sdio"},
40 {IWM_DM_NTF, "ntf"},
41 {IWM_DM_RX, "rx"},
42 {IWM_DM_TX, "tx"},
43 {IWM_DM_MLME, "mlme"},
44 {IWM_DM_CMD, "cmd"},
45 {IWM_DM_WEXT, "wext"},
46};
47
48#define add_dbg_module(dbg, name, id, initlevel) \
49do { \
50 struct dentry *d; \
51 dbg.dbg_module[id] = (initlevel); \
52 d = debugfs_create_x8(name, 0600, dbg.dbgdir, \
53 &(dbg.dbg_module[id])); \
54 if (!IS_ERR(d)) \
55 dbg.dbg_module_dentries[id] = d; \
56} while (0)
57
58static int iwm_debugfs_u32_read(void *data, u64 *val)
59{
60 struct iwm_priv *iwm = data;
61
62 *val = iwm->dbg.dbg_level;
63 return 0;
64}
65
66static int iwm_debugfs_dbg_level_write(void *data, u64 val)
67{
68 struct iwm_priv *iwm = data;
69 int i;
70
71 iwm->dbg.dbg_level = val;
72
73 for (i = 0; i < __IWM_DM_NR; i++)
74 iwm->dbg.dbg_module[i] = val;
75
76 return 0;
77}
78DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_level,
79 iwm_debugfs_u32_read, iwm_debugfs_dbg_level_write,
80 "%llu\n");
81
82static int iwm_debugfs_dbg_modules_write(void *data, u64 val)
83{
84 struct iwm_priv *iwm = data;
85 int i, bit;
86
87 iwm->dbg.dbg_modules = val;
88
89 for (i = 0; i < __IWM_DM_NR; i++)
90 iwm->dbg.dbg_module[i] = 0;
91
92 for_each_bit(bit, &iwm->dbg.dbg_modules, __IWM_DM_NR)
93 iwm->dbg.dbg_module[bit] = iwm->dbg.dbg_level;
94
95 return 0;
96}
97DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules,
98 iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write,
99 "%llu\n");
100
101static int iwm_txrx_open(struct inode *inode, struct file *filp)
102{
103 filp->private_data = inode->i_private;
104 return 0;
105}
106
107
108static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer,
109 size_t count, loff_t *ppos)
110{
111 struct iwm_priv *iwm = filp->private_data;
112 char *buf;
113 int i, buf_len = 4096;
114 size_t len = 0;
115 ssize_t ret;
116
117 if (*ppos != 0)
118 return 0;
119 if (count < sizeof(buf))
120 return -ENOSPC;
121
122 buf = kzalloc(buf_len, GFP_KERNEL);
123 if (!buf)
124 return -ENOMEM;
125
126 for (i = 0; i < IWM_TX_QUEUES; i++) {
127 struct iwm_tx_queue *txq = &iwm->txq[i];
128 struct sk_buff *skb;
129 int j;
130 unsigned long flags;
131
132 spin_lock_irqsave(&txq->queue.lock, flags);
133
134 skb = (struct sk_buff *)&txq->queue;
135
136 len += snprintf(buf + len, buf_len - len, "TXQ #%d\n", i);
137 len += snprintf(buf + len, buf_len - len, "\tStopped: %d\n",
138 __netif_subqueue_stopped(iwm_to_ndev(iwm),
139 txq->id));
140 len += snprintf(buf + len, buf_len - len, "\tConcat count:%d\n",
141 txq->concat_count);
142 len += snprintf(buf + len, buf_len - len, "\tQueue len: %d\n",
143 skb_queue_len(&txq->queue));
144 for (j = 0; j < skb_queue_len(&txq->queue); j++) {
145 struct iwm_tx_info *tx_info;
146
147 skb = skb->next;
148 tx_info = skb_to_tx_info(skb);
149
150 len += snprintf(buf + len, buf_len - len,
151 "\tSKB #%d\n", j);
152 len += snprintf(buf + len, buf_len - len,
153 "\t\tsta: %d\n", tx_info->sta);
154 len += snprintf(buf + len, buf_len - len,
155 "\t\tcolor: %d\n", tx_info->color);
156 len += snprintf(buf + len, buf_len - len,
157 "\t\ttid: %d\n", tx_info->tid);
158 }
159
160 spin_unlock_irqrestore(&txq->queue.lock, flags);
161 }
162
163 ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
164 kfree(buf);
165
166 return ret;
167}
168
169static ssize_t iwm_debugfs_tx_credit_read(struct file *filp,
170 char __user *buffer,
171 size_t count, loff_t *ppos)
172{
173 struct iwm_priv *iwm = filp->private_data;
174 struct iwm_tx_credit *credit = &iwm->tx_credit;
175 char *buf;
176 int i, buf_len = 4096;
177 size_t len = 0;
178 ssize_t ret;
179
180 if (*ppos != 0)
181 return 0;
182 if (count < sizeof(buf))
183 return -ENOSPC;
184
185 buf = kzalloc(buf_len, GFP_KERNEL);
186 if (!buf)
187 return -ENOMEM;
188
189 len += snprintf(buf + len, buf_len - len,
190 "NR pools: %d\n", credit->pool_nr);
191 len += snprintf(buf + len, buf_len - len,
192 "pools map: 0x%lx\n", credit->full_pools_map);
193
194 len += snprintf(buf + len, buf_len - len, "\n### POOLS ###\n");
195 for (i = 0; i < IWM_MACS_OUT_GROUPS; i++) {
196 len += snprintf(buf + len, buf_len - len,
197 "pools entry #%d\n", i);
198 len += snprintf(buf + len, buf_len - len,
199 "\tid: %d\n",
200 credit->pools[i].id);
201 len += snprintf(buf + len, buf_len - len,
202 "\tsid: %d\n",
203 credit->pools[i].sid);
204 len += snprintf(buf + len, buf_len - len,
205 "\tmin_pages: %d\n",
206 credit->pools[i].min_pages);
207 len += snprintf(buf + len, buf_len - len,
208 "\tmax_pages: %d\n",
209 credit->pools[i].max_pages);
210 len += snprintf(buf + len, buf_len - len,
211 "\talloc_pages: %d\n",
212 credit->pools[i].alloc_pages);
213 len += snprintf(buf + len, buf_len - len,
214 "\tfreed_pages: %d\n",
215 credit->pools[i].total_freed_pages);
216 }
217
218 len += snprintf(buf + len, buf_len - len, "\n### SPOOLS ###\n");
219 for (i = 0; i < IWM_MACS_OUT_SGROUPS; i++) {
220 len += snprintf(buf + len, buf_len - len,
221 "spools entry #%d\n", i);
222 len += snprintf(buf + len, buf_len - len,
223 "\tid: %d\n",
224 credit->spools[i].id);
225 len += snprintf(buf + len, buf_len - len,
226 "\tmax_pages: %d\n",
227 credit->spools[i].max_pages);
228 len += snprintf(buf + len, buf_len - len,
229 "\talloc_pages: %d\n",
230 credit->spools[i].alloc_pages);
231
232 }
233
234 ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
235 kfree(buf);
236
237 return ret;
238}
239
240static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp,
241 char __user *buffer,
242 size_t count, loff_t *ppos)
243{
244 struct iwm_priv *iwm = filp->private_data;
245 struct iwm_rx_ticket_node *ticket, *next;
246 char *buf;
247 int buf_len = 4096, i;
248 size_t len = 0;
249 ssize_t ret;
250
251 if (*ppos != 0)
252 return 0;
253 if (count < sizeof(buf))
254 return -ENOSPC;
255
256 buf = kzalloc(buf_len, GFP_KERNEL);
257 if (!buf)
258 return -ENOMEM;
259
260 list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
261 len += snprintf(buf + len, buf_len - len, "Ticket #%d\n",
262 ticket->ticket->id);
263 len += snprintf(buf + len, buf_len - len, "\taction: 0x%x\n",
264 ticket->ticket->action);
265 len += snprintf(buf + len, buf_len - len, "\tflags: 0x%x\n",
266 ticket->ticket->flags);
267 }
268
269 for (i = 0; i < IWM_RX_ID_HASH; i++) {
270 struct iwm_rx_packet *packet, *nxt;
271 struct list_head *pkt_list = &iwm->rx_packets[i];
272 if (!list_empty(pkt_list)) {
273 len += snprintf(buf + len, buf_len - len,
274 "Packet hash #%d\n", i);
275 list_for_each_entry_safe(packet, nxt, pkt_list, node) {
276 len += snprintf(buf + len, buf_len - len,
277 "\tPacket id: %d\n",
278 packet->id);
279 len += snprintf(buf + len, buf_len - len,
280 "\tPacket length: %lu\n",
281 packet->pkt_size);
282 }
283 }
284 }
285
286 ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
287 kfree(buf);
288
289 return ret;
290}
291
292
293static const struct file_operations iwm_debugfs_txq_fops = {
294 .owner = THIS_MODULE,
295 .open = iwm_txrx_open,
296 .read = iwm_debugfs_txq_read,
297};
298
299static const struct file_operations iwm_debugfs_tx_credit_fops = {
300 .owner = THIS_MODULE,
301 .open = iwm_txrx_open,
302 .read = iwm_debugfs_tx_credit_read,
303};
304
305static const struct file_operations iwm_debugfs_rx_ticket_fops = {
306 .owner = THIS_MODULE,
307 .open = iwm_txrx_open,
308 .read = iwm_debugfs_rx_ticket_read,
309};
310
311int iwm_debugfs_init(struct iwm_priv *iwm)
312{
313 int i, result;
314 char devdir[16];
315
316 iwm->dbg.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
317 result = PTR_ERR(iwm->dbg.rootdir);
318 if (!result || IS_ERR(iwm->dbg.rootdir)) {
319 if (result == -ENODEV) {
320 IWM_ERR(iwm, "DebugFS (CONFIG_DEBUG_FS) not "
321 "enabled in kernel config\n");
322 result = 0; /* No debugfs support */
323 }
324 IWM_ERR(iwm, "Couldn't create rootdir: %d\n", result);
325 goto error;
326 }
327
328 snprintf(devdir, sizeof(devdir), "%s", wiphy_name(iwm_to_wiphy(iwm)));
329
330 iwm->dbg.devdir = debugfs_create_dir(devdir, iwm->dbg.rootdir);
331 result = PTR_ERR(iwm->dbg.devdir);
332 if (IS_ERR(iwm->dbg.devdir) && (result != -ENODEV)) {
333 IWM_ERR(iwm, "Couldn't create devdir: %d\n", result);
334 goto error;
335 }
336
337 iwm->dbg.dbgdir = debugfs_create_dir("debug", iwm->dbg.devdir);
338 result = PTR_ERR(iwm->dbg.dbgdir);
339 if (IS_ERR(iwm->dbg.dbgdir) && (result != -ENODEV)) {
340 IWM_ERR(iwm, "Couldn't create dbgdir: %d\n", result);
341 goto error;
342 }
343
344 iwm->dbg.rxdir = debugfs_create_dir("rx", iwm->dbg.devdir);
345 result = PTR_ERR(iwm->dbg.rxdir);
346 if (IS_ERR(iwm->dbg.rxdir) && (result != -ENODEV)) {
347 IWM_ERR(iwm, "Couldn't create rx dir: %d\n", result);
348 goto error;
349 }
350
351 iwm->dbg.txdir = debugfs_create_dir("tx", iwm->dbg.devdir);
352 result = PTR_ERR(iwm->dbg.txdir);
353 if (IS_ERR(iwm->dbg.txdir) && (result != -ENODEV)) {
354 IWM_ERR(iwm, "Couldn't create tx dir: %d\n", result);
355 goto error;
356 }
357
358 iwm->dbg.busdir = debugfs_create_dir("bus", iwm->dbg.devdir);
359 result = PTR_ERR(iwm->dbg.busdir);
360 if (IS_ERR(iwm->dbg.busdir) && (result != -ENODEV)) {
361 IWM_ERR(iwm, "Couldn't create bus dir: %d\n", result);
362 goto error;
363 }
364
365 if (iwm->bus_ops->debugfs_init) {
366 result = iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir);
367 if (result < 0) {
368 IWM_ERR(iwm, "Couldn't create bus entry: %d\n", result);
369 goto error;
370 }
371 }
372
373
374 iwm->dbg.dbg_level = IWM_DL_NONE;
375 iwm->dbg.dbg_level_dentry =
376 debugfs_create_file("level", 0200, iwm->dbg.dbgdir, iwm,
377 &fops_iwm_dbg_level);
378 result = PTR_ERR(iwm->dbg.dbg_level_dentry);
379 if (IS_ERR(iwm->dbg.dbg_level_dentry) && (result != -ENODEV)) {
380 IWM_ERR(iwm, "Couldn't create dbg_level: %d\n", result);
381 goto error;
382 }
383
384
385 iwm->dbg.dbg_modules = IWM_DM_DEFAULT;
386 iwm->dbg.dbg_modules_dentry =
387 debugfs_create_file("modules", 0200, iwm->dbg.dbgdir, iwm,
388 &fops_iwm_dbg_modules);
389 result = PTR_ERR(iwm->dbg.dbg_modules_dentry);
390 if (IS_ERR(iwm->dbg.dbg_modules_dentry) && (result != -ENODEV)) {
391 IWM_ERR(iwm, "Couldn't create dbg_modules: %d\n", result);
392 goto error;
393 }
394
395 for (i = 0; i < __IWM_DM_NR; i++)
396 add_dbg_module(iwm->dbg, iwm_debug_module[i].name,
397 iwm_debug_module[i].id, IWM_DL_DEFAULT);
398
399 iwm->dbg.txq_dentry = debugfs_create_file("queues", 0200,
400 iwm->dbg.txdir, iwm,
401 &iwm_debugfs_txq_fops);
402 result = PTR_ERR(iwm->dbg.txq_dentry);
403 if (IS_ERR(iwm->dbg.txq_dentry) && (result != -ENODEV)) {
404 IWM_ERR(iwm, "Couldn't create tx queue: %d\n", result);
405 goto error;
406 }
407
408 iwm->dbg.tx_credit_dentry = debugfs_create_file("credits", 0200,
409 iwm->dbg.txdir, iwm,
410 &iwm_debugfs_tx_credit_fops);
411 result = PTR_ERR(iwm->dbg.tx_credit_dentry);
412 if (IS_ERR(iwm->dbg.tx_credit_dentry) && (result != -ENODEV)) {
413 IWM_ERR(iwm, "Couldn't create tx credit: %d\n", result);
414 goto error;
415 }
416
417 iwm->dbg.rx_ticket_dentry = debugfs_create_file("tickets", 0200,
418 iwm->dbg.rxdir, iwm,
419 &iwm_debugfs_rx_ticket_fops);
420 result = PTR_ERR(iwm->dbg.rx_ticket_dentry);
421 if (IS_ERR(iwm->dbg.rx_ticket_dentry) && (result != -ENODEV)) {
422 IWM_ERR(iwm, "Couldn't create rx ticket: %d\n", result);
423 goto error;
424 }
425
426 return 0;
427
428 error:
429 return result;
430}
431
432void iwm_debugfs_exit(struct iwm_priv *iwm)
433{
434 int i;
435
436 for (i = 0; i < __IWM_DM_NR; i++)
437 debugfs_remove(iwm->dbg.dbg_module_dentries[i]);
438
439 debugfs_remove(iwm->dbg.dbg_modules_dentry);
440 debugfs_remove(iwm->dbg.dbg_level_dentry);
441 debugfs_remove(iwm->dbg.txq_dentry);
442 debugfs_remove(iwm->dbg.tx_credit_dentry);
443 debugfs_remove(iwm->dbg.rx_ticket_dentry);
444 if (iwm->bus_ops->debugfs_exit)
445 iwm->bus_ops->debugfs_exit(iwm);
446
447 debugfs_remove(iwm->dbg.busdir);
448 debugfs_remove(iwm->dbg.dbgdir);
449 debugfs_remove(iwm->dbg.txdir);
450 debugfs_remove(iwm->dbg.rxdir);
451 debugfs_remove(iwm->dbg.devdir);
452 debugfs_remove(iwm->dbg.rootdir);
453}
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.c b/drivers/net/wireless/iwmc3200wifi/eeprom.c
new file mode 100644
index 000000000000..0f34b84fd2eb
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/eeprom.c
@@ -0,0 +1,187 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#include <linux/kernel.h>
40
41#include "iwm.h"
42#include "umac.h"
43#include "commands.h"
44#include "eeprom.h"
45
46static struct iwm_eeprom_entry eeprom_map[] = {
47 [IWM_EEPROM_SIG] =
48 {"Signature", IWM_EEPROM_SIG_OFF, IWM_EEPROM_SIG_LEN},
49
50 [IWM_EEPROM_VERSION] =
51 {"Version", IWM_EEPROM_VERSION_OFF, IWM_EEPROM_VERSION_LEN},
52
53 [IWM_EEPROM_OEM_HW_VERSION] =
54 {"OEM HW version", IWM_EEPROM_OEM_HW_VERSION_OFF,
55 IWM_EEPROM_OEM_HW_VERSION_LEN},
56
57 [IWM_EEPROM_MAC_VERSION] =
58 {"MAC version", IWM_EEPROM_MAC_VERSION_OFF, IWM_EEPROM_MAC_VERSION_LEN},
59
60 [IWM_EEPROM_CARD_ID] =
61 {"Card ID", IWM_EEPROM_CARD_ID_OFF, IWM_EEPROM_CARD_ID_LEN},
62
63 [IWM_EEPROM_RADIO_CONF] =
64 {"Radio config", IWM_EEPROM_RADIO_CONF_OFF, IWM_EEPROM_RADIO_CONF_LEN},
65
66 [IWM_EEPROM_SKU_CAP] =
67 {"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN},
68
69 [IWM_EEPROM_CALIB_RXIQ_OFFSET] =
70 {"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN},
71
72 [IWM_EEPROM_CALIB_RXIQ] =
73 {"Calib RX IQ", 0, IWM_EEPROM_CALIB_RXIQ_LEN},
74};
75
76
77static int iwm_eeprom_read(struct iwm_priv *iwm, u8 eeprom_id)
78{
79 int ret;
80 u32 entry_size, chunk_size, data_offset = 0, addr_offset = 0;
81 u32 addr;
82 struct iwm_udma_wifi_cmd udma_cmd;
83 struct iwm_umac_cmd umac_cmd;
84 struct iwm_umac_cmd_eeprom_proxy eeprom_cmd;
85
86 if (eeprom_id > (IWM_EEPROM_LAST - 1))
87 return -EINVAL;
88
89 entry_size = eeprom_map[eeprom_id].length;
90
91 if (eeprom_id >= IWM_EEPROM_INDIRECT_DATA) {
92 /* indirect data */
93 u32 off_id = eeprom_id - IWM_EEPROM_INDIRECT_DATA +
94 IWM_EEPROM_INDIRECT_OFFSET;
95
96 eeprom_map[eeprom_id].offset =
97 *(u16 *)(iwm->eeprom + eeprom_map[off_id].offset) << 1;
98 }
99
100 addr = eeprom_map[eeprom_id].offset;
101
102 udma_cmd.eop = 1;
103 udma_cmd.credit_group = 0x4;
104 udma_cmd.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD;
105 udma_cmd.lmac_offset = 0;
106
107 umac_cmd.id = UMAC_CMD_OPCODE_EEPROM_PROXY;
108 umac_cmd.resp = 1;
109
110 while (entry_size > 0) {
111 chunk_size = min_t(u32, entry_size, IWM_MAX_EEPROM_DATA_LEN);
112
113 eeprom_cmd.hdr.type =
114 cpu_to_le32(IWM_UMAC_CMD_EEPROM_TYPE_READ);
115 eeprom_cmd.hdr.offset = cpu_to_le32(addr + addr_offset);
116 eeprom_cmd.hdr.len = cpu_to_le32(chunk_size);
117
118 ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd,
119 &umac_cmd, &eeprom_cmd,
120 sizeof(struct iwm_umac_cmd_eeprom_proxy));
121 if (ret < 0) {
122 IWM_ERR(iwm, "Couldn't read eeprom\n");
123 return ret;
124 }
125
126 ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_EEPROM_PROXY,
127 IWM_SRC_UMAC, 2*HZ);
128 if (ret < 0) {
129 IWM_ERR(iwm, "Did not get any eeprom answer\n");
130 return ret;
131 }
132
133 data_offset += chunk_size;
134 addr_offset += chunk_size;
135 entry_size -= chunk_size;
136 }
137
138 return 0;
139}
140
141u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id)
142{
143 if (!iwm->eeprom)
144 return ERR_PTR(-ENODEV);
145
146 return iwm->eeprom + eeprom_map[eeprom_id].offset;
147}
148
149int iwm_eeprom_init(struct iwm_priv *iwm)
150{
151 int i, ret = 0;
152 char name[32];
153
154 iwm->eeprom = kzalloc(IWM_EEPROM_LEN, GFP_KERNEL);
155 if (!iwm->eeprom)
156 return -ENOMEM;
157
158 for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) {
159#ifdef CONFIG_IWM_B0_HW_SUPPORT
160 if (iwm->conf.hw_b0 && (i >= IWM_EEPROM_INDIRECT_OFFSET))
161 break;
162#endif
163 ret = iwm_eeprom_read(iwm, i);
164 if (ret < 0) {
165 IWM_ERR(iwm, "Couldn't read eeprom entry #%d: %s\n",
166 i, eeprom_map[i].name);
167 break;
168 }
169 }
170
171 IWM_DBG_BOOT(iwm, DBG, "EEPROM dump:\n");
172 for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) {
173 memset(name, 0, 32);
174 sprintf(name, "%s: ", eeprom_map[i].name);
175
176 IWM_HEXDUMP(iwm, DBG, BOOT, name,
177 iwm->eeprom + eeprom_map[i].offset,
178 eeprom_map[i].length);
179 }
180
181 return ret;
182}
183
184void iwm_eeprom_exit(struct iwm_priv *iwm)
185{
186 kfree(iwm->eeprom);
187}
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.h b/drivers/net/wireless/iwmc3200wifi/eeprom.h
new file mode 100644
index 000000000000..cdb31a6a1f5f
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/eeprom.h
@@ -0,0 +1,114 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef __IWM_EEPROM_H__
40#define __IWM_EEPROM_H__
41
42enum {
43 IWM_EEPROM_SIG = 0,
44 IWM_EEPROM_FIRST = IWM_EEPROM_SIG,
45 IWM_EEPROM_VERSION,
46 IWM_EEPROM_OEM_HW_VERSION,
47 IWM_EEPROM_MAC_VERSION,
48 IWM_EEPROM_CARD_ID,
49 IWM_EEPROM_RADIO_CONF,
50 IWM_EEPROM_SKU_CAP,
51
52 IWM_EEPROM_INDIRECT_OFFSET,
53 IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET,
54
55 IWM_EEPROM_INDIRECT_DATA,
56 IWM_EEPROM_CALIB_RXIQ = IWM_EEPROM_INDIRECT_DATA,
57
58 IWM_EEPROM_LAST,
59};
60
61#define IWM_EEPROM_SIG_OFF 0x00
62#define IWM_EEPROM_VERSION_OFF (0x54 << 1)
63#define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1)
64#define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1)
65#define IWM_EEPROM_CARD_ID_OFF (0x5d << 1)
66#define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1)
67#define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1)
68#define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1)
69
70#define IWM_EEPROM_SIG_LEN 4
71#define IWM_EEPROM_VERSION_LEN 2
72#define IWM_EEPROM_OEM_HW_VERSION_LEN 2
73#define IWM_EEPROM_MAC_VERSION_LEN 1
74#define IWM_EEPROM_CARD_ID_LEN 2
75#define IWM_EEPROM_RADIO_CONF_LEN 2
76#define IWM_EEPROM_SKU_CAP_LEN 2
77#define IWM_EEPROM_INDIRECT_LEN 2
78
79#define IWM_MAX_EEPROM_DATA_LEN 240
80#define IWM_EEPROM_LEN 0x800
81
82#define IWM_EEPROM_MIN_ALLOWED_VERSION 0x0610
83#define IWM_EEPROM_MAX_ALLOWED_VERSION 0x0700
84#define IWM_EEPROM_CURRENT_VERSION 0x0612
85
86#define IWM_EEPROM_SKU_CAP_BAND_24GHZ (1 << 4)
87#define IWM_EEPROM_SKU_CAP_BAND_52GHZ (1 << 5)
88#define IWM_EEPROM_SKU_CAP_11N_ENABLE (1 << 6)
89
90enum {
91 IWM_EEPROM_CALIB_CAL_HDR,
92 IWM_EEPROM_CALIB_TX_POWER,
93 IWM_EEPROM_CALIB_XTAL,
94 IWM_EEPROM_CALIB_TEMPERATURE,
95 IWM_EEPROM_CALIB_RX_BB_FILTER,
96 IWM_EEPROM_CALIB_RX_IQ,
97 IWM_EEPROM_CALIB_MAX,
98};
99
100#define IWM_EEPROM_CALIB_RXIQ_OFF (IWM_EEPROM_CALIB_CONFIG_OFF + \
101 (IWM_EEPROM_CALIB_RX_IQ << 1))
102#define IWM_EEPROM_CALIB_RXIQ_LEN sizeof(struct iwm_lmac_calib_rxiq)
103
104struct iwm_eeprom_entry {
105 char *name;
106 u32 offset;
107 u32 length;
108};
109
110int iwm_eeprom_init(struct iwm_priv *iwm);
111void iwm_eeprom_exit(struct iwm_priv *iwm);
112u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id);
113
114#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c
new file mode 100644
index 000000000000..db4ba0864730
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/fw.c
@@ -0,0 +1,388 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#include <linux/kernel.h>
40#include <linux/firmware.h>
41
42#include "iwm.h"
43#include "bus.h"
44#include "hal.h"
45#include "umac.h"
46#include "debug.h"
47#include "fw.h"
48#include "commands.h"
49
50static const char fw_barker[] = "*WESTOPFORNOONE*";
51
52/*
53 * @op_code: Op code we're looking for.
54 * @index: There can be several instances of the same opcode within
55 * the firmware. Index specifies which one we're looking for.
56 */
57static int iwm_fw_op_offset(struct iwm_priv *iwm, const struct firmware *fw,
58 u16 op_code, u32 index)
59{
60 int offset = -EINVAL, fw_offset;
61 u32 op_index = 0;
62 const u8 *fw_ptr;
63 struct iwm_fw_hdr_rec *rec;
64
65 fw_offset = 0;
66 fw_ptr = fw->data;
67
68 /* We first need to look for the firmware barker */
69 if (memcmp(fw_ptr, fw_barker, IWM_HDR_BARKER_LEN)) {
70 IWM_ERR(iwm, "No barker string in this FW\n");
71 return -EINVAL;
72 }
73
74 if (fw->size < IWM_HDR_LEN) {
75 IWM_ERR(iwm, "FW is too small (%d)\n", fw->size);
76 return -EINVAL;
77 }
78
79 fw_offset += IWM_HDR_BARKER_LEN;
80
81 while (fw_offset < fw->size) {
82 rec = (struct iwm_fw_hdr_rec *)(fw_ptr + fw_offset);
83
84 IWM_DBG_FW(iwm, DBG, "FW: op_code: 0x%x, len: %d @ 0x%x\n",
85 rec->op_code, rec->len, fw_offset);
86
87 if (rec->op_code == IWM_HDR_REC_OP_INVALID) {
88 IWM_DBG_FW(iwm, DBG, "Reached INVALID op code\n");
89 break;
90 }
91
92 if (rec->op_code == op_code) {
93 if (op_index == index) {
94 fw_offset += sizeof(struct iwm_fw_hdr_rec);
95 offset = fw_offset;
96 goto out;
97 }
98 op_index++;
99 }
100
101 fw_offset += sizeof(struct iwm_fw_hdr_rec) + rec->len;
102 }
103
104 out:
105 return offset;
106}
107
108static int iwm_load_firmware_chunk(struct iwm_priv *iwm,
109 const struct firmware *fw,
110 struct iwm_fw_img_desc *img_desc)
111{
112 struct iwm_udma_nonwifi_cmd target_cmd;
113 u32 chunk_size;
114 const u8 *chunk_ptr;
115 int ret = 0;
116
117 IWM_DBG_FW(iwm, INFO, "Loading FW chunk: %d bytes @ 0x%x\n",
118 img_desc->length, img_desc->address);
119
120 target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
121 target_cmd.handle_by_hw = 1;
122 target_cmd.op2 = 0;
123 target_cmd.resp = 0;
124 target_cmd.eop = 1;
125
126 chunk_size = img_desc->length;
127 chunk_ptr = fw->data + img_desc->offset;
128
129 while (chunk_size > 0) {
130 u32 tmp_chunk_size;
131
132 tmp_chunk_size = min_t(u32, chunk_size,
133 IWM_MAX_NONWIFI_CMD_BUFF_SIZE);
134
135 target_cmd.addr = cpu_to_le32(img_desc->address +
136 (chunk_ptr - fw->data - img_desc->offset));
137 target_cmd.op1_sz = cpu_to_le32(tmp_chunk_size);
138
139 IWM_DBG_FW(iwm, DBG, "\t%d bytes @ 0x%x\n",
140 tmp_chunk_size, target_cmd.addr);
141
142 ret = iwm_hal_send_target_cmd(iwm, &target_cmd, chunk_ptr);
143 if (ret < 0) {
144 IWM_ERR(iwm, "Couldn't load FW chunk\n");
145 break;
146 }
147
148 chunk_size -= tmp_chunk_size;
149 chunk_ptr += tmp_chunk_size;
150 }
151
152 return ret;
153}
154/*
155 * To load a fw image to the target, we basically go through the
156 * fw, looking for OP_MEM_DESC records. Once we found one, we
157 * pass it to iwm_load_firmware_chunk().
158 * The OP_MEM_DESC records contain the actuall memory chunk to be
159 * sent, but also the destination address.
160 */
161static int iwm_load_img(struct iwm_priv *iwm, const char *img_name)
162{
163 const struct firmware *fw;
164 struct iwm_fw_img_desc *img_desc;
165 struct iwm_fw_img_ver *ver;
166 int ret = 0, fw_offset;
167 u32 opcode_idx = 0, build_date;
168 char *build_tag;
169
170 ret = request_firmware(&fw, img_name, iwm_to_dev(iwm));
171 if (ret) {
172 IWM_ERR(iwm, "Request firmware failed");
173 return ret;
174 }
175
176 IWM_DBG_FW(iwm, INFO, "Start to load FW %s\n", img_name);
177
178 while (1) {
179 fw_offset = iwm_fw_op_offset(iwm, fw,
180 IWM_HDR_REC_OP_MEM_DESC,
181 opcode_idx);
182 if (fw_offset < 0)
183 break;
184
185 img_desc = (struct iwm_fw_img_desc *)(fw->data + fw_offset);
186 ret = iwm_load_firmware_chunk(iwm, fw, img_desc);
187 if (ret < 0)
188 goto err_release_fw;
189 opcode_idx++;
190 };
191
192 /* Read firmware version */
193 fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_SW_VER, 0);
194 if (fw_offset < 0)
195 goto err_release_fw;
196
197 ver = (struct iwm_fw_img_ver *)(fw->data + fw_offset);
198
199 /* Read build tag */
200 fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_TAG, 0);
201 if (fw_offset < 0)
202 goto err_release_fw;
203
204 build_tag = (char *)(fw->data + fw_offset);
205
206 /* Read build date */
207 fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_DATE, 0);
208 if (fw_offset < 0)
209 goto err_release_fw;
210
211 build_date = *(u32 *)(fw->data + fw_offset);
212
213 IWM_INFO(iwm, "%s:\n", img_name);
214 IWM_INFO(iwm, "\tVersion: %02X.%02X\n", ver->major, ver->minor);
215 IWM_INFO(iwm, "\tBuild tag: %s\n", build_tag);
216 IWM_INFO(iwm, "\tBuild date: %x-%x-%x\n",
217 IWM_BUILD_YEAR(build_date), IWM_BUILD_MONTH(build_date),
218 IWM_BUILD_DAY(build_date));
219
220
221 err_release_fw:
222 release_firmware(fw);
223
224 return ret;
225}
226
227static int iwm_load_umac(struct iwm_priv *iwm)
228{
229 struct iwm_udma_nonwifi_cmd target_cmd;
230 int ret;
231
232 ret = iwm_load_img(iwm, iwm->bus_ops->umac_name);
233 if (ret < 0)
234 return ret;
235
236 /* We've loaded the UMAC, we can tell the target to jump there */
237 target_cmd.opcode = UMAC_HDI_OUT_OPCODE_JUMP;
238 target_cmd.addr = cpu_to_le32(UMAC_MU_FW_INST_DATA_12_ADDR);
239 target_cmd.op1_sz = 0;
240 target_cmd.op2 = 0;
241 target_cmd.handle_by_hw = 0;
242 target_cmd.resp = 1 ;
243 target_cmd.eop = 1;
244
245 ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
246 if (ret < 0)
247 IWM_ERR(iwm, "Couldn't send JMP command\n");
248
249 return ret;
250}
251
252static int iwm_load_lmac(struct iwm_priv *iwm, const char *img_name)
253{
254 int ret;
255
256 ret = iwm_load_img(iwm, img_name);
257 if (ret < 0)
258 return ret;
259
260 return iwm_send_umac_reset(iwm,
261 cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_CLK_EN), 0);
262}
263
264/*
265 * We currently have to load 3 FWs:
266 * 1) The UMAC (Upper MAC).
267 * 2) The calibration LMAC (Lower MAC).
268 * We then send the calibration init command, so that the device can
269 * run a first calibration round.
270 * 3) The operational LMAC, which replaces the calibration one when it's
271 * done with the first calibration round.
272 *
273 * Once those 3 FWs have been loaded, we send the periodic calibration
274 * command, and then the device is available for regular 802.11 operations.
275 */
276int iwm_load_fw(struct iwm_priv *iwm)
277{
278 int ret;
279
280 /* We first start downloading the UMAC */
281 ret = iwm_load_umac(iwm);
282 if (ret < 0) {
283 IWM_ERR(iwm, "UMAC loading failed\n");
284 return ret;
285 }
286
287 /* Handle UMAC_ALIVE notification */
288 ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_ALIVE, IWM_SRC_UMAC,
289 WAIT_NOTIF_TIMEOUT);
290 if (ret) {
291 IWM_ERR(iwm, "Handle UMAC_ALIVE failed: %d\n", ret);
292 return ret;
293 }
294
295 /* UMAC is alive, we can download the calibration LMAC */
296 ret = iwm_load_lmac(iwm, iwm->bus_ops->calib_lmac_name);
297 if (ret) {
298 IWM_ERR(iwm, "Calibration LMAC loading failed\n");
299 return ret;
300 }
301
302 /* Handle UMAC_INIT_COMPLETE notification */
303 ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE,
304 IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
305 if (ret) {
306 IWM_ERR(iwm, "Handle INIT_COMPLETE failed for calibration "
307 "LMAC: %d\n", ret);
308 return ret;
309 }
310
311 /* Read EEPROM data */
312 ret = iwm_eeprom_init(iwm);
313 if (ret < 0) {
314 IWM_ERR(iwm, "Couldn't init eeprom array\n");
315 return ret;
316 }
317
318#ifdef CONFIG_IWM_B0_HW_SUPPORT
319 if (iwm->conf.hw_b0) {
320 clear_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map);
321 clear_bit(PHY_CALIBRATE_RX_IQ_CMD,
322 &iwm->conf.periodic_calib_map);
323 }
324#endif
325 /* Read RX IQ calibration result from EEPROM */
326 if (test_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map)) {
327 iwm_store_rxiq_calib_result(iwm);
328 set_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->calib_done_map);
329 }
330
331 iwm_send_prio_table(iwm);
332 iwm_send_init_calib_cfg(iwm, iwm->conf.init_calib_map);
333
334 while (iwm->calib_done_map != iwm->conf.init_calib_map) {
335 ret = iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION,
336 IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
337 if (ret) {
338 IWM_ERR(iwm, "Wait for calibration result timeout\n");
339 goto out;
340 }
341 IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: "
342 "0x%lx, requested calibrations: 0x%lx\n",
343 iwm->calib_done_map, iwm->conf.init_calib_map);
344 }
345
346 /* Handle LMAC CALIBRATION_COMPLETE notification */
347 ret = iwm_notif_handle(iwm, CALIBRATION_COMPLETE_NOTIFICATION,
348 IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
349 if (ret) {
350 IWM_ERR(iwm, "Wait for CALIBRATION_COMPLETE timeout\n");
351 goto out;
352 }
353
354 IWM_INFO(iwm, "LMAC calibration done: 0x%lx\n", iwm->calib_done_map);
355
356 iwm_send_umac_reset(iwm, cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_RESET), 1);
357
358 ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC,
359 WAIT_NOTIF_TIMEOUT);
360 if (ret) {
361 IWM_ERR(iwm, "Wait for UMAC RESET timeout\n");
362 goto out;
363 }
364
365 /* Download the operational LMAC */
366 ret = iwm_load_lmac(iwm, iwm->bus_ops->lmac_name);
367 if (ret) {
368 IWM_ERR(iwm, "LMAC loading failed\n");
369 goto out;
370 }
371
372 ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE,
373 IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
374 if (ret) {
375 IWM_ERR(iwm, "Handle INIT_COMPLETE failed for LMAC: %d\n", ret);
376 goto out;
377 }
378
379 iwm_send_prio_table(iwm);
380 iwm_send_calib_results(iwm);
381 iwm_send_periodic_calib_cfg(iwm, iwm->conf.periodic_calib_map);
382
383 return 0;
384
385 out:
386 iwm_eeprom_exit(iwm);
387 return ret;
388}
diff --git a/drivers/net/wireless/iwmc3200wifi/fw.h b/drivers/net/wireless/iwmc3200wifi/fw.h
new file mode 100644
index 000000000000..c70a3b40dad3
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/fw.h
@@ -0,0 +1,100 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef __IWM_FW_H__
40#define __IWM_FW_H__
41
42/**
43 * struct iwm_fw_hdr_rec - An iwm firmware image is a
44 * concatenation of various records. Each of them is
45 * defined by an ID (aka op code), a length, and the
46 * actual data.
47 * @op_code: The record ID, see IWM_HDR_REC_OP_*
48 *
49 * @len: The record payload length
50 *
51 * @buf: The record payload
52 */
53struct iwm_fw_hdr_rec {
54 u16 op_code;
55 u16 len;
56 u8 buf[0];
57};
58
59/* Header's definitions */
60#define IWM_HDR_LEN (512)
61#define IWM_HDR_BARKER_LEN (16)
62
63/* Header's opcodes */
64#define IWM_HDR_REC_OP_INVALID (0x00)
65#define IWM_HDR_REC_OP_BUILD_DATE (0x01)
66#define IWM_HDR_REC_OP_BUILD_TAG (0x02)
67#define IWM_HDR_REC_OP_SW_VER (0x03)
68#define IWM_HDR_REC_OP_HW_SKU (0x04)
69#define IWM_HDR_REC_OP_BUILD_OPT (0x05)
70#define IWM_HDR_REC_OP_MEM_DESC (0x06)
71#define IWM_HDR_REC_USERDEFS (0x07)
72
73/* Header's records length (in bytes) */
74#define IWM_HDR_REC_LEN_BUILD_DATE (4)
75#define IWM_HDR_REC_LEN_BUILD_TAG (64)
76#define IWM_HDR_REC_LEN_SW_VER (4)
77#define IWM_HDR_REC_LEN_HW_SKU (4)
78#define IWM_HDR_REC_LEN_BUILD_OPT (4)
79#define IWM_HDR_REC_LEN_MEM_DESC (12)
80#define IWM_HDR_REC_LEN_USERDEF (64)
81
82#define IWM_BUILD_YEAR(date) ((date >> 16) & 0xffff)
83#define IWM_BUILD_MONTH(date) ((date >> 8) & 0xff)
84#define IWM_BUILD_DAY(date) (date & 0xff)
85
86struct iwm_fw_img_desc {
87 u32 offset;
88 u32 address;
89 u32 length;
90};
91
92struct iwm_fw_img_ver {
93 u8 minor;
94 u8 major;
95 u16 reserved;
96};
97
98int iwm_load_fw(struct iwm_priv *iwm);
99
100#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/hal.c b/drivers/net/wireless/iwmc3200wifi/hal.c
new file mode 100644
index 000000000000..ee127fe4f43f
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/hal.c
@@ -0,0 +1,464 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39/*
40 * Hardware Abstraction Layer for iwm.
41 *
42 * This file mostly defines an abstraction API for
43 * sending various commands to the target.
44 *
45 * We have 2 types of commands: wifi and non-wifi ones.
46 *
47 * - wifi commands:
48 * They are used for sending LMAC and UMAC commands,
49 * and thus are the most commonly used ones.
50 * There are 2 different wifi command types, the regular
51 * one and the LMAC one. The former is used to send
52 * UMAC commands (see UMAC_CMD_OPCODE_* from umac.h)
53 * while the latter is used for sending commands to the
54 * LMAC. If you look at LMAC commands you'll se that they
55 * are actually regular iwlwifi target commands encapsulated
56 * into a special UMAC command called UMAC passthrough.
57 * This is due to the fact the the host talks exclusively
58 * to the UMAC and so there needs to be a special UMAC
59 * command for talking to the LMAC.
60 * This is how a wifi command is layed out:
61 * ------------------------
62 * | iwm_udma_out_wifi_hdr |
63 * ------------------------
64 * | SW meta_data (32 bits) |
65 * ------------------------
66 * | iwm_dev_cmd_hdr |
67 * ------------------------
68 * | payload |
69 * | .... |
70 *
71 * - non-wifi, or general commands:
72 * Those commands are handled by the device's bootrom,
73 * and are typically sent when the UMAC and the LMAC
74 * are not yet available.
75 * * This is how a non-wifi command is layed out:
76 * ---------------------------
77 * | iwm_udma_out_nonwifi_hdr |
78 * ---------------------------
79 * | payload |
80 * | .... |
81
82 *
83 * All the commands start with a UDMA header, which is
84 * basically a 32 bits field. The 4 LSB there define
85 * an opcode that allows the target to differentiate
86 * between wifi (opcode is 0xf) and non-wifi commands
87 * (opcode is [0..0xe]).
88 *
89 * When a command (wifi or non-wifi) is supposed to receive
90 * an answer, we queue the command buffer. When we do receive
91 * a command response from the UMAC, we go through the list
92 * of pending command, and pass both the command and the answer
93 * to the rx handler. Each command is sent with a unique
94 * sequence id, and the answer is sent with the same one. This
95 * is how we're supposed to match an answer with its command.
96 * See rx.c:iwm_rx_handle_[non]wifi() and iwm_get_pending_[non]wifi()
97 * for the implementation details.
98 */
99#include <linux/kernel.h>
100#include <linux/netdevice.h>
101
102#include "iwm.h"
103#include "bus.h"
104#include "hal.h"
105#include "umac.h"
106#include "debug.h"
107
108static void iwm_nonwifi_cmd_init(struct iwm_priv *iwm,
109 struct iwm_nonwifi_cmd *cmd,
110 struct iwm_udma_nonwifi_cmd *udma_cmd)
111{
112 INIT_LIST_HEAD(&cmd->pending);
113
114 spin_lock(&iwm->cmd_lock);
115
116 cmd->resp_received = 0;
117
118 cmd->seq_num = iwm->nonwifi_seq_num;
119 udma_cmd->seq_num = cpu_to_le16(cmd->seq_num);
120
121 cmd->seq_num = iwm->nonwifi_seq_num++;
122 iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX;
123
124 if (udma_cmd->resp)
125 list_add_tail(&cmd->pending, &iwm->nonwifi_pending_cmd);
126
127 spin_unlock(&iwm->cmd_lock);
128
129 cmd->buf.start = cmd->buf.payload;
130 cmd->buf.len = 0;
131
132 memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
133}
134
135u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm)
136{
137 u16 seq_num = iwm->wifi_seq_num;
138
139 iwm->wifi_seq_num++;
140 iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX;
141
142 return seq_num;
143}
144
145static void iwm_wifi_cmd_init(struct iwm_priv *iwm,
146 struct iwm_wifi_cmd *cmd,
147 struct iwm_udma_wifi_cmd *udma_cmd,
148 struct iwm_umac_cmd *umac_cmd,
149 struct iwm_lmac_cmd *lmac_cmd,
150 u16 payload_size)
151{
152 INIT_LIST_HEAD(&cmd->pending);
153
154 spin_lock(&iwm->cmd_lock);
155
156 cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm);
157 umac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
158
159 if (umac_cmd->resp)
160 list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd);
161
162 spin_unlock(&iwm->cmd_lock);
163
164 cmd->buf.start = cmd->buf.payload;
165 cmd->buf.len = 0;
166
167 if (lmac_cmd) {
168 cmd->buf.start -= sizeof(struct iwm_lmac_hdr);
169
170 lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
171 lmac_cmd->count = cpu_to_le16(payload_size);
172
173 memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd));
174
175 umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr));
176 } else
177 umac_cmd->count = 0;
178
179 umac_cmd->count = cpu_to_le16(payload_size +
180 le16_to_cpu(umac_cmd->count));
181 udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) +
182 le16_to_cpu(umac_cmd->count));
183
184 memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
185 memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd));
186}
187
188void iwm_cmd_flush(struct iwm_priv *iwm)
189{
190 struct iwm_wifi_cmd *wcmd, *wnext;
191 struct iwm_nonwifi_cmd *nwcmd, *nwnext;
192
193 list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) {
194 list_del(&wcmd->pending);
195 kfree(wcmd);
196 }
197
198 list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd,
199 pending) {
200 list_del(&nwcmd->pending);
201 kfree(nwcmd);
202 }
203}
204
205struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num)
206{
207 struct iwm_wifi_cmd *cmd, *next;
208
209 list_for_each_entry_safe(cmd, next, &iwm->wifi_pending_cmd, pending)
210 if (cmd->seq_num == seq_num) {
211 list_del(&cmd->pending);
212 return cmd;
213 }
214
215 return NULL;
216}
217
218struct iwm_nonwifi_cmd *
219iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, u8 seq_num, u8 cmd_opcode)
220{
221 struct iwm_nonwifi_cmd *cmd, *next;
222
223 list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
224 if ((cmd->seq_num == seq_num) &&
225 (cmd->udma_cmd.opcode == cmd_opcode) &&
226 (cmd->resp_received)) {
227 list_del(&cmd->pending);
228 return cmd;
229 }
230
231 return NULL;
232}
233
234static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm,
235 struct iwm_udma_out_nonwifi_hdr *hdr,
236 struct iwm_udma_nonwifi_cmd *cmd)
237{
238 memset(hdr, 0, sizeof(*hdr));
239
240 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode);
241 SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp);
242 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1);
243 SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW,
244 cmd->handle_by_hw);
245 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
246 SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM,
247 le16_to_cpu(cmd->seq_num));
248
249 hdr->addr = cmd->addr;
250 hdr->op1_sz = cmd->op1_sz;
251 hdr->op2 = cmd->op2;
252}
253
254static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm,
255 struct iwm_nonwifi_cmd *cmd)
256{
257 struct iwm_udma_out_nonwifi_hdr *udma_hdr;
258 struct iwm_nonwifi_cmd_buff *buf;
259 struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd;
260
261 buf = &cmd->buf;
262
263 buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr);
264 buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr);
265
266 udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start);
267
268 iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd);
269
270 IWM_DBG_CMD(iwm, DBG,
271 "Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, "
272 "hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, "
273 "op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp,
274 udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr,
275 udma_cmd->op1_sz, udma_cmd->op2);
276
277 return iwm_bus_send_chunk(iwm, buf->start, buf->len);
278}
279
280void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop)
281{
282 struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf;
283
284 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop);
285}
286
287void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
288 struct iwm_udma_out_wifi_hdr *hdr,
289 struct iwm_udma_wifi_cmd *cmd)
290{
291 memset(hdr, 0, sizeof(*hdr));
292
293 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI);
294 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop);
295 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
296
297 SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT,
298 le16_to_cpu(cmd->count));
299 SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group);
300 SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid);
301 SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset);
302}
303
304void iwm_build_umac_hdr(struct iwm_priv *iwm,
305 struct iwm_umac_fw_cmd_hdr *hdr,
306 struct iwm_umac_cmd *cmd)
307{
308 memset(hdr, 0, sizeof(*hdr));
309
310 SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT,
311 le16_to_cpu(cmd->count));
312 SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color);
313 SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp);
314
315 hdr->cmd.cmd = cmd->id;
316 hdr->cmd.seq_num = cmd->seq_num;
317}
318
319static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm,
320 struct iwm_wifi_cmd *cmd)
321{
322 struct iwm_umac_wifi_out_hdr *umac_hdr;
323 struct iwm_wifi_cmd_buff *buf;
324 struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd;
325 struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd;
326 int ret;
327
328 buf = &cmd->buf;
329
330 buf->start -= sizeof(struct iwm_umac_wifi_out_hdr);
331 buf->len += sizeof(struct iwm_umac_wifi_out_hdr);
332
333 umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start);
334
335 iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd);
336 iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd);
337
338 IWM_DBG_CMD(iwm, DBG,
339 "Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, "
340 "eop = 0x%x, count = 0x%x, credit_group = 0x%x, "
341 "ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n",
342 UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id,
343 udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group,
344 udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num);
345
346 if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH)
347 IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n",
348 cmd->lmac_cmd.id);
349
350 ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len);
351
352 /* We keep sending UMAC reset regardless of the command credits.
353 * The UMAC is supposed to be reset anyway and the Tx credits are
354 * reinitialized afterwards. If we are lucky, the reset could
355 * still be done even though we have run out of credits for the
356 * command pool at this moment.*/
357 if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) {
358 IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n",
359 umac_cmd->id);
360 return ret;
361 }
362
363 return iwm_bus_send_chunk(iwm, buf->start, buf->len);
364}
365
366/* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */
367int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
368 struct iwm_udma_nonwifi_cmd *udma_cmd,
369 const void *payload)
370{
371 struct iwm_nonwifi_cmd *cmd;
372 int ret;
373
374 cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL);
375 if (!cmd) {
376 IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n");
377 return -ENOMEM;
378 }
379
380 iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd);
381
382 if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE ||
383 cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) {
384 cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz);
385 memcpy(&cmd->buf.payload, payload, cmd->buf.len);
386 }
387
388 ret = iwm_send_udma_nonwifi_cmd(iwm, cmd);
389
390 if (!udma_cmd->resp)
391 kfree(cmd);
392
393 if (ret < 0)
394 return ret;
395
396 return cmd->seq_num;
397}
398
399static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
400 struct iwm_lmac_cmd *cmd)
401{
402 memset(hdr, 0, sizeof(*hdr));
403
404 hdr->id = cmd->id;
405 hdr->flags = 0; /* Is this ever used? */
406 hdr->seq_num = cmd->seq_num;
407}
408
409/*
410 * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
411 * Sending command to the LMAC is equivalent to sending a
412 * regular UMAC command with the LMAC passtrough or the LMAC
413 * wrapper UMAC command IDs.
414 */
415int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
416 struct iwm_udma_wifi_cmd *udma_cmd,
417 struct iwm_umac_cmd *umac_cmd,
418 struct iwm_lmac_cmd *lmac_cmd,
419 const void *payload, u16 payload_size)
420{
421 struct iwm_wifi_cmd *cmd;
422 struct iwm_lmac_hdr *hdr;
423 int lmac_hdr_len = 0;
424 int ret;
425
426 cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL);
427 if (!cmd) {
428 IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n");
429 return -ENOMEM;
430 }
431
432 iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size);
433
434 if (lmac_cmd) {
435 hdr = (struct iwm_lmac_hdr *)(cmd->buf.start);
436
437 iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd);
438 lmac_hdr_len = sizeof(struct iwm_lmac_hdr);
439 }
440
441 memcpy(cmd->buf.payload, payload, payload_size);
442 cmd->buf.len = le16_to_cpu(umac_cmd->count);
443
444 ret = iwm_send_udma_wifi_cmd(iwm, cmd);
445
446 /* We free the cmd if we're not expecting any response */
447 if (!umac_cmd->resp)
448 kfree(cmd);
449 return ret;
450}
451
452/*
453 * iwm_hal_send_umac_cmd(): This is a special case for
454 * iwm_hal_send_host_cmd() to send direct UMAC cmd (without
455 * LMAC involved).
456 */
457int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
458 struct iwm_udma_wifi_cmd *udma_cmd,
459 struct iwm_umac_cmd *umac_cmd,
460 const void *payload, u16 payload_size)
461{
462 return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL,
463 payload, payload_size);
464}
diff --git a/drivers/net/wireless/iwmc3200wifi/hal.h b/drivers/net/wireless/iwmc3200wifi/hal.h
new file mode 100644
index 000000000000..0adfdc85765d
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/hal.h
@@ -0,0 +1,236 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef _IWM_HAL_H_
40#define _IWM_HAL_H_
41
42#include "umac.h"
43
44#define GET_VAL8(s, name) ((s >> name##_POS) & name##_SEED)
45#define GET_VAL16(s, name) ((le16_to_cpu(s) >> name##_POS) & name##_SEED)
46#define GET_VAL32(s, name) ((le32_to_cpu(s) >> name##_POS) & name##_SEED)
47
48#define SET_VAL8(s, name, val) \
49do { \
50 s = (s & ~(name##_SEED << name##_POS)) | \
51 ((val & name##_SEED) << name##_POS); \
52} while (0)
53
54#define SET_VAL16(s, name, val) \
55do { \
56 s = cpu_to_le16((le16_to_cpu(s) & ~(name##_SEED << name##_POS)) | \
57 ((val & name##_SEED) << name##_POS)); \
58} while (0)
59
60#define SET_VAL32(s, name, val) \
61do { \
62 s = cpu_to_le32((le32_to_cpu(s) & ~(name##_SEED << name##_POS)) | \
63 ((val & name##_SEED) << name##_POS)); \
64} while (0)
65
66
67#define UDMA_UMAC_INIT { .eop = 1, \
68 .credit_group = 0x4, \
69 .ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD, \
70 .lmac_offset = 0 }
71#define UDMA_LMAC_INIT { .eop = 1, \
72 .credit_group = 0x4, \
73 .ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD, \
74 .lmac_offset = 4 }
75
76
77/* UDMA IN OP CODE -- cmd bits [3:0] */
78#define UDMA_IN_OPCODE_MASK 0xF
79
80#define UDMA_IN_OPCODE_GENERAL_RESP 0x0
81#define UDMA_IN_OPCODE_READ_RESP 0x1
82#define UDMA_IN_OPCODE_WRITE_RESP 0x2
83#define UDMA_IN_OPCODE_PERS_WRITE_RESP 0x5
84#define UDMA_IN_OPCODE_PERS_READ_RESP 0x6
85#define UDMA_IN_OPCODE_RD_MDFY_WR_RESP 0x7
86#define UDMA_IN_OPCODE_EP_MNGMT_MSG 0x8
87#define UDMA_IN_OPCODE_CRDT_CHNG_MSG 0x9
88#define UDMA_IN_OPCODE_CNTRL_DATABASE_MSG 0xA
89#define UDMA_IN_OPCODE_SW_MSG 0xB
90#define UDMA_IN_OPCODE_WIFI 0xF
91#define UDMA_IN_OPCODE_WIFI_LMAC 0x1F
92#define UDMA_IN_OPCODE_WIFI_UMAC 0x2F
93
94/* HW API: udma_hdi_nonwifi API (OUT and IN) */
95
96/* iwm_udma_nonwifi_cmd request response -- bits [9:9] */
97#define UDMA_HDI_OUT_NW_CMD_RESP_POS 9
98#define UDMA_HDI_OUT_NW_CMD_RESP_SEED 0x1
99
100/* iwm_udma_nonwifi_cmd handle by HW -- bits [11:11] */
101#define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_POS 11
102#define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_SEED 0x1
103
104/* iwm_udma_nonwifi_cmd sequence-number -- bits [12:15] */
105#define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_POS 12
106#define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_SEED 0xF
107
108/* UDMA IN Non-WIFI HW sequence number -- bits [12:15] */
109#define UDMA_IN_NW_HW_SEQ_NUM_POS 12
110#define UDMA_IN_NW_HW_SEQ_NUM_SEED 0xF
111
112/* UDMA IN Non-WIFI HW signature -- bits [16:31] */
113#define UDMA_IN_NW_HW_SIG_POS 16
114#define UDMA_IN_NW_HW_SIG_SEED 0xFFFF
115
116/* fixed signature */
117#define UDMA_IN_NW_HW_SIG 0xCBBC
118
119/* UDMA IN Non-WIFI HW block length -- bits [32:35] */
120#define UDMA_IN_NW_HW_LENGTH_SEED 0xF
121#define UDMA_IN_NW_HW_LENGTH_POS 32
122
123/* End of HW API: udma_hdi_nonwifi API (OUT and IN) */
124
125#define IWM_SDIO_FW_MAX_CHUNK_SIZE 2032
126#define IWM_MAX_WIFI_HEADERS_SIZE 32
127#define IWM_MAX_NONWIFI_HEADERS_SIZE 16
128#define IWM_MAX_NONWIFI_CMD_BUFF_SIZE (IWM_SDIO_FW_MAX_CHUNK_SIZE - \
129 IWM_MAX_NONWIFI_HEADERS_SIZE)
130#define IWM_MAX_WIFI_CMD_BUFF_SIZE (IWM_SDIO_FW_MAX_CHUNK_SIZE - \
131 IWM_MAX_WIFI_HEADERS_SIZE)
132
133#define IWM_HAL_CONCATENATE_BUF_SIZE 8192
134
135struct iwm_wifi_cmd_buff {
136 u16 len;
137 u8 *start;
138 u8 hdr[IWM_MAX_WIFI_HEADERS_SIZE];
139 u8 payload[IWM_MAX_WIFI_CMD_BUFF_SIZE];
140};
141
142struct iwm_nonwifi_cmd_buff {
143 u16 len;
144 u8 *start;
145 u8 hdr[IWM_MAX_NONWIFI_HEADERS_SIZE];
146 u8 payload[IWM_MAX_NONWIFI_CMD_BUFF_SIZE];
147};
148
149struct iwm_udma_nonwifi_cmd {
150 u8 opcode;
151 u8 eop;
152 u8 resp;
153 u8 handle_by_hw;
154 __le32 addr;
155 __le32 op1_sz;
156 __le32 op2;
157 __le16 seq_num;
158};
159
160struct iwm_udma_wifi_cmd {
161 __le16 count;
162 u8 eop;
163 u8 credit_group;
164 u8 ra_tid;
165 u8 lmac_offset;
166};
167
168struct iwm_umac_cmd {
169 u8 id;
170 __le16 count;
171 u8 resp;
172 __le16 seq_num;
173 u8 color;
174};
175
176struct iwm_lmac_cmd {
177 u8 id;
178 __le16 count;
179 u8 resp;
180 __le16 seq_num;
181};
182
183struct iwm_nonwifi_cmd {
184 u16 seq_num;
185 bool resp_received;
186 struct list_head pending;
187 struct iwm_udma_nonwifi_cmd udma_cmd;
188 struct iwm_umac_cmd umac_cmd;
189 struct iwm_lmac_cmd lmac_cmd;
190 struct iwm_nonwifi_cmd_buff buf;
191 u32 flags;
192};
193
194struct iwm_wifi_cmd {
195 u16 seq_num;
196 struct list_head pending;
197 struct iwm_udma_wifi_cmd udma_cmd;
198 struct iwm_umac_cmd umac_cmd;
199 struct iwm_lmac_cmd lmac_cmd;
200 struct iwm_wifi_cmd_buff buf;
201 u32 flags;
202};
203
204void iwm_cmd_flush(struct iwm_priv *iwm);
205
206struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm,
207 u16 seq_num);
208struct iwm_nonwifi_cmd *iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm,
209 u8 seq_num, u8 cmd_opcode);
210
211
212int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
213 struct iwm_udma_nonwifi_cmd *ucmd,
214 const void *payload);
215
216int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
217 struct iwm_udma_wifi_cmd *udma_cmd,
218 struct iwm_umac_cmd *umac_cmd,
219 struct iwm_lmac_cmd *lmac_cmd,
220 const void *payload, u16 payload_size);
221
222int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
223 struct iwm_udma_wifi_cmd *udma_cmd,
224 struct iwm_umac_cmd *umac_cmd,
225 const void *payload, u16 payload_size);
226
227u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm);
228
229void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop);
230void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
231 struct iwm_udma_out_wifi_hdr *hdr,
232 struct iwm_udma_wifi_cmd *cmd);
233void iwm_build_umac_hdr(struct iwm_priv *iwm,
234 struct iwm_umac_fw_cmd_hdr *hdr,
235 struct iwm_umac_cmd *cmd);
236#endif /* _IWM_HAL_H_ */
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
new file mode 100644
index 000000000000..3b29681792bb
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -0,0 +1,350 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef __IWM_H__
40#define __IWM_H__
41
42#include <linux/netdevice.h>
43#include <linux/wireless.h>
44#include <net/cfg80211.h>
45
46#include "debug.h"
47#include "hal.h"
48#include "umac.h"
49#include "lmac.h"
50#include "eeprom.h"
51
52#define IWM_COPYRIGHT "Copyright(c) 2009 Intel Corporation"
53#define IWM_AUTHOR "<ilw@linux.intel.com>"
54
55#define CONFIG_IWM_B0_HW_SUPPORT 1
56
57#define IWM_SRC_LMAC UMAC_HDI_IN_SOURCE_FHRX
58#define IWM_SRC_UDMA UMAC_HDI_IN_SOURCE_UDMA
59#define IWM_SRC_UMAC UMAC_HDI_IN_SOURCE_FW
60#define IWM_SRC_NUM 3
61
62#define IWM_POWER_INDEX_MIN 0
63#define IWM_POWER_INDEX_MAX 5
64#define IWM_POWER_INDEX_DEFAULT 3
65
66struct iwm_conf {
67 u32 sdio_ior_timeout;
68 unsigned long init_calib_map;
69 unsigned long periodic_calib_map;
70 bool reset_on_fatal_err;
71 bool auto_connect;
72 bool wimax_not_present;
73 bool enable_qos;
74 u32 mode;
75
76 u32 power_index;
77 u32 frag_threshold;
78 u32 rts_threshold;
79 bool cts_to_self;
80
81 u32 assoc_timeout;
82 u32 roam_timeout;
83 u32 wireless_mode;
84 u32 coexist_mode;
85
86 u8 ibss_band;
87 u8 ibss_channel;
88
89 u8 mac_addr[ETH_ALEN];
90#ifdef CONFIG_IWM_B0_HW_SUPPORT
91 bool hw_b0;
92#endif
93};
94
95enum {
96 COEX_MODE_SA = 1,
97 COEX_MODE_XOR,
98 COEX_MODE_CM,
99 COEX_MODE_MAX,
100};
101
102struct iwm_if_ops;
103struct iwm_wifi_cmd;
104
105struct pool_entry {
106 int id; /* group id */
107 int sid; /* super group id */
108 int min_pages; /* min capacity in pages */
109 int max_pages; /* max capacity in pages */
110 int alloc_pages; /* allocated # of pages. incresed by driver */
111 int total_freed_pages; /* total freed # of pages. incresed by UMAC */
112};
113
114struct spool_entry {
115 int id;
116 int max_pages;
117 int alloc_pages;
118};
119
120struct iwm_tx_credit {
121 spinlock_t lock;
122 int pool_nr;
123 unsigned long full_pools_map; /* bitmap for # of filled tx pools */
124 struct pool_entry pools[IWM_MACS_OUT_GROUPS];
125 struct spool_entry spools[IWM_MACS_OUT_SGROUPS];
126};
127
128struct iwm_notif {
129 struct list_head pending;
130 u32 cmd_id;
131 void *cmd;
132 u8 src;
133 void *buf;
134 unsigned long buf_size;
135};
136
137struct iwm_sta_info {
138 u8 addr[ETH_ALEN];
139 bool valid;
140 bool qos;
141 u8 color;
142};
143
144struct iwm_tx_info {
145 u8 sta;
146 u8 color;
147 u8 tid;
148};
149
150struct iwm_rx_info {
151 unsigned long rx_size;
152 unsigned long rx_buf_size;
153};
154
155#define IWM_NUM_KEYS 4
156
157struct iwm_umac_key_hdr {
158 u8 mac[ETH_ALEN];
159 u8 key_idx;
160 u8 multicast; /* BCast encrypt & BCast decrypt of frames FROM mac */
161} __attribute__ ((packed));
162
163struct iwm_key {
164 struct iwm_umac_key_hdr hdr;
165 u8 in_use;
166 u8 alg;
167 u32 flags;
168 u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE];
169 u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE];
170 u8 key_len;
171 u8 key[32];
172};
173
174#define IWM_RX_ID_HASH 0xff
175#define IWM_RX_ID_GET_HASH(id) ((id) % IWM_RX_ID_HASH)
176
177#define IWM_STA_TABLE_NUM 16
178#define IWM_TX_LIST_SIZE 64
179#define IWM_RX_LIST_SIZE 256
180
181#define IWM_SCAN_ID_MAX 0xff
182
183#define IWM_STATUS_READY 0
184#define IWM_STATUS_SCANNING 1
185#define IWM_STATUS_SCAN_ABORTING 2
186#define IWM_STATUS_ASSOCIATING 3
187#define IWM_STATUS_ASSOCIATED 4
188
189#define IWM_RADIO_RFKILL_OFF 0
190#define IWM_RADIO_RFKILL_HW 1
191#define IWM_RADIO_RFKILL_SW 2
192
193struct iwm_tx_queue {
194 int id;
195 struct sk_buff_head queue;
196 struct workqueue_struct *wq;
197 struct work_struct worker;
198 u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE];
199 int concat_count;
200 u8 *concat_ptr;
201};
202
203/* Queues 0 ~ 3 for AC data, 5 for iPAN */
204#define IWM_TX_QUEUES 5
205#define IWM_TX_DATA_QUEUES 4
206#define IWM_TX_CMD_QUEUE 4
207
208struct iwm_bss_info {
209 struct list_head node;
210 struct cfg80211_bss *cfg_bss;
211 struct iwm_umac_notif_bss_info *bss;
212};
213
214typedef int (*iwm_handler)(struct iwm_priv *priv, u8 *buf,
215 unsigned long buf_size, struct iwm_wifi_cmd *cmd);
216
217#define IWM_WATCHDOG_PERIOD (6 * HZ)
218
219struct iwm_priv {
220 struct wireless_dev *wdev;
221 struct iwm_if_ops *bus_ops;
222
223 struct iwm_conf conf;
224
225 unsigned long status;
226 unsigned long radio;
227
228 struct list_head pending_notif;
229 wait_queue_head_t notif_queue;
230
231 wait_queue_head_t nonwifi_queue;
232
233 unsigned long calib_done_map;
234 struct {
235 u8 *buf;
236 u32 size;
237 } calib_res[CALIBRATION_CMD_NUM];
238
239 struct iwm_umac_profile *umac_profile;
240 bool umac_profile_active;
241
242 u8 bssid[ETH_ALEN];
243 u8 channel;
244 u16 rate;
245
246 struct iwm_sta_info sta_table[IWM_STA_TABLE_NUM];
247 struct list_head bss_list;
248
249 void (*nonwifi_rx_handlers[UMAC_HDI_IN_OPCODE_NONWIFI_MAX])
250 (struct iwm_priv *priv, u8 *buf, unsigned long buf_size);
251
252 const iwm_handler *umac_handlers;
253 const iwm_handler *lmac_handlers;
254 DECLARE_BITMAP(lmac_handler_map, LMAC_COMMAND_ID_NUM);
255 DECLARE_BITMAP(umac_handler_map, LMAC_COMMAND_ID_NUM);
256 DECLARE_BITMAP(udma_handler_map, LMAC_COMMAND_ID_NUM);
257
258 struct list_head wifi_pending_cmd;
259 struct list_head nonwifi_pending_cmd;
260 u16 wifi_seq_num;
261 u8 nonwifi_seq_num;
262 spinlock_t cmd_lock;
263
264 u32 core_enabled;
265
266 u8 scan_id;
267 struct cfg80211_scan_request *scan_request;
268
269 struct sk_buff_head rx_list;
270 struct list_head rx_tickets;
271 struct list_head rx_packets[IWM_RX_ID_HASH];
272 struct workqueue_struct *rx_wq;
273 struct work_struct rx_worker;
274
275 struct iwm_tx_credit tx_credit;
276 struct iwm_tx_queue txq[IWM_TX_QUEUES];
277
278 struct iwm_key keys[IWM_NUM_KEYS];
279 struct iwm_key *default_key;
280
281 wait_queue_head_t mlme_queue;
282
283 struct iw_statistics wstats;
284 struct delayed_work stats_request;
285
286 struct iwm_debugfs dbg;
287
288 u8 *eeprom;
289 struct timer_list watchdog;
290 struct work_struct reset_worker;
291 struct rfkill *rfkill;
292
293 char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
294};
295
296static inline void *iwm_private(struct iwm_priv *iwm)
297{
298 BUG_ON(!iwm);
299 return &iwm->private;
300}
301
302#define hw_to_iwm(h) (h->iwm)
303#define iwm_to_dev(i) (wiphy_dev(i->wdev->wiphy))
304#define iwm_to_wiphy(i) (i->wdev->wiphy)
305#define wiphy_to_iwm(w) (struct iwm_priv *)(wiphy_priv(w))
306#define iwm_to_wdev(i) (i->wdev)
307#define wdev_to_iwm(w) (struct iwm_priv *)(wdev_priv(w))
308#define iwm_to_ndev(i) (i->wdev->netdev)
309#define ndev_to_iwm(n) (wdev_to_iwm(n->ieee80211_ptr))
310#define skb_to_rx_info(s) ((struct iwm_rx_info *)(s->cb))
311#define skb_to_tx_info(s) ((struct iwm_tx_info *)s->cb)
312
313extern const struct iw_handler_def iwm_iw_handler_def;
314
315void *iwm_if_alloc(int sizeof_bus, struct device *dev,
316 struct iwm_if_ops *if_ops);
317void iwm_if_free(struct iwm_priv *iwm);
318int iwm_mode_to_nl80211_iftype(int mode);
319int iwm_priv_init(struct iwm_priv *iwm);
320void iwm_reset(struct iwm_priv *iwm);
321void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
322 struct iwm_umac_notif_alive *alive);
323int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb);
324int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd,
325 u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size);
326int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout);
327void iwm_init_default_profile(struct iwm_priv *iwm,
328 struct iwm_umac_profile *profile);
329void iwm_link_on(struct iwm_priv *iwm);
330void iwm_link_off(struct iwm_priv *iwm);
331int iwm_up(struct iwm_priv *iwm);
332int iwm_down(struct iwm_priv *iwm);
333
334/* TX API */
335void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages);
336void iwm_tx_worker(struct work_struct *work);
337int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
338
339/* RX API */
340void iwm_rx_setup_handlers(struct iwm_priv *iwm);
341int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size);
342int iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size,
343 struct iwm_wifi_cmd *cmd);
344void iwm_rx_free(struct iwm_priv *iwm);
345
346/* RF Kill API */
347int iwm_rfkill_init(struct iwm_priv *iwm);
348void iwm_rfkill_exit(struct iwm_priv *iwm);
349
350#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/lmac.h b/drivers/net/wireless/iwmc3200wifi/lmac.h
new file mode 100644
index 000000000000..db2e5eea1895
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/lmac.h
@@ -0,0 +1,457 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef __IWM_LMAC_H__
40#define __IWM_LMAC_H__
41
42struct iwm_lmac_hdr {
43 u8 id;
44 u8 flags;
45 __le16 seq_num;
46} __attribute__ ((packed));
47
48/* LMAC commands */
49#define CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK 0x1
50
51struct iwm_lmac_cal_cfg_elt {
52 __le32 enable; /* 1 means LMAC needs to do something */
53 __le32 start; /* 1 to start calibration, 0 to stop */
54 __le32 send_res; /* 1 for sending back results */
55 __le32 apply_res; /* 1 for applying calibration results to HW */
56 __le32 reserved;
57} __attribute__ ((packed));
58
59struct iwm_lmac_cal_cfg_status {
60 struct iwm_lmac_cal_cfg_elt init;
61 struct iwm_lmac_cal_cfg_elt periodic;
62 __le32 flags; /* CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK */
63} __attribute__ ((packed));
64
65struct iwm_lmac_cal_cfg_cmd {
66 struct iwm_lmac_cal_cfg_status ucode_cfg;
67 struct iwm_lmac_cal_cfg_status driver_cfg;
68 __le32 reserved;
69} __attribute__ ((packed));
70
71struct iwm_lmac_cal_cfg_resp {
72 __le32 status;
73} __attribute__ ((packed));
74
75#define IWM_CARD_STATE_SW_HW_ENABLED 0x00
76#define IWM_CARD_STATE_HW_DISABLED 0x01
77#define IWM_CARD_STATE_SW_DISABLED 0x02
78#define IWM_CARD_STATE_CTKILL_DISABLED 0x04
79#define IWM_CARD_STATE_IS_RXON 0x10
80
81struct iwm_lmac_card_state {
82 __le32 flags;
83} __attribute__ ((packed));
84
85/**
86 * COEX_PRIORITY_TABLE_CMD
87 *
88 * Priority entry for each state
89 * Will keep two tables, for STA and WIPAN
90 */
91enum {
92 /* UN-ASSOCIATION PART */
93 COEX_UNASSOC_IDLE = 0,
94 COEX_UNASSOC_MANUAL_SCAN,
95 COEX_UNASSOC_AUTO_SCAN,
96
97 /* CALIBRATION */
98 COEX_CALIBRATION,
99 COEX_PERIODIC_CALIBRATION,
100
101 /* CONNECTION */
102 COEX_CONNECTION_ESTAB,
103
104 /* ASSOCIATION PART */
105 COEX_ASSOCIATED_IDLE,
106 COEX_ASSOC_MANUAL_SCAN,
107 COEX_ASSOC_AUTO_SCAN,
108 COEX_ASSOC_ACTIVE_LEVEL,
109
110 /* RF ON/OFF */
111 COEX_RF_ON,
112 COEX_RF_OFF,
113 COEX_STAND_ALONE_DEBUG,
114
115 /* IPNN */
116 COEX_IPAN_ASSOC_LEVEL,
117
118 /* RESERVED */
119 COEX_RSRVD1,
120 COEX_RSRVD2,
121
122 COEX_EVENTS_NUM
123};
124
125#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK 0x1
126#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK 0x2
127#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK 0x4
128
129struct coex_event {
130 u8 req_prio;
131 u8 win_med_prio;
132 u8 reserved;
133 u8 flags;
134} __attribute__ ((packed));
135
136#define COEX_FLAGS_STA_TABLE_VALID_MSK 0x1
137#define COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK 0x4
138#define COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK 0x8
139#define COEX_FLAGS_COEX_ENABLE_MSK 0x80
140
141struct iwm_coex_prio_table_cmd {
142 u8 flags;
143 u8 reserved[3];
144 struct coex_event sta_prio[COEX_EVENTS_NUM];
145} __attribute__ ((packed));
146
147/* Coexistence definitions
148 *
149 * Constants to fill in the Priorities' Tables
150 * RP - Requested Priority
151 * WP - Win Medium Priority: priority assigned when the contention has been won
152 * FLAGS - Combination of COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK and
153 * COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK
154 */
155
156#define COEX_UNASSOC_IDLE_FLAGS 0
157#define COEX_UNASSOC_MANUAL_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
158 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
159#define COEX_UNASSOC_AUTO_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
160 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
161#define COEX_CALIBRATION_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
162 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
163#define COEX_PERIODIC_CALIBRATION_FLAGS 0
164/* COEX_CONNECTION_ESTAB: we need DELAY_MEDIUM_FREE_NTFY to let WiMAX
165 * disconnect from network. */
166#define COEX_CONNECTION_ESTAB_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
167 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
168 COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
169#define COEX_ASSOCIATED_IDLE_FLAGS 0
170#define COEX_ASSOC_MANUAL_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
171 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
172#define COEX_ASSOC_AUTO_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
173 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
174#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0
175#define COEX_RF_ON_FLAGS 0
176#define COEX_RF_OFF_FLAGS 0
177#define COEX_STAND_ALONE_DEBUG_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
178 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
179#define COEX_IPAN_ASSOC_LEVEL_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
180 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
181 COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
182#define COEX_RSRVD1_FLAGS 0
183#define COEX_RSRVD2_FLAGS 0
184/* XOR_RF_ON is the event wrapping all radio ownership. We need
185 * DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. */
186#define COEX_XOR_RF_ON_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
187 COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
188 COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
189
190/* LMAC OP CODES */
191#define REPLY_PAD 0x0
192#define REPLY_ALIVE 0x1
193#define REPLY_ERROR 0x2
194#define REPLY_ECHO 0x3
195#define REPLY_HALT 0x6
196
197/* RXON state commands */
198#define REPLY_RX_ON 0x10
199#define REPLY_RX_ON_ASSOC 0x11
200#define REPLY_RX_OFF 0x12
201#define REPLY_QOS_PARAM 0x13
202#define REPLY_RX_ON_TIMING 0x14
203#define REPLY_INTERNAL_QOS_PARAM 0x15
204#define REPLY_RX_INT_TIMEOUT_CNFG 0x16
205#define REPLY_NULL 0x17
206
207/* Multi-Station support */
208#define REPLY_ADD_STA 0x18
209#define REPLY_REMOVE_STA 0x19
210#define REPLY_RESET_ALL_STA 0x1a
211
212/* RX, TX */
213#define REPLY_ALM_RX 0x1b
214#define REPLY_TX 0x1c
215#define REPLY_TXFIFO_FLUSH 0x1e
216
217/* MISC commands */
218#define REPLY_MGMT_MCAST_KEY 0x1f
219#define REPLY_WEPKEY 0x20
220#define REPLY_INIT_IV 0x21
221#define REPLY_WRITE_MIB 0x22
222#define REPLY_READ_MIB 0x23
223#define REPLY_RADIO_FE 0x24
224#define REPLY_TXFIFO_CFG 0x25
225#define REPLY_WRITE_READ 0x26
226#define REPLY_INSTALL_SEC_KEY 0x27
227
228
229#define REPLY_RATE_SCALE 0x47
230#define REPLY_LEDS_CMD 0x48
231#define REPLY_TX_LINK_QUALITY_CMD 0x4e
232#define REPLY_ANA_MIB_OVERRIDE_CMD 0x4f
233#define REPLY_WRITE2REG_CMD 0x50
234
235/* winfi-wifi coexistence */
236#define COEX_PRIORITY_TABLE_CMD 0x5a
237#define COEX_MEDIUM_NOTIFICATION 0x5b
238#define COEX_EVENT_CMD 0x5c
239
240/* more Protocol and Protocol-test commands */
241#define REPLY_MAX_SLEEP_TIME_CMD 0x61
242#define CALIBRATION_CFG_CMD 0x65
243#define CALIBRATION_RES_NOTIFICATION 0x66
244#define CALIBRATION_COMPLETE_NOTIFICATION 0x67
245
246/* Measurements */
247#define REPLY_QUIET_CMD 0x71
248#define REPLY_CHANNEL_SWITCH 0x72
249#define CHANNEL_SWITCH_NOTIFICATION 0x73
250
251#define REPLY_SPECTRUM_MEASUREMENT_CMD 0x74
252#define SPECTRUM_MEASURE_NOTIFICATION 0x75
253#define REPLY_MEASUREMENT_ABORT_CMD 0x76
254
255/* Power Management */
256#define POWER_TABLE_CMD 0x77
257#define SAVE_RESTORE_ADRESS_CMD 0x78
258#define REPLY_WATERMARK_CMD 0x79
259#define PM_DEBUG_STATISTIC_NOTIFIC 0x7B
260#define PD_FLUSH_N_NOTIFICATION 0x7C
261
262/* Scan commands and notifications */
263#define REPLY_SCAN_REQUEST_CMD 0x80
264#define REPLY_SCAN_ABORT_CMD 0x81
265#define SCAN_START_NOTIFICATION 0x82
266#define SCAN_RESULTS_NOTIFICATION 0x83
267#define SCAN_COMPLETE_NOTIFICATION 0x84
268
269/* Continuous TX commands */
270#define REPLY_CONT_TX_CMD 0x85
271#define END_OF_CONT_TX_NOTIFICATION 0x86
272
273/* Timer/Eeprom commands */
274#define TIMER_CMD 0x87
275#define EEPROM_WRITE_CMD 0x88
276
277/* PAPD commands */
278#define FEEDBACK_REQUEST_NOTIFICATION 0x8b
279#define REPLY_CW_CMD 0x8c
280
281/* IBSS/AP commands Continue */
282#define BEACON_NOTIFICATION 0x90
283#define REPLY_TX_BEACON 0x91
284#define REPLY_REQUEST_ATIM 0x93
285#define WHO_IS_AWAKE_NOTIFICATION 0x94
286#define TX_PWR_DBM_LIMIT_CMD 0x95
287#define QUIET_NOTIFICATION 0x96
288#define TX_PWR_TABLE_CMD 0x97
289#define TX_ANT_CONFIGURATION_CMD 0x98
290#define MEASURE_ABORT_NOTIFICATION 0x99
291#define REPLY_CALIBRATION_TUNE 0x9a
292
293/* bt config command */
294#define REPLY_BT_CONFIG 0x9b
295#define REPLY_STATISTICS_CMD 0x9c
296#define STATISTICS_NOTIFICATION 0x9d
297
298/* RF-KILL commands and notifications */
299#define REPLY_CARD_STATE_CMD 0xa0
300#define CARD_STATE_NOTIFICATION 0xa1
301
302/* Missed beacons notification */
303#define MISSED_BEACONS_NOTIFICATION 0xa2
304#define MISSED_BEACONS_NOTIFICATION_TH_CMD 0xa3
305
306#define REPLY_CT_KILL_CONFIG_CMD 0xa4
307
308/* HD commands and notifications */
309#define REPLY_HD_PARAMS_CMD 0xa6
310#define HD_PARAMS_NOTIFICATION 0xa7
311#define SENSITIVITY_CMD 0xa8
312#define U_APSD_PARAMS_CMD 0xa9
313#define NOISY_PLATFORM_CMD 0xaa
314#define ILLEGAL_CMD 0xac
315#define REPLY_PHY_CALIBRATION_CMD 0xb0
316#define REPLAY_RX_GAIN_CALIB_CMD 0xb1
317
318/* WiPAN commands */
319#define REPLY_WIPAN_PARAMS_CMD 0xb2
320#define REPLY_WIPAN_RX_ON_CMD 0xb3
321#define REPLY_WIPAN_RX_ON_TIMING 0xb4
322#define REPLY_WIPAN_TX_PWR_TABLE_CMD 0xb5
323#define REPLY_WIPAN_RXON_ASSOC_CMD 0xb6
324#define REPLY_WIPAN_QOS_PARAM 0xb7
325#define WIPAN_REPLY_WEPKEY 0xb8
326
327/* BeamForming commands */
328#define BEAMFORMER_CFG_CMD 0xba
329#define BEAMFORMEE_NOTIFICATION 0xbb
330
331/* TGn new Commands */
332#define REPLY_RX_PHY_CMD 0xc0
333#define REPLY_RX_MPDU_CMD 0xc1
334#define REPLY_MULTICAST_HASH 0xc2
335#define REPLY_KDR_RX 0xc3
336#define REPLY_RX_DSP_EXT_INFO 0xc4
337#define REPLY_COMPRESSED_BA 0xc5
338
339/* PNC commands */
340#define PNC_CONFIG_CMD 0xc8
341#define PNC_UPDATE_TABLE_CMD 0xc9
342#define XVT_GENERAL_CTRL_CMD 0xca
343#define REPLY_LEGACY_RADIO_FE 0xdd
344
345/* WoWLAN commands */
346#define WOWLAN_PATTERNS 0xe0
347#define WOWLAN_WAKEUP_FILTER 0xe1
348#define WOWLAN_TSC_RSC_PARAM 0xe2
349#define WOWLAN_TKIP_PARAM 0xe3
350#define WOWLAN_KEK_KCK_MATERIAL 0xe4
351#define WOWLAN_GET_STATUSES 0xe5
352#define WOWLAN_TX_POWER_PER_DB 0xe6
353#define REPLY_WOWLAN_GET_STATUSES WOWLAN_GET_STATUSES
354
355#define REPLY_DEBUG_CMD 0xf0
356#define REPLY_DSP_DEBUG_CMD 0xf1
357#define REPLY_DEBUG_MONITOR_CMD 0xf2
358#define REPLY_DEBUG_XVT_CMD 0xf3
359#define REPLY_DEBUG_DC_CALIB 0xf4
360#define REPLY_DYNAMIC_BP 0xf5
361
362/* General purpose Commands */
363#define REPLY_GP1_CMD 0xfa
364#define REPLY_GP2_CMD 0xfb
365#define REPLY_GP3_CMD 0xfc
366#define REPLY_GP4_CMD 0xfd
367#define REPLY_REPLAY_WRAPPER 0xfe
368#define REPLY_FRAME_DURATION_CALC_CMD 0xff
369
370#define LMAC_COMMAND_ID_MAX 0xff
371#define LMAC_COMMAND_ID_NUM (LMAC_COMMAND_ID_MAX + 1)
372
373
374/* Calibration */
375
376enum {
377 PHY_CALIBRATE_DC_CMD = 0,
378 PHY_CALIBRATE_LO_CMD = 1,
379 PHY_CALIBRATE_RX_BB_CMD = 2,
380 PHY_CALIBRATE_TX_IQ_CMD = 3,
381 PHY_CALIBRATE_RX_IQ_CMD = 4,
382 PHY_CALIBRATION_NOISE_CMD = 5,
383 PHY_CALIBRATE_AGC_TABLE_CMD = 6,
384 PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 7,
385 PHY_CALIBRATE_OPCODES_NUM,
386 SHILOH_PHY_CALIBRATE_DC_CMD = 8,
387 SHILOH_PHY_CALIBRATE_LO_CMD = 9,
388 SHILOH_PHY_CALIBRATE_RX_BB_CMD = 10,
389 SHILOH_PHY_CALIBRATE_TX_IQ_CMD = 11,
390 SHILOH_PHY_CALIBRATE_RX_IQ_CMD = 12,
391 SHILOH_PHY_CALIBRATION_NOISE_CMD = 13,
392 SHILOH_PHY_CALIBRATE_AGC_TABLE_CMD = 14,
393 SHILOH_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15,
394 SHILOH_PHY_CALIBRATE_BASE_BAND_CMD = 16,
395 SHILOH_PHY_CALIBRATE_TXIQ_PERIODIC_CMD = 17,
396 CALIBRATION_CMD_NUM,
397};
398
399struct iwm_lmac_calib_hdr {
400 u8 opcode;
401 u8 first_grp;
402 u8 grp_num;
403 u8 all_data_valid;
404} __attribute__ ((packed));
405
406#define IWM_LMAC_CALIB_FREQ_GROUPS_NR 7
407#define IWM_CALIB_FREQ_GROUPS_NR 5
408#define IWM_CALIB_DC_MODES_NR 12
409
410struct iwm_calib_rxiq_entry {
411 u16 ptam_postdist_ars;
412 u16 ptam_postdist_arc;
413} __attribute__ ((packed));
414
415struct iwm_calib_rxiq_group {
416 struct iwm_calib_rxiq_entry mode[IWM_CALIB_DC_MODES_NR];
417} __attribute__ ((packed));
418
419struct iwm_lmac_calib_rxiq {
420 struct iwm_calib_rxiq_group group[IWM_LMAC_CALIB_FREQ_GROUPS_NR];
421} __attribute__ ((packed));
422
423struct iwm_calib_rxiq {
424 struct iwm_lmac_calib_hdr hdr;
425 struct iwm_calib_rxiq_group group[IWM_CALIB_FREQ_GROUPS_NR];
426} __attribute__ ((packed));
427
428#define LMAC_STA_ID_SEED 0x0f
429#define LMAC_STA_ID_POS 0
430
431#define LMAC_STA_COLOR_SEED 0x7
432#define LMAC_STA_COLOR_POS 4
433
434struct iwm_lmac_power_report {
435 u8 pa_status;
436 u8 pa_integ_res_A[3];
437 u8 pa_integ_res_B[3];
438 u8 pa_integ_res_C[3];
439} __attribute__ ((packed));
440
441struct iwm_lmac_tx_resp {
442 u8 frame_cnt; /* 1-no aggregation, greater then 1 - aggregation */
443 u8 bt_kill_cnt;
444 __le16 retry_cnt;
445 __le32 initial_tx_rate;
446 __le16 wireless_media_time;
447 struct iwm_lmac_power_report power_report;
448 __le32 tfd_info;
449 __le16 seq_ctl;
450 __le16 byte_cnt;
451 u8 tlc_rate_info;
452 u8 ra_tid;
453 __le16 frame_ctl;
454 __le32 status;
455} __attribute__ ((packed));
456
457#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
new file mode 100644
index 000000000000..6a2640f16b6d
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -0,0 +1,680 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#include <linux/kernel.h>
40#include <linux/netdevice.h>
41#include <linux/ieee80211.h>
42#include <linux/wireless.h>
43
44#include "iwm.h"
45#include "debug.h"
46#include "bus.h"
47#include "umac.h"
48#include "commands.h"
49#include "hal.h"
50#include "fw.h"
51#include "rx.h"
52
53static struct iwm_conf def_iwm_conf = {
54
55 .sdio_ior_timeout = 5000,
56 .init_calib_map = BIT(PHY_CALIBRATE_DC_CMD) |
57 BIT(PHY_CALIBRATE_LO_CMD) |
58 BIT(PHY_CALIBRATE_TX_IQ_CMD) |
59 BIT(PHY_CALIBRATE_RX_IQ_CMD),
60 .periodic_calib_map = BIT(PHY_CALIBRATE_DC_CMD) |
61 BIT(PHY_CALIBRATE_LO_CMD) |
62 BIT(PHY_CALIBRATE_TX_IQ_CMD) |
63 BIT(PHY_CALIBRATE_RX_IQ_CMD) |
64 BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD),
65 .reset_on_fatal_err = 1,
66 .auto_connect = 1,
67 .wimax_not_present = 0,
68 .enable_qos = 1,
69 .mode = UMAC_MODE_BSS,
70
71 /* UMAC configuration */
72 .power_index = 0,
73 .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
74 .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD,
75 .cts_to_self = 0,
76
77 .assoc_timeout = 2,
78 .roam_timeout = 10,
79 .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G,
80 .coexist_mode = COEX_MODE_CM,
81
82 /* IBSS */
83 .ibss_band = UMAC_BAND_2GHZ,
84 .ibss_channel = 1,
85
86 .mac_addr = {0x00, 0x02, 0xb3, 0x01, 0x02, 0x03},
87};
88
89static int modparam_reset;
90module_param_named(reset, modparam_reset, bool, 0644);
91MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])");
92
93int iwm_mode_to_nl80211_iftype(int mode)
94{
95 switch (mode) {
96 case UMAC_MODE_BSS:
97 return NL80211_IFTYPE_STATION;
98 case UMAC_MODE_IBSS:
99 return NL80211_IFTYPE_ADHOC;
100 default:
101 return NL80211_IFTYPE_UNSPECIFIED;
102 }
103
104 return 0;
105}
106
107static void iwm_statistics_request(struct work_struct *work)
108{
109 struct iwm_priv *iwm =
110 container_of(work, struct iwm_priv, stats_request.work);
111
112 iwm_send_umac_stats_req(iwm, 0);
113}
114
115static void iwm_reset_worker(struct work_struct *work)
116{
117 struct iwm_priv *iwm;
118 struct iwm_umac_profile *profile = NULL;
119 int uninitialized_var(ret), retry = 0;
120
121 iwm = container_of(work, struct iwm_priv, reset_worker);
122
123 if (iwm->umac_profile_active) {
124 profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL);
125 if (profile)
126 memcpy(profile, iwm->umac_profile, sizeof(*profile));
127 else
128 IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
129 }
130
131 iwm_down(iwm);
132
133 while (retry++ < 3) {
134 ret = iwm_up(iwm);
135 if (!ret)
136 break;
137
138 schedule_timeout_uninterruptible(10 * HZ);
139 }
140
141 if (ret) {
142 IWM_WARN(iwm, "iwm_up() failed: %d\n", ret);
143
144 kfree(profile);
145 return;
146 }
147
148 if (profile) {
149 IWM_DBG_MLME(iwm, DBG, "Resend UMAC profile\n");
150 memcpy(iwm->umac_profile, profile, sizeof(*profile));
151 iwm_send_mlme_profile(iwm);
152 kfree(profile);
153 }
154}
155
156static void iwm_watchdog(unsigned long data)
157{
158 struct iwm_priv *iwm = (struct iwm_priv *)data;
159
160 IWM_WARN(iwm, "Watchdog expired: UMAC stalls!\n");
161
162 if (modparam_reset)
163 schedule_work(&iwm->reset_worker);
164}
165
166int iwm_priv_init(struct iwm_priv *iwm)
167{
168 int i;
169 char name[32];
170
171 iwm->status = 0;
172 INIT_LIST_HEAD(&iwm->pending_notif);
173 init_waitqueue_head(&iwm->notif_queue);
174 init_waitqueue_head(&iwm->nonwifi_queue);
175 init_waitqueue_head(&iwm->mlme_queue);
176 memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf));
177 spin_lock_init(&iwm->tx_credit.lock);
178 INIT_LIST_HEAD(&iwm->wifi_pending_cmd);
179 INIT_LIST_HEAD(&iwm->nonwifi_pending_cmd);
180 iwm->wifi_seq_num = UMAC_WIFI_SEQ_NUM_BASE;
181 iwm->nonwifi_seq_num = UMAC_NONWIFI_SEQ_NUM_BASE;
182 spin_lock_init(&iwm->cmd_lock);
183 iwm->scan_id = 1;
184 INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request);
185 INIT_WORK(&iwm->reset_worker, iwm_reset_worker);
186 INIT_LIST_HEAD(&iwm->bss_list);
187
188 skb_queue_head_init(&iwm->rx_list);
189 INIT_LIST_HEAD(&iwm->rx_tickets);
190 for (i = 0; i < IWM_RX_ID_HASH; i++)
191 INIT_LIST_HEAD(&iwm->rx_packets[i]);
192
193 INIT_WORK(&iwm->rx_worker, iwm_rx_worker);
194
195 iwm->rx_wq = create_singlethread_workqueue(KBUILD_MODNAME "_rx");
196 if (!iwm->rx_wq)
197 return -EAGAIN;
198
199 for (i = 0; i < IWM_TX_QUEUES; i++) {
200 INIT_WORK(&iwm->txq[i].worker, iwm_tx_worker);
201 snprintf(name, 32, KBUILD_MODNAME "_tx_%d", i);
202 iwm->txq[i].id = i;
203 iwm->txq[i].wq = create_singlethread_workqueue(name);
204 if (!iwm->txq[i].wq)
205 return -EAGAIN;
206
207 skb_queue_head_init(&iwm->txq[i].queue);
208 }
209
210 for (i = 0; i < IWM_NUM_KEYS; i++)
211 memset(&iwm->keys[i], 0, sizeof(struct iwm_key));
212
213 iwm->default_key = NULL;
214
215 init_timer(&iwm->watchdog);
216 iwm->watchdog.function = iwm_watchdog;
217 iwm->watchdog.data = (unsigned long)iwm;
218
219 return 0;
220}
221
222/*
223 * We reset all the structures, and we reset the UMAC.
224 * After calling this routine, you're expected to reload
225 * the firmware.
226 */
227void iwm_reset(struct iwm_priv *iwm)
228{
229 struct iwm_notif *notif, *next;
230
231 if (test_bit(IWM_STATUS_READY, &iwm->status))
232 iwm_target_reset(iwm);
233
234 iwm->status = 0;
235 iwm->scan_id = 1;
236
237 list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
238 list_del(&notif->pending);
239 kfree(notif->buf);
240 kfree(notif);
241 }
242
243 iwm_cmd_flush(iwm);
244
245 flush_workqueue(iwm->rx_wq);
246
247 iwm_link_off(iwm);
248}
249
250/*
251 * Notification code:
252 *
253 * We're faced with the following issue: Any host command can
254 * have an answer or not, and if there's an answer to expect,
255 * it can be treated synchronously or asynchronously.
256 * To work around the synchronous answer case, we implemented
257 * our notification mechanism.
258 * When a code path needs to wait for a command response
259 * synchronously, it calls notif_handle(), which waits for the
260 * right notification to show up, and then process it. Before
261 * starting to wait, it registered as a waiter for this specific
262 * answer (by toggling a bit in on of the handler_map), so that
263 * the rx code knows that it needs to send a notification to the
264 * waiting processes. It does so by calling iwm_notif_send(),
265 * which adds the notification to the pending notifications list,
266 * and then wakes the waiting processes up.
267 */
268int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd,
269 u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size)
270{
271 struct iwm_notif *notif;
272
273 notif = kzalloc(sizeof(struct iwm_notif), GFP_KERNEL);
274 if (!notif) {
275 IWM_ERR(iwm, "Couldn't alloc memory for notification\n");
276 return -ENOMEM;
277 }
278
279 INIT_LIST_HEAD(&notif->pending);
280 notif->cmd = cmd;
281 notif->cmd_id = cmd_id;
282 notif->src = source;
283 notif->buf = kzalloc(buf_size, GFP_KERNEL);
284 if (!notif->buf) {
285 IWM_ERR(iwm, "Couldn't alloc notification buffer\n");
286 kfree(notif);
287 return -ENOMEM;
288 }
289 notif->buf_size = buf_size;
290 memcpy(notif->buf, buf, buf_size);
291 list_add_tail(&notif->pending, &iwm->pending_notif);
292
293 wake_up_interruptible(&iwm->notif_queue);
294
295 return 0;
296}
297
298static struct iwm_notif *iwm_notif_find(struct iwm_priv *iwm, u32 cmd,
299 u8 source)
300{
301 struct iwm_notif *notif, *next;
302
303 list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
304 if ((notif->cmd_id == cmd) && (notif->src == source)) {
305 list_del(&notif->pending);
306 return notif;
307 }
308 }
309
310 return NULL;
311}
312
313static struct iwm_notif *iwm_notif_wait(struct iwm_priv *iwm, u32 cmd,
314 u8 source, long timeout)
315{
316 int ret;
317 struct iwm_notif *notif;
318 unsigned long *map = NULL;
319
320 switch (source) {
321 case IWM_SRC_LMAC:
322 map = &iwm->lmac_handler_map[0];
323 break;
324 case IWM_SRC_UMAC:
325 map = &iwm->umac_handler_map[0];
326 break;
327 case IWM_SRC_UDMA:
328 map = &iwm->udma_handler_map[0];
329 break;
330 }
331
332 set_bit(cmd, map);
333
334 ret = wait_event_interruptible_timeout(iwm->notif_queue,
335 ((notif = iwm_notif_find(iwm, cmd, source)) != NULL),
336 timeout);
337 clear_bit(cmd, map);
338
339 if (!ret)
340 return NULL;
341
342 return notif;
343}
344
345int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout)
346{
347 int ret;
348 struct iwm_notif *notif;
349
350 notif = iwm_notif_wait(iwm, cmd, source, timeout);
351 if (!notif)
352 return -ETIME;
353
354 ret = iwm_rx_handle_resp(iwm, notif->buf, notif->buf_size, notif->cmd);
355 kfree(notif->buf);
356 kfree(notif);
357
358 return ret;
359}
360
361static int iwm_config_boot_params(struct iwm_priv *iwm)
362{
363 struct iwm_udma_nonwifi_cmd target_cmd;
364 int ret;
365
366 /* check Wimax is off and config debug monitor */
367 if (iwm->conf.wimax_not_present) {
368 u32 data1 = 0x1f;
369 u32 addr1 = 0x606BE258;
370
371 u32 data2_set = 0x0;
372 u32 data2_clr = 0x1;
373 u32 addr2 = 0x606BE100;
374
375 u32 data3 = 0x1;
376 u32 addr3 = 0x606BEC00;
377
378 target_cmd.resp = 0;
379 target_cmd.handle_by_hw = 0;
380 target_cmd.eop = 1;
381
382 target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
383 target_cmd.addr = cpu_to_le32(addr1);
384 target_cmd.op1_sz = cpu_to_le32(sizeof(u32));
385 target_cmd.op2 = 0;
386
387 ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1);
388 if (ret < 0) {
389 IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
390 return ret;
391 }
392
393 target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE;
394 target_cmd.addr = cpu_to_le32(addr2);
395 target_cmd.op1_sz = cpu_to_le32(data2_set);
396 target_cmd.op2 = cpu_to_le32(data2_clr);
397
398 ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1);
399 if (ret < 0) {
400 IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
401 return ret;
402 }
403
404 target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
405 target_cmd.addr = cpu_to_le32(addr3);
406 target_cmd.op1_sz = cpu_to_le32(sizeof(u32));
407 target_cmd.op2 = 0;
408
409 ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data3);
410 if (ret < 0) {
411 IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
412 return ret;
413 }
414 }
415
416 return 0;
417}
418
419void iwm_init_default_profile(struct iwm_priv *iwm,
420 struct iwm_umac_profile *profile)
421{
422 memset(profile, 0, sizeof(struct iwm_umac_profile));
423
424 profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
425 profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
426 profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_NONE;
427 profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_NONE;
428
429 if (iwm->conf.enable_qos)
430 profile->flags |= cpu_to_le16(UMAC_PROFILE_QOS_ALLOWED);
431
432 profile->wireless_mode = iwm->conf.wireless_mode;
433 profile->mode = cpu_to_le32(iwm->conf.mode);
434
435 profile->ibss.atim = 0;
436 profile->ibss.beacon_interval = 100;
437 profile->ibss.join_only = 0;
438 profile->ibss.band = iwm->conf.ibss_band;
439 profile->ibss.channel = iwm->conf.ibss_channel;
440}
441
442void iwm_link_on(struct iwm_priv *iwm)
443{
444 netif_carrier_on(iwm_to_ndev(iwm));
445 netif_tx_wake_all_queues(iwm_to_ndev(iwm));
446
447 iwm_send_umac_stats_req(iwm, 0);
448}
449
450void iwm_link_off(struct iwm_priv *iwm)
451{
452 struct iw_statistics *wstats = &iwm->wstats;
453 int i;
454
455 netif_tx_stop_all_queues(iwm_to_ndev(iwm));
456 netif_carrier_off(iwm_to_ndev(iwm));
457
458 for (i = 0; i < IWM_TX_QUEUES; i++) {
459 skb_queue_purge(&iwm->txq[i].queue);
460
461 iwm->txq[i].concat_count = 0;
462 iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf;
463
464 flush_workqueue(iwm->txq[i].wq);
465 }
466
467 iwm_rx_free(iwm);
468
469 cancel_delayed_work(&iwm->stats_request);
470 memset(wstats, 0, sizeof(struct iw_statistics));
471 wstats->qual.updated = IW_QUAL_ALL_INVALID;
472
473 del_timer_sync(&iwm->watchdog);
474}
475
476static void iwm_bss_list_clean(struct iwm_priv *iwm)
477{
478 struct iwm_bss_info *bss, *next;
479
480 list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
481 list_del(&bss->node);
482 kfree(bss->bss);
483 kfree(bss);
484 }
485}
486
487static int iwm_channels_init(struct iwm_priv *iwm)
488{
489 int ret;
490
491#ifdef CONFIG_IWM_B0_HW_SUPPORT
492 if (iwm->conf.hw_b0) {
493 IWM_INFO(iwm, "Workaround EEPROM channels for B0 hardware\n");
494 return 0;
495 }
496#endif
497
498 ret = iwm_send_umac_channel_list(iwm);
499 if (ret) {
500 IWM_ERR(iwm, "Send channel list failed\n");
501 return ret;
502 }
503
504 ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST,
505 IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
506 if (ret) {
507 IWM_ERR(iwm, "Didn't get a channel list notification\n");
508 return ret;
509 }
510
511 return 0;
512}
513
514int iwm_up(struct iwm_priv *iwm)
515{
516 int ret;
517 struct iwm_notif *notif_reboot, *notif_ack = NULL;
518
519 ret = iwm_bus_enable(iwm);
520 if (ret) {
521 IWM_ERR(iwm, "Couldn't enable function\n");
522 return ret;
523 }
524
525 iwm_rx_setup_handlers(iwm);
526
527 /* Wait for initial BARKER_REBOOT from hardware */
528 notif_reboot = iwm_notif_wait(iwm, IWM_BARKER_REBOOT_NOTIFICATION,
529 IWM_SRC_UDMA, 2 * HZ);
530 if (!notif_reboot) {
531 IWM_ERR(iwm, "Wait for REBOOT_BARKER timeout\n");
532 goto err_disable;
533 }
534
535 /* We send the barker back */
536 ret = iwm_bus_send_chunk(iwm, notif_reboot->buf, 16);
537 if (ret) {
538 IWM_ERR(iwm, "REBOOT barker response failed\n");
539 kfree(notif_reboot);
540 goto err_disable;
541 }
542
543 kfree(notif_reboot->buf);
544 kfree(notif_reboot);
545
546 /* Wait for ACK_BARKER from hardware */
547 notif_ack = iwm_notif_wait(iwm, IWM_ACK_BARKER_NOTIFICATION,
548 IWM_SRC_UDMA, 2 * HZ);
549 if (!notif_ack) {
550 IWM_ERR(iwm, "Wait for ACK_BARKER timeout\n");
551 goto err_disable;
552 }
553
554 kfree(notif_ack->buf);
555 kfree(notif_ack);
556
557 /* We start to config static boot parameters */
558 ret = iwm_config_boot_params(iwm);
559 if (ret) {
560 IWM_ERR(iwm, "Config boot parameters failed\n");
561 goto err_disable;
562 }
563
564 ret = iwm_read_mac(iwm, iwm_to_ndev(iwm)->dev_addr);
565 if (ret) {
566 IWM_ERR(iwm, "MAC reading failed\n");
567 goto err_disable;
568 }
569
570 /* We can load the FWs */
571 ret = iwm_load_fw(iwm);
572 if (ret) {
573 IWM_ERR(iwm, "FW loading failed\n");
574 goto err_disable;
575 }
576
577 /* We configure the UMAC and enable the wifi module */
578 ret = iwm_send_umac_config(iwm,
579 cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) |
580 cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_LINK_EN) |
581 cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_MLME_EN));
582 if (ret) {
583 IWM_ERR(iwm, "UMAC config failed\n");
584 goto err_fw;
585 }
586
587 ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS,
588 IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
589 if (ret) {
590 IWM_ERR(iwm, "Didn't get a wifi core status notification\n");
591 goto err_fw;
592 }
593
594 if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN |
595 UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) {
596 IWM_DBG_BOOT(iwm, DBG, "Not all cores enabled:0x%x\n",
597 iwm->core_enabled);
598 ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS,
599 IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
600 if (ret) {
601 IWM_ERR(iwm, "Didn't get a core status notification\n");
602 goto err_fw;
603 }
604
605 if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN |
606 UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) {
607 IWM_ERR(iwm, "Not all cores enabled: 0x%x\n",
608 iwm->core_enabled);
609 goto err_fw;
610 } else {
611 IWM_INFO(iwm, "All cores enabled\n");
612 }
613 }
614
615 iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile),
616 GFP_KERNEL);
617 if (!iwm->umac_profile) {
618 IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
619 goto err_fw;
620 }
621
622 iwm_init_default_profile(iwm, iwm->umac_profile);
623
624 ret = iwm_channels_init(iwm);
625 if (ret < 0) {
626 IWM_ERR(iwm, "Couldn't init channels\n");
627 goto err_profile;
628 }
629
630 /* Set the READY bit to indicate interface is brought up successfully */
631 set_bit(IWM_STATUS_READY, &iwm->status);
632
633 return 0;
634
635 err_profile:
636 kfree(iwm->umac_profile);
637 iwm->umac_profile = NULL;
638
639 err_fw:
640 iwm_eeprom_exit(iwm);
641
642 err_disable:
643 ret = iwm_bus_disable(iwm);
644 if (ret < 0)
645 IWM_ERR(iwm, "Couldn't disable function\n");
646
647 return -EIO;
648}
649
650int iwm_down(struct iwm_priv *iwm)
651{
652 int ret;
653
654 /* The interface is already down */
655 if (!test_bit(IWM_STATUS_READY, &iwm->status))
656 return 0;
657
658 if (iwm->scan_request) {
659 cfg80211_scan_done(iwm->scan_request, true);
660 iwm->scan_request = NULL;
661 }
662
663 clear_bit(IWM_STATUS_READY, &iwm->status);
664
665 iwm_eeprom_exit(iwm);
666 kfree(iwm->umac_profile);
667 iwm->umac_profile = NULL;
668 iwm_bss_list_clean(iwm);
669
670 iwm->default_key = NULL;
671 iwm->core_enabled = 0;
672
673 ret = iwm_bus_disable(iwm);
674 if (ret < 0) {
675 IWM_ERR(iwm, "Couldn't disable function\n");
676 return ret;
677 }
678
679 return 0;
680}
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
new file mode 100644
index 000000000000..eec7201e91a8
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -0,0 +1,172 @@
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/*
25 * This is the netdev related hooks for iwm.
26 *
27 * Some interesting code paths:
28 *
29 * iwm_open() (Called at netdev interface bringup time)
30 * -> iwm_up() (main.c)
31 * -> iwm_bus_enable()
32 * -> if_sdio_enable() (In case of an SDIO bus)
33 * -> sdio_enable_func()
34 * -> iwm_notif_wait(BARKER_REBOOT) (wait for reboot barker)
35 * -> iwm_notif_wait(ACK_BARKER) (wait for ACK barker)
36 * -> iwm_load_fw() (fw.c)
37 * -> iwm_load_umac()
38 * -> iwm_load_lmac() (Calibration LMAC)
39 * -> iwm_load_lmac() (Operational LMAC)
40 * -> iwm_send_umac_config()
41 *
42 * iwm_stop() (Called at netdev interface bringdown time)
43 * -> iwm_down()
44 * -> iwm_bus_disable()
45 * -> if_sdio_disable() (In case of an SDIO bus)
46 * -> sdio_disable_func()
47 */
48#include <linux/netdevice.h>
49
50#include "iwm.h"
51#include "cfg80211.h"
52#include "debug.h"
53
54static int iwm_open(struct net_device *ndev)
55{
56 struct iwm_priv *iwm = ndev_to_iwm(ndev);
57 int ret = 0;
58
59 if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
60 ret = iwm_up(iwm);
61
62 return ret;
63}
64
65static int iwm_stop(struct net_device *ndev)
66{
67 struct iwm_priv *iwm = ndev_to_iwm(ndev);
68 int ret = 0;
69
70 if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
71 ret = iwm_down(iwm);
72
73 return ret;
74}
75
76/*
77 * iwm AC to queue mapping
78 *
79 * AC_VO -> queue 3
80 * AC_VI -> queue 2
81 * AC_BE -> queue 1
82 * AC_BK -> queue 0
83 */
84static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
85
86static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb)
87{
88 skb->priority = cfg80211_classify8021d(skb);
89
90 return iwm_1d_to_queue[skb->priority];
91}
92
93static const struct net_device_ops iwm_netdev_ops = {
94 .ndo_open = iwm_open,
95 .ndo_stop = iwm_stop,
96 .ndo_start_xmit = iwm_xmit_frame,
97 .ndo_select_queue = iwm_select_queue,
98};
99
100void *iwm_if_alloc(int sizeof_bus, struct device *dev,
101 struct iwm_if_ops *if_ops)
102{
103 struct net_device *ndev;
104 struct wireless_dev *wdev;
105 struct iwm_priv *iwm;
106 int ret = 0;
107
108 wdev = iwm_wdev_alloc(sizeof_bus, dev);
109 if (!wdev) {
110 dev_err(dev, "no memory for wireless device instance\n");
111 return ERR_PTR(-ENOMEM);
112 }
113
114 iwm = wdev_to_iwm(wdev);
115 iwm->bus_ops = if_ops;
116 iwm->wdev = wdev;
117 iwm_priv_init(iwm);
118 wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode);
119
120 ndev = alloc_netdev_mq(0, "wlan%d", ether_setup,
121 IWM_TX_QUEUES);
122 if (!ndev) {
123 dev_err(dev, "no memory for network device instance\n");
124 goto out_wdev;
125 }
126
127 ndev->netdev_ops = &iwm_netdev_ops;
128 ndev->wireless_handlers = &iwm_iw_handler_def;
129 ndev->ieee80211_ptr = wdev;
130 SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
131 ret = register_netdev(ndev);
132 if (ret < 0) {
133 dev_err(dev, "Failed to register netdev: %d\n", ret);
134 goto out_ndev;
135 }
136
137 wdev->netdev = ndev;
138
139 ret = iwm_rfkill_init(iwm);
140 if (ret) {
141 dev_err(dev, "Failed to init rfkill\n");
142 goto out_rfkill;
143 }
144
145 return iwm;
146
147 out_rfkill:
148 unregister_netdev(ndev);
149
150 out_ndev:
151 free_netdev(ndev);
152
153 out_wdev:
154 iwm_wdev_free(iwm);
155 return ERR_PTR(ret);
156}
157
158void iwm_if_free(struct iwm_priv *iwm)
159{
160 int i;
161
162 if (!iwm_to_ndev(iwm))
163 return;
164
165 iwm_rfkill_exit(iwm);
166 unregister_netdev(iwm_to_ndev(iwm));
167 free_netdev(iwm_to_ndev(iwm));
168 iwm_wdev_free(iwm);
169 destroy_workqueue(iwm->rx_wq);
170 for (i = 0; i < IWM_TX_QUEUES; i++)
171 destroy_workqueue(iwm->txq[i].wq);
172}
diff --git a/drivers/net/wireless/iwmc3200wifi/rfkill.c b/drivers/net/wireless/iwmc3200wifi/rfkill.c
new file mode 100644
index 000000000000..4ca8b495f82d
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/rfkill.c
@@ -0,0 +1,88 @@
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/rfkill.h>
25
26#include "iwm.h"
27
28static int iwm_rfkill_soft_toggle(void *data, enum rfkill_state state)
29{
30 struct iwm_priv *iwm = data;
31
32 switch (state) {
33 case RFKILL_STATE_UNBLOCKED:
34 if (test_bit(IWM_RADIO_RFKILL_HW, &iwm->radio))
35 return -EBUSY;
36
37 if (test_and_clear_bit(IWM_RADIO_RFKILL_SW, &iwm->radio) &&
38 (iwm_to_ndev(iwm)->flags & IFF_UP))
39 iwm_up(iwm);
40
41 break;
42 case RFKILL_STATE_SOFT_BLOCKED:
43 if (!test_and_set_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
44 iwm_down(iwm);
45
46 break;
47 default:
48 break;
49 }
50
51 return 0;
52}
53
54int iwm_rfkill_init(struct iwm_priv *iwm)
55{
56 int ret;
57
58 iwm->rfkill = rfkill_allocate(iwm_to_dev(iwm), RFKILL_TYPE_WLAN);
59 if (!iwm->rfkill) {
60 IWM_ERR(iwm, "Unable to allocate rfkill device\n");
61 return -ENOMEM;
62 }
63
64 iwm->rfkill->name = KBUILD_MODNAME;
65 iwm->rfkill->data = iwm;
66 iwm->rfkill->state = RFKILL_STATE_UNBLOCKED;
67 iwm->rfkill->toggle_radio = iwm_rfkill_soft_toggle;
68
69 ret = rfkill_register(iwm->rfkill);
70 if (ret) {
71 IWM_ERR(iwm, "Failed to register rfkill device\n");
72 goto fail;
73 }
74
75 return 0;
76 fail:
77 rfkill_free(iwm->rfkill);
78 return ret;
79}
80
81void iwm_rfkill_exit(struct iwm_priv *iwm)
82{
83 if (iwm->rfkill)
84 rfkill_unregister(iwm->rfkill);
85
86 rfkill_free(iwm->rfkill);
87 iwm->rfkill = NULL;
88}
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
new file mode 100644
index 000000000000..d73cf96c6dc6
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -0,0 +1,1431 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#include <linux/kernel.h>
40#include <linux/netdevice.h>
41#include <linux/etherdevice.h>
42#include <linux/wireless.h>
43#include <linux/ieee80211.h>
44#include <linux/if_arp.h>
45#include <linux/list.h>
46#include <net/iw_handler.h>
47
48#include "iwm.h"
49#include "debug.h"
50#include "hal.h"
51#include "umac.h"
52#include "lmac.h"
53#include "commands.h"
54#include "rx.h"
55#include "cfg80211.h"
56#include "eeprom.h"
57
58static int iwm_rx_check_udma_hdr(struct iwm_udma_in_hdr *hdr)
59{
60 if ((le32_to_cpu(hdr->cmd) == UMAC_PAD_TERMINAL) ||
61 (le32_to_cpu(hdr->size) == UMAC_PAD_TERMINAL))
62 return -EINVAL;
63
64 return 0;
65}
66
67static inline int iwm_rx_resp_size(struct iwm_udma_in_hdr *hdr)
68{
69 return ALIGN(le32_to_cpu(hdr->size) + sizeof(struct iwm_udma_in_hdr),
70 16);
71}
72
73/*
74 * Notification handlers:
75 *
76 * For every possible notification we can receive from the
77 * target, we have a handler.
78 * When we get a target notification, and there is no one
79 * waiting for it, it's just processed through the rx code
80 * path:
81 *
82 * iwm_rx_handle()
83 * -> iwm_rx_handle_umac()
84 * -> iwm_rx_handle_wifi()
85 * -> iwm_rx_handle_resp()
86 * -> iwm_ntf_*()
87 *
88 * OR
89 *
90 * -> iwm_rx_handle_non_wifi()
91 *
92 * If there are processes waiting for this notification, then
93 * iwm_rx_handle_wifi() just wakes those processes up and they
94 * grab the pending notification.
95 */
96static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf,
97 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
98{
99 struct iwm_umac_notif_error *error;
100 struct iwm_fw_error_hdr *fw_err;
101
102 error = (struct iwm_umac_notif_error *)buf;
103 fw_err = &error->err;
104
105
106 IWM_ERR(iwm, "%cMAC FW ERROR:\n",
107 (le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U');
108 IWM_ERR(iwm, "\tCategory: %d\n", le32_to_cpu(fw_err->category));
109 IWM_ERR(iwm, "\tStatus: 0x%x\n", le32_to_cpu(fw_err->status));
110 IWM_ERR(iwm, "\tPC: 0x%x\n", le32_to_cpu(fw_err->pc));
111 IWM_ERR(iwm, "\tblink1: %d\n", le32_to_cpu(fw_err->blink1));
112 IWM_ERR(iwm, "\tblink2: %d\n", le32_to_cpu(fw_err->blink2));
113 IWM_ERR(iwm, "\tilink1: %d\n", le32_to_cpu(fw_err->ilink1));
114 IWM_ERR(iwm, "\tilink2: %d\n", le32_to_cpu(fw_err->ilink2));
115 IWM_ERR(iwm, "\tData1: 0x%x\n", le32_to_cpu(fw_err->data1));
116 IWM_ERR(iwm, "\tData2: 0x%x\n", le32_to_cpu(fw_err->data2));
117 IWM_ERR(iwm, "\tLine number: %d\n", le32_to_cpu(fw_err->line_num));
118 IWM_ERR(iwm, "\tUMAC status: 0x%x\n", le32_to_cpu(fw_err->umac_status));
119 IWM_ERR(iwm, "\tLMAC status: 0x%x\n", le32_to_cpu(fw_err->lmac_status));
120 IWM_ERR(iwm, "\tSDIO status: 0x%x\n", le32_to_cpu(fw_err->sdio_status));
121
122 return 0;
123}
124
125static int iwm_ntf_umac_alive(struct iwm_priv *iwm, u8 *buf,
126 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
127{
128 struct iwm_umac_notif_alive *alive_resp =
129 (struct iwm_umac_notif_alive *)(buf);
130 u16 status = le16_to_cpu(alive_resp->status);
131
132 if (status == UMAC_NTFY_ALIVE_STATUS_ERR) {
133 IWM_ERR(iwm, "Receive error UMAC_ALIVE\n");
134 return -EIO;
135 }
136
137 iwm_tx_credit_init_pools(iwm, alive_resp);
138
139 return 0;
140}
141
142static int iwm_ntf_init_complete(struct iwm_priv *iwm, u8 *buf,
143 unsigned long buf_size,
144 struct iwm_wifi_cmd *cmd)
145{
146 struct iwm_umac_notif_init_complete *init_complete =
147 (struct iwm_umac_notif_init_complete *)(buf);
148 u16 status = le16_to_cpu(init_complete->status);
149
150 if (status == UMAC_NTFY_INIT_COMPLETE_STATUS_ERR) {
151 IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is on (radio off)\n");
152 set_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
153 } else {
154 IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is off (radio on)\n");
155 clear_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
156 }
157
158 return 0;
159}
160
161static int iwm_ntf_tx_credit_update(struct iwm_priv *iwm, u8 *buf,
162 unsigned long buf_size,
163 struct iwm_wifi_cmd *cmd)
164{
165 int pool_nr, total_freed_pages;
166 unsigned long pool_map;
167 int i, id;
168 struct iwm_umac_notif_page_dealloc *dealloc =
169 (struct iwm_umac_notif_page_dealloc *)buf;
170
171 pool_nr = GET_VAL32(dealloc->changes, UMAC_DEALLOC_NTFY_CHANGES_CNT);
172 pool_map = GET_VAL32(dealloc->changes, UMAC_DEALLOC_NTFY_CHANGES_MSK);
173
174 IWM_DBG_TX(iwm, DBG, "UMAC dealloc notification: pool nr %d, "
175 "update map 0x%lx\n", pool_nr, pool_map);
176
177 spin_lock(&iwm->tx_credit.lock);
178
179 for (i = 0; i < pool_nr; i++) {
180 id = GET_VAL32(dealloc->grp_info[i],
181 UMAC_DEALLOC_NTFY_GROUP_NUM);
182 if (test_bit(id, &pool_map)) {
183 total_freed_pages = GET_VAL32(dealloc->grp_info[i],
184 UMAC_DEALLOC_NTFY_PAGE_CNT);
185 iwm_tx_credit_inc(iwm, id, total_freed_pages);
186 }
187 }
188
189 spin_unlock(&iwm->tx_credit.lock);
190
191 return 0;
192}
193
194static int iwm_ntf_umac_reset(struct iwm_priv *iwm, u8 *buf,
195 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
196{
197 IWM_DBG_NTF(iwm, DBG, "UMAC RESET done\n");
198
199 return 0;
200}
201
202static int iwm_ntf_lmac_version(struct iwm_priv *iwm, u8 *buf,
203 unsigned long buf_size,
204 struct iwm_wifi_cmd *cmd)
205{
206 IWM_DBG_NTF(iwm, INFO, "LMAC Version: %x.%x\n", buf[9], buf[8]);
207
208 return 0;
209}
210
211static int iwm_ntf_tx(struct iwm_priv *iwm, u8 *buf,
212 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
213{
214 struct iwm_lmac_tx_resp *tx_resp;
215 struct iwm_umac_wifi_in_hdr *hdr;
216
217 tx_resp = (struct iwm_lmac_tx_resp *)
218 (buf + sizeof(struct iwm_umac_wifi_in_hdr));
219 hdr = (struct iwm_umac_wifi_in_hdr *)buf;
220
221 IWM_DBG_NTF(iwm, DBG, "REPLY_TX, buf size: %lu\n", buf_size);
222
223 IWM_DBG_NTF(iwm, DBG, "Seqnum: %d\n",
224 le16_to_cpu(hdr->sw_hdr.cmd.seq_num));
225 IWM_DBG_NTF(iwm, DBG, "\tFrame cnt: %d\n", tx_resp->frame_cnt);
226 IWM_DBG_NTF(iwm, DBG, "\tRetry cnt: %d\n",
227 le16_to_cpu(tx_resp->retry_cnt));
228 IWM_DBG_NTF(iwm, DBG, "\tSeq ctl: %d\n", le16_to_cpu(tx_resp->seq_ctl));
229 IWM_DBG_NTF(iwm, DBG, "\tByte cnt: %d\n",
230 le16_to_cpu(tx_resp->byte_cnt));
231 IWM_DBG_NTF(iwm, DBG, "\tStatus: 0x%x\n", le32_to_cpu(tx_resp->status));
232
233 return 0;
234}
235
236
237static int iwm_ntf_calib_res(struct iwm_priv *iwm, u8 *buf,
238 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
239{
240 u8 opcode;
241 u8 *calib_buf;
242 struct iwm_lmac_calib_hdr *hdr = (struct iwm_lmac_calib_hdr *)
243 (buf + sizeof(struct iwm_umac_wifi_in_hdr));
244
245 opcode = hdr->opcode;
246
247 BUG_ON(opcode >= CALIBRATION_CMD_NUM ||
248 opcode < PHY_CALIBRATE_OPCODES_NUM);
249
250 IWM_DBG_NTF(iwm, DBG, "Store calibration result for opcode: %d\n",
251 opcode);
252
253 buf_size -= sizeof(struct iwm_umac_wifi_in_hdr);
254 calib_buf = iwm->calib_res[opcode].buf;
255
256 if (!calib_buf || (iwm->calib_res[opcode].size < buf_size)) {
257 kfree(calib_buf);
258 calib_buf = kzalloc(buf_size, GFP_KERNEL);
259 if (!calib_buf) {
260 IWM_ERR(iwm, "Memory allocation failed: calib_res\n");
261 return -ENOMEM;
262 }
263 iwm->calib_res[opcode].buf = calib_buf;
264 iwm->calib_res[opcode].size = buf_size;
265 }
266
267 memcpy(calib_buf, hdr, buf_size);
268 set_bit(opcode - PHY_CALIBRATE_OPCODES_NUM, &iwm->calib_done_map);
269
270 return 0;
271}
272
273static int iwm_ntf_calib_complete(struct iwm_priv *iwm, u8 *buf,
274 unsigned long buf_size,
275 struct iwm_wifi_cmd *cmd)
276{
277 IWM_DBG_NTF(iwm, DBG, "Calibration completed\n");
278
279 return 0;
280}
281
282static int iwm_ntf_calib_cfg(struct iwm_priv *iwm, u8 *buf,
283 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
284{
285 struct iwm_lmac_cal_cfg_resp *cal_resp;
286
287 cal_resp = (struct iwm_lmac_cal_cfg_resp *)
288 (buf + sizeof(struct iwm_umac_wifi_in_hdr));
289
290 IWM_DBG_NTF(iwm, DBG, "Calibration CFG command status: %d\n",
291 le32_to_cpu(cal_resp->status));
292
293 return 0;
294}
295
296static int iwm_ntf_wifi_status(struct iwm_priv *iwm, u8 *buf,
297 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
298{
299 struct iwm_umac_notif_wifi_status *status =
300 (struct iwm_umac_notif_wifi_status *)buf;
301
302 iwm->core_enabled |= le16_to_cpu(status->status);
303
304 return 0;
305}
306
307static struct iwm_rx_ticket_node *
308iwm_rx_ticket_node_alloc(struct iwm_priv *iwm, struct iwm_rx_ticket *ticket)
309{
310 struct iwm_rx_ticket_node *ticket_node;
311
312 ticket_node = kzalloc(sizeof(struct iwm_rx_ticket_node), GFP_KERNEL);
313 if (!ticket_node) {
314 IWM_ERR(iwm, "Couldn't allocate ticket node\n");
315 return ERR_PTR(-ENOMEM);
316 }
317
318 ticket_node->ticket = kzalloc(sizeof(struct iwm_rx_ticket), GFP_KERNEL);
319 if (!ticket_node->ticket) {
320 IWM_ERR(iwm, "Couldn't allocate RX ticket\n");
321 kfree(ticket_node);
322 return ERR_PTR(-ENOMEM);
323 }
324
325 memcpy(ticket_node->ticket, ticket, sizeof(struct iwm_rx_ticket));
326 INIT_LIST_HEAD(&ticket_node->node);
327
328 return ticket_node;
329}
330
331static void iwm_rx_ticket_node_free(struct iwm_rx_ticket_node *ticket_node)
332{
333 kfree(ticket_node->ticket);
334 kfree(ticket_node);
335}
336
337static struct iwm_rx_packet *iwm_rx_packet_get(struct iwm_priv *iwm, u16 id)
338{
339 u8 id_hash = IWM_RX_ID_GET_HASH(id);
340 struct list_head *packet_list;
341 struct iwm_rx_packet *packet, *next;
342
343 packet_list = &iwm->rx_packets[id_hash];
344
345 list_for_each_entry_safe(packet, next, packet_list, node)
346 if (packet->id == id)
347 return packet;
348
349 return NULL;
350}
351
352static struct iwm_rx_packet *iwm_rx_packet_alloc(struct iwm_priv *iwm, u8 *buf,
353 u32 size, u16 id)
354{
355 struct iwm_rx_packet *packet;
356
357 packet = kzalloc(sizeof(struct iwm_rx_packet), GFP_KERNEL);
358 if (!packet) {
359 IWM_ERR(iwm, "Couldn't allocate packet\n");
360 return ERR_PTR(-ENOMEM);
361 }
362
363 packet->skb = dev_alloc_skb(size);
364 if (!packet->skb) {
365 IWM_ERR(iwm, "Couldn't allocate packet SKB\n");
366 kfree(packet);
367 return ERR_PTR(-ENOMEM);
368 }
369
370 packet->pkt_size = size;
371
372 skb_put(packet->skb, size);
373 memcpy(packet->skb->data, buf, size);
374 INIT_LIST_HEAD(&packet->node);
375 packet->id = id;
376
377 return packet;
378}
379
380void iwm_rx_free(struct iwm_priv *iwm)
381{
382 struct iwm_rx_ticket_node *ticket, *nt;
383 struct iwm_rx_packet *packet, *np;
384 int i;
385
386 list_for_each_entry_safe(ticket, nt, &iwm->rx_tickets, node) {
387 list_del(&ticket->node);
388 iwm_rx_ticket_node_free(ticket);
389 }
390
391 for (i = 0; i < IWM_RX_ID_HASH; i++) {
392 list_for_each_entry_safe(packet, np, &iwm->rx_packets[i],
393 node) {
394 list_del(&packet->node);
395 kfree_skb(packet->skb);
396 kfree(packet);
397 }
398 }
399}
400
401static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf,
402 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
403{
404 struct iwm_umac_notif_rx_ticket *ntf_rx_ticket =
405 (struct iwm_umac_notif_rx_ticket *)buf;
406 struct iwm_rx_ticket *ticket =
407 (struct iwm_rx_ticket *)ntf_rx_ticket->tickets;
408 int i, schedule_rx = 0;
409
410 for (i = 0; i < ntf_rx_ticket->num_tickets; i++) {
411 struct iwm_rx_ticket_node *ticket_node;
412
413 switch (le16_to_cpu(ticket->action)) {
414 case IWM_RX_TICKET_RELEASE:
415 case IWM_RX_TICKET_DROP:
416 /* We can push the packet to the stack */
417 ticket_node = iwm_rx_ticket_node_alloc(iwm, ticket);
418 if (IS_ERR(ticket_node))
419 return PTR_ERR(ticket_node);
420
421 IWM_DBG_NTF(iwm, DBG, "TICKET RELEASE(%d)\n",
422 ticket->id);
423 list_add_tail(&ticket_node->node, &iwm->rx_tickets);
424
425 /*
426 * We received an Rx ticket, most likely there's
427 * a packet pending for it, it's not worth going
428 * through the packet hash list to double check.
429 * Let's just fire the rx worker..
430 */
431 schedule_rx = 1;
432
433 break;
434
435 default:
436 IWM_ERR(iwm, "Invalid RX ticket action: 0x%x\n",
437 ticket->action);
438 }
439
440 ticket++;
441 }
442
443 if (schedule_rx)
444 queue_work(iwm->rx_wq, &iwm->rx_worker);
445
446 return 0;
447}
448
449static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf,
450 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
451{
452 struct iwm_umac_wifi_in_hdr *wifi_hdr;
453 struct iwm_rx_packet *packet;
454 u16 id, buf_offset;
455 u32 packet_size;
456
457 IWM_DBG_NTF(iwm, DBG, "\n");
458
459 wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
460 id = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);
461 buf_offset = sizeof(struct iwm_umac_wifi_in_hdr);
462 packet_size = buf_size - sizeof(struct iwm_umac_wifi_in_hdr);
463
464 IWM_DBG_NTF(iwm, DBG, "CMD:0x%x, seqnum: %d, packet size: %d\n",
465 wifi_hdr->sw_hdr.cmd.cmd, id, packet_size);
466 IWM_DBG_RX(iwm, DBG, "Packet id: %d\n", id);
467 IWM_HEXDUMP(iwm, DBG, RX, "PACKET: ", buf + buf_offset, packet_size);
468
469 packet = iwm_rx_packet_alloc(iwm, buf + buf_offset, packet_size, id);
470 if (IS_ERR(packet))
471 return PTR_ERR(packet);
472
473 list_add_tail(&packet->node, &iwm->rx_packets[IWM_RX_ID_GET_HASH(id)]);
474
475 /* We might (unlikely) have received the packet _after_ the ticket */
476 queue_work(iwm->rx_wq, &iwm->rx_worker);
477
478 return 0;
479}
480
481/* MLME handlers */
482static int iwm_mlme_assoc_start(struct iwm_priv *iwm, u8 *buf,
483 unsigned long buf_size,
484 struct iwm_wifi_cmd *cmd)
485{
486 struct iwm_umac_notif_assoc_start *start;
487
488 start = (struct iwm_umac_notif_assoc_start *)buf;
489
490 set_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
491
492 IWM_DBG_MLME(iwm, INFO, "Association with %pM Started, reason: %d\n",
493 start->bssid, le32_to_cpu(start->roam_reason));
494
495 wake_up_interruptible(&iwm->mlme_queue);
496
497 return 0;
498}
499
500static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
501 unsigned long buf_size,
502 struct iwm_wifi_cmd *cmd)
503{
504 struct iwm_umac_notif_assoc_complete *complete =
505 (struct iwm_umac_notif_assoc_complete *)buf;
506 union iwreq_data wrqu;
507
508 IWM_DBG_MLME(iwm, INFO, "Association with %pM completed, status: %d\n",
509 complete->bssid, complete->status);
510
511 memset(&wrqu, 0, sizeof(wrqu));
512
513 clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
514
515 switch (le32_to_cpu(complete->status)) {
516 case UMAC_ASSOC_COMPLETE_SUCCESS:
517 set_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
518 memcpy(iwm->bssid, complete->bssid, ETH_ALEN);
519 iwm->channel = complete->channel;
520
521 iwm_link_on(iwm);
522
523 memcpy(wrqu.ap_addr.sa_data, complete->bssid, ETH_ALEN);
524 break;
525 case UMAC_ASSOC_COMPLETE_FAILURE:
526 clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
527 memset(iwm->bssid, 0, ETH_ALEN);
528 iwm->channel = 0;
529
530 iwm_link_off(iwm);
531 default:
532 break;
533 }
534
535 if (iwm->conf.mode == UMAC_MODE_IBSS) {
536 cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL);
537 return 0;
538 }
539
540 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
541 wireless_send_event(iwm_to_ndev(iwm), SIOCGIWAP, &wrqu, NULL);
542
543 return 0;
544}
545
546static int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf,
547 unsigned long buf_size,
548 struct iwm_wifi_cmd *cmd)
549{
550 struct iwm_umac_notif_profile_invalidate *invalid;
551
552 invalid = (struct iwm_umac_notif_profile_invalidate *)buf;
553
554 IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n",
555 le32_to_cpu(invalid->reason));
556
557 clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status);
558 clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status);
559
560 iwm->umac_profile_active = 0;
561 memset(iwm->bssid, 0, ETH_ALEN);
562 iwm->channel = 0;
563
564 iwm_link_off(iwm);
565
566 wake_up_interruptible(&iwm->mlme_queue);
567
568 return 0;
569}
570
571static int iwm_mlme_scan_complete(struct iwm_priv *iwm, u8 *buf,
572 unsigned long buf_size,
573 struct iwm_wifi_cmd *cmd)
574{
575 int ret;
576 struct iwm_umac_notif_scan_complete *scan_complete =
577 (struct iwm_umac_notif_scan_complete *)buf;
578 u32 result = le32_to_cpu(scan_complete->result);
579
580 IWM_DBG_MLME(iwm, INFO, "type:0x%x result:0x%x seq:%d\n",
581 le32_to_cpu(scan_complete->type),
582 le32_to_cpu(scan_complete->result),
583 scan_complete->seq_num);
584
585 if (!test_and_clear_bit(IWM_STATUS_SCANNING, &iwm->status)) {
586 IWM_ERR(iwm, "Scan complete while device not scanning\n");
587 return -EIO;
588 }
589 if (!iwm->scan_request)
590 return 0;
591
592 ret = iwm_cfg80211_inform_bss(iwm);
593
594 cfg80211_scan_done(iwm->scan_request,
595 (result & UMAC_SCAN_RESULT_ABORTED) ? 1 : !!ret);
596 iwm->scan_request = NULL;
597
598 return ret;
599}
600
601static int iwm_mlme_update_sta_table(struct iwm_priv *iwm, u8 *buf,
602 unsigned long buf_size,
603 struct iwm_wifi_cmd *cmd)
604{
605 struct iwm_umac_notif_sta_info *umac_sta =
606 (struct iwm_umac_notif_sta_info *)buf;
607 struct iwm_sta_info *sta;
608 int i;
609
610 switch (le32_to_cpu(umac_sta->opcode)) {
611 case UMAC_OPCODE_ADD_MODIFY:
612 sta = &iwm->sta_table[GET_VAL8(umac_sta->sta_id, LMAC_STA_ID)];
613
614 IWM_DBG_MLME(iwm, INFO, "%s STA: ID = %d, Color = %d, "
615 "addr = %pM, qos = %d\n",
616 sta->valid ? "Modify" : "Add",
617 GET_VAL8(umac_sta->sta_id, LMAC_STA_ID),
618 GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR),
619 umac_sta->mac_addr,
620 umac_sta->flags & UMAC_STA_FLAG_QOS);
621
622 sta->valid = 1;
623 sta->qos = umac_sta->flags & UMAC_STA_FLAG_QOS;
624 sta->color = GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR);
625 memcpy(sta->addr, umac_sta->mac_addr, ETH_ALEN);
626 break;
627 case UMAC_OPCODE_REMOVE:
628 IWM_DBG_MLME(iwm, INFO, "Remove STA: ID = %d, Color = %d, "
629 "addr = %pM\n",
630 GET_VAL8(umac_sta->sta_id, LMAC_STA_ID),
631 GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR),
632 umac_sta->mac_addr);
633
634 sta = &iwm->sta_table[GET_VAL8(umac_sta->sta_id, LMAC_STA_ID)];
635
636 if (!memcmp(sta->addr, umac_sta->mac_addr, ETH_ALEN))
637 sta->valid = 0;
638
639 break;
640 case UMAC_OPCODE_CLEAR_ALL:
641 for (i = 0; i < IWM_STA_TABLE_NUM; i++)
642 iwm->sta_table[i].valid = 0;
643
644 break;
645 default:
646 break;
647 }
648
649 return 0;
650}
651
652static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,
653 unsigned long buf_size,
654 struct iwm_wifi_cmd *cmd)
655{
656 struct wiphy *wiphy = iwm_to_wiphy(iwm);
657 struct ieee80211_mgmt *mgmt;
658 struct iwm_umac_notif_bss_info *umac_bss =
659 (struct iwm_umac_notif_bss_info *)buf;
660 struct ieee80211_channel *channel;
661 struct ieee80211_supported_band *band;
662 struct iwm_bss_info *bss, *next;
663 s32 signal;
664 int freq;
665 u16 frame_len = le16_to_cpu(umac_bss->frame_len);
666 size_t bss_len = sizeof(struct iwm_umac_notif_bss_info) + frame_len;
667
668 mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
669
670 IWM_DBG_MLME(iwm, DBG, "New BSS info entry: %pM\n", mgmt->bssid);
671 IWM_DBG_MLME(iwm, DBG, "\tType: 0x%x\n", le32_to_cpu(umac_bss->type));
672 IWM_DBG_MLME(iwm, DBG, "\tTimestamp: %d\n",
673 le32_to_cpu(umac_bss->timestamp));
674 IWM_DBG_MLME(iwm, DBG, "\tTable Index: %d\n",
675 le16_to_cpu(umac_bss->table_idx));
676 IWM_DBG_MLME(iwm, DBG, "\tBand: %d\n", umac_bss->band);
677 IWM_DBG_MLME(iwm, DBG, "\tChannel: %d\n", umac_bss->channel);
678 IWM_DBG_MLME(iwm, DBG, "\tRSSI: %d\n", umac_bss->rssi);
679 IWM_DBG_MLME(iwm, DBG, "\tFrame Length: %d\n", frame_len);
680
681 list_for_each_entry_safe(bss, next, &iwm->bss_list, node)
682 if (bss->bss->table_idx == umac_bss->table_idx)
683 break;
684
685 if (&bss->node != &iwm->bss_list) {
686 /* Remove the old BSS entry, we will add it back later. */
687 list_del(&bss->node);
688 kfree(bss->bss);
689 } else {
690 /* New BSS entry */
691
692 bss = kzalloc(sizeof(struct iwm_bss_info), GFP_KERNEL);
693 if (!bss) {
694 IWM_ERR(iwm, "Couldn't allocate bss_info\n");
695 return -ENOMEM;
696 }
697 }
698
699 bss->bss = kzalloc(bss_len, GFP_KERNEL);
700 if (!bss) {
701 kfree(bss);
702 IWM_ERR(iwm, "Couldn't allocate bss\n");
703 return -ENOMEM;
704 }
705
706 INIT_LIST_HEAD(&bss->node);
707 memcpy(bss->bss, umac_bss, bss_len);
708
709 if (umac_bss->band == UMAC_BAND_2GHZ)
710 band = wiphy->bands[IEEE80211_BAND_2GHZ];
711 else if (umac_bss->band == UMAC_BAND_5GHZ)
712 band = wiphy->bands[IEEE80211_BAND_5GHZ];
713 else {
714 IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
715 goto err;
716 }
717
718 freq = ieee80211_channel_to_frequency(umac_bss->channel);
719 channel = ieee80211_get_channel(wiphy, freq);
720 signal = umac_bss->rssi * 100;
721
722 bss->cfg_bss = cfg80211_inform_bss_frame(wiphy, channel,
723 mgmt, frame_len,
724 signal, GFP_KERNEL);
725 if (!bss->cfg_bss)
726 goto err;
727
728 list_add_tail(&bss->node, &iwm->bss_list);
729
730 return 0;
731 err:
732 kfree(bss->bss);
733 kfree(bss);
734
735 return -EINVAL;
736}
737
738static int iwm_mlme_remove_bss(struct iwm_priv *iwm, u8 *buf,
739 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
740{
741 struct iwm_umac_notif_bss_removed *bss_rm =
742 (struct iwm_umac_notif_bss_removed *)buf;
743 struct iwm_bss_info *bss, *next;
744 u16 table_idx;
745 int i;
746
747 for (i = 0; i < le32_to_cpu(bss_rm->count); i++) {
748 table_idx = (le16_to_cpu(bss_rm->entries[i])
749 & IWM_BSS_REMOVE_INDEX_MSK);
750 list_for_each_entry_safe(bss, next, &iwm->bss_list, node)
751 if (bss->bss->table_idx == cpu_to_le16(table_idx)) {
752 struct ieee80211_mgmt *mgmt;
753
754 mgmt = (struct ieee80211_mgmt *)
755 (bss->bss->frame_buf);
756 IWM_DBG_MLME(iwm, ERR,
757 "BSS removed: %pM\n",
758 mgmt->bssid);
759 list_del(&bss->node);
760 kfree(bss->bss);
761 kfree(bss);
762 }
763 }
764
765 return 0;
766}
767
768static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf,
769 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
770{
771 struct iwm_umac_notif_mgt_frame *mgt_frame =
772 (struct iwm_umac_notif_mgt_frame *)buf;
773 struct ieee80211_mgmt *mgt = (struct ieee80211_mgmt *)mgt_frame->frame;
774 u8 *ie;
775 unsigned int event;
776 union iwreq_data wrqu;
777
778 IWM_HEXDUMP(iwm, DBG, MLME, "MGT: ", mgt_frame->frame,
779 le16_to_cpu(mgt_frame->len));
780
781 if (ieee80211_is_assoc_req(mgt->frame_control)) {
782 ie = mgt->u.assoc_req.variable;;
783 event = IWEVASSOCREQIE;
784 } else if (ieee80211_is_reassoc_req(mgt->frame_control)) {
785 ie = mgt->u.reassoc_req.variable;;
786 event = IWEVASSOCREQIE;
787 } else if (ieee80211_is_assoc_resp(mgt->frame_control)) {
788 ie = mgt->u.assoc_resp.variable;;
789 event = IWEVASSOCRESPIE;
790 } else if (ieee80211_is_reassoc_resp(mgt->frame_control)) {
791 ie = mgt->u.reassoc_resp.variable;;
792 event = IWEVASSOCRESPIE;
793 } else {
794 IWM_ERR(iwm, "Unsupported management frame");
795 return 0;
796 }
797
798 wrqu.data.length = le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
799
800 IWM_HEXDUMP(iwm, DBG, MLME, "EVT: ", ie, wrqu.data.length);
801 wireless_send_event(iwm_to_ndev(iwm), event, &wrqu, ie);
802
803 return 0;
804}
805
806static int iwm_ntf_mlme(struct iwm_priv *iwm, u8 *buf,
807 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
808{
809 struct iwm_umac_notif_wifi_if *notif =
810 (struct iwm_umac_notif_wifi_if *)buf;
811
812 switch (notif->status) {
813 case WIFI_IF_NTFY_ASSOC_START:
814 return iwm_mlme_assoc_start(iwm, buf, buf_size, cmd);
815 case WIFI_IF_NTFY_ASSOC_COMPLETE:
816 return iwm_mlme_assoc_complete(iwm, buf, buf_size, cmd);
817 case WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE:
818 return iwm_mlme_profile_invalidate(iwm, buf, buf_size, cmd);
819 case WIFI_IF_NTFY_CONNECTION_TERMINATED:
820 IWM_DBG_MLME(iwm, DBG, "Connection terminated\n");
821 break;
822 case WIFI_IF_NTFY_SCAN_COMPLETE:
823 return iwm_mlme_scan_complete(iwm, buf, buf_size, cmd);
824 case WIFI_IF_NTFY_STA_TABLE_CHANGE:
825 return iwm_mlme_update_sta_table(iwm, buf, buf_size, cmd);
826 case WIFI_IF_NTFY_EXTENDED_IE_REQUIRED:
827 IWM_DBG_MLME(iwm, DBG, "Extended IE required\n");
828 break;
829 case WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED:
830 return iwm_mlme_update_bss_table(iwm, buf, buf_size, cmd);
831 case WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED:
832 return iwm_mlme_remove_bss(iwm, buf, buf_size, cmd);
833 break;
834 case WIFI_IF_NTFY_MGMT_FRAME:
835 return iwm_mlme_mgt_frame(iwm, buf, buf_size, cmd);
836 case WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START:
837 case WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE:
838 case WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START:
839 case WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT:
840 case WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START:
841 case WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE:
842 case WIFI_DBG_IF_NTFY_CNCT_ATC_START:
843 case WIFI_DBG_IF_NTFY_COEX_NOTIFICATION:
844 case WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP:
845 case WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP:
846 IWM_DBG_MLME(iwm, DBG, "MLME debug notification: 0x%x\n",
847 notif->status);
848 break;
849 default:
850 IWM_ERR(iwm, "Unhandled notification: 0x%x\n", notif->status);
851 break;
852 }
853
854 return 0;
855}
856
857#define IWM_STATS_UPDATE_INTERVAL (2 * HZ)
858
859static int iwm_ntf_statistics(struct iwm_priv *iwm, u8 *buf,
860 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
861{
862 struct iwm_umac_notif_stats *stats = (struct iwm_umac_notif_stats *)buf;
863 struct iw_statistics *wstats = &iwm->wstats;
864 u16 max_rate = 0;
865 int i;
866
867 IWM_DBG_MLME(iwm, DBG, "Statistics notification received\n");
868
869 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
870 for (i = 0; i < UMAC_NTF_RATE_SAMPLE_NR; i++) {
871 max_rate = max_t(u16, max_rate,
872 max(le16_to_cpu(stats->tx_rate[i]),
873 le16_to_cpu(stats->rx_rate[i])));
874 }
875 /* UMAC passes rate info multiplies by 2 */
876 iwm->rate = max_rate >> 1;
877 }
878
879 wstats->status = 0;
880
881 wstats->discard.nwid = le32_to_cpu(stats->rx_drop_other_bssid);
882 wstats->discard.code = le32_to_cpu(stats->rx_drop_decode);
883 wstats->discard.fragment = le32_to_cpu(stats->rx_drop_reassembly);
884 wstats->discard.retries = le32_to_cpu(stats->tx_drop_max_retry);
885
886 wstats->miss.beacon = le32_to_cpu(stats->missed_beacons);
887
888 /* according to cfg80211 */
889 if (stats->rssi_dbm < -110)
890 wstats->qual.qual = 0;
891 else if (stats->rssi_dbm > -40)
892 wstats->qual.qual = 70;
893 else
894 wstats->qual.qual = stats->rssi_dbm + 110;
895
896 wstats->qual.level = stats->rssi_dbm;
897 wstats->qual.noise = stats->noise_dbm;
898 wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
899
900 schedule_delayed_work(&iwm->stats_request, IWM_STATS_UPDATE_INTERVAL);
901
902 mod_timer(&iwm->watchdog, round_jiffies(jiffies + IWM_WATCHDOG_PERIOD));
903
904 return 0;
905}
906
907static int iwm_ntf_eeprom_proxy(struct iwm_priv *iwm, u8 *buf,
908 unsigned long buf_size,
909 struct iwm_wifi_cmd *cmd)
910{
911 struct iwm_umac_cmd_eeprom_proxy *eeprom_proxy =
912 (struct iwm_umac_cmd_eeprom_proxy *)
913 (buf + sizeof(struct iwm_umac_wifi_in_hdr));
914 struct iwm_umac_cmd_eeprom_proxy_hdr *hdr = &eeprom_proxy->hdr;
915 u32 hdr_offset = le32_to_cpu(hdr->offset);
916 u32 hdr_len = le32_to_cpu(hdr->len);
917 u32 hdr_type = le32_to_cpu(hdr->type);
918
919 IWM_DBG_NTF(iwm, DBG, "type: 0x%x, len: %d, offset: 0x%x\n",
920 hdr_type, hdr_len, hdr_offset);
921
922 if ((hdr_offset + hdr_len) > IWM_EEPROM_LEN)
923 return -EINVAL;
924
925#ifdef CONFIG_IWM_B0_HW_SUPPORT
926 if (hdr_offset == IWM_EEPROM_SKU_CAP_OFF) {
927 if (eeprom_proxy->buf[0] == 0xff)
928 iwm->conf.hw_b0 = 1;
929 }
930#endif
931
932 switch (hdr_type) {
933 case IWM_UMAC_CMD_EEPROM_TYPE_READ:
934 memcpy(iwm->eeprom + hdr_offset, eeprom_proxy->buf, hdr_len);
935 break;
936 case IWM_UMAC_CMD_EEPROM_TYPE_WRITE:
937 default:
938 return -ENOTSUPP;
939 }
940
941 return 0;
942}
943
944static int iwm_ntf_channel_info_list(struct iwm_priv *iwm, u8 *buf,
945 unsigned long buf_size,
946 struct iwm_wifi_cmd *cmd)
947{
948 struct iwm_umac_cmd_get_channel_list *ch_list =
949 (struct iwm_umac_cmd_get_channel_list *)
950 (buf + sizeof(struct iwm_umac_wifi_in_hdr));
951 struct wiphy *wiphy = iwm_to_wiphy(iwm);
952 struct ieee80211_supported_band *band;
953 int i;
954
955 band = wiphy->bands[IEEE80211_BAND_2GHZ];
956
957 for (i = 0; i < band->n_channels; i++) {
958 unsigned long ch_mask_0 =
959 le32_to_cpu(ch_list->ch[0].channels_mask);
960 unsigned long ch_mask_2 =
961 le32_to_cpu(ch_list->ch[2].channels_mask);
962
963 if (!test_bit(i, &ch_mask_0))
964 band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
965
966 if (!test_bit(i, &ch_mask_2))
967 band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS;
968 }
969
970 band = wiphy->bands[IEEE80211_BAND_5GHZ];
971
972 for (i = 0; i < min(band->n_channels, 32); i++) {
973 unsigned long ch_mask_1 =
974 le32_to_cpu(ch_list->ch[1].channels_mask);
975 unsigned long ch_mask_3 =
976 le32_to_cpu(ch_list->ch[3].channels_mask);
977
978 if (!test_bit(i, &ch_mask_1))
979 band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
980
981 if (!test_bit(i, &ch_mask_3))
982 band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS;
983 }
984
985 return 0;
986}
987
988static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
989 unsigned long buf_size,
990 struct iwm_wifi_cmd *cmd)
991{
992 struct iwm_umac_wifi_if *hdr =
993 (struct iwm_umac_wifi_if *)cmd->buf.payload;
994
995 IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: "
996 "oid is %d\n", hdr->oid);
997
998 switch (hdr->oid) {
999 case UMAC_WIFI_IF_CMD_SET_PROFILE:
1000 iwm->umac_profile_active = 1;
1001 wake_up_interruptible(&iwm->mlme_queue);
1002 break;
1003 default:
1004 break;
1005 }
1006
1007 return 0;
1008}
1009
1010static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf,
1011 unsigned long buf_size, struct iwm_wifi_cmd *cmd)
1012{
1013 struct iwm_lmac_card_state *state = (struct iwm_lmac_card_state *)
1014 (buf + sizeof(struct iwm_umac_wifi_in_hdr));
1015 u32 flags = le32_to_cpu(state->flags);
1016
1017 IWM_INFO(iwm, "HW RF Kill %s, CT Kill %s\n",
1018 flags & IWM_CARD_STATE_HW_DISABLED ? "ON" : "OFF",
1019 flags & IWM_CARD_STATE_CTKILL_DISABLED ? "ON" : "OFF");
1020
1021 if (flags & IWM_CARD_STATE_HW_DISABLED)
1022 set_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
1023 else
1024 clear_bit(IWM_RADIO_RFKILL_HW, &iwm->radio);
1025
1026 return 0;
1027}
1028
1029static int iwm_rx_handle_wifi(struct iwm_priv *iwm, u8 *buf,
1030 unsigned long buf_size)
1031{
1032 struct iwm_umac_wifi_in_hdr *wifi_hdr;
1033 struct iwm_wifi_cmd *cmd;
1034 u8 source, cmd_id;
1035 u16 seq_num;
1036 u32 count;
1037 u8 resp;
1038
1039 wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
1040 cmd_id = wifi_hdr->sw_hdr.cmd.cmd;
1041
1042 source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
1043 if (source >= IWM_SRC_NUM) {
1044 IWM_CRIT(iwm, "invalid source %d\n", source);
1045 return -EINVAL;
1046 }
1047
1048 count = (GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT));
1049 count += sizeof(struct iwm_umac_wifi_in_hdr) -
1050 sizeof(struct iwm_dev_cmd_hdr);
1051 if (count > buf_size) {
1052 IWM_CRIT(iwm, "count %d, buf size:%ld\n", count, buf_size);
1053 return -EINVAL;
1054 }
1055
1056 resp = GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_STATUS);
1057
1058 seq_num = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num);
1059
1060 IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x, seqnum: %d\n",
1061 cmd_id, source, seq_num);
1062
1063 /*
1064 * If this is a response to a previously sent command, there must
1065 * be a pending command for this sequence number.
1066 */
1067 cmd = iwm_get_pending_wifi_cmd(iwm, seq_num);
1068
1069 /* Notify the caller only for sync commands. */
1070 switch (source) {
1071 case UMAC_HDI_IN_SOURCE_FHRX:
1072 if (iwm->lmac_handlers[cmd_id] &&
1073 test_bit(cmd_id, &iwm->lmac_handler_map[0]))
1074 return iwm_notif_send(iwm, cmd, cmd_id, source,
1075 buf, count);
1076 break;
1077 case UMAC_HDI_IN_SOURCE_FW:
1078 if (iwm->umac_handlers[cmd_id] &&
1079 test_bit(cmd_id, &iwm->umac_handler_map[0]))
1080 return iwm_notif_send(iwm, cmd, cmd_id, source,
1081 buf, count);
1082 break;
1083 case UMAC_HDI_IN_SOURCE_UDMA:
1084 break;
1085 }
1086
1087 return iwm_rx_handle_resp(iwm, buf, count, cmd);
1088}
1089
1090int iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size,
1091 struct iwm_wifi_cmd *cmd)
1092{
1093 u8 source, cmd_id;
1094 struct iwm_umac_wifi_in_hdr *wifi_hdr;
1095 int ret = 0;
1096
1097 wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf;
1098 cmd_id = wifi_hdr->sw_hdr.cmd.cmd;
1099
1100 source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE);
1101
1102 IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x\n", cmd_id, source);
1103
1104 switch (source) {
1105 case UMAC_HDI_IN_SOURCE_FHRX:
1106 if (iwm->lmac_handlers[cmd_id])
1107 ret = iwm->lmac_handlers[cmd_id]
1108 (iwm, buf, buf_size, cmd);
1109 break;
1110 case UMAC_HDI_IN_SOURCE_FW:
1111 if (iwm->umac_handlers[cmd_id])
1112 ret = iwm->umac_handlers[cmd_id]
1113 (iwm, buf, buf_size, cmd);
1114 break;
1115 case UMAC_HDI_IN_SOURCE_UDMA:
1116 ret = -EINVAL;
1117 break;
1118 }
1119
1120 kfree(cmd);
1121
1122 return ret;
1123}
1124
1125static int iwm_rx_handle_nonwifi(struct iwm_priv *iwm, u8 *buf,
1126 unsigned long buf_size)
1127{
1128 u8 seq_num;
1129 struct iwm_udma_in_hdr *hdr = (struct iwm_udma_in_hdr *)buf;
1130 struct iwm_nonwifi_cmd *cmd, *next;
1131
1132 seq_num = GET_VAL32(hdr->cmd, UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM);
1133
1134 /*
1135 * We received a non wifi answer.
1136 * Let's check if there's a pending command for it, and if so
1137 * replace the command payload with the buffer, and then wake the
1138 * callers up.
1139 * That means we only support synchronised non wifi command response
1140 * schemes.
1141 */
1142 list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
1143 if (cmd->seq_num == seq_num) {
1144 cmd->resp_received = 1;
1145 cmd->buf.len = buf_size;
1146 memcpy(cmd->buf.hdr, buf, buf_size);
1147 wake_up_interruptible(&iwm->nonwifi_queue);
1148 }
1149
1150 return 0;
1151}
1152
1153static int iwm_rx_handle_umac(struct iwm_priv *iwm, u8 *buf,
1154 unsigned long buf_size)
1155{
1156 int ret = 0;
1157 u8 op_code;
1158 unsigned long buf_offset = 0;
1159 struct iwm_udma_in_hdr *hdr;
1160
1161 /*
1162 * To allow for a more efficient bus usage, UMAC
1163 * messages are encapsulated into UDMA ones. This
1164 * way we can have several UMAC messages in one bus
1165 * transfer.
1166 * A UDMA frame size is always aligned on 16 bytes,
1167 * and a UDMA frame must not start with a UMAC_PAD_TERMINAL
1168 * word. This is how we parse a bus frame into several
1169 * UDMA ones.
1170 */
1171 while (buf_offset < buf_size) {
1172
1173 hdr = (struct iwm_udma_in_hdr *)(buf + buf_offset);
1174
1175 if (iwm_rx_check_udma_hdr(hdr) < 0) {
1176 IWM_DBG_RX(iwm, DBG, "End of frame\n");
1177 break;
1178 }
1179
1180 op_code = GET_VAL32(hdr->cmd, UMAC_HDI_IN_CMD_OPCODE);
1181
1182 IWM_DBG_RX(iwm, DBG, "Op code: 0x%x\n", op_code);
1183
1184 if (op_code == UMAC_HDI_IN_OPCODE_WIFI) {
1185 ret |= iwm_rx_handle_wifi(iwm, buf + buf_offset,
1186 buf_size - buf_offset);
1187 } else if (op_code < UMAC_HDI_IN_OPCODE_NONWIFI_MAX) {
1188 if (GET_VAL32(hdr->cmd,
1189 UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG) !=
1190 UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG) {
1191 IWM_ERR(iwm, "Incorrect hw signature\n");
1192 return -EINVAL;
1193 }
1194 ret |= iwm_rx_handle_nonwifi(iwm, buf + buf_offset,
1195 buf_size - buf_offset);
1196 } else {
1197 IWM_ERR(iwm, "Invalid RX opcode: 0x%x\n", op_code);
1198 ret |= -EINVAL;
1199 }
1200
1201 buf_offset += iwm_rx_resp_size(hdr);
1202 }
1203
1204 return ret;
1205}
1206
1207int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size)
1208{
1209 struct iwm_udma_in_hdr *hdr;
1210
1211 hdr = (struct iwm_udma_in_hdr *)buf;
1212
1213 switch (le32_to_cpu(hdr->cmd)) {
1214 case UMAC_REBOOT_BARKER:
1215 return iwm_notif_send(iwm, NULL, IWM_BARKER_REBOOT_NOTIFICATION,
1216 IWM_SRC_UDMA, buf, buf_size);
1217 case UMAC_ACK_BARKER:
1218 return iwm_notif_send(iwm, NULL, IWM_ACK_BARKER_NOTIFICATION,
1219 IWM_SRC_UDMA, NULL, 0);
1220 default:
1221 IWM_DBG_RX(iwm, DBG, "Received cmd: 0x%x\n", hdr->cmd);
1222 return iwm_rx_handle_umac(iwm, buf, buf_size);
1223 }
1224
1225 return 0;
1226}
1227
1228static const iwm_handler iwm_umac_handlers[] =
1229{
1230 [UMAC_NOTIFY_OPCODE_ERROR] = iwm_ntf_error,
1231 [UMAC_NOTIFY_OPCODE_ALIVE] = iwm_ntf_umac_alive,
1232 [UMAC_NOTIFY_OPCODE_INIT_COMPLETE] = iwm_ntf_init_complete,
1233 [UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS] = iwm_ntf_wifi_status,
1234 [UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_mlme,
1235 [UMAC_NOTIFY_OPCODE_PAGE_DEALLOC] = iwm_ntf_tx_credit_update,
1236 [UMAC_NOTIFY_OPCODE_RX_TICKET] = iwm_ntf_rx_ticket,
1237 [UMAC_CMD_OPCODE_RESET] = iwm_ntf_umac_reset,
1238 [UMAC_NOTIFY_OPCODE_STATS] = iwm_ntf_statistics,
1239 [UMAC_CMD_OPCODE_EEPROM_PROXY] = iwm_ntf_eeprom_proxy,
1240 [UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST] = iwm_ntf_channel_info_list,
1241 [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet,
1242 [UMAC_CMD_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_wifi_if_wrapper,
1243};
1244
1245static const iwm_handler iwm_lmac_handlers[] =
1246{
1247 [REPLY_TX] = iwm_ntf_tx,
1248 [REPLY_ALIVE] = iwm_ntf_lmac_version,
1249 [CALIBRATION_RES_NOTIFICATION] = iwm_ntf_calib_res,
1250 [CALIBRATION_COMPLETE_NOTIFICATION] = iwm_ntf_calib_complete,
1251 [CALIBRATION_CFG_CMD] = iwm_ntf_calib_cfg,
1252 [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet,
1253 [CARD_STATE_NOTIFICATION] = iwm_ntf_card_state,
1254};
1255
1256void iwm_rx_setup_handlers(struct iwm_priv *iwm)
1257{
1258 iwm->umac_handlers = (iwm_handler *) iwm_umac_handlers;
1259 iwm->lmac_handlers = (iwm_handler *) iwm_lmac_handlers;
1260}
1261
1262static void iwm_remove_iv(struct sk_buff *skb, u32 hdr_total_len)
1263{
1264 struct ieee80211_hdr *hdr;
1265 unsigned int hdr_len;
1266
1267 hdr = (struct ieee80211_hdr *)skb->data;
1268
1269 if (!ieee80211_has_protected(hdr->frame_control))
1270 return;
1271
1272 hdr_len = ieee80211_hdrlen(hdr->frame_control);
1273 if (hdr_total_len <= hdr_len)
1274 return;
1275
1276 memmove(skb->data + (hdr_total_len - hdr_len), skb->data, hdr_len);
1277 skb_pull(skb, (hdr_total_len - hdr_len));
1278}
1279
1280static void iwm_rx_adjust_packet(struct iwm_priv *iwm,
1281 struct iwm_rx_packet *packet,
1282 struct iwm_rx_ticket_node *ticket_node)
1283{
1284 u32 payload_offset = 0, payload_len;
1285 struct iwm_rx_ticket *ticket = ticket_node->ticket;
1286 struct iwm_rx_mpdu_hdr *mpdu_hdr;
1287 struct ieee80211_hdr *hdr;
1288
1289 mpdu_hdr = (struct iwm_rx_mpdu_hdr *)packet->skb->data;
1290 payload_offset += sizeof(struct iwm_rx_mpdu_hdr);
1291 /* Padding is 0 or 2 bytes */
1292 payload_len = le16_to_cpu(mpdu_hdr->len) +
1293 (le16_to_cpu(ticket->flags) & IWM_RX_TICKET_PAD_SIZE_MSK);
1294 payload_len -= ticket->tail_len;
1295
1296 IWM_DBG_RX(iwm, DBG, "Packet adjusted, len:%d, offset:%d, "
1297 "ticket offset:%d ticket tail len:%d\n",
1298 payload_len, payload_offset, ticket->payload_offset,
1299 ticket->tail_len);
1300
1301 IWM_HEXDUMP(iwm, DBG, RX, "RAW: ", packet->skb->data, packet->skb->len);
1302
1303 skb_pull(packet->skb, payload_offset);
1304 skb_trim(packet->skb, payload_len);
1305
1306 iwm_remove_iv(packet->skb, ticket->payload_offset);
1307
1308 hdr = (struct ieee80211_hdr *) packet->skb->data;
1309 if (ieee80211_is_data_qos(hdr->frame_control)) {
1310 /* UMAC handed QOS_DATA frame with 2 padding bytes appended
1311 * to the qos_ctl field in IEEE 802.11 headers. */
1312 memmove(packet->skb->data + IEEE80211_QOS_CTL_LEN + 2,
1313 packet->skb->data,
1314 ieee80211_hdrlen(hdr->frame_control) -
1315 IEEE80211_QOS_CTL_LEN);
1316 hdr = (struct ieee80211_hdr *) skb_pull(packet->skb,
1317 IEEE80211_QOS_CTL_LEN + 2);
1318 hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
1319 }
1320
1321 IWM_HEXDUMP(iwm, DBG, RX, "ADJUSTED: ",
1322 packet->skb->data, packet->skb->len);
1323}
1324
1325static void classify8023(struct sk_buff *skb)
1326{
1327 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
1328
1329 if (ieee80211_is_data_qos(hdr->frame_control)) {
1330 u8 *qc = ieee80211_get_qos_ctl(hdr);
1331 /* frame has qos control */
1332 skb->priority = *qc & IEEE80211_QOS_CTL_TID_MASK;
1333 } else {
1334 skb->priority = 0;
1335 }
1336}
1337
1338static void iwm_rx_process_packet(struct iwm_priv *iwm,
1339 struct iwm_rx_packet *packet,
1340 struct iwm_rx_ticket_node *ticket_node)
1341{
1342 int ret;
1343 struct sk_buff *skb = packet->skb;
1344 struct wireless_dev *wdev = iwm_to_wdev(iwm);
1345 struct net_device *ndev = iwm_to_ndev(iwm);
1346
1347 IWM_DBG_RX(iwm, DBG, "Processing packet ID %d\n", packet->id);
1348
1349 switch (le16_to_cpu(ticket_node->ticket->action)) {
1350 case IWM_RX_TICKET_RELEASE:
1351 IWM_DBG_RX(iwm, DBG, "RELEASE packet\n");
1352 classify8023(skb);
1353 iwm_rx_adjust_packet(iwm, packet, ticket_node);
1354 ret = ieee80211_data_to_8023(skb, ndev->dev_addr, wdev->iftype);
1355 if (ret < 0) {
1356 IWM_DBG_RX(iwm, DBG, "Couldn't convert 802.11 header - "
1357 "%d\n", ret);
1358 break;
1359 }
1360
1361 IWM_HEXDUMP(iwm, DBG, RX, "802.3: ", skb->data, skb->len);
1362
1363 skb->dev = iwm_to_ndev(iwm);
1364 skb->protocol = eth_type_trans(skb, ndev);
1365 skb->ip_summed = CHECKSUM_UNNECESSARY;
1366 memset(skb->cb, 0, sizeof(skb->cb));
1367
1368 ndev->stats.rx_packets++;
1369 ndev->stats.rx_bytes += skb->len;
1370
1371 if (netif_rx(skb) == NET_RX_DROP) {
1372 IWM_ERR(iwm, "Packet dropped\n");
1373 ndev->stats.rx_dropped++;
1374 }
1375 break;
1376 case IWM_RX_TICKET_DROP:
1377 IWM_DBG_RX(iwm, DBG, "DROP packet\n");
1378 kfree_skb(packet->skb);
1379 break;
1380 default:
1381 IWM_ERR(iwm, "Unknow ticket action: %d\n",
1382 le16_to_cpu(ticket_node->ticket->action));
1383 kfree_skb(packet->skb);
1384 }
1385
1386 kfree(packet);
1387 iwm_rx_ticket_node_free(ticket_node);
1388}
1389
1390/*
1391 * Rx data processing:
1392 *
1393 * We're receiving Rx packet from the LMAC, and Rx ticket from
1394 * the UMAC.
1395 * To forward a target data packet upstream (i.e. to the
1396 * kernel network stack), we must have received an Rx ticket
1397 * that tells us we're allowed to release this packet (ticket
1398 * action is IWM_RX_TICKET_RELEASE). The Rx ticket also indicates,
1399 * among other things, where valid data actually starts in the Rx
1400 * packet.
1401 */
1402void iwm_rx_worker(struct work_struct *work)
1403{
1404 struct iwm_priv *iwm;
1405 struct iwm_rx_ticket_node *ticket, *next;
1406
1407 iwm = container_of(work, struct iwm_priv, rx_worker);
1408
1409 /*
1410 * We go through the tickets list and if there is a pending
1411 * packet for it, we push it upstream.
1412 * We stop whenever a ticket is missing its packet, as we're
1413 * supposed to send the packets in order.
1414 */
1415 list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
1416 struct iwm_rx_packet *packet =
1417 iwm_rx_packet_get(iwm, le16_to_cpu(ticket->ticket->id));
1418
1419 if (!packet) {
1420 IWM_DBG_RX(iwm, DBG, "Skip rx_work: Wait for ticket %d "
1421 "to be handled first\n",
1422 le16_to_cpu(ticket->ticket->id));
1423 return;
1424 }
1425
1426 list_del(&ticket->node);
1427 list_del(&packet->node);
1428 iwm_rx_process_packet(iwm, packet, ticket);
1429 }
1430}
1431
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.h b/drivers/net/wireless/iwmc3200wifi/rx.h
new file mode 100644
index 000000000000..da0db91cee59
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/rx.h
@@ -0,0 +1,60 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef __IWM_RX_H__
40#define __IWM_RX_H__
41
42#include <linux/skbuff.h>
43
44#include "umac.h"
45
46struct iwm_rx_ticket_node {
47 struct list_head node;
48 struct iwm_rx_ticket *ticket;
49};
50
51struct iwm_rx_packet {
52 struct list_head node;
53 u16 id;
54 struct sk_buff *skb;
55 unsigned long pkt_size;
56};
57
58void iwm_rx_worker(struct work_struct *work);
59
60#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
new file mode 100644
index 000000000000..edc0a0091058
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -0,0 +1,516 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39/*
40 * This is the SDIO bus specific hooks for iwm.
41 * It also is the module's entry point.
42 *
43 * Interesting code paths:
44 * iwm_sdio_probe() (Called by an SDIO bus scan)
45 * -> iwm_if_alloc() (netdev.c)
46 * -> iwm_wdev_alloc() (cfg80211.c, allocates and register our wiphy)
47 * -> wiphy_new()
48 * -> wiphy_register()
49 * -> alloc_netdev_mq()
50 * -> register_netdev()
51 *
52 * iwm_sdio_remove()
53 * -> iwm_if_free() (netdev.c)
54 * -> unregister_netdev()
55 * -> iwm_wdev_free() (cfg80211.c)
56 * -> wiphy_unregister()
57 * -> wiphy_free()
58 *
59 * iwm_sdio_isr() (called in process context from the SDIO core code)
60 * -> queue_work(.., isr_worker)
61 * -- [async] --> iwm_sdio_isr_worker()
62 * -> iwm_rx_handle()
63 */
64
65#include <linux/kernel.h>
66#include <linux/netdevice.h>
67#include <linux/debugfs.h>
68#include <linux/mmc/sdio.h>
69#include <linux/mmc/sdio_func.h>
70
71#include "iwm.h"
72#include "debug.h"
73#include "bus.h"
74#include "sdio.h"
75
76static void iwm_sdio_isr_worker(struct work_struct *work)
77{
78 struct iwm_sdio_priv *hw;
79 struct iwm_priv *iwm;
80 struct iwm_rx_info *rx_info;
81 struct sk_buff *skb;
82 u8 *rx_buf;
83 unsigned long rx_size;
84
85 hw = container_of(work, struct iwm_sdio_priv, isr_worker);
86 iwm = hw_to_iwm(hw);
87
88 while (!skb_queue_empty(&iwm->rx_list)) {
89 skb = skb_dequeue(&iwm->rx_list);
90 rx_info = skb_to_rx_info(skb);
91 rx_size = rx_info->rx_size;
92 rx_buf = skb->data;
93
94 IWM_HEXDUMP(iwm, DBG, SDIO, "RX: ", rx_buf, rx_size);
95 if (iwm_rx_handle(iwm, rx_buf, rx_size) < 0)
96 IWM_WARN(iwm, "RX error\n");
97
98 kfree_skb(skb);
99 }
100}
101
102static void iwm_sdio_isr(struct sdio_func *func)
103{
104 struct iwm_priv *iwm;
105 struct iwm_sdio_priv *hw;
106 struct iwm_rx_info *rx_info;
107 struct sk_buff *skb;
108 unsigned long buf_size, read_size;
109 int ret;
110 u8 val;
111
112 hw = sdio_get_drvdata(func);
113 iwm = hw_to_iwm(hw);
114
115 buf_size = hw->blk_size;
116
117 /* We're checking the status */
118 val = sdio_readb(func, IWM_SDIO_INTR_STATUS_ADDR, &ret);
119 if (val == 0 || ret < 0) {
120 IWM_ERR(iwm, "Wrong INTR_STATUS\n");
121 return;
122 }
123
124 /* See if we have free buffers */
125 if (skb_queue_len(&iwm->rx_list) > IWM_RX_LIST_SIZE) {
126 IWM_ERR(iwm, "No buffer for more Rx frames\n");
127 return;
128 }
129
130 /* We first read the transaction size */
131 read_size = sdio_readb(func, IWM_SDIO_INTR_GET_SIZE_ADDR + 1, &ret);
132 read_size = read_size << 8;
133
134 if (ret < 0) {
135 IWM_ERR(iwm, "Couldn't read the xfer size\n");
136 return;
137 }
138
139 /* We need to clear the INT register */
140 sdio_writeb(func, 1, IWM_SDIO_INTR_CLEAR_ADDR, &ret);
141 if (ret < 0) {
142 IWM_ERR(iwm, "Couldn't clear the INT register\n");
143 return;
144 }
145
146 while (buf_size < read_size)
147 buf_size <<= 1;
148
149 skb = dev_alloc_skb(buf_size);
150 if (!skb) {
151 IWM_ERR(iwm, "Couldn't alloc RX skb\n");
152 return;
153 }
154 rx_info = skb_to_rx_info(skb);
155 rx_info->rx_size = read_size;
156 rx_info->rx_buf_size = buf_size;
157
158 /* Now we can read the actual buffer */
159 ret = sdio_memcpy_fromio(func, skb_put(skb, read_size),
160 IWM_SDIO_DATA_ADDR, read_size);
161
162 /* The skb is put on a driver's specific Rx SKB list */
163 skb_queue_tail(&iwm->rx_list, skb);
164
165 /* We can now schedule the actual worker */
166 queue_work(hw->isr_wq, &hw->isr_worker);
167}
168
169static void iwm_sdio_rx_free(struct iwm_sdio_priv *hw)
170{
171 struct iwm_priv *iwm = hw_to_iwm(hw);
172
173 flush_workqueue(hw->isr_wq);
174
175 skb_queue_purge(&iwm->rx_list);
176}
177
178/* Bus ops */
179static int if_sdio_enable(struct iwm_priv *iwm)
180{
181 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
182 int ret;
183
184 sdio_claim_host(hw->func);
185
186 ret = sdio_enable_func(hw->func);
187 if (ret) {
188 IWM_ERR(iwm, "Couldn't enable the device: is TOP driver "
189 "loaded and functional?\n");
190 goto release_host;
191 }
192
193 iwm_reset(iwm);
194
195 ret = sdio_claim_irq(hw->func, iwm_sdio_isr);
196 if (ret) {
197 IWM_ERR(iwm, "Failed to claim irq: %d\n", ret);
198 goto release_host;
199 }
200
201 sdio_writeb(hw->func, 1, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
202 if (ret < 0) {
203 IWM_ERR(iwm, "Couldn't enable INTR: %d\n", ret);
204 goto release_irq;
205 }
206
207 sdio_release_host(hw->func);
208
209 IWM_DBG_SDIO(iwm, INFO, "IWM SDIO enable\n");
210
211 return 0;
212
213 release_irq:
214 sdio_release_irq(hw->func);
215 release_host:
216 sdio_release_host(hw->func);
217
218 return ret;
219}
220
221static int if_sdio_disable(struct iwm_priv *iwm)
222{
223 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
224 int ret;
225
226 iwm_reset(iwm);
227
228 sdio_claim_host(hw->func);
229 sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
230 if (ret < 0)
231 IWM_WARN(iwm, "Couldn't disable INTR: %d\n", ret);
232
233 sdio_release_irq(hw->func);
234 sdio_disable_func(hw->func);
235 sdio_release_host(hw->func);
236
237 iwm_sdio_rx_free(hw);
238
239 IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n");
240
241 return 0;
242}
243
244static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
245{
246 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
247 int aligned_count = ALIGN(count, hw->blk_size);
248 int ret;
249
250 if ((unsigned long)buf & 0x3) {
251 IWM_ERR(iwm, "buf <%p> is not dword aligned\n", buf);
252 /* TODO: Is this a hardware limitation? use get_unligned */
253 return -EINVAL;
254 }
255
256 sdio_claim_host(hw->func);
257 ret = sdio_memcpy_toio(hw->func, IWM_SDIO_DATA_ADDR, buf,
258 aligned_count);
259 sdio_release_host(hw->func);
260
261 return ret;
262}
263
264/* debugfs hooks */
265static int iwm_debugfs_sdio_open(struct inode *inode, struct file *filp)
266{
267 filp->private_data = inode->i_private;
268 return 0;
269}
270
271static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer,
272 size_t count, loff_t *ppos)
273{
274 struct iwm_priv *iwm = filp->private_data;
275 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
276 char *buf;
277 u8 cccr;
278 int buf_len = 4096, ret;
279 size_t len = 0;
280
281 if (*ppos != 0)
282 return 0;
283 if (count < sizeof(buf))
284 return -ENOSPC;
285
286 buf = kzalloc(buf_len, GFP_KERNEL);
287 if (!buf)
288 return -ENOMEM;
289
290 sdio_claim_host(hw->func);
291
292 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IOEx, &ret);
293 if (ret) {
294 IWM_ERR(iwm, "Could not read SDIO_CCCR_IOEx\n");
295 goto err;
296 }
297 len += snprintf(buf + len, buf_len - len, "CCCR_IOEx: 0x%x\n", cccr);
298
299 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IORx, &ret);
300 if (ret) {
301 IWM_ERR(iwm, "Could not read SDIO_CCCR_IORx\n");
302 goto err;
303 }
304 len += snprintf(buf + len, buf_len - len, "CCCR_IORx: 0x%x\n", cccr);
305
306
307 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IENx, &ret);
308 if (ret) {
309 IWM_ERR(iwm, "Could not read SDIO_CCCR_IENx\n");
310 goto err;
311 }
312 len += snprintf(buf + len, buf_len - len, "CCCR_IENx: 0x%x\n", cccr);
313
314
315 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_INTx, &ret);
316 if (ret) {
317 IWM_ERR(iwm, "Could not read SDIO_CCCR_INTx\n");
318 goto err;
319 }
320 len += snprintf(buf + len, buf_len - len, "CCCR_INTx: 0x%x\n", cccr);
321
322
323 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_ABORT, &ret);
324 if (ret) {
325 IWM_ERR(iwm, "Could not read SDIO_CCCR_ABORTx\n");
326 goto err;
327 }
328 len += snprintf(buf + len, buf_len - len, "CCCR_ABORT: 0x%x\n", cccr);
329
330 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IF, &ret);
331 if (ret) {
332 IWM_ERR(iwm, "Could not read SDIO_CCCR_IF\n");
333 goto err;
334 }
335 len += snprintf(buf + len, buf_len - len, "CCCR_IF: 0x%x\n", cccr);
336
337
338 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CAPS, &ret);
339 if (ret) {
340 IWM_ERR(iwm, "Could not read SDIO_CCCR_CAPS\n");
341 goto err;
342 }
343 len += snprintf(buf + len, buf_len - len, "CCCR_CAPS: 0x%x\n", cccr);
344
345 cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CIS, &ret);
346 if (ret) {
347 IWM_ERR(iwm, "Could not read SDIO_CCCR_CIS\n");
348 goto err;
349 }
350 len += snprintf(buf + len, buf_len - len, "CCCR_CIS: 0x%x\n", cccr);
351
352 ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
353err:
354 sdio_release_host(hw->func);
355
356 kfree(buf);
357
358 return ret;
359}
360
361static const struct file_operations iwm_debugfs_sdio_fops = {
362 .owner = THIS_MODULE,
363 .open = iwm_debugfs_sdio_open,
364 .read = iwm_debugfs_sdio_read,
365};
366
367static int if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir)
368{
369 int result;
370 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
371
372 hw->cccr_dentry = debugfs_create_file("cccr", 0200,
373 parent_dir, iwm,
374 &iwm_debugfs_sdio_fops);
375 result = PTR_ERR(hw->cccr_dentry);
376 if (IS_ERR(hw->cccr_dentry) && (result != -ENODEV)) {
377 IWM_ERR(iwm, "Couldn't create CCCR entry: %d\n", result);
378 return result;
379 }
380
381 return 0;
382}
383
384static void if_sdio_debugfs_exit(struct iwm_priv *iwm)
385{
386 struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
387
388 debugfs_remove(hw->cccr_dentry);
389}
390
391static struct iwm_if_ops if_sdio_ops = {
392 .enable = if_sdio_enable,
393 .disable = if_sdio_disable,
394 .send_chunk = if_sdio_send_chunk,
395 .debugfs_init = if_sdio_debugfs_init,
396 .debugfs_exit = if_sdio_debugfs_exit,
397 .umac_name = "iwmc3200wifi-umac-sdio.bin",
398 .calib_lmac_name = "iwmc3200wifi-lmac-calib-sdio.bin",
399 .lmac_name = "iwmc3200wifi-lmac-sdio.bin",
400};
401
402static int iwm_sdio_probe(struct sdio_func *func,
403 const struct sdio_device_id *id)
404{
405 struct iwm_priv *iwm;
406 struct iwm_sdio_priv *hw;
407 struct device *dev = &func->dev;
408 int ret;
409
410 /* check if TOP has already initialized the card */
411 sdio_claim_host(func);
412 ret = sdio_enable_func(func);
413 if (ret) {
414 dev_err(dev, "wait for TOP to enable the device\n");
415 sdio_release_host(func);
416 return ret;
417 }
418
419 ret = sdio_set_block_size(func, IWM_SDIO_BLK_SIZE);
420
421 sdio_disable_func(func);
422 sdio_release_host(func);
423
424 if (ret < 0) {
425 dev_err(dev, "Failed to set block size: %d\n", ret);
426 return ret;
427 }
428
429 iwm = iwm_if_alloc(sizeof(struct iwm_sdio_priv), dev, &if_sdio_ops);
430 if (IS_ERR(iwm)) {
431 dev_err(dev, "allocate SDIO interface failed\n");
432 return PTR_ERR(iwm);
433 }
434
435 hw = iwm_private(iwm);
436 hw->iwm = iwm;
437
438 ret = iwm_debugfs_init(iwm);
439 if (ret < 0) {
440 IWM_ERR(iwm, "Debugfs registration failed\n");
441 goto if_free;
442 }
443
444 sdio_set_drvdata(func, hw);
445
446 hw->func = func;
447 hw->blk_size = IWM_SDIO_BLK_SIZE;
448
449 hw->isr_wq = create_singlethread_workqueue(KBUILD_MODNAME "_sdio");
450 if (!hw->isr_wq) {
451 ret = -ENOMEM;
452 goto debugfs_exit;
453 }
454
455 INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
456
457 dev_info(dev, "IWM SDIO probe\n");
458
459 return 0;
460
461 debugfs_exit:
462 iwm_debugfs_exit(iwm);
463 if_free:
464 iwm_if_free(iwm);
465 return ret;
466}
467
468static void iwm_sdio_remove(struct sdio_func *func)
469{
470 struct iwm_sdio_priv *hw = sdio_get_drvdata(func);
471 struct iwm_priv *iwm = hw_to_iwm(hw);
472 struct device *dev = &func->dev;
473
474 iwm_debugfs_exit(iwm);
475 iwm_if_free(iwm);
476 destroy_workqueue(hw->isr_wq);
477
478 sdio_set_drvdata(func, NULL);
479
480 dev_info(dev, "IWM SDIO remove\n");
481
482 return;
483}
484
485static const struct sdio_device_id iwm_sdio_ids[] = {
486 { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, SDIO_DEVICE_ID_IWM) },
487 { /* end: all zeroes */ },
488};
489MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids);
490
491static struct sdio_driver iwm_sdio_driver = {
492 .name = "iwm_sdio",
493 .id_table = iwm_sdio_ids,
494 .probe = iwm_sdio_probe,
495 .remove = iwm_sdio_remove,
496};
497
498static int __init iwm_sdio_init_module(void)
499{
500 int ret;
501
502 ret = sdio_register_driver(&iwm_sdio_driver);
503
504 return ret;
505}
506
507static void __exit iwm_sdio_exit_module(void)
508{
509 sdio_unregister_driver(&iwm_sdio_driver);
510}
511
512module_init(iwm_sdio_init_module);
513module_exit(iwm_sdio_exit_module);
514
515MODULE_LICENSE("GPL");
516MODULE_AUTHOR(IWM_COPYRIGHT " " IWM_AUTHOR);
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.h b/drivers/net/wireless/iwmc3200wifi/sdio.h
new file mode 100644
index 000000000000..b3c156b08dda
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.h
@@ -0,0 +1,67 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef __IWM_SDIO_H__
40#define __IWM_SDIO_H__
41
42#define SDIO_VENDOR_ID_INTEL 0x89
43#define SDIO_DEVICE_ID_IWM 0x1403
44
45#define IWM_SDIO_DATA_ADDR 0x0
46#define IWM_SDIO_INTR_ENABLE_ADDR 0x14
47#define IWM_SDIO_INTR_STATUS_ADDR 0x13
48#define IWM_SDIO_INTR_CLEAR_ADDR 0x13
49#define IWM_SDIO_INTR_GET_SIZE_ADDR 0x2C
50
51#define IWM_SDIO_BLK_SIZE 256
52
53#define iwm_to_if_sdio(i) (struct iwm_sdio_priv *)(iwm->private)
54
55struct iwm_sdio_priv {
56 struct sdio_func *func;
57 struct iwm_priv *iwm;
58
59 struct workqueue_struct *isr_wq;
60 struct work_struct isr_worker;
61
62 struct dentry *cccr_dentry;
63
64 unsigned int blk_size;
65};
66
67#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/tx.c b/drivers/net/wireless/iwmc3200wifi/tx.c
new file mode 100644
index 000000000000..e3b4f7902daf
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/tx.c
@@ -0,0 +1,492 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39/*
40 * iwm Tx theory of operation:
41 *
42 * 1) We receive a 802.3 frame from the stack
43 * 2) We convert it to a 802.11 frame [iwm_xmit_frame]
44 * 3) We queue it to its corresponding tx queue [iwm_xmit_frame]
45 * 4) We schedule the tx worker. There is one worker per tx
46 * queue. [iwm_xmit_frame]
47 * 5) The tx worker is scheduled
48 * 6) We go through every queued skb on the tx queue, and for each
49 * and every one of them: [iwm_tx_worker]
50 * a) We check if we have enough Tx credits (see below for a Tx
51 * credits description) for the frame length. [iwm_tx_worker]
52 * b) If we do, we aggregate the Tx frame into a UDMA one, by
53 * concatenating one REPLY_TX command per Tx frame. [iwm_tx_worker]
54 * c) When we run out of credits, or when we reach the maximum
55 * concatenation size, we actually send the concatenated UDMA
56 * frame. [iwm_tx_worker]
57 *
58 * When we run out of Tx credits, the skbs are filling the tx queue,
59 * and eventually we will stop the netdev queue. [iwm_tx_worker]
60 * The tx queue is emptied as we're getting new tx credits, by
61 * scheduling the tx_worker. [iwm_tx_credit_inc]
62 * The netdev queue is started again when we have enough tx credits,
63 * and when our tx queue has some reasonable amout of space available
64 * (i.e. half of the max size). [iwm_tx_worker]
65 */
66
67#include <linux/skbuff.h>
68#include <linux/netdevice.h>
69#include <linux/ieee80211.h>
70
71#include "iwm.h"
72#include "debug.h"
73#include "commands.h"
74#include "hal.h"
75#include "umac.h"
76#include "bus.h"
77
78#define IWM_UMAC_PAGE_ALLOC_WRAP 0xffff
79
80#define BYTES_TO_PAGES(n) (1 + ((n) >> ilog2(IWM_UMAC_PAGE_SIZE)) - \
81 (((n) & (IWM_UMAC_PAGE_SIZE - 1)) == 0))
82
83#define pool_id_to_queue(id) ((id < IWM_TX_CMD_QUEUE) ? id : id - 1)
84#define queue_to_pool_id(q) ((q < IWM_TX_CMD_QUEUE) ? q : q + 1)
85
86/* require to hold tx_credit lock */
87static int iwm_tx_credit_get(struct iwm_tx_credit *tx_credit, int id)
88{
89 struct pool_entry *pool = &tx_credit->pools[id];
90 struct spool_entry *spool = &tx_credit->spools[pool->sid];
91 int spool_pages;
92
93 /* number of pages can be taken from spool by this pool */
94 spool_pages = spool->max_pages - spool->alloc_pages +
95 max(pool->min_pages - pool->alloc_pages, 0);
96
97 return min(pool->max_pages - pool->alloc_pages, spool_pages);
98}
99
100static bool iwm_tx_credit_ok(struct iwm_priv *iwm, int id, int nb)
101{
102 u32 npages = BYTES_TO_PAGES(nb);
103
104 if (npages <= iwm_tx_credit_get(&iwm->tx_credit, id))
105 return 1;
106
107 set_bit(id, &iwm->tx_credit.full_pools_map);
108
109 IWM_DBG_TX(iwm, DBG, "LINK: stop txq[%d], available credit: %d\n",
110 pool_id_to_queue(id),
111 iwm_tx_credit_get(&iwm->tx_credit, id));
112
113 return 0;
114}
115
116void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages)
117{
118 struct pool_entry *pool;
119 struct spool_entry *spool;
120 int freed_pages;
121 int queue;
122
123 BUG_ON(id >= IWM_MACS_OUT_GROUPS);
124
125 pool = &iwm->tx_credit.pools[id];
126 spool = &iwm->tx_credit.spools[pool->sid];
127
128 freed_pages = total_freed_pages - pool->total_freed_pages;
129 IWM_DBG_TX(iwm, DBG, "Free %d pages for pool[%d]\n", freed_pages, id);
130
131 if (!freed_pages) {
132 IWM_DBG_TX(iwm, DBG, "No pages are freed by UMAC\n");
133 return;
134 } else if (freed_pages < 0)
135 freed_pages += IWM_UMAC_PAGE_ALLOC_WRAP + 1;
136
137 if (pool->alloc_pages > pool->min_pages) {
138 int spool_pages = pool->alloc_pages - pool->min_pages;
139 spool_pages = min(spool_pages, freed_pages);
140 spool->alloc_pages -= spool_pages;
141 }
142
143 pool->alloc_pages -= freed_pages;
144 pool->total_freed_pages = total_freed_pages;
145
146 IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, "
147 "Spool[%d] pages alloc: %d\n", id, pool->alloc_pages,
148 pool->total_freed_pages, pool->sid, spool->alloc_pages);
149
150 if (test_bit(id, &iwm->tx_credit.full_pools_map) &&
151 (pool->alloc_pages < pool->max_pages / 2)) {
152 clear_bit(id, &iwm->tx_credit.full_pools_map);
153
154 queue = pool_id_to_queue(id);
155
156 IWM_DBG_TX(iwm, DBG, "LINK: start txq[%d], available "
157 "credit: %d\n", queue,
158 iwm_tx_credit_get(&iwm->tx_credit, id));
159 queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
160 }
161}
162
163static void iwm_tx_credit_dec(struct iwm_priv *iwm, int id, int alloc_pages)
164{
165 struct pool_entry *pool;
166 struct spool_entry *spool;
167 int spool_pages;
168
169 IWM_DBG_TX(iwm, DBG, "Allocate %d pages for pool[%d]\n",
170 alloc_pages, id);
171
172 BUG_ON(id >= IWM_MACS_OUT_GROUPS);
173
174 pool = &iwm->tx_credit.pools[id];
175 spool = &iwm->tx_credit.spools[pool->sid];
176
177 spool_pages = pool->alloc_pages + alloc_pages - pool->min_pages;
178
179 if (pool->alloc_pages >= pool->min_pages)
180 spool->alloc_pages += alloc_pages;
181 else if (spool_pages > 0)
182 spool->alloc_pages += spool_pages;
183
184 pool->alloc_pages += alloc_pages;
185
186 IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, "
187 "Spool[%d] pages alloc: %d\n", id, pool->alloc_pages,
188 pool->total_freed_pages, pool->sid, spool->alloc_pages);
189}
190
191int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb)
192{
193 u32 npages = BYTES_TO_PAGES(nb);
194 int ret = 0;
195
196 spin_lock(&iwm->tx_credit.lock);
197
198 if (!iwm_tx_credit_ok(iwm, id, nb)) {
199 IWM_DBG_TX(iwm, DBG, "No credit avaliable for pool[%d]\n", id);
200 ret = -ENOSPC;
201 goto out;
202 }
203
204 iwm_tx_credit_dec(iwm, id, npages);
205
206 out:
207 spin_unlock(&iwm->tx_credit.lock);
208 return ret;
209}
210
211/*
212 * Since we're on an SDIO or USB bus, we are not sharing memory
213 * for storing to be transmitted frames. The host needs to push
214 * them upstream. As a consequence there needs to be a way for
215 * the target to let us know if it can actually take more TX frames
216 * or not. This is what Tx credits are for.
217 *
218 * For each Tx HW queue, we have a Tx pool, and then we have one
219 * unique super pool (spool), which is actually a global pool of
220 * all the UMAC pages.
221 * For each Tx pool we have a min_pages, a max_pages fields, and a
222 * alloc_pages fields. The alloc_pages tracks the number of pages
223 * currently allocated from the tx pool.
224 * Here are the rules to check if given a tx frame we have enough
225 * tx credits for it:
226 * 1) We translate the frame length into a number of UMAC pages.
227 * Let's call them n_pages.
228 * 2) For the corresponding tx pool, we check if n_pages +
229 * pool->alloc_pages is higher than pool->min_pages. min_pages
230 * represent a set of pre-allocated pages on the tx pool. If
231 * that's the case, then we need to allocate those pages from
232 * the spool. We can do so until we reach spool->max_pages.
233 * 3) Each tx pool is not allowed to allocate more than pool->max_pages
234 * from the spool, so once we're over min_pages, we can allocate
235 * pages from the spool, but not more than max_pages.
236 *
237 * When the tx code path needs to send a tx frame, it checks first
238 * if it has enough tx credits, following those rules. [iwm_tx_credit_get]
239 * If it does, it then updates the pool and spool counters and
240 * then send the frame. [iwm_tx_credit_alloc and iwm_tx_credit_dec]
241 * On the other side, when the UMAC is done transmitting frames, it
242 * will send a credit update notification to the host. This is when
243 * the pool and spool counters gets to be decreased. [iwm_tx_credit_inc,
244 * called from rx.c:iwm_ntf_tx_credit_update]
245 *
246 */
247void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
248 struct iwm_umac_notif_alive *alive)
249{
250 int i, sid, pool_pages;
251
252 spin_lock(&iwm->tx_credit.lock);
253
254 iwm->tx_credit.pool_nr = le16_to_cpu(alive->page_grp_count);
255 iwm->tx_credit.full_pools_map = 0;
256 memset(&iwm->tx_credit.spools[0], 0, sizeof(struct spool_entry));
257
258 IWM_DBG_TX(iwm, DBG, "Pools number is %d\n", iwm->tx_credit.pool_nr);
259
260 for (i = 0; i < iwm->tx_credit.pool_nr; i++) {
261 __le32 page_grp_state = alive->page_grp_state[i];
262
263 iwm->tx_credit.pools[i].id = GET_VAL32(page_grp_state,
264 UMAC_ALIVE_PAGE_STS_GRP_NUM);
265 iwm->tx_credit.pools[i].sid = GET_VAL32(page_grp_state,
266 UMAC_ALIVE_PAGE_STS_SGRP_NUM);
267 iwm->tx_credit.pools[i].min_pages = GET_VAL32(page_grp_state,
268 UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE);
269 iwm->tx_credit.pools[i].max_pages = GET_VAL32(page_grp_state,
270 UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE);
271 iwm->tx_credit.pools[i].alloc_pages = 0;
272 iwm->tx_credit.pools[i].total_freed_pages = 0;
273
274 sid = iwm->tx_credit.pools[i].sid;
275 pool_pages = iwm->tx_credit.pools[i].min_pages;
276
277 if (iwm->tx_credit.spools[sid].max_pages == 0) {
278 iwm->tx_credit.spools[sid].id = sid;
279 iwm->tx_credit.spools[sid].max_pages =
280 GET_VAL32(page_grp_state,
281 UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE);
282 iwm->tx_credit.spools[sid].alloc_pages = 0;
283 }
284
285 iwm->tx_credit.spools[sid].alloc_pages += pool_pages;
286
287 IWM_DBG_TX(iwm, DBG, "Pool idx: %d, id: %d, sid: %d, capacity "
288 "min: %d, max: %d, pool alloc: %d, total_free: %d, "
289 "super poll alloc: %d\n",
290 i, iwm->tx_credit.pools[i].id,
291 iwm->tx_credit.pools[i].sid,
292 iwm->tx_credit.pools[i].min_pages,
293 iwm->tx_credit.pools[i].max_pages,
294 iwm->tx_credit.pools[i].alloc_pages,
295 iwm->tx_credit.pools[i].total_freed_pages,
296 iwm->tx_credit.spools[sid].alloc_pages);
297 }
298
299 spin_unlock(&iwm->tx_credit.lock);
300}
301
302#define IWM_UDMA_HDR_LEN sizeof(struct iwm_umac_wifi_out_hdr)
303
304static int iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb,
305 int pool_id, u8 *buf)
306{
307 struct iwm_umac_wifi_out_hdr *hdr = (struct iwm_umac_wifi_out_hdr *)buf;
308 struct iwm_udma_wifi_cmd udma_cmd;
309 struct iwm_umac_cmd umac_cmd;
310 struct iwm_tx_info *tx_info = skb_to_tx_info(skb);
311
312 udma_cmd.count = cpu_to_le16(skb->len +
313 sizeof(struct iwm_umac_fw_cmd_hdr));
314 /* set EOP to 0 here. iwm_udma_wifi_hdr_set_eop() will be
315 * called later to set EOP for the last packet. */
316 udma_cmd.eop = 0;
317 udma_cmd.credit_group = pool_id;
318 udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid;
319 udma_cmd.lmac_offset = 0;
320
321 umac_cmd.id = REPLY_TX;
322 umac_cmd.count = cpu_to_le16(skb->len);
323 umac_cmd.color = tx_info->color;
324 umac_cmd.resp = 0;
325 umac_cmd.seq_num = cpu_to_le16(iwm_alloc_wifi_cmd_seq(iwm));
326
327 iwm_build_udma_wifi_hdr(iwm, &hdr->hw_hdr, &udma_cmd);
328 iwm_build_umac_hdr(iwm, &hdr->sw_hdr, &umac_cmd);
329
330 memcpy(buf + sizeof(*hdr), skb->data, skb->len);
331
332 return 0;
333}
334
335static int iwm_tx_send_concat_packets(struct iwm_priv *iwm,
336 struct iwm_tx_queue *txq)
337{
338 int ret;
339
340 if (!txq->concat_count)
341 return 0;
342
343 IWM_DBG_TX(iwm, DBG, "Send concatenated Tx: queue %d, %d bytes\n",
344 txq->id, txq->concat_count);
345
346 /* mark EOP for the last packet */
347 iwm_udma_wifi_hdr_set_eop(iwm, txq->concat_ptr, 1);
348
349 ret = iwm_bus_send_chunk(iwm, txq->concat_buf, txq->concat_count);
350
351 txq->concat_count = 0;
352 txq->concat_ptr = txq->concat_buf;
353
354 return ret;
355}
356
357#define CONFIG_IWM_TX_CONCATENATED 1
358
359void iwm_tx_worker(struct work_struct *work)
360{
361 struct iwm_priv *iwm;
362 struct iwm_tx_info *tx_info = NULL;
363 struct sk_buff *skb;
364 int cmdlen, ret;
365 struct iwm_tx_queue *txq;
366 int pool_id;
367
368 txq = container_of(work, struct iwm_tx_queue, worker);
369 iwm = container_of(txq, struct iwm_priv, txq[txq->id]);
370
371 pool_id = queue_to_pool_id(txq->id);
372
373 while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
374 !skb_queue_empty(&txq->queue)) {
375
376 skb = skb_dequeue(&txq->queue);
377 tx_info = skb_to_tx_info(skb);
378 cmdlen = IWM_UDMA_HDR_LEN + skb->len;
379
380 IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: "
381 "%d, color: %d\n", txq->id, skb, tx_info->sta,
382 tx_info->color);
383
384#if !CONFIG_IWM_TX_CONCATENATED
385 /* temporarily keep this to comparing the performance */
386 ret = iwm_send_packet(iwm, skb, pool_id);
387#else
388
389 if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE)
390 iwm_tx_send_concat_packets(iwm, txq);
391
392 ret = iwm_tx_credit_alloc(iwm, pool_id, cmdlen);
393 if (ret) {
394 IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue "
395 "%d, Tx worker stopped\n", txq->id);
396 skb_queue_head(&txq->queue, skb);
397 break;
398 }
399
400 txq->concat_ptr = txq->concat_buf + txq->concat_count;
401 iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr);
402 txq->concat_count += ALIGN(cmdlen, 16);
403#endif
404 kfree_skb(skb);
405 }
406
407 iwm_tx_send_concat_packets(iwm, txq);
408
409 if (__netif_subqueue_stopped(iwm_to_ndev(iwm), txq->id) &&
410 !test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
411 (skb_queue_len(&txq->queue) < IWM_TX_LIST_SIZE / 2)) {
412 IWM_DBG_TX(iwm, DBG, "LINK: start netif_subqueue[%d]", txq->id);
413 netif_wake_subqueue(iwm_to_ndev(iwm), txq->id);
414 }
415}
416
417int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
418{
419 struct iwm_priv *iwm = ndev_to_iwm(netdev);
420 struct net_device *ndev = iwm_to_ndev(iwm);
421 struct wireless_dev *wdev = iwm_to_wdev(iwm);
422 u8 *dst_addr;
423 struct iwm_tx_info *tx_info;
424 struct iwm_tx_queue *txq;
425 struct iwm_sta_info *sta_info;
426 u8 sta_id;
427 u16 queue;
428 int ret;
429
430 if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
431 IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: "
432 "not associated\n");
433 netif_tx_stop_all_queues(netdev);
434 goto drop;
435 }
436
437 queue = skb_get_queue_mapping(skb);
438 BUG_ON(queue >= IWM_TX_DATA_QUEUES); /* no iPAN yet */
439
440 txq = &iwm->txq[queue];
441
442 /* No free space for Tx, tx_worker is too slow */
443 if (skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) {
444 IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue);
445 netif_stop_subqueue(netdev, queue);
446 return NETDEV_TX_BUSY;
447 }
448
449 ret = ieee80211_data_from_8023(skb, netdev->dev_addr, wdev->iftype,
450 iwm->bssid, 0);
451 if (ret) {
452 IWM_ERR(iwm, "build wifi header failed\n");
453 goto drop;
454 }
455
456 dst_addr = ((struct ieee80211_hdr *)(skb->data))->addr1;
457
458 for (sta_id = 0; sta_id < IWM_STA_TABLE_NUM; sta_id++) {
459 sta_info = &iwm->sta_table[sta_id];
460 if (sta_info->valid &&
461 !memcmp(dst_addr, sta_info->addr, ETH_ALEN))
462 break;
463 }
464
465 if (sta_id == IWM_STA_TABLE_NUM) {
466 IWM_ERR(iwm, "STA %pM not found in sta_table, Tx ignored\n",
467 dst_addr);
468 goto drop;
469 }
470
471 tx_info = skb_to_tx_info(skb);
472 tx_info->sta = sta_id;
473 tx_info->color = sta_info->color;
474 /* UMAC uses TID 8 (vs. 0) for non QoS packets */
475 if (sta_info->qos)
476 tx_info->tid = skb->priority;
477 else
478 tx_info->tid = IWM_UMAC_MGMT_TID;
479
480 skb_queue_tail(&iwm->txq[queue].queue, skb);
481
482 queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
483
484 ndev->stats.tx_packets++;
485 ndev->stats.tx_bytes += skb->len;
486 return NETDEV_TX_OK;
487
488 drop:
489 ndev->stats.tx_dropped++;
490 dev_kfree_skb_any(skb);
491 return NETDEV_TX_OK;
492}
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h
new file mode 100644
index 000000000000..4a95cce1f0a6
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/umac.h
@@ -0,0 +1,744 @@
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39#ifndef __IWM_UMAC_H__
40#define __IWM_UMAC_H__
41
42struct iwm_udma_in_hdr {
43 __le32 cmd;
44 __le32 size;
45} __attribute__ ((packed));
46
47struct iwm_udma_out_nonwifi_hdr {
48 __le32 cmd;
49 __le32 addr;
50 __le32 op1_sz;
51 __le32 op2;
52} __attribute__ ((packed));
53
54struct iwm_udma_out_wifi_hdr {
55 __le32 cmd;
56 __le32 meta_data;
57} __attribute__ ((packed));
58
59/* Sequence numbering */
60#define UMAC_WIFI_SEQ_NUM_BASE 1
61#define UMAC_WIFI_SEQ_NUM_MAX 0x4000
62#define UMAC_NONWIFI_SEQ_NUM_BASE 1
63#define UMAC_NONWIFI_SEQ_NUM_MAX 0x10
64
65/* MAC address address */
66#define WICO_MAC_ADDRESS_ADDR 0x604008F8
67
68/* RA / TID */
69#define UMAC_HDI_ACT_TBL_IDX_TID_POS 0
70#define UMAC_HDI_ACT_TBL_IDX_TID_SEED 0xF
71
72#define UMAC_HDI_ACT_TBL_IDX_RA_POS 4
73#define UMAC_HDI_ACT_TBL_IDX_RA_SEED 0xF
74
75#define UMAC_HDI_ACT_TBL_IDX_RA_UMAC 0xF
76#define UMAC_HDI_ACT_TBL_IDX_TID_UMAC 0x9
77#define UMAC_HDI_ACT_TBL_IDX_TID_LMAC 0xA
78
79#define UMAC_HDI_ACT_TBL_IDX_HOST_CMD \
80 ((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
81 (UMAC_HDI_ACT_TBL_IDX_TID_UMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
82#define UMAC_HDI_ACT_TBL_IDX_UMAC_CMD \
83 ((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
84 (UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
85
86/* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */
87#define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0
88#define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF
89
90/* iwm_umac_notif_alive.page_grp_state Super group number -- bits [7:4] */
91#define UMAC_ALIVE_PAGE_STS_SGRP_NUM_POS 4
92#define UMAC_ALIVE_PAGE_STS_SGRP_NUM_SEED 0xF
93
94/* iwm_umac_notif_alive.page_grp_state Group min size -- bits [15:8] */
95#define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_POS 8
96#define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_SEED 0xFF
97
98/* iwm_umac_notif_alive.page_grp_state Group max size -- bits [23:16] */
99#define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_POS 16
100#define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_SEED 0xFF
101
102/* iwm_umac_notif_alive.page_grp_state Super group max size -- bits [31:24] */
103#define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_POS 24
104#define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_SEED 0xFF
105
106/* Barkers */
107#define UMAC_REBOOT_BARKER 0xdeadbeef
108#define UMAC_ACK_BARKER 0xfeedbabe
109#define UMAC_PAD_TERMINAL 0xadadadad
110
111/* UMAC JMP address */
112#define UMAC_MU_FW_INST_DATA_12_ADDR 0xBF0000
113
114/* iwm_umac_hdi_out_hdr.cmd OP code -- bits [3:0] */
115#define UMAC_HDI_OUT_CMD_OPCODE_POS 0
116#define UMAC_HDI_OUT_CMD_OPCODE_SEED 0xF
117
118/* iwm_umac_hdi_out_hdr.cmd End-Of-Transfer -- bits [10:10] */
119#define UMAC_HDI_OUT_CMD_EOT_POS 10
120#define UMAC_HDI_OUT_CMD_EOT_SEED 0x1
121
122/* iwm_umac_hdi_out_hdr.cmd UTFD only usage -- bits [11:11] */
123#define UMAC_HDI_OUT_CMD_UTFD_ONLY_POS 11
124#define UMAC_HDI_OUT_CMD_UTFD_ONLY_SEED 0x1
125
126/* iwm_umac_hdi_out_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */
127#define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_POS 12
128#define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_SEED 0xF
129
130/* iwm_umac_hdi_out_hdr.cmd Signature -- bits [31:16] */
131#define UMAC_HDI_OUT_CMD_SIGNATURE_POS 16
132#define UMAC_HDI_OUT_CMD_SIGNATURE_SEED 0xFFFF
133
134/* iwm_umac_hdi_out_hdr.meta_data Byte count -- bits [11:0] */
135#define UMAC_HDI_OUT_BYTE_COUNT_POS 0
136#define UMAC_HDI_OUT_BYTE_COUNT_SEED 0xFFF
137
138/* iwm_umac_hdi_out_hdr.meta_data Credit group -- bits [15:12] */
139#define UMAC_HDI_OUT_CREDIT_GRP_POS 12
140#define UMAC_HDI_OUT_CREDIT_GRP_SEED 0xF
141
142/* iwm_umac_hdi_out_hdr.meta_data RA/TID -- bits [23:16] */
143#define UMAC_HDI_OUT_RATID_POS 16
144#define UMAC_HDI_OUT_RATID_SEED 0xFF
145
146/* iwm_umac_hdi_out_hdr.meta_data LMAC offset -- bits [31:24] */
147#define UMAC_HDI_OUT_LMAC_OFFSET_POS 24
148#define UMAC_HDI_OUT_LMAC_OFFSET_SEED 0xFF
149
150/* Signature */
151#define UMAC_HDI_OUT_SIGNATURE 0xCBBC
152
153/* buffer alignment */
154#define UMAC_HDI_BUF_ALIGN_MSK 0xF
155
156/* iwm_umac_hdi_in_hdr.cmd OP code -- bits [3:0] */
157#define UMAC_HDI_IN_CMD_OPCODE_POS 0
158#define UMAC_HDI_IN_CMD_OPCODE_SEED 0xF
159
160/* iwm_umac_hdi_in_hdr.cmd Non-WiFi API response -- bits [6:4] */
161#define UMAC_HDI_IN_CMD_NON_WIFI_RESP_POS 4
162#define UMAC_HDI_IN_CMD_NON_WIFI_RESP_SEED 0x7
163
164/* iwm_umac_hdi_in_hdr.cmd WiFi API source -- bits [5:4] */
165#define UMAC_HDI_IN_CMD_SOURCE_POS 4
166#define UMAC_HDI_IN_CMD_SOURCE_SEED 0x3
167
168/* iwm_umac_hdi_in_hdr.cmd WiFi API EOT -- bits [6:6] */
169#define UMAC_HDI_IN_CMD_EOT_POS 6
170#define UMAC_HDI_IN_CMD_EOT_SEED 0x1
171
172/* iwm_umac_hdi_in_hdr.cmd timestamp present -- bits [7:7] */
173#define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_POS 7
174#define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_SEED 0x1
175
176/* iwm_umac_hdi_in_hdr.cmd WiFi Non-last AMSDU -- bits [8:8] */
177#define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_POS 8
178#define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_SEED 0x1
179
180/* iwm_umac_hdi_in_hdr.cmd WiFi HW sequence number -- bits [31:9] */
181#define UMAC_HDI_IN_CMD_HW_SEQ_NUM_POS 9
182#define UMAC_HDI_IN_CMD_HW_SEQ_NUM_SEED 0x7FFFFF
183
184/* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */
185#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_POS 12
186#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_SEED 0xF
187
188/* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW signature -- bits [16:31] */
189#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_POS 16
190#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_SEED 0xFFFF
191
192/* Fixed Non-WiFi signature */
193#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG 0xCBBC
194
195/* IN NTFY op-codes */
196#define UMAC_NOTIFY_OPCODE_ALIVE 0xA1
197#define UMAC_NOTIFY_OPCODE_INIT_COMPLETE 0xA2
198#define UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS 0xA3
199#define UMAC_NOTIFY_OPCODE_ERROR 0xA4
200#define UMAC_NOTIFY_OPCODE_DEBUG 0xA5
201#define UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER 0xB0
202#define UMAC_NOTIFY_OPCODE_STATS 0xB1
203#define UMAC_NOTIFY_OPCODE_PAGE_DEALLOC 0xB3
204#define UMAC_NOTIFY_OPCODE_RX_TICKET 0xB4
205#define UMAC_NOTIFY_OPCODE_MAX (UMAC_NOTIFY_OPCODE_RX_TICKET -\
206 UMAC_NOTIFY_OPCODE_ALIVE + 1)
207#define UMAC_NOTIFY_OPCODE_FIRST (UMAC_NOTIFY_OPCODE_ALIVE)
208
209/* HDI OUT OP CODE */
210#define UMAC_HDI_OUT_OPCODE_PING 0x0
211#define UMAC_HDI_OUT_OPCODE_READ 0x1
212#define UMAC_HDI_OUT_OPCODE_WRITE 0x2
213#define UMAC_HDI_OUT_OPCODE_JUMP 0x3
214#define UMAC_HDI_OUT_OPCODE_REBOOT 0x4
215#define UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT 0x5
216#define UMAC_HDI_OUT_OPCODE_READ_PERSISTENT 0x6
217#define UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE 0x7
218/* #define UMAC_HDI_OUT_OPCODE_RESERVED 0x8..0xA */
219#define UMAC_HDI_OUT_OPCODE_WRITE_AUX_REG 0xB
220#define UMAC_HDI_OUT_OPCODE_WIFI 0xF
221
222/* HDI IN OP CODE -- Non WiFi*/
223#define UMAC_HDI_IN_OPCODE_PING 0x0
224#define UMAC_HDI_IN_OPCODE_READ 0x1
225#define UMAC_HDI_IN_OPCODE_WRITE 0x2
226#define UMAC_HDI_IN_OPCODE_WRITE_PERSISTENT 0x5
227#define UMAC_HDI_IN_OPCODE_READ_PERSISTENT 0x6
228#define UMAC_HDI_IN_OPCODE_READ_MODIFY_WRITE 0x7
229#define UMAC_HDI_IN_OPCODE_EP_MGMT 0x8
230#define UMAC_HDI_IN_OPCODE_CREDIT_CHANGE 0x9
231#define UMAC_HDI_IN_OPCODE_CTRL_DATABASE 0xA
232#define UMAC_HDI_IN_OPCODE_WRITE_AUX_REG 0xB
233#define UMAC_HDI_IN_OPCODE_NONWIFI_MAX \
234 (UMAC_HDI_IN_OPCODE_WRITE_AUX_REG + 1)
235#define UMAC_HDI_IN_OPCODE_WIFI 0xF
236
237/* HDI IN SOURCE */
238#define UMAC_HDI_IN_SOURCE_FHRX 0x0
239#define UMAC_HDI_IN_SOURCE_UDMA 0x1
240#define UMAC_HDI_IN_SOURCE_FW 0x2
241#define UMAC_HDI_IN_SOURCE_RESERVED 0x3
242
243/* OUT CMD op-codes */
244#define UMAC_CMD_OPCODE_ECHO 0x01
245#define UMAC_CMD_OPCODE_HALT 0x02
246#define UMAC_CMD_OPCODE_RESET 0x03
247#define UMAC_CMD_OPCODE_BULK_EP_INACT_TIMEOUT 0x09
248#define UMAC_CMD_OPCODE_URB_CANCEL_ACK 0x0A
249#define UMAC_CMD_OPCODE_DCACHE_FLUSH 0x0B
250#define UMAC_CMD_OPCODE_EEPROM_PROXY 0x0C
251#define UMAC_CMD_OPCODE_TX_ECHO 0x0D
252#define UMAC_CMD_OPCODE_DBG_MON 0x0E
253#define UMAC_CMD_OPCODE_INTERNAL_TX 0x0F
254#define UMAC_CMD_OPCODE_SET_PARAM_FIX 0x10
255#define UMAC_CMD_OPCODE_SET_PARAM_VAR 0x11
256#define UMAC_CMD_OPCODE_GET_PARAM 0x12
257#define UMAC_CMD_OPCODE_DBG_EVENT_WRAPPER 0x13
258#define UMAC_CMD_OPCODE_TARGET 0x14
259#define UMAC_CMD_OPCODE_STATISTIC_REQUEST 0x15
260#define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16
261#define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17
262#define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18
263#define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA
264#define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB
265#define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC
266#define UMAC_CMD_OPCODE_WIFI_IF_WRAPPER 0xFD
267#define UMAC_CMD_OPCODE_WIFI_WRAPPER 0xFE
268#define UMAC_CMD_OPCODE_WIFI_PASS_THROUGH 0xFF
269
270/* UMAC WiFi interface op-codes */
271#define UMAC_WIFI_IF_CMD_SET_PROFILE 0x11
272#define UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE 0x12
273#define UMAC_WIFI_IF_CMD_SET_EXCLUDE_LIST 0x13
274#define UMAC_WIFI_IF_CMD_SCAN_REQUEST 0x14
275#define UMAC_WIFI_IF_CMD_SCAN_CONFIG 0x15
276#define UMAC_WIFI_IF_CMD_ADD_WEP40_KEY 0x16
277#define UMAC_WIFI_IF_CMD_ADD_WEP104_KEY 0x17
278#define UMAC_WIFI_IF_CMD_ADD_TKIP_KEY 0x18
279#define UMAC_WIFI_IF_CMD_ADD_CCMP_KEY 0x19
280#define UMAC_WIFI_IF_CMD_REMOVE_KEY 0x1A
281#define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B
282#define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C
283#define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E
284#define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20
285
286/* UMAC WiFi interface ports */
287#define UMAC_WIFI_IF_FLG_PORT_DEF 0x00
288#define UMAC_WIFI_IF_FLG_PORT_PAN 0x01
289#define UMAC_WIFI_IF_FLG_PORT_PAN_INVALID WIFI_IF_FLG_PORT_DEF
290
291/* UMAC WiFi interface actions */
292#define UMAC_WIFI_IF_FLG_ACT_GET 0x10
293#define UMAC_WIFI_IF_FLG_ACT_SET 0x20
294
295/* iwm_umac_fw_cmd_hdr.meta_data byte count -- bits [11:0] */
296#define UMAC_FW_CMD_BYTE_COUNT_POS 0
297#define UMAC_FW_CMD_BYTE_COUNT_SEED 0xFFF
298
299/* iwm_umac_fw_cmd_hdr.meta_data status -- bits [15:12] */
300#define UMAC_FW_CMD_STATUS_POS 12
301#define UMAC_FW_CMD_STATUS_SEED 0xF
302
303/* iwm_umac_fw_cmd_hdr.meta_data full TX command by Driver -- bits [16:16] */
304#define UMAC_FW_CMD_TX_DRV_FULL_CMD_POS 16
305#define UMAC_FW_CMD_TX_DRV_FULL_CMD_SEED 0x1
306
307/* iwm_umac_fw_cmd_hdr.meta_data TX command by FW -- bits [17:17] */
308#define UMAC_FW_CMD_TX_FW_CMD_POS 17
309#define UMAC_FW_CMD_TX_FW_CMD_SEED 0x1
310
311/* iwm_umac_fw_cmd_hdr.meta_data TX plaintext mode -- bits [18:18] */
312#define UMAC_FW_CMD_TX_PLAINTEXT_POS 18
313#define UMAC_FW_CMD_TX_PLAINTEXT_SEED 0x1
314
315/* iwm_umac_fw_cmd_hdr.meta_data STA color -- bits [22:20] */
316#define UMAC_FW_CMD_TX_STA_COLOR_POS 20
317#define UMAC_FW_CMD_TX_STA_COLOR_SEED 0x7
318
319/* iwm_umac_fw_cmd_hdr.meta_data TX life time (TU) -- bits [31:24] */
320#define UMAC_FW_CMD_TX_LIFETIME_TU_POS 24
321#define UMAC_FW_CMD_TX_LIFETIME_TU_SEED 0xFF
322
323/* iwm_dev_cmd_hdr.flags Response required -- bits [5:5] */
324#define UMAC_DEV_CMD_FLAGS_RESP_REQ_POS 5
325#define UMAC_DEV_CMD_FLAGS_RESP_REQ_SEED 0x1
326
327/* iwm_dev_cmd_hdr.flags Aborted command -- bits [6:6] */
328#define UMAC_DEV_CMD_FLAGS_ABORT_POS 6
329#define UMAC_DEV_CMD_FLAGS_ABORT_SEED 0x1
330
331/* iwm_dev_cmd_hdr.flags Internal command -- bits [7:7] */
332#define DEV_CMD_FLAGS_FLD_INTERNAL_POS 7
333#define DEV_CMD_FLAGS_FLD_INTERNAL_SEED 0x1
334
335/* Rx */
336/* Rx actions */
337#define IWM_RX_TICKET_DROP 0x0
338#define IWM_RX_TICKET_RELEASE 0x1
339#define IWM_RX_TICKET_SNIFFER 0x2
340#define IWM_RX_TICKET_ENQUEUE 0x3
341
342/* Rx flags */
343#define IWM_RX_TICKET_PAD_SIZE_MSK 0x2
344#define IWM_RX_TICKET_SPECIAL_SNAP_MSK 0x4
345#define IWM_RX_TICKET_AMSDU_MSK 0x8
346#define IWM_RX_TICKET_DROP_REASON_POS 4
347#define IWM_RX_TICKET_DROP_REASON_MSK (0x1F << RX_TICKET_FLAGS_DROP_REASON_POS)
348
349#define IWM_RX_DROP_NO_DROP 0x0
350#define IWM_RX_DROP_BAD_CRC 0x1
351/* L2P no address match */
352#define IWM_RX_DROP_LMAC_ADDR_FILTER 0x2
353/* Multicast address not in list */
354#define IWM_RX_DROP_MCAST_ADDR_FILTER 0x3
355/* Control frames are not sent to the driver */
356#define IWM_RX_DROP_CTL_FRAME 0x4
357/* Our frame is back */
358#define IWM_RX_DROP_OUR_TX 0x5
359/* Association class filtering */
360#define IWM_RX_DROP_CLASS_FILTER 0x6
361/* Duplicated frame */
362#define IWM_RX_DROP_DUPLICATE_FILTER 0x7
363/* Decryption error */
364#define IWM_RX_DROP_SEC_ERR 0x8
365/* Unencrypted frame while encryption is on */
366#define IWM_RX_DROP_SEC_NO_ENCRYPTION 0x9
367/* Replay check failure */
368#define IWM_RX_DROP_SEC_REPLAY_ERR 0xa
369/* uCode and FW key color mismatch, check before replay */
370#define IWM_RX_DROP_SEC_KEY_COLOR_MISMATCH 0xb
371#define IWM_RX_DROP_SEC_TKIP_COUNTER_MEASURE 0xc
372/* No fragmentations Db is found */
373#define IWM_RX_DROP_FRAG_NO_RESOURCE 0xd
374/* Fragmention Db has seqCtl mismatch Vs. non-1st frag */
375#define IWM_RX_DROP_FRAG_ERR 0xe
376#define IWM_RX_DROP_FRAG_LOST 0xf
377#define IWM_RX_DROP_FRAG_COMPLETE 0x10
378/* Should be handled by UMAC */
379#define IWM_RX_DROP_MANAGEMENT 0x11
380/* STA not found by UMAC */
381#define IWM_RX_DROP_NO_STATION 0x12
382/* NULL or QoS NULL */
383#define IWM_RX_DROP_NULL_DATA 0x13
384#define IWM_RX_DROP_BA_REORDER_OLD_SEQCTL 0x14
385#define IWM_RX_DROP_BA_REORDER_DUPLICATE 0x15
386
387struct iwm_rx_ticket {
388 __le16 action;
389 __le16 id;
390 __le16 flags;
391 u8 payload_offset; /* includes: MAC header, pad, IV */
392 u8 tail_len; /* includes: MIC, ICV, CRC (w/o STATUS) */
393} __attribute__ ((packed));
394
395struct iwm_rx_mpdu_hdr {
396 __le16 len;
397 __le16 reserved;
398} __attribute__ ((packed));
399
400/* UMAC SW WIFI API */
401
402struct iwm_dev_cmd_hdr {
403 u8 cmd;
404 u8 flags;
405 __le16 seq_num;
406} __attribute__ ((packed));
407
408struct iwm_umac_fw_cmd_hdr {
409 __le32 meta_data;
410 struct iwm_dev_cmd_hdr cmd;
411} __attribute__ ((packed));
412
413struct iwm_umac_wifi_out_hdr {
414 struct iwm_udma_out_wifi_hdr hw_hdr;
415 struct iwm_umac_fw_cmd_hdr sw_hdr;
416} __attribute__ ((packed));
417
418struct iwm_umac_nonwifi_out_hdr {
419 struct iwm_udma_out_nonwifi_hdr hw_hdr;
420} __attribute__ ((packed));
421
422struct iwm_umac_wifi_in_hdr {
423 struct iwm_udma_in_hdr hw_hdr;
424 struct iwm_umac_fw_cmd_hdr sw_hdr;
425} __attribute__ ((packed));
426
427struct iwm_umac_nonwifi_in_hdr {
428 struct iwm_udma_in_hdr hw_hdr;
429 __le32 time_stamp;
430} __attribute__ ((packed));
431
432#define IWM_UMAC_PAGE_SIZE 0x200
433
434/* Notify structures */
435struct iwm_fw_version {
436 u8 minor;
437 u8 major;
438 __le16 id;
439};
440
441struct iwm_fw_build {
442 u8 type;
443 u8 subtype;
444 u8 platform;
445 u8 opt;
446};
447
448struct iwm_fw_alive_hdr {
449 struct iwm_fw_version ver;
450 struct iwm_fw_build build;
451 __le32 os_build;
452 __le32 log_hdr_addr;
453 __le32 log_buf_addr;
454 __le32 sys_timer_addr;
455};
456
457#define WAIT_NOTIF_TIMEOUT (2 * HZ)
458#define SCAN_COMPLETE_TIMEOUT (3 * HZ)
459
460#define UMAC_NTFY_ALIVE_STATUS_ERR 0xDEAD
461#define UMAC_NTFY_ALIVE_STATUS_OK 0xCAFE
462
463#define UMAC_NTFY_INIT_COMPLETE_STATUS_ERR 0xDEAD
464#define UMAC_NTFY_INIT_COMPLETE_STATUS_OK 0xCAFE
465
466#define UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN 0x40
467#define UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN 0x80
468
469#define IWM_MACS_OUT_GROUPS 6
470#define IWM_MACS_OUT_SGROUPS 1
471
472
473#define WIFI_IF_NTFY_ASSOC_START 0x80
474#define WIFI_IF_NTFY_ASSOC_COMPLETE 0x81
475#define WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE 0x82
476#define WIFI_IF_NTFY_CONNECTION_TERMINATED 0x83
477#define WIFI_IF_NTFY_SCAN_COMPLETE 0x84
478#define WIFI_IF_NTFY_STA_TABLE_CHANGE 0x85
479#define WIFI_IF_NTFY_EXTENDED_IE_REQUIRED 0x86
480#define WIFI_IF_NTFY_RADIO_PREEMPTION 0x87
481#define WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED 0x88
482#define WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED 0x89
483#define WIFI_IF_NTFY_LINK_QUALITY_STATISTICS 0x8A
484#define WIFI_IF_NTFY_MGMT_FRAME 0x8B
485
486/* DEBUG INDICATIONS */
487#define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START 0xE0
488#define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE 0xE1
489#define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START 0xE2
490#define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT 0xE3
491#define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START 0xE4
492#define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE 0xE5
493#define WIFI_DBG_IF_NTFY_CNCT_ATC_START 0xE6
494#define WIFI_DBG_IF_NTFY_COEX_NOTIFICATION 0xE7
495#define WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP 0xE8
496#define WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP 0xE9
497
498/* Notification structures */
499struct iwm_umac_notif_wifi_if {
500 struct iwm_umac_wifi_in_hdr hdr;
501 u8 status;
502 u8 flags;
503 __le16 buf_size;
504} __attribute__ ((packed));
505
506#define UMAC_ROAM_REASON_FIRST_SELECTION 0x1
507#define UMAC_ROAM_REASON_AP_DEAUTH 0x2
508#define UMAC_ROAM_REASON_AP_CONNECT_LOST 0x3
509#define UMAC_ROAM_REASON_RSSI 0x4
510#define UMAC_ROAM_REASON_AP_ASSISTED_ROAM 0x5
511#define UMAC_ROAM_REASON_IBSS_COALESCING 0x6
512
513struct iwm_umac_notif_assoc_start {
514 struct iwm_umac_notif_wifi_if mlme_hdr;
515 __le32 roam_reason;
516 u8 bssid[ETH_ALEN];
517 u8 reserved[2];
518} __attribute__ ((packed));
519
520#define UMAC_ASSOC_COMPLETE_SUCCESS 0x0
521#define UMAC_ASSOC_COMPLETE_FAILURE 0x1
522
523struct iwm_umac_notif_assoc_complete {
524 struct iwm_umac_notif_wifi_if mlme_hdr;
525 __le32 status;
526 u8 bssid[ETH_ALEN];
527 u8 band;
528 u8 channel;
529} __attribute__ ((packed));
530
531#define UMAC_PROFILE_INVALID_ASSOC_TIMEOUT 0x0
532#define UMAC_PROFILE_INVALID_ROAM_TIMEOUT 0x1
533#define UMAC_PROFILE_INVALID_REQUEST 0x2
534#define UMAC_PROFILE_INVALID_RF_PREEMPTED 0x3
535
536struct iwm_umac_notif_profile_invalidate {
537 struct iwm_umac_notif_wifi_if mlme_hdr;
538 __le32 reason;
539} __attribute__ ((packed));
540
541#define UMAC_SCAN_RESULT_SUCCESS 0x0
542#define UMAC_SCAN_RESULT_ABORTED 0x1
543#define UMAC_SCAN_RESULT_REJECTED 0x2
544#define UMAC_SCAN_RESULT_FAILED 0x3
545
546struct iwm_umac_notif_scan_complete {
547 struct iwm_umac_notif_wifi_if mlme_hdr;
548 __le32 type;
549 __le32 result;
550 u8 seq_num;
551} __attribute__ ((packed));
552
553#define UMAC_OPCODE_ADD_MODIFY 0x0
554#define UMAC_OPCODE_REMOVE 0x1
555#define UMAC_OPCODE_CLEAR_ALL 0x2
556
557#define UMAC_STA_FLAG_QOS 0x1
558
559struct iwm_umac_notif_sta_info {
560 struct iwm_umac_notif_wifi_if mlme_hdr;
561 __le32 opcode;
562 u8 mac_addr[ETH_ALEN];
563 u8 sta_id; /* bits 0-3: station ID, bits 4-7: station color */
564 u8 flags;
565} __attribute__ ((packed));
566
567#define UMAC_BAND_2GHZ 0
568#define UMAC_BAND_5GHZ 1
569
570#define UMAC_CHANNEL_WIDTH_20MHZ 0
571#define UMAC_CHANNEL_WIDTH_40MHZ 1
572
573struct iwm_umac_notif_bss_info {
574 struct iwm_umac_notif_wifi_if mlme_hdr;
575 __le32 type;
576 __le32 timestamp;
577 __le16 table_idx;
578 __le16 frame_len;
579 u8 band;
580 u8 channel;
581 s8 rssi;
582 u8 reserved;
583 u8 frame_buf[1];
584} __attribute__ ((packed));
585
586#define IWM_BSS_REMOVE_INDEX_MSK 0x0fff
587#define IWM_BSS_REMOVE_FLAGS_MSK 0xfc00
588
589#define IWM_BSS_REMOVE_FLG_AGE 0x1000
590#define IWM_BSS_REMOVE_FLG_TIMEOUT 0x2000
591#define IWM_BSS_REMOVE_FLG_TABLE_FULL 0x4000
592
593struct iwm_umac_notif_bss_removed {
594 struct iwm_umac_notif_wifi_if mlme_hdr;
595 __le32 count;
596 __le16 entries[0];
597} __attribute__ ((packed));
598
599struct iwm_umac_notif_mgt_frame {
600 struct iwm_umac_notif_wifi_if mlme_hdr;
601 __le16 len;
602 u8 frame[1];
603} __attribute__ ((packed));
604
605struct iwm_umac_notif_alive {
606 struct iwm_umac_wifi_in_hdr hdr;
607 __le16 status;
608 __le16 reserved1;
609 struct iwm_fw_alive_hdr alive_data;
610 __le16 reserved2;
611 __le16 page_grp_count;
612 __le32 page_grp_state[IWM_MACS_OUT_GROUPS];
613} __attribute__ ((packed));
614
615struct iwm_umac_notif_init_complete {
616 __le16 status;
617 __le16 reserved;
618} __attribute__ ((packed));
619
620/* error categories */
621enum {
622 UMAC_SYS_ERR_CAT_NONE = 0,
623 UMAC_SYS_ERR_CAT_BOOT,
624 UMAC_SYS_ERR_CAT_UMAC,
625 UMAC_SYS_ERR_CAT_UAXM,
626 UMAC_SYS_ERR_CAT_LMAC,
627 UMAC_SYS_ERR_CAT_MAX
628};
629
630struct iwm_fw_error_hdr {
631 __le32 category;
632 __le32 status;
633 __le32 pc;
634 __le32 blink1;
635 __le32 blink2;
636 __le32 ilink1;
637 __le32 ilink2;
638 __le32 data1;
639 __le32 data2;
640 __le32 line_num;
641 __le32 umac_status;
642 __le32 lmac_status;
643 __le32 sdio_status;
644} __attribute__ ((packed));
645
646struct iwm_umac_notif_error {
647 struct iwm_umac_wifi_in_hdr hdr;
648 struct iwm_fw_error_hdr err;
649} __attribute__ ((packed));
650
651#define UMAC_DEALLOC_NTFY_CHANGES_CNT_POS 0
652#define UMAC_DEALLOC_NTFY_CHANGES_CNT_SEED 0xff
653#define UMAC_DEALLOC_NTFY_CHANGES_MSK_POS 8
654#define UMAC_DEALLOC_NTFY_CHANGES_MSK_SEED 0xffffff
655#define UMAC_DEALLOC_NTFY_PAGE_CNT_POS 0
656#define UMAC_DEALLOC_NTFY_PAGE_CNT_SEED 0xffffff
657#define UMAC_DEALLOC_NTFY_GROUP_NUM_POS 24
658#define UMAC_DEALLOC_NTFY_GROUP_NUM_SEED 0xf
659
660struct iwm_umac_notif_page_dealloc {
661 struct iwm_umac_wifi_in_hdr hdr;
662 __le32 changes;
663 __le32 grp_info[IWM_MACS_OUT_GROUPS];
664} __attribute__ ((packed));
665
666struct iwm_umac_notif_wifi_status {
667 struct iwm_umac_wifi_in_hdr hdr;
668 __le16 status;
669 __le16 reserved;
670} __attribute__ ((packed));
671
672struct iwm_umac_notif_rx_ticket {
673 struct iwm_umac_wifi_in_hdr hdr;
674 u8 num_tickets;
675 u8 reserved[3];
676 struct iwm_rx_ticket tickets[1];
677} __attribute__ ((packed));
678
679/* Tx/Rx rates window (number of max of last update window per second) */
680#define UMAC_NTF_RATE_SAMPLE_NR 4
681
682#define IWM_UMAC_MGMT_TID 8
683#define IWM_UMAC_TID_NR 8
684
685struct iwm_umac_notif_stats {
686 struct iwm_umac_wifi_in_hdr hdr;
687 __le32 flags;
688 __le32 timestamp;
689 __le16 tid_load[IWM_UMAC_TID_NR + 2]; /* 1 non-QoS + 1 dword align */
690 __le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR];
691 __le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR];
692 s32 rssi_dbm;
693 s32 noise_dbm;
694 __le32 supp_rates;
695 __le32 missed_beacons;
696 __le32 rx_beacons;
697 __le32 rx_dir_pkts;
698 __le32 rx_nondir_pkts;
699 __le32 rx_multicast;
700 __le32 rx_errors;
701 __le32 rx_drop_other_bssid;
702 __le32 rx_drop_decode;
703 __le32 rx_drop_reassembly;
704 __le32 rx_drop_bad_len;
705 __le32 rx_drop_overflow;
706 __le32 rx_drop_crc;
707 __le32 rx_drop_missed;
708 __le32 tx_dir_pkts;
709 __le32 tx_nondir_pkts;
710 __le32 tx_failure;
711 __le32 tx_errors;
712 __le32 tx_drop_max_retry;
713 __le32 tx_err_abort;
714 __le32 tx_err_carrier;
715 __le32 rx_bytes;
716 __le32 tx_bytes;
717 __le32 tx_power;
718 __le32 tx_max_power;
719 __le32 roam_threshold;
720 __le32 ap_assoc_nr;
721 __le32 scan_full;
722 __le32 scan_abort;
723 __le32 ap_nr;
724 __le32 roam_nr;
725 __le32 roam_missed_beacons;
726 __le32 roam_rssi;
727 __le32 roam_unassoc;
728 __le32 roam_deauth;
729 __le32 roam_ap_loadblance;
730} __attribute__ ((packed));
731
732/* WiFi interface wrapper header */
733struct iwm_umac_wifi_if {
734 u8 oid;
735 u8 flags;
736 __le16 buf_size;
737} __attribute__ ((packed));
738
739#define IWM_SEQ_NUM_HOST_MSK 0x0000
740#define IWM_SEQ_NUM_UMAC_MSK 0x4000
741#define IWM_SEQ_NUM_LMAC_MSK 0x8000
742#define IWM_SEQ_NUM_MSK 0xC000
743
744#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/wext.c b/drivers/net/wireless/iwmc3200wifi/wext.c
new file mode 100644
index 000000000000..584c94d0f399
--- /dev/null
+++ b/drivers/net/wireless/iwmc3200wifi/wext.c
@@ -0,0 +1,723 @@
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/if_arp.h>
28#include <linux/etherdevice.h>
29#include <net/cfg80211.h>
30#include <net/iw_handler.h>
31
32#include "iwm.h"
33#include "umac.h"
34#include "commands.h"
35#include "debug.h"
36
37static struct iw_statistics *iwm_get_wireless_stats(struct net_device *dev)
38{
39 struct iwm_priv *iwm = ndev_to_iwm(dev);
40 struct iw_statistics *wstats = &iwm->wstats;
41
42 if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
43 memset(wstats, 0, sizeof(struct iw_statistics));
44 wstats->qual.updated = IW_QUAL_ALL_INVALID;
45 }
46
47 return wstats;
48}
49
50static int iwm_wext_siwfreq(struct net_device *dev,
51 struct iw_request_info *info,
52 struct iw_freq *freq, char *extra)
53{
54 struct iwm_priv *iwm = ndev_to_iwm(dev);
55
56 if (freq->flags == IW_FREQ_AUTO)
57 return 0;
58
59 /* frequency/channel can only be set in IBSS mode */
60 if (iwm->conf.mode != UMAC_MODE_IBSS)
61 return -EOPNOTSUPP;
62
63 return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
64}
65
66static int iwm_wext_giwfreq(struct net_device *dev,
67 struct iw_request_info *info,
68 struct iw_freq *freq, char *extra)
69{
70 struct iwm_priv *iwm = ndev_to_iwm(dev);
71
72 if (iwm->conf.mode == UMAC_MODE_IBSS)
73 return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
74
75 freq->e = 0;
76 freq->m = iwm->channel;
77
78 return 0;
79}
80
81static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
82 struct sockaddr *ap_addr, char *extra)
83{
84 struct iwm_priv *iwm = ndev_to_iwm(dev);
85
86 if (iwm->conf.mode == UMAC_MODE_IBSS)
87 return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
88
89 if (!test_bit(IWM_STATUS_READY, &iwm->status))
90 return -EIO;
91
92 if (is_zero_ether_addr(ap_addr->sa_data) ||
93 is_broadcast_ether_addr(ap_addr->sa_data)) {
94 IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n",
95 iwm->umac_profile->bssid[0]);
96 memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
97 iwm->umac_profile->bss_num = 0;
98 } else {
99 IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n",
100 ap_addr->sa_data);
101 memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data,
102 ETH_ALEN);
103 iwm->umac_profile->bss_num = 1;
104 }
105
106 if (iwm->umac_profile_active) {
107 if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
108 return 0;
109
110 iwm_invalidate_mlme_profile(iwm);
111 }
112
113 if (iwm->umac_profile->ssid.ssid_len)
114 return iwm_send_mlme_profile(iwm);
115
116 return 0;
117}
118
119static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info,
120 struct sockaddr *ap_addr, char *extra)
121{
122 struct iwm_priv *iwm = ndev_to_iwm(dev);
123
124 switch (iwm->conf.mode) {
125 case UMAC_MODE_IBSS:
126 return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
127 case UMAC_MODE_BSS:
128 if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
129 ap_addr->sa_family = ARPHRD_ETHER;
130 memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN);
131 } else
132 memset(&ap_addr->sa_data, 0, ETH_ALEN);
133 break;
134 default:
135 return -EOPNOTSUPP;
136 }
137
138 return 0;
139}
140
141static int iwm_wext_siwessid(struct net_device *dev,
142 struct iw_request_info *info,
143 struct iw_point *data, char *ssid)
144{
145 struct iwm_priv *iwm = ndev_to_iwm(dev);
146 size_t len = data->length;
147 int ret;
148
149 if (iwm->conf.mode == UMAC_MODE_IBSS)
150 return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
151
152 if (!test_bit(IWM_STATUS_READY, &iwm->status))
153 return -EIO;
154
155 if (len > 0 && ssid[len - 1] == '\0')
156 len--;
157
158 if (iwm->umac_profile_active) {
159 if (iwm->umac_profile->ssid.ssid_len == len &&
160 !memcmp(iwm->umac_profile->ssid.ssid, ssid, len))
161 return 0;
162
163 ret = iwm_invalidate_mlme_profile(iwm);
164 if (ret < 0) {
165 IWM_ERR(iwm, "Couldn't invalidate profile\n");
166 return ret;
167 }
168 }
169
170 iwm->umac_profile->ssid.ssid_len = len;
171 memcpy(iwm->umac_profile->ssid.ssid, ssid, len);
172
173 return iwm_send_mlme_profile(iwm);
174}
175
176static int iwm_wext_giwessid(struct net_device *dev,
177 struct iw_request_info *info,
178 struct iw_point *data, char *ssid)
179{
180 struct iwm_priv *iwm = ndev_to_iwm(dev);
181
182 if (iwm->conf.mode == UMAC_MODE_IBSS)
183 return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
184
185 if (!test_bit(IWM_STATUS_READY, &iwm->status))
186 return -EIO;
187
188 data->length = iwm->umac_profile->ssid.ssid_len;
189 if (data->length) {
190 memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length);
191 data->flags = 1;
192 } else
193 data->flags = 0;
194
195 return 0;
196}
197
198static struct iwm_key *
199iwm_key_init(struct iwm_priv *iwm, u8 key_idx, bool in_use,
200 struct iw_encode_ext *ext, u8 alg)
201{
202 struct iwm_key *key = &iwm->keys[key_idx];
203
204 memset(key, 0, sizeof(struct iwm_key));
205 memcpy(key->hdr.mac, ext->addr.sa_data, ETH_ALEN);
206 key->hdr.key_idx = key_idx;
207 if (is_broadcast_ether_addr(ext->addr.sa_data))
208 key->hdr.multicast = 1;
209
210 key->in_use = in_use;
211 key->flags = ext->ext_flags;
212 key->alg = alg;
213 key->key_len = ext->key_len;
214 memcpy(key->key, ext->key, ext->key_len);
215
216 return key;
217}
218
219static int iwm_wext_giwrate(struct net_device *dev,
220 struct iw_request_info *info,
221 struct iw_param *rate, char *extra)
222{
223 struct iwm_priv *iwm = ndev_to_iwm(dev);
224
225 rate->value = iwm->rate * 1000000;
226
227 return 0;
228}
229
230static int iwm_wext_siwencode(struct net_device *dev,
231 struct iw_request_info *info,
232 struct iw_point *erq, char *key_buf)
233{
234 struct iwm_priv *iwm = ndev_to_iwm(dev);
235 struct iwm_key *uninitialized_var(key);
236 int idx, i, uninitialized_var(alg), remove = 0, ret;
237
238 IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", erq->length);
239 IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
240
241 if (!iwm->umac_profile) {
242 IWM_ERR(iwm, "UMAC profile not allocated yet\n");
243 return -ENODEV;
244 }
245
246 if (erq->length == WLAN_KEY_LEN_WEP40) {
247 alg = UMAC_CIPHER_TYPE_WEP_40;
248 iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_40;
249 iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
250 } else if (erq->length == WLAN_KEY_LEN_WEP104) {
251 alg = UMAC_CIPHER_TYPE_WEP_104;
252 iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_104;
253 iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_104;
254 }
255
256 if (erq->flags & IW_ENCODE_RESTRICTED)
257 iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
258 else
259 iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
260
261 idx = erq->flags & IW_ENCODE_INDEX;
262 if (idx == 0) {
263 if (iwm->default_key)
264 for (i = 0; i < IWM_NUM_KEYS; i++) {
265 if (iwm->default_key == &iwm->keys[i]) {
266 idx = i;
267 break;
268 }
269 }
270 else
271 iwm->default_key = &iwm->keys[idx];
272 } else if (idx < 1 || idx > 4) {
273 return -EINVAL;
274 } else
275 idx--;
276
277 if (erq->flags & IW_ENCODE_DISABLED)
278 remove = 1;
279 else if (erq->length == 0) {
280 if (!iwm->keys[idx].in_use)
281 return -EINVAL;
282 iwm->default_key = &iwm->keys[idx];
283 }
284
285 if (erq->length) {
286 key = &iwm->keys[idx];
287 memset(key, 0, sizeof(struct iwm_key));
288 memset(key->hdr.mac, 0xff, ETH_ALEN);
289 key->hdr.key_idx = idx;
290 key->hdr.multicast = 1;
291 key->in_use = !remove;
292 key->alg = alg;
293 key->key_len = erq->length;
294 memcpy(key->key, key_buf, erq->length);
295
296 IWM_DBG_WEXT(iwm, DBG, "Setting key %d, default: %d\n",
297 idx, !!iwm->default_key);
298 }
299
300 if (remove) {
301 if ((erq->flags & IW_ENCODE_NOKEY) || (erq->length == 0)) {
302 int j;
303 for (j = 0; j < IWM_NUM_KEYS; j++)
304 if (iwm->keys[j].in_use) {
305 struct iwm_key *k = &iwm->keys[j];
306
307 k->in_use = 0;
308 ret = iwm_set_key(iwm, remove, 0, k);
309 if (ret < 0)
310 return ret;
311 }
312
313 iwm->umac_profile->sec.ucast_cipher =
314 UMAC_CIPHER_TYPE_NONE;
315 iwm->umac_profile->sec.mcast_cipher =
316 UMAC_CIPHER_TYPE_NONE;
317 iwm->umac_profile->sec.auth_type =
318 UMAC_AUTH_TYPE_OPEN;
319
320 return 0;
321 } else {
322 key->in_use = 0;
323 return iwm_set_key(iwm, remove, 0, key);
324 }
325 }
326
327 /*
328 * If we havent set a profile yet, we cant set keys.
329 * Keys will be pushed after we're associated.
330 */
331 if (!iwm->umac_profile_active)
332 return 0;
333
334 /*
335 * If there is a current active profile, but no
336 * default key, it's not worth trying to associate again.
337 */
338 if (!iwm->default_key)
339 return 0;
340
341 /*
342 * Here we have an active profile, but a key setting changed.
343 * We thus have to invalidate the current profile, and push the
344 * new one. Keys will be pushed when association takes place.
345 */
346 ret = iwm_invalidate_mlme_profile(iwm);
347 if (ret < 0) {
348 IWM_ERR(iwm, "Couldn't invalidate profile\n");
349 return ret;
350 }
351
352 return iwm_send_mlme_profile(iwm);
353}
354
355static int iwm_wext_giwencode(struct net_device *dev,
356 struct iw_request_info *info,
357 struct iw_point *erq, char *key)
358{
359 struct iwm_priv *iwm = ndev_to_iwm(dev);
360 int idx, i;
361
362 idx = erq->flags & IW_ENCODE_INDEX;
363 if (idx < 1 || idx > 4) {
364 idx = -1;
365 if (!iwm->default_key) {
366 erq->length = 0;
367 erq->flags |= IW_ENCODE_NOKEY;
368 return 0;
369 } else
370 for (i = 0; i < IWM_NUM_KEYS; i++) {
371 if (iwm->default_key == &iwm->keys[i]) {
372 idx = i;
373 break;
374 }
375 }
376 if (idx < 0)
377 return -EINVAL;
378 } else
379 idx--;
380
381 erq->flags = idx + 1;
382
383 if (!iwm->keys[idx].in_use) {
384 erq->length = 0;
385 erq->flags |= IW_ENCODE_DISABLED;
386 return 0;
387 }
388
389 memcpy(key, iwm->keys[idx].key,
390 min_t(int, erq->length, iwm->keys[idx].key_len));
391 erq->length = iwm->keys[idx].key_len;
392 erq->flags |= IW_ENCODE_ENABLED;
393
394 if (iwm->umac_profile->mode == UMAC_MODE_BSS) {
395 switch (iwm->umac_profile->sec.auth_type) {
396 case UMAC_AUTH_TYPE_OPEN:
397 erq->flags |= IW_ENCODE_OPEN;
398 break;
399 default:
400 erq->flags |= IW_ENCODE_RESTRICTED;
401 break;
402 }
403 }
404
405 return 0;
406}
407
408static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
409{
410 if (wpa_version & IW_AUTH_WPA_VERSION_WPA2)
411 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
412 else if (wpa_version & IW_AUTH_WPA_VERSION_WPA)
413 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
414 else
415 iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
416
417 return 0;
418}
419
420static int iwm_wext_siwpower(struct net_device *dev,
421 struct iw_request_info *info,
422 struct iw_param *wrq, char *extra)
423{
424 struct iwm_priv *iwm = ndev_to_iwm(dev);
425 u32 power_index;
426
427 if (wrq->disabled) {
428 power_index = IWM_POWER_INDEX_MIN;
429 goto set;
430 } else
431 power_index = IWM_POWER_INDEX_DEFAULT;
432
433 switch (wrq->flags & IW_POWER_MODE) {
434 case IW_POWER_ON:
435 case IW_POWER_MODE:
436 case IW_POWER_ALL_R:
437 break;
438 default:
439 return -EINVAL;
440 }
441
442 set:
443 if (power_index == iwm->conf.power_index)
444 return 0;
445
446 iwm->conf.power_index = power_index;
447
448 return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
449 CFG_POWER_INDEX, iwm->conf.power_index);
450}
451
452static int iwm_wext_giwpower(struct net_device *dev,
453 struct iw_request_info *info,
454 union iwreq_data *wrqu, char *extra)
455{
456 struct iwm_priv *iwm = ndev_to_iwm(dev);
457
458 wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN);
459
460 return 0;
461}
462
463static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
464{
465 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
466
467 if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
468 *auth_type = UMAC_AUTH_TYPE_8021X;
469 else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
470 if (iwm->umac_profile->sec.flags &
471 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
472 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
473 else
474 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
475 } else {
476 IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
477 return -EINVAL;
478 }
479
480 return 0;
481}
482
483static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast)
484{
485 u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
486 &iwm->umac_profile->sec.mcast_cipher;
487
488 switch (cipher) {
489 case IW_AUTH_CIPHER_NONE:
490 *profile_cipher = UMAC_CIPHER_TYPE_NONE;
491 break;
492 case IW_AUTH_CIPHER_WEP40:
493 *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
494 break;
495 case IW_AUTH_CIPHER_TKIP:
496 *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
497 break;
498 case IW_AUTH_CIPHER_CCMP:
499 *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
500 break;
501 case IW_AUTH_CIPHER_WEP104:
502 *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
503 break;
504 default:
505 IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
506 return -ENOTSUPP;
507 }
508
509 return 0;
510}
511
512static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
513{
514 u8 *auth_type = &iwm->umac_profile->sec.auth_type;
515
516 switch (auth_alg) {
517 case IW_AUTH_ALG_OPEN_SYSTEM:
518 *auth_type = UMAC_AUTH_TYPE_OPEN;
519 break;
520 case IW_AUTH_ALG_SHARED_KEY:
521 if (iwm->umac_profile->sec.flags &
522 (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
523 if (*auth_type == UMAC_AUTH_TYPE_8021X)
524 return -EINVAL;
525 *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
526 } else {
527 *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
528 }
529 break;
530 case IW_AUTH_ALG_LEAP:
531 default:
532 IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg);
533 return -ENOTSUPP;
534 }
535
536 return 0;
537}
538
539static int iwm_wext_siwauth(struct net_device *dev,
540 struct iw_request_info *info,
541 struct iw_param *data, char *extra)
542{
543 struct iwm_priv *iwm = ndev_to_iwm(dev);
544 int ret;
545
546 if ((data->flags) &
547 (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT |
548 IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) {
549 /* We need to invalidate the current profile */
550 if (iwm->umac_profile_active) {
551 ret = iwm_invalidate_mlme_profile(iwm);
552 if (ret < 0) {
553 IWM_ERR(iwm, "Couldn't invalidate profile\n");
554 return ret;
555 }
556 }
557 }
558
559 switch (data->flags & IW_AUTH_INDEX) {
560 case IW_AUTH_WPA_VERSION:
561 return iwm_set_wpa_version(iwm, data->value);
562 break;
563 case IW_AUTH_CIPHER_PAIRWISE:
564 return iwm_set_cipher(iwm, data->value, 1);
565 break;
566 case IW_AUTH_CIPHER_GROUP:
567 return iwm_set_cipher(iwm, data->value, 0);
568 break;
569 case IW_AUTH_KEY_MGMT:
570 return iwm_set_key_mgt(iwm, data->value);
571 break;
572 case IW_AUTH_80211_AUTH_ALG:
573 return iwm_set_auth_alg(iwm, data->value);
574 break;
575 default:
576 return -ENOTSUPP;
577 }
578
579 return 0;
580}
581
582static int iwm_wext_giwauth(struct net_device *dev,
583 struct iw_request_info *info,
584 struct iw_param *data, char *extra)
585{
586 return 0;
587}
588
589static int iwm_wext_siwencodeext(struct net_device *dev,
590 struct iw_request_info *info,
591 struct iw_point *erq, char *extra)
592{
593 struct iwm_priv *iwm = ndev_to_iwm(dev);
594 struct iwm_key *key;
595 struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
596 int uninitialized_var(alg), idx, i, remove = 0;
597
598 IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg);
599 IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len);
600 IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags);
601 IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
602 IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length);
603
604 switch (ext->alg) {
605 case IW_ENCODE_ALG_NONE:
606 remove = 1;
607 break;
608 case IW_ENCODE_ALG_WEP:
609 if (ext->key_len == WLAN_KEY_LEN_WEP40)
610 alg = UMAC_CIPHER_TYPE_WEP_40;
611 else if (ext->key_len == WLAN_KEY_LEN_WEP104)
612 alg = UMAC_CIPHER_TYPE_WEP_104;
613 else {
614 IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len);
615 return -EINVAL;
616 }
617
618 break;
619 case IW_ENCODE_ALG_TKIP:
620 alg = UMAC_CIPHER_TYPE_TKIP;
621 break;
622 case IW_ENCODE_ALG_CCMP:
623 alg = UMAC_CIPHER_TYPE_CCMP;
624 break;
625 default:
626 return -EOPNOTSUPP;
627 }
628
629 idx = erq->flags & IW_ENCODE_INDEX;
630
631 if (idx == 0) {
632 if (iwm->default_key)
633 for (i = 0; i < IWM_NUM_KEYS; i++) {
634 if (iwm->default_key == &iwm->keys[i]) {
635 idx = i;
636 break;
637 }
638 }
639 } else if (idx < 1 || idx > 4) {
640 return -EINVAL;
641 } else
642 idx--;
643
644 if (erq->flags & IW_ENCODE_DISABLED)
645 remove = 1;
646 else if ((erq->length == 0) ||
647 (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
648 iwm->default_key = &iwm->keys[idx];
649 if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP)
650 return iwm_set_tx_key(iwm, idx);
651 }
652
653 key = iwm_key_init(iwm, idx, !remove, ext, alg);
654
655 return iwm_set_key(iwm, remove, !iwm->default_key, key);
656}
657
658static const iw_handler iwm_handlers[] =
659{
660 (iw_handler) NULL, /* SIOCSIWCOMMIT */
661 (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */
662 (iw_handler) NULL, /* SIOCSIWNWID */
663 (iw_handler) NULL, /* SIOCGIWNWID */
664 (iw_handler) iwm_wext_siwfreq, /* SIOCSIWFREQ */
665 (iw_handler) iwm_wext_giwfreq, /* SIOCGIWFREQ */
666 (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */
667 (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */
668 (iw_handler) NULL, /* SIOCSIWSENS */
669 (iw_handler) NULL, /* SIOCGIWSENS */
670 (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
671 (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */
672 (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
673 (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
674 (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
675 (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
676 (iw_handler) NULL, /* SIOCSIWSPY */
677 (iw_handler) NULL, /* SIOCGIWSPY */
678 (iw_handler) NULL, /* SIOCSIWTHRSPY */
679 (iw_handler) NULL, /* SIOCGIWTHRSPY */
680 (iw_handler) iwm_wext_siwap, /* SIOCSIWAP */
681 (iw_handler) iwm_wext_giwap, /* SIOCGIWAP */
682 (iw_handler) NULL, /* SIOCSIWMLME */
683 (iw_handler) NULL, /* SIOCGIWAPLIST */
684 (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */
685 (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */
686 (iw_handler) iwm_wext_siwessid, /* SIOCSIWESSID */
687 (iw_handler) iwm_wext_giwessid, /* SIOCGIWESSID */
688 (iw_handler) NULL, /* SIOCSIWNICKN */
689 (iw_handler) NULL, /* SIOCGIWNICKN */
690 (iw_handler) NULL, /* -- hole -- */
691 (iw_handler) NULL, /* -- hole -- */
692 (iw_handler) NULL, /* SIOCSIWRATE */
693 (iw_handler) iwm_wext_giwrate, /* SIOCGIWRATE */
694 (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */
695 (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */
696 (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */
697 (iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */
698 (iw_handler) NULL, /* SIOCSIWTXPOW */
699 (iw_handler) NULL, /* SIOCGIWTXPOW */
700 (iw_handler) NULL, /* SIOCSIWRETRY */
701 (iw_handler) NULL, /* SIOCGIWRETRY */
702 (iw_handler) iwm_wext_siwencode, /* SIOCSIWENCODE */
703 (iw_handler) iwm_wext_giwencode, /* SIOCGIWENCODE */
704 (iw_handler) iwm_wext_siwpower, /* SIOCSIWPOWER */
705 (iw_handler) iwm_wext_giwpower, /* SIOCGIWPOWER */
706 (iw_handler) NULL, /* -- hole -- */
707 (iw_handler) NULL, /* -- hole -- */
708 (iw_handler) NULL, /* SIOCSIWGENIE */
709 (iw_handler) NULL, /* SIOCGIWGENIE */
710 (iw_handler) iwm_wext_siwauth, /* SIOCSIWAUTH */
711 (iw_handler) iwm_wext_giwauth, /* SIOCGIWAUTH */
712 (iw_handler) iwm_wext_siwencodeext, /* SIOCSIWENCODEEXT */
713 (iw_handler) NULL, /* SIOCGIWENCODEEXT */
714 (iw_handler) NULL, /* SIOCSIWPMKSA */
715 (iw_handler) NULL, /* -- hole -- */
716};
717
718const struct iw_handler_def iwm_iw_handler_def = {
719 .num_standard = ARRAY_SIZE(iwm_handlers),
720 .standard = (iw_handler *) iwm_handlers,
721 .get_wireless_stats = iwm_get_wireless_stats,
722};
723