blob: 8a858f7e36f445374843b3090638f23ef28357be [file] [log] [blame]
James Ketrenos43f66a62005-03-25 12:31:53 -06001/******************************************************************************
Jeff Garzikbf794512005-07-31 13:07:26 -04002
Zhu Yi171e7b22006-02-15 07:17:56 +08003 Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
James Ketrenos43f66a62005-03-25 12:31:53 -06004
5 802.11 status code portion of this file from ethereal-0.10.6:
6 Copyright 2000, Axis Communications AB
7 Ethereal - Network traffic analyzer
8 By Gerald Combs <gerald@ethereal.com>
9 Copyright 1998 Gerald Combs
10
Jeff Garzikbf794512005-07-31 13:07:26 -040011 This program is free software; you can redistribute it and/or modify it
12 under the terms of version 2 of the GNU General Public License as
James Ketrenos43f66a62005-03-25 12:31:53 -060013 published by the Free Software Foundation.
Jeff Garzikbf794512005-07-31 13:07:26 -040014
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
James Ketrenos43f66a62005-03-25 12:31:53 -060018 more details.
Jeff Garzikbf794512005-07-31 13:07:26 -040019
James Ketrenos43f66a62005-03-25 12:31:53 -060020 You should have received a copy of the GNU General Public License along with
Jeff Garzikbf794512005-07-31 13:07:26 -040021 this program; if not, write to the Free Software Foundation, Inc., 59
James Ketrenos43f66a62005-03-25 12:31:53 -060022 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Jeff Garzikbf794512005-07-31 13:07:26 -040023
James Ketrenos43f66a62005-03-25 12:31:53 -060024 The full GNU General Public License is included in this distribution in the
25 file called LICENSE.
Jeff Garzikbf794512005-07-31 13:07:26 -040026
James Ketrenos43f66a62005-03-25 12:31:53 -060027 Contact Information:
Reinette Chatrec1eb2c82009-08-21 13:34:26 -070028 Intel Linux Wireless <ilw@linux.intel.com>
James Ketrenos43f66a62005-03-25 12:31:53 -060029 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
32
Alexey Dobriyand43c36d2009-10-07 17:09:06 +040033#include <linux/sched.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090034#include <linux/slab.h>
Johannes Berg262eb9b22011-07-13 10:39:09 +020035#include <net/cfg80211-wext.h>
James Ketrenos43f66a62005-03-25 12:31:53 -060036#include "ipw2200.h"
Stanislav Yakovleva141e6a2012-04-10 21:44:47 -040037#include "ipw.h"
James Ketrenos43f66a62005-03-25 12:31:53 -060038
Zhu Yiae4af612006-04-13 17:20:34 +080039
40#ifndef KBUILD_EXTMOD
41#define VK "k"
42#else
43#define VK
44#endif
45
46#ifdef CONFIG_IPW2200_DEBUG
47#define VD "d"
48#else
49#define VD
50#endif
51
52#ifdef CONFIG_IPW2200_MONITOR
53#define VM "m"
54#else
55#define VM
56#endif
57
58#ifdef CONFIG_IPW2200_PROMISCUOUS
59#define VP "p"
60#else
61#define VP
62#endif
63
Zhu Yi459d4082006-04-13 17:21:00 +080064#ifdef CONFIG_IPW2200_RADIOTAP
Zhu Yiae4af612006-04-13 17:20:34 +080065#define VR "r"
66#else
67#define VR
68#endif
69
70#ifdef CONFIG_IPW2200_QOS
71#define VQ "q"
72#else
73#define VQ
74#endif
75
Zhu Yiee2c4ad2007-07-12 16:09:47 +080076#define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ
James Ketrenos43f66a62005-03-25 12:31:53 -060077#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
Zhu Yi171e7b22006-02-15 07:17:56 +080078#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
James Ketrenos43f66a62005-03-25 12:31:53 -060079#define DRV_VERSION IPW2200_VERSION
80
James Ketrenosb095c382005-08-24 22:04:42 -050081#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
82
James Ketrenos43f66a62005-03-25 12:31:53 -060083MODULE_DESCRIPTION(DRV_DESCRIPTION);
84MODULE_VERSION(DRV_VERSION);
85MODULE_AUTHOR(DRV_COPYRIGHT);
86MODULE_LICENSE("GPL");
Ben Hutchings873395a2009-11-07 21:59:10 +000087MODULE_FIRMWARE("ipw2200-ibss.fw");
88#ifdef CONFIG_IPW2200_MONITOR
89MODULE_FIRMWARE("ipw2200-sniffer.fw");
90#endif
91MODULE_FIRMWARE("ipw2200-bss.fw");
James Ketrenos43f66a62005-03-25 12:31:53 -060092
James Ketrenosf6c5cb72005-08-25 00:39:09 -050093static int cmdlog = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060094static int debug = 0;
Reinette Chatre21f8a732009-08-18 10:25:05 -070095static int default_channel = 0;
96static int network_mode = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060097
98static u32 ipw_debug_level;
Tim Gardner5c7f9b72008-10-14 10:38:03 -060099static int associate;
James Ketrenos43f66a62005-03-25 12:31:53 -0600100static int auto_create = 1;
ubuntu@tjworld.netc086aba2009-03-23 20:29:28 +0000101static int led_support = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -0600102static int disable = 0;
Zhu Yi810dabd2006-01-24 16:36:59 +0800103static int bt_coexist = 0;
Zhu Yibde37d02006-01-24 16:38:08 +0800104static int hwcrypto = 0;
Zhu Yi4bfdb912006-01-24 16:37:16 +0800105static int roaming = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -0600106static const char ipw_modes[] = {
107 'a', 'b', 'g', '?'
108};
Zhu Yid2b83e12006-04-13 17:19:36 +0800109static int antenna = CFG_SYS_ANTENNA_BOTH;
James Ketrenos43f66a62005-03-25 12:31:53 -0600110
Zhu Yid685b8c2006-04-13 17:20:27 +0800111#ifdef CONFIG_IPW2200_PROMISCUOUS
112static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */
113#endif
114
John W. Linvillea3caa992009-08-25 14:12:25 -0400115static struct ieee80211_rate ipw2200_rates[] = {
116 { .bitrate = 10 },
117 { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
118 { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
119 { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
120 { .bitrate = 60 },
121 { .bitrate = 90 },
122 { .bitrate = 120 },
123 { .bitrate = 180 },
124 { .bitrate = 240 },
125 { .bitrate = 360 },
126 { .bitrate = 480 },
127 { .bitrate = 540 }
128};
129
130#define ipw2200_a_rates (ipw2200_rates + 4)
131#define ipw2200_num_a_rates 8
132#define ipw2200_bg_rates (ipw2200_rates + 0)
133#define ipw2200_num_bg_rates 12
Zhu Yid685b8c2006-04-13 17:20:27 +0800134
Johannes Berg776d68f2011-11-09 21:33:45 +0100135/* Ugly macro to convert literal channel numbers into their mhz equivalents
136 * There are certianly some conditions that will break this (like feeding it '30')
137 * but they shouldn't arise since nothing talks on channel 30. */
138#define ieee80211chan2mhz(x) \
139 (((x) <= 14) ? \
140 (((x) == 14) ? 2484 : ((x) * 5) + 2407) : \
141 ((x) + 1000) * 5)
142
Zhu Yie43e3c12006-04-13 17:20:45 +0800143#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -0500144static int qos_enable = 0;
145static int qos_burst_enable = 0;
146static int qos_no_ack_mask = 0;
147static int burst_duration_CCK = 0;
148static int burst_duration_OFDM = 0;
149
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400150static struct libipw_qos_parameters def_qos_parameters_OFDM = {
James Ketrenosb095c382005-08-24 22:04:42 -0500151 {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM,
152 QOS_TX3_CW_MIN_OFDM},
153 {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM,
154 QOS_TX3_CW_MAX_OFDM},
155 {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
156 {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
157 {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM,
158 QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM}
159};
160
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400161static struct libipw_qos_parameters def_qos_parameters_CCK = {
James Ketrenosb095c382005-08-24 22:04:42 -0500162 {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK,
163 QOS_TX3_CW_MIN_CCK},
164 {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK,
165 QOS_TX3_CW_MAX_CCK},
166 {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
167 {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
168 {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK,
169 QOS_TX3_TXOP_LIMIT_CCK}
170};
171
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400172static struct libipw_qos_parameters def_parameters_OFDM = {
James Ketrenosb095c382005-08-24 22:04:42 -0500173 {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM,
174 DEF_TX3_CW_MIN_OFDM},
175 {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM,
176 DEF_TX3_CW_MAX_OFDM},
177 {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
178 {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
179 {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM,
180 DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM}
181};
182
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400183static struct libipw_qos_parameters def_parameters_CCK = {
James Ketrenosb095c382005-08-24 22:04:42 -0500184 {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK,
185 DEF_TX3_CW_MIN_CCK},
186 {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK,
187 DEF_TX3_CW_MAX_CCK},
188 {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
189 {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
190 {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK,
191 DEF_TX3_TXOP_LIMIT_CCK}
192};
193
194static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
195
196static int from_priority_to_tx_queue[] = {
197 IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1,
198 IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
199};
200
201static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv);
202
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400203static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters
James Ketrenosb095c382005-08-24 22:04:42 -0500204 *qos_param);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -0400205static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element
James Ketrenosb095c382005-08-24 22:04:42 -0500206 *qos_param);
Zhu Yie43e3c12006-04-13 17:20:45 +0800207#endif /* CONFIG_IPW2200_QOS */
James Ketrenosb095c382005-08-24 22:04:42 -0500208
Benoit Boissinot97a78ca2005-09-15 17:30:28 +0000209static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
James Ketrenosb095c382005-08-24 22:04:42 -0500210static void ipw_remove_current_network(struct ipw_priv *priv);
James Ketrenos43f66a62005-03-25 12:31:53 -0600211static void ipw_rx(struct ipw_priv *priv);
Jeff Garzikbf794512005-07-31 13:07:26 -0400212static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -0600213 struct clx2_tx_queue *txq, int qindex);
214static int ipw_queue_reset(struct ipw_priv *priv);
215
216static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
217 int len, int sync);
218
219static void ipw_tx_queue_free(struct ipw_priv *);
220
221static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
222static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
223static void ipw_rx_queue_replenish(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600224static int ipw_up(struct ipw_priv *);
David Howellsc4028952006-11-22 14:57:56 +0000225static void ipw_bg_up(struct work_struct *work);
James Ketrenos43f66a62005-03-25 12:31:53 -0600226static void ipw_down(struct ipw_priv *);
David Howellsc4028952006-11-22 14:57:56 +0000227static void ipw_bg_down(struct work_struct *work);
James Ketrenos43f66a62005-03-25 12:31:53 -0600228static int ipw_config(struct ipw_priv *);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400229static int init_supported_rates(struct ipw_priv *priv,
230 struct ipw_supported_rates *prates);
James Ketrenosb095c382005-08-24 22:04:42 -0500231static void ipw_set_hwcrypto_keys(struct ipw_priv *);
232static void ipw_send_wep_keys(struct ipw_priv *, int);
James Ketrenos43f66a62005-03-25 12:31:53 -0600233
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500234static int snprint_line(char *buf, size_t count,
235 const u8 * data, u32 len, u32 ofs)
James Ketrenos43f66a62005-03-25 12:31:53 -0600236{
237 int out, i, j, l;
238 char c;
Jeff Garzikbf794512005-07-31 13:07:26 -0400239
James Ketrenos43f66a62005-03-25 12:31:53 -0600240 out = snprintf(buf, count, "%08X", ofs);
241
242 for (l = 0, i = 0; i < 2; i++) {
243 out += snprintf(buf + out, count - out, " ");
Jeff Garzikbf794512005-07-31 13:07:26 -0400244 for (j = 0; j < 8 && l < len; j++, l++)
245 out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos43f66a62005-03-25 12:31:53 -0600246 data[(i * 8 + j)]);
247 for (; j < 8; j++)
248 out += snprintf(buf + out, count - out, " ");
249 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400250
James Ketrenos43f66a62005-03-25 12:31:53 -0600251 out += snprintf(buf + out, count - out, " ");
252 for (l = 0, i = 0; i < 2; i++) {
253 out += snprintf(buf + out, count - out, " ");
254 for (j = 0; j < 8 && l < len; j++, l++) {
255 c = data[(i * 8 + j)];
256 if (!isascii(c) || !isprint(c))
257 c = '.';
Jeff Garzikbf794512005-07-31 13:07:26 -0400258
James Ketrenos43f66a62005-03-25 12:31:53 -0600259 out += snprintf(buf + out, count - out, "%c", c);
260 }
261
262 for (; j < 8; j++)
263 out += snprintf(buf + out, count - out, " ");
264 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400265
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500266 return out;
James Ketrenos43f66a62005-03-25 12:31:53 -0600267}
268
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400269static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600270{
271 char line[81];
272 u32 ofs = 0;
273 if (!(ipw_debug_level & level))
274 return;
275
276 while (len) {
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500277 snprint_line(line, sizeof(line), &data[ofs],
278 min(len, 16U), ofs);
279 printk(KERN_DEBUG "%s\n", line);
James Ketrenos43f66a62005-03-25 12:31:53 -0600280 ofs += 16;
281 len -= min(len, 16U);
282 }
283}
284
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500285static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
286{
287 size_t out = size;
288 u32 ofs = 0;
289 int total = 0;
290
291 while (size && len) {
292 out = snprint_line(output, size, &data[ofs],
293 min_t(size_t, len, 16U), ofs);
294
295 ofs += 16;
296 output += out;
297 size -= out;
298 len -= min_t(size_t, len, 16U);
299 total += out;
300 }
301 return total;
302}
303
Zhu Yic8fe6672006-01-24 16:36:36 +0800304/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600305static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
306#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
307
Zhu Yic8fe6672006-01-24 16:36:36 +0800308/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600309static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
310#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
311
Zhu Yic8fe6672006-01-24 16:36:36 +0800312/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600313static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
314static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
315{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400316 IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
317 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600318 _ipw_write_reg8(a, b, c);
319}
320
Zhu Yic8fe6672006-01-24 16:36:36 +0800321/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600322static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
323static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
324{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400325 IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
326 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600327 _ipw_write_reg16(a, b, c);
328}
329
Zhu Yic8fe6672006-01-24 16:36:36 +0800330/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600331static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
332static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
333{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400334 IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
335 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600336 _ipw_write_reg32(a, b, c);
337}
338
Zhu Yic8fe6672006-01-24 16:36:36 +0800339/* 8-bit direct write (low 4K) */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100340static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs,
341 u8 val)
342{
343 writeb(val, ipw->hw_base + ofs);
344}
Zhu Yic8fe6672006-01-24 16:36:36 +0800345
346/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Takashi Iwaifb55d882008-07-31 19:02:06 +0200347#define ipw_write8(ipw, ofs, val) do { \
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100348 IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, \
349 __LINE__, (u32)(ofs), (u32)(val)); \
350 _ipw_write8(ipw, ofs, val); \
351} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -0600352
Zhu Yic8fe6672006-01-24 16:36:36 +0800353/* 16-bit direct write (low 4K) */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100354static inline void _ipw_write16(struct ipw_priv *ipw, unsigned long ofs,
355 u16 val)
356{
357 writew(val, ipw->hw_base + ofs);
358}
Zhu Yic8fe6672006-01-24 16:36:36 +0800359
360/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100361#define ipw_write16(ipw, ofs, val) do { \
362 IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, \
363 __LINE__, (u32)(ofs), (u32)(val)); \
364 _ipw_write16(ipw, ofs, val); \
365} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -0600366
Zhu Yic8fe6672006-01-24 16:36:36 +0800367/* 32-bit direct write (low 4K) */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100368static inline void _ipw_write32(struct ipw_priv *ipw, unsigned long ofs,
369 u32 val)
370{
371 writel(val, ipw->hw_base + ofs);
372}
Zhu Yic8fe6672006-01-24 16:36:36 +0800373
374/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100375#define ipw_write32(ipw, ofs, val) do { \
376 IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, \
377 __LINE__, (u32)(ofs), (u32)(val)); \
378 _ipw_write32(ipw, ofs, val); \
379} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -0600380
Zhu Yic8fe6672006-01-24 16:36:36 +0800381/* 8-bit direct read (low 4K) */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100382static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400383{
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100384 return readb(ipw->hw_base + ofs);
James Ketrenos43f66a62005-03-25 12:31:53 -0600385}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400386
Zhu Yic8fe6672006-01-24 16:36:36 +0800387/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100388#define ipw_read8(ipw, ofs) ({ \
389 IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", __FILE__, __LINE__, \
390 (u32)(ofs)); \
391 _ipw_read8(ipw, ofs); \
392})
James Ketrenos43f66a62005-03-25 12:31:53 -0600393
Zhu Yic8fe6672006-01-24 16:36:36 +0800394/* 16-bit direct read (low 4K) */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100395static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400396{
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100397 return readw(ipw->hw_base + ofs);
James Ketrenos43f66a62005-03-25 12:31:53 -0600398}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400399
Zhu Yic8fe6672006-01-24 16:36:36 +0800400/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100401#define ipw_read16(ipw, ofs) ({ \
402 IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", __FILE__, __LINE__, \
403 (u32)(ofs)); \
404 _ipw_read16(ipw, ofs); \
405})
James Ketrenos43f66a62005-03-25 12:31:53 -0600406
Zhu Yic8fe6672006-01-24 16:36:36 +0800407/* 32-bit direct read (low 4K) */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100408static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400409{
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100410 return readl(ipw->hw_base + ofs);
James Ketrenos43f66a62005-03-25 12:31:53 -0600411}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400412
Zhu Yic8fe6672006-01-24 16:36:36 +0800413/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100414#define ipw_read32(ipw, ofs) ({ \
415 IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", __FILE__, __LINE__, \
416 (u32)(ofs)); \
417 _ipw_read32(ipw, ofs); \
418})
James Ketrenos43f66a62005-03-25 12:31:53 -0600419
420static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
Zhu Yic8fe6672006-01-24 16:36:36 +0800421/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100422#define ipw_read_indirect(a, b, c, d) ({ \
423 IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %u bytes\n", __FILE__, \
424 __LINE__, (u32)(b), (u32)(d)); \
425 _ipw_read_indirect(a, b, c, d); \
426})
James Ketrenos43f66a62005-03-25 12:31:53 -0600427
Zhu Yic8fe6672006-01-24 16:36:36 +0800428/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400429static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
430 int num);
Jiri Slaby1788bcd2009-02-12 22:29:40 +0100431#define ipw_write_indirect(a, b, c, d) do { \
432 IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %u bytes\n", __FILE__, \
433 __LINE__, (u32)(b), (u32)(d)); \
434 _ipw_write_indirect(a, b, c, d); \
435} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -0600436
Zhu Yic8fe6672006-01-24 16:36:36 +0800437/* 32-bit indirect write (above 4K) */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400438static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600439{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400440 IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
James Ketrenosb095c382005-08-24 22:04:42 -0500441 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
442 _ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600443}
444
Zhu Yic8fe6672006-01-24 16:36:36 +0800445/* 8-bit indirect write (above 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600446static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
447{
Zhu Yi2638bc32006-01-24 16:37:52 +0800448 u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
Zhu Yic8fe6672006-01-24 16:36:36 +0800449 u32 dif_len = reg - aligned_addr;
450
James Ketrenos43f66a62005-03-25 12:31:53 -0600451 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
Zhu Yic8fe6672006-01-24 16:36:36 +0800452 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
453 _ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600454}
455
Zhu Yic8fe6672006-01-24 16:36:36 +0800456/* 16-bit indirect write (above 4K) */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400457static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600458{
Zhu Yi2638bc32006-01-24 16:37:52 +0800459 u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
Zhu Yic8fe6672006-01-24 16:36:36 +0800460 u32 dif_len = (reg - aligned_addr) & (~0x1ul);
461
James Ketrenos43f66a62005-03-25 12:31:53 -0600462 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
Zhu Yic8fe6672006-01-24 16:36:36 +0800463 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
464 _ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600465}
466
Zhu Yic8fe6672006-01-24 16:36:36 +0800467/* 8-bit indirect read (above 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600468static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
469{
470 u32 word;
James Ketrenosb095c382005-08-24 22:04:42 -0500471 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
Frans Pop9fd1ea42010-03-24 19:46:31 +0100472 IPW_DEBUG_IO(" reg = 0x%8X :\n", reg);
James Ketrenosb095c382005-08-24 22:04:42 -0500473 word = _ipw_read32(priv, IPW_INDIRECT_DATA);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400474 return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos43f66a62005-03-25 12:31:53 -0600475}
476
Zhu Yic8fe6672006-01-24 16:36:36 +0800477/* 32-bit indirect read (above 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600478static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
479{
480 u32 value;
481
482 IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
483
James Ketrenosb095c382005-08-24 22:04:42 -0500484 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
485 value = _ipw_read32(priv, IPW_INDIRECT_DATA);
Frans Pop9fd1ea42010-03-24 19:46:31 +0100486 IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x\n", reg, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600487 return value;
488}
489
Zhu Yic8fe6672006-01-24 16:36:36 +0800490/* General purpose, no alignment requirement, iterative (multi-byte) read, */
491/* for area above 1st 4K of SRAM/reg space */
James Ketrenos43f66a62005-03-25 12:31:53 -0600492static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
493 int num)
494{
Zhu Yi2638bc32006-01-24 16:37:52 +0800495 u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
James Ketrenos43f66a62005-03-25 12:31:53 -0600496 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600497 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400498
James Ketrenos43f66a62005-03-25 12:31:53 -0600499 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
500
James Ketrenosea2b26e2005-08-24 21:25:16 -0500501 if (num <= 0) {
502 return;
503 }
504
Zhu Yic8fe6672006-01-24 16:36:36 +0800505 /* Read the first dword (or portion) byte by byte */
James Ketrenos43f66a62005-03-25 12:31:53 -0600506 if (unlikely(dif_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500507 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -0600508 /* Start reading at aligned_addr + dif_len */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500509 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
James Ketrenosb095c382005-08-24 22:04:42 -0500510 *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenos43f66a62005-03-25 12:31:53 -0600511 aligned_addr += 4;
512 }
513
Zhu Yic8fe6672006-01-24 16:36:36 +0800514 /* Read all of the middle dwords as dwords, with auto-increment */
James Ketrenosb095c382005-08-24 22:04:42 -0500515 _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500516 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
James Ketrenosb095c382005-08-24 22:04:42 -0500517 *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
Jeff Garzikbf794512005-07-31 13:07:26 -0400518
Zhu Yic8fe6672006-01-24 16:36:36 +0800519 /* Read the last dword (or portion) byte by byte */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500520 if (unlikely(num)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500521 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500522 for (i = 0; num > 0; i++, num--)
James Ketrenosb095c382005-08-24 22:04:42 -0500523 *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500524 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600525}
526
Zhu Yic8fe6672006-01-24 16:36:36 +0800527/* General purpose, no alignment requirement, iterative (multi-byte) write, */
528/* for area above 1st 4K of SRAM/reg space */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400529static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600530 int num)
531{
Zhu Yi2638bc32006-01-24 16:37:52 +0800532 u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
James Ketrenos43f66a62005-03-25 12:31:53 -0600533 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600534 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400535
James Ketrenos43f66a62005-03-25 12:31:53 -0600536 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
Jeff Garzikbf794512005-07-31 13:07:26 -0400537
James Ketrenosea2b26e2005-08-24 21:25:16 -0500538 if (num <= 0) {
539 return;
540 }
541
Zhu Yic8fe6672006-01-24 16:36:36 +0800542 /* Write the first dword (or portion) byte by byte */
James Ketrenos43f66a62005-03-25 12:31:53 -0600543 if (unlikely(dif_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500544 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
Zhu Yic8fe6672006-01-24 16:36:36 +0800545 /* Start writing at aligned_addr + dif_len */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500546 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
James Ketrenosb095c382005-08-24 22:04:42 -0500547 _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenos43f66a62005-03-25 12:31:53 -0600548 aligned_addr += 4;
549 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400550
Zhu Yic8fe6672006-01-24 16:36:36 +0800551 /* Write all of the middle dwords as dwords, with auto-increment */
James Ketrenosb095c382005-08-24 22:04:42 -0500552 _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500553 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
James Ketrenosb095c382005-08-24 22:04:42 -0500554 _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
Jeff Garzikbf794512005-07-31 13:07:26 -0400555
Zhu Yic8fe6672006-01-24 16:36:36 +0800556 /* Write the last dword (or portion) byte by byte */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500557 if (unlikely(num)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500558 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500559 for (i = 0; num > 0; i++, num--, buf++)
James Ketrenosb095c382005-08-24 22:04:42 -0500560 _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500561 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600562}
563
Zhu Yic8fe6672006-01-24 16:36:36 +0800564/* General purpose, no alignment requirement, iterative (multi-byte) write, */
565/* for 1st 4K of SRAM/regs space */
Jeff Garzikbf794512005-07-31 13:07:26 -0400566static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600567 int num)
568{
569 memcpy_toio((priv->hw_base + addr), buf, num);
570}
571
Zhu Yic8fe6672006-01-24 16:36:36 +0800572/* Set bit(s) in low 4K of SRAM/regs */
James Ketrenos43f66a62005-03-25 12:31:53 -0600573static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
574{
575 ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
576}
577
Zhu Yic8fe6672006-01-24 16:36:36 +0800578/* Clear bit(s) in low 4K of SRAM/regs */
James Ketrenos43f66a62005-03-25 12:31:53 -0600579static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
580{
581 ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
582}
583
Zhu Yi89c318e2006-06-08 22:19:49 -0700584static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -0600585{
586 if (priv->status & STATUS_INT_ENABLED)
587 return;
588 priv->status |= STATUS_INT_ENABLED;
James Ketrenosb095c382005-08-24 22:04:42 -0500589 ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600590}
591
Zhu Yi89c318e2006-06-08 22:19:49 -0700592static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -0600593{
594 if (!(priv->status & STATUS_INT_ENABLED))
595 return;
596 priv->status &= ~STATUS_INT_ENABLED;
James Ketrenosb095c382005-08-24 22:04:42 -0500597 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600598}
599
Zhu Yi89c318e2006-06-08 22:19:49 -0700600static inline void ipw_enable_interrupts(struct ipw_priv *priv)
601{
602 unsigned long flags;
603
604 spin_lock_irqsave(&priv->irq_lock, flags);
605 __ipw_enable_interrupts(priv);
606 spin_unlock_irqrestore(&priv->irq_lock, flags);
607}
608
609static inline void ipw_disable_interrupts(struct ipw_priv *priv)
610{
611 unsigned long flags;
612
613 spin_lock_irqsave(&priv->irq_lock, flags);
614 __ipw_disable_interrupts(priv);
615 spin_unlock_irqrestore(&priv->irq_lock, flags);
616}
617
James Ketrenos43f66a62005-03-25 12:31:53 -0600618static char *ipw_error_desc(u32 val)
619{
620 switch (val) {
Jeff Garzikbf794512005-07-31 13:07:26 -0400621 case IPW_FW_ERROR_OK:
James Ketrenos43f66a62005-03-25 12:31:53 -0600622 return "ERROR_OK";
Jeff Garzikbf794512005-07-31 13:07:26 -0400623 case IPW_FW_ERROR_FAIL:
James Ketrenos43f66a62005-03-25 12:31:53 -0600624 return "ERROR_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400625 case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600626 return "MEMORY_UNDERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400627 case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600628 return "MEMORY_OVERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400629 case IPW_FW_ERROR_BAD_PARAM:
James Ketrenosb095c382005-08-24 22:04:42 -0500630 return "BAD_PARAM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400631 case IPW_FW_ERROR_BAD_CHECKSUM:
James Ketrenosb095c382005-08-24 22:04:42 -0500632 return "BAD_CHECKSUM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400633 case IPW_FW_ERROR_NMI_INTERRUPT:
James Ketrenosb095c382005-08-24 22:04:42 -0500634 return "NMI_INTERRUPT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400635 case IPW_FW_ERROR_BAD_DATABASE:
James Ketrenosb095c382005-08-24 22:04:42 -0500636 return "BAD_DATABASE";
Jeff Garzikbf794512005-07-31 13:07:26 -0400637 case IPW_FW_ERROR_ALLOC_FAIL:
James Ketrenosb095c382005-08-24 22:04:42 -0500638 return "ALLOC_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400639 case IPW_FW_ERROR_DMA_UNDERRUN:
James Ketrenosb095c382005-08-24 22:04:42 -0500640 return "DMA_UNDERRUN";
Jeff Garzikbf794512005-07-31 13:07:26 -0400641 case IPW_FW_ERROR_DMA_STATUS:
James Ketrenosb095c382005-08-24 22:04:42 -0500642 return "DMA_STATUS";
643 case IPW_FW_ERROR_DINO_ERROR:
644 return "DINO_ERROR";
645 case IPW_FW_ERROR_EEPROM_ERROR:
646 return "EEPROM_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400647 case IPW_FW_ERROR_SYSASSERT:
James Ketrenosb095c382005-08-24 22:04:42 -0500648 return "SYSASSERT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400649 case IPW_FW_ERROR_FATAL_ERROR:
James Ketrenosb095c382005-08-24 22:04:42 -0500650 return "FATAL_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400651 default:
James Ketrenosb095c382005-08-24 22:04:42 -0500652 return "UNKNOWN_ERROR";
James Ketrenos43f66a62005-03-25 12:31:53 -0600653 }
654}
655
James Ketrenosb39860c2005-08-12 09:36:32 -0500656static void ipw_dump_error_log(struct ipw_priv *priv,
657 struct ipw_fw_error *error)
James Ketrenos43f66a62005-03-25 12:31:53 -0600658{
James Ketrenosb39860c2005-08-12 09:36:32 -0500659 u32 i;
James Ketrenos43f66a62005-03-25 12:31:53 -0600660
James Ketrenosb39860c2005-08-12 09:36:32 -0500661 if (!error) {
662 IPW_ERROR("Error allocating and capturing error log. "
663 "Nothing to dump.\n");
664 return;
James Ketrenos43f66a62005-03-25 12:31:53 -0600665 }
666
James Ketrenosb39860c2005-08-12 09:36:32 -0500667 IPW_ERROR("Start IPW Error Log Dump:\n");
668 IPW_ERROR("Status: 0x%08X, Config: %08X\n",
669 error->status, error->config);
James Ketrenos43f66a62005-03-25 12:31:53 -0600670
James Ketrenosb39860c2005-08-12 09:36:32 -0500671 for (i = 0; i < error->elem_len; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400672 IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
James Ketrenosb39860c2005-08-12 09:36:32 -0500673 ipw_error_desc(error->elem[i].desc),
674 error->elem[i].time,
675 error->elem[i].blink1,
676 error->elem[i].blink2,
677 error->elem[i].link1,
678 error->elem[i].link2, error->elem[i].data);
679 for (i = 0; i < error->log_len; i++)
680 IPW_ERROR("%i\t0x%08x\t%i\n",
681 error->log[i].time,
James Ketrenos286568a2005-08-30 10:34:25 -0500682 error->log[i].data, error->log[i].event);
James Ketrenos43f66a62005-03-25 12:31:53 -0600683}
James Ketrenos43f66a62005-03-25 12:31:53 -0600684
James Ketrenosc848d0a2005-08-24 21:56:24 -0500685static inline int ipw_is_init(struct ipw_priv *priv)
686{
687 return (priv->status & STATUS_INIT) ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -0600688}
689
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400690static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600691{
692 u32 addr, field_info, field_len, field_count, total_len;
693
694 IPW_DEBUG_ORD("ordinal = %i\n", ord);
695
696 if (!priv || !val || !len) {
697 IPW_DEBUG_ORD("Invalid argument\n");
698 return -EINVAL;
699 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400700
James Ketrenos43f66a62005-03-25 12:31:53 -0600701 /* verify device ordinal tables have been initialized */
702 if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
703 IPW_DEBUG_ORD("Access ordinals before initialization\n");
704 return -EINVAL;
705 }
706
707 switch (IPW_ORD_TABLE_ID_MASK & ord) {
708 case IPW_ORD_TABLE_0_MASK:
709 /*
710 * TABLE 0: Direct access to a table of 32 bit values
711 *
Jeff Garzikbf794512005-07-31 13:07:26 -0400712 * This is a very simple table with the data directly
James Ketrenos43f66a62005-03-25 12:31:53 -0600713 * read from the table
714 */
715
716 /* remove the table id from the ordinal */
717 ord &= IPW_ORD_TABLE_VALUE_MASK;
718
719 /* boundary check */
720 if (ord > priv->table0_len) {
721 IPW_DEBUG_ORD("ordinal value (%i) longer then "
722 "max (%i)\n", ord, priv->table0_len);
723 return -EINVAL;
724 }
725
726 /* verify we have enough room to store the value */
727 if (*len < sizeof(u32)) {
728 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200729 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600730 return -EINVAL;
731 }
732
733 IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400734 ord, priv->table0_addr + (ord << 2));
James Ketrenos43f66a62005-03-25 12:31:53 -0600735
736 *len = sizeof(u32);
737 ord <<= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400738 *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos43f66a62005-03-25 12:31:53 -0600739 break;
740
741 case IPW_ORD_TABLE_1_MASK:
742 /*
743 * TABLE 1: Indirect access to a table of 32 bit values
Jeff Garzikbf794512005-07-31 13:07:26 -0400744 *
745 * This is a fairly large table of u32 values each
James Ketrenos43f66a62005-03-25 12:31:53 -0600746 * representing starting addr for the data (which is
747 * also a u32)
748 */
749
750 /* remove the table id from the ordinal */
751 ord &= IPW_ORD_TABLE_VALUE_MASK;
Jeff Garzikbf794512005-07-31 13:07:26 -0400752
James Ketrenos43f66a62005-03-25 12:31:53 -0600753 /* boundary check */
754 if (ord > priv->table1_len) {
755 IPW_DEBUG_ORD("ordinal value too long\n");
756 return -EINVAL;
757 }
758
759 /* verify we have enough room to store the value */
760 if (*len < sizeof(u32)) {
761 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200762 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600763 return -EINVAL;
764 }
765
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400766 *((u32 *) val) =
767 ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos43f66a62005-03-25 12:31:53 -0600768 *len = sizeof(u32);
769 break;
770
771 case IPW_ORD_TABLE_2_MASK:
772 /*
773 * TABLE 2: Indirect access to a table of variable sized values
774 *
775 * This table consist of six values, each containing
776 * - dword containing the starting offset of the data
777 * - dword containing the lengh in the first 16bits
778 * and the count in the second 16bits
779 */
780
781 /* remove the table id from the ordinal */
782 ord &= IPW_ORD_TABLE_VALUE_MASK;
783
784 /* boundary check */
785 if (ord > priv->table2_len) {
786 IPW_DEBUG_ORD("ordinal value too long\n");
787 return -EINVAL;
788 }
789
790 /* get the address of statistic */
791 addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
Jeff Garzikbf794512005-07-31 13:07:26 -0400792
793 /* get the second DW of statistics ;
James Ketrenos43f66a62005-03-25 12:31:53 -0600794 * two 16-bit words - first is length, second is count */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400795 field_info =
796 ipw_read_reg32(priv,
797 priv->table2_addr + (ord << 3) +
798 sizeof(u32));
Jeff Garzikbf794512005-07-31 13:07:26 -0400799
James Ketrenos43f66a62005-03-25 12:31:53 -0600800 /* get each entry length */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400801 field_len = *((u16 *) & field_info);
Jeff Garzikbf794512005-07-31 13:07:26 -0400802
James Ketrenos43f66a62005-03-25 12:31:53 -0600803 /* get number of entries */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400804 field_count = *(((u16 *) & field_info) + 1);
Jeff Garzikbf794512005-07-31 13:07:26 -0400805
André Goddard Rosaaf901ca2009-11-14 13:09:05 -0200806 /* abort if not enough memory */
James Ketrenos43f66a62005-03-25 12:31:53 -0600807 total_len = field_len * field_count;
808 if (total_len > *len) {
809 *len = total_len;
810 return -EINVAL;
811 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400812
James Ketrenos43f66a62005-03-25 12:31:53 -0600813 *len = total_len;
814 if (!total_len)
815 return 0;
816
817 IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
Jeff Garzikbf794512005-07-31 13:07:26 -0400818 "field_info = 0x%08x\n",
James Ketrenos43f66a62005-03-25 12:31:53 -0600819 addr, total_len, field_info);
820 ipw_read_indirect(priv, addr, val, total_len);
821 break;
822
823 default:
824 IPW_DEBUG_ORD("Invalid ordinal!\n");
825 return -EINVAL;
826
827 }
828
James Ketrenos43f66a62005-03-25 12:31:53 -0600829 return 0;
830}
831
832static void ipw_init_ordinals(struct ipw_priv *priv)
833{
834 priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
Jeff Garzikbf794512005-07-31 13:07:26 -0400835 priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -0600836
837 IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
838 priv->table0_addr, priv->table0_len);
839
840 priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
841 priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
842
843 IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
844 priv->table1_addr, priv->table1_len);
845
846 priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
847 priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400848 priv->table2_len &= 0x0000ffff; /* use first two bytes */
James Ketrenos43f66a62005-03-25 12:31:53 -0600849
850 IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
851 priv->table2_addr, priv->table2_len);
852
853}
854
Adrian Bunka73e22b2006-01-21 01:39:42 +0100855static u32 ipw_register_toggle(u32 reg)
James Ketrenosa613bff2005-08-24 21:43:11 -0500856{
James Ketrenosb095c382005-08-24 22:04:42 -0500857 reg &= ~IPW_START_STANDBY;
858 if (reg & IPW_GATE_ODMA)
859 reg &= ~IPW_GATE_ODMA;
860 if (reg & IPW_GATE_IDMA)
861 reg &= ~IPW_GATE_IDMA;
862 if (reg & IPW_GATE_ADMA)
863 reg &= ~IPW_GATE_ADMA;
James Ketrenosa613bff2005-08-24 21:43:11 -0500864 return reg;
865}
866
867/*
868 * LED behavior:
869 * - On radio ON, turn on any LEDs that require to be on during start
870 * - On initialization, start unassociated blink
871 * - On association, disable unassociated blink
872 * - On disassociation, start unassociated blink
873 * - On radio OFF, turn off any LEDs started during radio on
874 *
875 */
Zhu Yiede61112006-01-24 16:37:10 +0800876#define LD_TIME_LINK_ON msecs_to_jiffies(300)
877#define LD_TIME_LINK_OFF msecs_to_jiffies(2700)
878#define LD_TIME_ACT_ON msecs_to_jiffies(250)
James Ketrenosa613bff2005-08-24 21:43:11 -0500879
Adrian Bunka73e22b2006-01-21 01:39:42 +0100880static void ipw_led_link_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500881{
882 unsigned long flags;
883 u32 led;
884
885 /* If configured to not use LEDs, or nic_type is 1,
886 * then we don't toggle a LINK led */
887 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
888 return;
889
890 spin_lock_irqsave(&priv->lock, flags);
891
892 if (!(priv->status & STATUS_RF_KILL_MASK) &&
893 !(priv->status & STATUS_LED_LINK_ON)) {
894 IPW_DEBUG_LED("Link LED On\n");
James Ketrenosb095c382005-08-24 22:04:42 -0500895 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500896 led |= priv->led_association_on;
897
898 led = ipw_register_toggle(led);
899
900 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500901 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500902
903 priv->status |= STATUS_LED_LINK_ON;
904
905 /* If we aren't associated, schedule turning the LED off */
906 if (!(priv->status & STATUS_ASSOCIATED))
Tejun Heobcb6d912011-01-26 12:12:50 +0100907 schedule_delayed_work(&priv->led_link_off,
908 LD_TIME_LINK_ON);
James Ketrenosa613bff2005-08-24 21:43:11 -0500909 }
910
911 spin_unlock_irqrestore(&priv->lock, flags);
912}
913
David Howellsc4028952006-11-22 14:57:56 +0000914static void ipw_bg_led_link_on(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -0500915{
David Howellsc4028952006-11-22 14:57:56 +0000916 struct ipw_priv *priv =
917 container_of(work, struct ipw_priv, led_link_on.work);
Zhu Yi46441512006-01-24 16:37:59 +0800918 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +0000919 ipw_led_link_on(priv);
Zhu Yi46441512006-01-24 16:37:59 +0800920 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -0500921}
922
Adrian Bunka73e22b2006-01-21 01:39:42 +0100923static void ipw_led_link_off(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500924{
925 unsigned long flags;
926 u32 led;
927
928 /* If configured not to use LEDs, or nic type is 1,
929 * then we don't goggle the LINK led. */
930 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
931 return;
932
933 spin_lock_irqsave(&priv->lock, flags);
934
935 if (priv->status & STATUS_LED_LINK_ON) {
James Ketrenosb095c382005-08-24 22:04:42 -0500936 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500937 led &= priv->led_association_off;
938 led = ipw_register_toggle(led);
939
940 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500941 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500942
943 IPW_DEBUG_LED("Link LED Off\n");
944
945 priv->status &= ~STATUS_LED_LINK_ON;
946
947 /* If we aren't associated and the radio is on, schedule
948 * turning the LED on (blink while unassociated) */
949 if (!(priv->status & STATUS_RF_KILL_MASK) &&
950 !(priv->status & STATUS_ASSOCIATED))
Tejun Heobcb6d912011-01-26 12:12:50 +0100951 schedule_delayed_work(&priv->led_link_on,
952 LD_TIME_LINK_OFF);
James Ketrenosa613bff2005-08-24 21:43:11 -0500953
954 }
955
956 spin_unlock_irqrestore(&priv->lock, flags);
957}
958
David Howellsc4028952006-11-22 14:57:56 +0000959static void ipw_bg_led_link_off(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -0500960{
David Howellsc4028952006-11-22 14:57:56 +0000961 struct ipw_priv *priv =
962 container_of(work, struct ipw_priv, led_link_off.work);
Zhu Yi46441512006-01-24 16:37:59 +0800963 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +0000964 ipw_led_link_off(priv);
Zhu Yi46441512006-01-24 16:37:59 +0800965 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -0500966}
967
Arjan van de Ven858119e2006-01-14 13:20:43 -0800968static void __ipw_led_activity_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500969{
James Ketrenosa613bff2005-08-24 21:43:11 -0500970 u32 led;
971
972 if (priv->config & CFG_NO_LED)
973 return;
974
James Ketrenosb095c382005-08-24 22:04:42 -0500975 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -0500976 return;
James Ketrenosa613bff2005-08-24 21:43:11 -0500977
978 if (!(priv->status & STATUS_LED_ACT_ON)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500979 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500980 led |= priv->led_activity_on;
981
982 led = ipw_register_toggle(led);
983
984 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500985 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500986
987 IPW_DEBUG_LED("Activity LED On\n");
988
989 priv->status |= STATUS_LED_ACT_ON;
990
James Ketrenosc848d0a2005-08-24 21:56:24 -0500991 cancel_delayed_work(&priv->led_act_off);
Tejun Heobcb6d912011-01-26 12:12:50 +0100992 schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
James Ketrenosa613bff2005-08-24 21:43:11 -0500993 } else {
994 /* Reschedule LED off for full time period */
995 cancel_delayed_work(&priv->led_act_off);
Tejun Heobcb6d912011-01-26 12:12:50 +0100996 schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON);
James Ketrenosa613bff2005-08-24 21:43:11 -0500997 }
James Ketrenosb095c382005-08-24 22:04:42 -0500998}
James Ketrenosa613bff2005-08-24 21:43:11 -0500999
Adrian Bunka73e22b2006-01-21 01:39:42 +01001000#if 0
James Ketrenosb095c382005-08-24 22:04:42 -05001001void ipw_led_activity_on(struct ipw_priv *priv)
1002{
1003 unsigned long flags;
1004 spin_lock_irqsave(&priv->lock, flags);
1005 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05001006 spin_unlock_irqrestore(&priv->lock, flags);
1007}
Adrian Bunka73e22b2006-01-21 01:39:42 +01001008#endif /* 0 */
James Ketrenosa613bff2005-08-24 21:43:11 -05001009
Adrian Bunka73e22b2006-01-21 01:39:42 +01001010static void ipw_led_activity_off(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001011{
1012 unsigned long flags;
1013 u32 led;
1014
1015 if (priv->config & CFG_NO_LED)
1016 return;
1017
1018 spin_lock_irqsave(&priv->lock, flags);
1019
1020 if (priv->status & STATUS_LED_ACT_ON) {
James Ketrenosb095c382005-08-24 22:04:42 -05001021 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -05001022 led &= priv->led_activity_off;
1023
1024 led = ipw_register_toggle(led);
1025
1026 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -05001027 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -05001028
1029 IPW_DEBUG_LED("Activity LED Off\n");
1030
1031 priv->status &= ~STATUS_LED_ACT_ON;
1032 }
1033
1034 spin_unlock_irqrestore(&priv->lock, flags);
1035}
1036
David Howellsc4028952006-11-22 14:57:56 +00001037static void ipw_bg_led_activity_off(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05001038{
David Howellsc4028952006-11-22 14:57:56 +00001039 struct ipw_priv *priv =
1040 container_of(work, struct ipw_priv, led_act_off.work);
Zhu Yi46441512006-01-24 16:37:59 +08001041 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00001042 ipw_led_activity_off(priv);
Zhu Yi46441512006-01-24 16:37:59 +08001043 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05001044}
1045
Adrian Bunka73e22b2006-01-21 01:39:42 +01001046static void ipw_led_band_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001047{
1048 unsigned long flags;
1049 u32 led;
1050
1051 /* Only nic type 1 supports mode LEDs */
James Ketrenosc848d0a2005-08-24 21:56:24 -05001052 if (priv->config & CFG_NO_LED ||
1053 priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
James Ketrenosa613bff2005-08-24 21:43:11 -05001054 return;
1055
1056 spin_lock_irqsave(&priv->lock, flags);
1057
James Ketrenosb095c382005-08-24 22:04:42 -05001058 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -05001059 if (priv->assoc_network->mode == IEEE_A) {
1060 led |= priv->led_ofdm_on;
1061 led &= priv->led_association_off;
1062 IPW_DEBUG_LED("Mode LED On: 802.11a\n");
1063 } else if (priv->assoc_network->mode == IEEE_G) {
1064 led |= priv->led_ofdm_on;
1065 led |= priv->led_association_on;
1066 IPW_DEBUG_LED("Mode LED On: 802.11g\n");
1067 } else {
1068 led &= priv->led_ofdm_off;
1069 led |= priv->led_association_on;
1070 IPW_DEBUG_LED("Mode LED On: 802.11b\n");
1071 }
1072
1073 led = ipw_register_toggle(led);
1074
1075 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -05001076 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -05001077
1078 spin_unlock_irqrestore(&priv->lock, flags);
1079}
1080
Adrian Bunka73e22b2006-01-21 01:39:42 +01001081static void ipw_led_band_off(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001082{
1083 unsigned long flags;
1084 u32 led;
1085
1086 /* Only nic type 1 supports mode LEDs */
1087 if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
1088 return;
1089
1090 spin_lock_irqsave(&priv->lock, flags);
1091
James Ketrenosb095c382005-08-24 22:04:42 -05001092 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -05001093 led &= priv->led_ofdm_off;
1094 led &= priv->led_association_off;
1095
1096 led = ipw_register_toggle(led);
1097
1098 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -05001099 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -05001100
1101 spin_unlock_irqrestore(&priv->lock, flags);
1102}
1103
Adrian Bunka73e22b2006-01-21 01:39:42 +01001104static void ipw_led_radio_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001105{
1106 ipw_led_link_on(priv);
1107}
1108
Adrian Bunka73e22b2006-01-21 01:39:42 +01001109static void ipw_led_radio_off(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001110{
1111 ipw_led_activity_off(priv);
1112 ipw_led_link_off(priv);
1113}
1114
Adrian Bunka73e22b2006-01-21 01:39:42 +01001115static void ipw_led_link_up(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001116{
1117 /* Set the Link Led on for all nic types */
1118 ipw_led_link_on(priv);
1119}
1120
Adrian Bunka73e22b2006-01-21 01:39:42 +01001121static void ipw_led_link_down(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001122{
1123 ipw_led_activity_off(priv);
1124 ipw_led_link_off(priv);
1125
1126 if (priv->status & STATUS_RF_KILL_MASK)
1127 ipw_led_radio_off(priv);
1128}
1129
Adrian Bunka73e22b2006-01-21 01:39:42 +01001130static void ipw_led_init(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001131{
1132 priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE];
1133
1134 /* Set the default PINs for the link and activity leds */
James Ketrenosb095c382005-08-24 22:04:42 -05001135 priv->led_activity_on = IPW_ACTIVITY_LED;
1136 priv->led_activity_off = ~(IPW_ACTIVITY_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -05001137
James Ketrenosb095c382005-08-24 22:04:42 -05001138 priv->led_association_on = IPW_ASSOCIATED_LED;
1139 priv->led_association_off = ~(IPW_ASSOCIATED_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -05001140
1141 /* Set the default PINs for the OFDM leds */
James Ketrenosb095c382005-08-24 22:04:42 -05001142 priv->led_ofdm_on = IPW_OFDM_LED;
1143 priv->led_ofdm_off = ~(IPW_OFDM_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -05001144
1145 switch (priv->nic_type) {
1146 case EEPROM_NIC_TYPE_1:
1147 /* In this NIC type, the LEDs are reversed.... */
James Ketrenosb095c382005-08-24 22:04:42 -05001148 priv->led_activity_on = IPW_ASSOCIATED_LED;
1149 priv->led_activity_off = ~(IPW_ASSOCIATED_LED);
1150 priv->led_association_on = IPW_ACTIVITY_LED;
1151 priv->led_association_off = ~(IPW_ACTIVITY_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -05001152
1153 if (!(priv->config & CFG_NO_LED))
1154 ipw_led_band_on(priv);
1155
1156 /* And we don't blink link LEDs for this nic, so
1157 * just return here */
1158 return;
1159
1160 case EEPROM_NIC_TYPE_3:
1161 case EEPROM_NIC_TYPE_2:
1162 case EEPROM_NIC_TYPE_4:
1163 case EEPROM_NIC_TYPE_0:
1164 break;
1165
1166 default:
1167 IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
1168 priv->nic_type);
1169 priv->nic_type = EEPROM_NIC_TYPE_0;
1170 break;
1171 }
1172
1173 if (!(priv->config & CFG_NO_LED)) {
1174 if (priv->status & STATUS_ASSOCIATED)
1175 ipw_led_link_on(priv);
1176 else
1177 ipw_led_link_off(priv);
1178 }
1179}
1180
Adrian Bunka73e22b2006-01-21 01:39:42 +01001181static void ipw_led_shutdown(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001182{
James Ketrenosa613bff2005-08-24 21:43:11 -05001183 ipw_led_activity_off(priv);
1184 ipw_led_link_off(priv);
1185 ipw_led_band_off(priv);
James Ketrenosafbf30a2005-08-25 00:05:33 -05001186 cancel_delayed_work(&priv->led_link_on);
1187 cancel_delayed_work(&priv->led_link_off);
1188 cancel_delayed_work(&priv->led_act_off);
James Ketrenosa613bff2005-08-24 21:43:11 -05001189}
1190
James Ketrenos43f66a62005-03-25 12:31:53 -06001191/*
1192 * The following adds a new attribute to the sysfs representation
1193 * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
Lucas De Marchi25985ed2011-03-30 22:57:33 -03001194 * used for controlling the debug level.
Jeff Garzikbf794512005-07-31 13:07:26 -04001195 *
James Ketrenos43f66a62005-03-25 12:31:53 -06001196 * See the level definitions in ipw for details.
1197 */
Greg Kroah-Hartman0457e1a2017-06-09 11:03:10 +02001198static ssize_t debug_level_show(struct device_driver *d, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001199{
1200 return sprintf(buf, "0x%08X\n", ipw_debug_level);
1201}
James Ketrenosa613bff2005-08-24 21:43:11 -05001202
Greg Kroah-Hartman0457e1a2017-06-09 11:03:10 +02001203static ssize_t debug_level_store(struct device_driver *d, const char *buf,
James Ketrenosa613bff2005-08-24 21:43:11 -05001204 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001205{
1206 char *p = (char *)buf;
1207 u32 val;
1208
1209 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1210 p++;
1211 if (p[0] == 'x' || p[0] == 'X')
1212 p++;
1213 val = simple_strtoul(p, &p, 16);
1214 } else
1215 val = simple_strtoul(p, &p, 10);
Jeff Garzikbf794512005-07-31 13:07:26 -04001216 if (p == buf)
1217 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06001218 ": %s is not in hex or decimal form.\n", buf);
1219 else
1220 ipw_debug_level = val;
1221
1222 return strnlen(buf, count);
1223}
Greg Kroah-Hartman0457e1a2017-06-09 11:03:10 +02001224static DRIVER_ATTR_RW(debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -06001225
James Ketrenosb39860c2005-08-12 09:36:32 -05001226static inline u32 ipw_get_event_log_len(struct ipw_priv *priv)
1227{
Zhu Yic8fe6672006-01-24 16:36:36 +08001228 /* length = 1st dword in log */
James Ketrenosb39860c2005-08-12 09:36:32 -05001229 return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG));
1230}
1231
1232static void ipw_capture_event_log(struct ipw_priv *priv,
1233 u32 log_len, struct ipw_event *log)
1234{
1235 u32 base;
1236
1237 if (log_len) {
1238 base = ipw_read32(priv, IPW_EVENT_LOG);
1239 ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32),
1240 (u8 *) log, sizeof(*log) * log_len);
1241 }
1242}
1243
1244static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
1245{
1246 struct ipw_fw_error *error;
1247 u32 log_len = ipw_get_event_log_len(priv);
1248 u32 base = ipw_read32(priv, IPW_ERROR_LOG);
1249 u32 elem_len = ipw_read_reg32(priv, base);
1250
1251 error = kmalloc(sizeof(*error) +
1252 sizeof(*error->elem) * elem_len +
1253 sizeof(*error->log) * log_len, GFP_ATOMIC);
1254 if (!error) {
1255 IPW_ERROR("Memory allocation for firmware error log "
1256 "failed.\n");
1257 return NULL;
1258 }
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001259 error->jiffies = jiffies;
James Ketrenosb39860c2005-08-12 09:36:32 -05001260 error->status = priv->status;
1261 error->config = priv->config;
1262 error->elem_len = elem_len;
1263 error->log_len = log_len;
1264 error->elem = (struct ipw_error_elem *)error->payload;
Zhu Yi3b26b112005-11-17 13:58:30 +08001265 error->log = (struct ipw_event *)(error->elem + elem_len);
James Ketrenosb39860c2005-08-12 09:36:32 -05001266
1267 ipw_capture_event_log(priv, log_len, error->log);
1268
1269 if (elem_len)
1270 ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem,
1271 sizeof(*error->elem) * elem_len);
1272
1273 return error;
1274}
1275
James Ketrenosb39860c2005-08-12 09:36:32 -05001276static ssize_t show_event_log(struct device *d,
1277 struct device_attribute *attr, char *buf)
1278{
1279 struct ipw_priv *priv = dev_get_drvdata(d);
1280 u32 log_len = ipw_get_event_log_len(priv);
Reinette Chatre412e9e72007-12-18 22:01:02 -08001281 u32 log_size;
1282 struct ipw_event *log;
James Ketrenosb39860c2005-08-12 09:36:32 -05001283 u32 len = 0, i;
1284
Reinette Chatre412e9e72007-12-18 22:01:02 -08001285 /* not using min() because of its strict type checking */
1286 log_size = PAGE_SIZE / sizeof(*log) > log_len ?
1287 sizeof(*log) * log_len : PAGE_SIZE;
1288 log = kzalloc(log_size, GFP_KERNEL);
1289 if (!log) {
1290 IPW_ERROR("Unable to allocate memory for log\n");
1291 return 0;
1292 }
1293 log_len = log_size / sizeof(*log);
James Ketrenosb39860c2005-08-12 09:36:32 -05001294 ipw_capture_event_log(priv, log_len, log);
1295
1296 len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
1297 for (i = 0; i < log_len; i++)
1298 len += snprintf(buf + len, PAGE_SIZE - len,
1299 "\n%08X%08X%08X",
1300 log[i].time, log[i].event, log[i].data);
1301 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
Reinette Chatre412e9e72007-12-18 22:01:02 -08001302 kfree(log);
James Ketrenosb39860c2005-08-12 09:36:32 -05001303 return len;
1304}
1305
Joe Perches2ef00c52018-03-23 15:54:37 -07001306static DEVICE_ATTR(event_log, 0444, show_event_log, NULL);
James Ketrenosb39860c2005-08-12 09:36:32 -05001307
1308static ssize_t show_error(struct device *d,
1309 struct device_attribute *attr, char *buf)
1310{
1311 struct ipw_priv *priv = dev_get_drvdata(d);
1312 u32 len = 0, i;
1313 if (!priv->error)
1314 return 0;
1315 len += snprintf(buf + len, PAGE_SIZE - len,
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001316 "%08lX%08X%08X%08X",
1317 priv->error->jiffies,
James Ketrenosb39860c2005-08-12 09:36:32 -05001318 priv->error->status,
1319 priv->error->config, priv->error->elem_len);
1320 for (i = 0; i < priv->error->elem_len; i++)
1321 len += snprintf(buf + len, PAGE_SIZE - len,
1322 "\n%08X%08X%08X%08X%08X%08X%08X",
1323 priv->error->elem[i].time,
1324 priv->error->elem[i].desc,
1325 priv->error->elem[i].blink1,
1326 priv->error->elem[i].blink2,
1327 priv->error->elem[i].link1,
1328 priv->error->elem[i].link2,
1329 priv->error->elem[i].data);
1330
1331 len += snprintf(buf + len, PAGE_SIZE - len,
1332 "\n%08X", priv->error->log_len);
1333 for (i = 0; i < priv->error->log_len; i++)
1334 len += snprintf(buf + len, PAGE_SIZE - len,
1335 "\n%08X%08X%08X",
1336 priv->error->log[i].time,
1337 priv->error->log[i].event,
1338 priv->error->log[i].data);
1339 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1340 return len;
1341}
1342
1343static ssize_t clear_error(struct device *d,
1344 struct device_attribute *attr,
1345 const char *buf, size_t count)
1346{
1347 struct ipw_priv *priv = dev_get_drvdata(d);
Jesper Juhl8f760782006-06-27 02:55:06 -07001348
1349 kfree(priv->error);
1350 priv->error = NULL;
James Ketrenosb39860c2005-08-12 09:36:32 -05001351 return count;
1352}
1353
Joe Perches2ef00c52018-03-23 15:54:37 -07001354static DEVICE_ATTR(error, 0644, show_error, clear_error);
James Ketrenosb39860c2005-08-12 09:36:32 -05001355
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001356static ssize_t show_cmd_log(struct device *d,
1357 struct device_attribute *attr, char *buf)
1358{
1359 struct ipw_priv *priv = dev_get_drvdata(d);
1360 u32 len = 0, i;
1361 if (!priv->cmdlog)
1362 return 0;
1363 for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
Dan Carpenter9fdc7232014-10-09 12:59:39 +03001364 (i != priv->cmdlog_pos) && (len < PAGE_SIZE);
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001365 i = (i + 1) % priv->cmdlog_len) {
1366 len +=
1367 snprintf(buf + len, PAGE_SIZE - len,
1368 "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies,
1369 priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd,
1370 priv->cmdlog[i].cmd.len);
1371 len +=
1372 snprintk_buf(buf + len, PAGE_SIZE - len,
1373 (u8 *) priv->cmdlog[i].cmd.param,
1374 priv->cmdlog[i].cmd.len);
1375 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1376 }
1377 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1378 return len;
1379}
1380
Joe Perches2ef00c52018-03-23 15:54:37 -07001381static DEVICE_ATTR(cmd_log, 0444, show_cmd_log, NULL);
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001382
Zhu Yid685b8c2006-04-13 17:20:27 +08001383#ifdef CONFIG_IPW2200_PROMISCUOUS
1384static void ipw_prom_free(struct ipw_priv *priv);
1385static int ipw_prom_alloc(struct ipw_priv *priv);
1386static ssize_t store_rtap_iface(struct device *d,
1387 struct device_attribute *attr,
1388 const char *buf, size_t count)
1389{
1390 struct ipw_priv *priv = dev_get_drvdata(d);
1391 int rc = 0;
1392
1393 if (count < 1)
1394 return -EINVAL;
1395
1396 switch (buf[0]) {
1397 case '0':
1398 if (!rtap_iface)
1399 return count;
1400
1401 if (netif_running(priv->prom_net_dev)) {
1402 IPW_WARNING("Interface is up. Cannot unregister.\n");
1403 return count;
1404 }
1405
1406 ipw_prom_free(priv);
1407 rtap_iface = 0;
1408 break;
1409
1410 case '1':
1411 if (rtap_iface)
1412 return count;
1413
1414 rc = ipw_prom_alloc(priv);
1415 if (!rc)
1416 rtap_iface = 1;
1417 break;
1418
1419 default:
1420 return -EINVAL;
1421 }
1422
1423 if (rc) {
1424 IPW_ERROR("Failed to register promiscuous network "
1425 "device (error %d).\n", rc);
1426 }
1427
1428 return count;
1429}
1430
1431static ssize_t show_rtap_iface(struct device *d,
1432 struct device_attribute *attr,
1433 char *buf)
1434{
1435 struct ipw_priv *priv = dev_get_drvdata(d);
1436 if (rtap_iface)
1437 return sprintf(buf, "%s", priv->prom_net_dev->name);
1438 else {
1439 buf[0] = '-';
1440 buf[1] = '1';
1441 buf[2] = '\0';
1442 return 3;
1443 }
1444}
1445
Joe Perches2ef00c52018-03-23 15:54:37 -07001446static DEVICE_ATTR(rtap_iface, 0600, show_rtap_iface, store_rtap_iface);
Zhu Yid685b8c2006-04-13 17:20:27 +08001447
1448static ssize_t store_rtap_filter(struct device *d,
1449 struct device_attribute *attr,
1450 const char *buf, size_t count)
1451{
1452 struct ipw_priv *priv = dev_get_drvdata(d);
1453
1454 if (!priv->prom_priv) {
1455 IPW_ERROR("Attempting to set filter without "
1456 "rtap_iface enabled.\n");
1457 return -EPERM;
1458 }
1459
1460 priv->prom_priv->filter = simple_strtol(buf, NULL, 0);
1461
1462 IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n",
1463 BIT_ARG16(priv->prom_priv->filter));
1464
1465 return count;
1466}
1467
1468static ssize_t show_rtap_filter(struct device *d,
1469 struct device_attribute *attr,
1470 char *buf)
1471{
1472 struct ipw_priv *priv = dev_get_drvdata(d);
1473 return sprintf(buf, "0x%04X",
1474 priv->prom_priv ? priv->prom_priv->filter : 0);
1475}
1476
Joe Perches2ef00c52018-03-23 15:54:37 -07001477static DEVICE_ATTR(rtap_filter, 0600, show_rtap_filter, store_rtap_filter);
Zhu Yid685b8c2006-04-13 17:20:27 +08001478#endif
1479
James Ketrenosa613bff2005-08-24 21:43:11 -05001480static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
1481 char *buf)
1482{
1483 struct ipw_priv *priv = dev_get_drvdata(d);
1484 return sprintf(buf, "%d\n", priv->ieee->scan_age);
1485}
1486
1487static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
1488 const char *buf, size_t count)
1489{
1490 struct ipw_priv *priv = dev_get_drvdata(d);
1491 struct net_device *dev = priv->net_dev;
1492 char buffer[] = "00000000";
1493 unsigned long len =
1494 (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
1495 unsigned long val;
1496 char *p = buffer;
1497
1498 IPW_DEBUG_INFO("enter\n");
1499
1500 strncpy(buffer, buf, len);
1501 buffer[len] = 0;
1502
1503 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1504 p++;
1505 if (p[0] == 'x' || p[0] == 'X')
1506 p++;
1507 val = simple_strtoul(p, &p, 16);
1508 } else
1509 val = simple_strtoul(p, &p, 10);
1510 if (p == buffer) {
1511 IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
1512 } else {
1513 priv->ieee->scan_age = val;
1514 IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
1515 }
1516
1517 IPW_DEBUG_INFO("exit\n");
1518 return len;
1519}
1520
Joe Perches2ef00c52018-03-23 15:54:37 -07001521static DEVICE_ATTR(scan_age, 0644, show_scan_age, store_scan_age);
James Ketrenosa613bff2005-08-24 21:43:11 -05001522
1523static ssize_t show_led(struct device *d, struct device_attribute *attr,
1524 char *buf)
1525{
1526 struct ipw_priv *priv = dev_get_drvdata(d);
1527 return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1);
1528}
1529
1530static ssize_t store_led(struct device *d, struct device_attribute *attr,
1531 const char *buf, size_t count)
1532{
1533 struct ipw_priv *priv = dev_get_drvdata(d);
1534
1535 IPW_DEBUG_INFO("enter\n");
1536
1537 if (count == 0)
1538 return 0;
1539
1540 if (*buf == 0) {
1541 IPW_DEBUG_LED("Disabling LED control.\n");
1542 priv->config |= CFG_NO_LED;
1543 ipw_led_shutdown(priv);
1544 } else {
1545 IPW_DEBUG_LED("Enabling LED control.\n");
1546 priv->config &= ~CFG_NO_LED;
1547 ipw_led_init(priv);
1548 }
1549
1550 IPW_DEBUG_INFO("exit\n");
1551 return count;
1552}
1553
Joe Perches2ef00c52018-03-23 15:54:37 -07001554static DEVICE_ATTR(led, 0644, show_led, store_led);
James Ketrenosa613bff2005-08-24 21:43:11 -05001555
Andrew Mortonad3fee52005-06-20 14:30:36 -07001556static ssize_t show_status(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001557 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001558{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001559 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001560 return sprintf(buf, "0x%08x\n", (int)p->status);
1561}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001562
Joe Perches2ef00c52018-03-23 15:54:37 -07001563static DEVICE_ATTR(status, 0444, show_status, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001564
Andrew Mortonad3fee52005-06-20 14:30:36 -07001565static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
1566 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001567{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001568 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001569 return sprintf(buf, "0x%08x\n", (int)p->config);
1570}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001571
Joe Perches2ef00c52018-03-23 15:54:37 -07001572static DEVICE_ATTR(cfg, 0444, show_cfg, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001573
Andrew Mortonad3fee52005-06-20 14:30:36 -07001574static ssize_t show_nic_type(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001575 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001576{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001577 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosa613bff2005-08-24 21:43:11 -05001578 return sprintf(buf, "TYPE: %d\n", priv->nic_type);
James Ketrenos43f66a62005-03-25 12:31:53 -06001579}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001580
Joe Perches2ef00c52018-03-23 15:54:37 -07001581static DEVICE_ATTR(nic_type, 0444, show_nic_type, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001582
Andrew Mortonad3fee52005-06-20 14:30:36 -07001583static ssize_t show_ucode_version(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001584 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001585{
1586 u32 len = sizeof(u32), tmp = 0;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001587 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001588
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001589 if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001590 return 0;
1591
1592 return sprintf(buf, "0x%08x\n", tmp);
1593}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001594
Joe Perches2ef00c52018-03-23 15:54:37 -07001595static DEVICE_ATTR(ucode_version, 0644, show_ucode_version, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001596
Andrew Mortonad3fee52005-06-20 14:30:36 -07001597static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
1598 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001599{
1600 u32 len = sizeof(u32), tmp = 0;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001601 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001602
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001603 if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001604 return 0;
1605
1606 return sprintf(buf, "0x%08x\n", tmp);
1607}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001608
Joe Perches2ef00c52018-03-23 15:54:37 -07001609static DEVICE_ATTR(rtc, 0644, show_rtc, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001610
1611/*
1612 * Add a device attribute to view/control the delay between eeprom
1613 * operations.
1614 */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001615static ssize_t show_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001616 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001617{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001618 struct ipw_priv *p = dev_get_drvdata(d);
1619 int n = p->eeprom_delay;
James Ketrenos43f66a62005-03-25 12:31:53 -06001620 return sprintf(buf, "%i\n", n);
1621}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001622static ssize_t store_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001623 struct device_attribute *attr,
1624 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001625{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001626 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001627 sscanf(buf, "%i", &p->eeprom_delay);
1628 return strnlen(buf, count);
1629}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001630
Joe Perches2ef00c52018-03-23 15:54:37 -07001631static DEVICE_ATTR(eeprom_delay, 0644, show_eeprom_delay, store_eeprom_delay);
James Ketrenos43f66a62005-03-25 12:31:53 -06001632
Andrew Mortonad3fee52005-06-20 14:30:36 -07001633static ssize_t show_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001634 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001635{
1636 u32 reg = 0;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001637 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001638
James Ketrenosb095c382005-08-24 22:04:42 -05001639 reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001640 return sprintf(buf, "0x%08x\n", reg);
1641}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001642static ssize_t store_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001643 struct device_attribute *attr,
1644 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001645{
1646 u32 reg;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001647 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001648
1649 sscanf(buf, "%x", &reg);
James Ketrenosb095c382005-08-24 22:04:42 -05001650 ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001651 return strnlen(buf, count);
1652}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001653
Joe Perches2ef00c52018-03-23 15:54:37 -07001654static DEVICE_ATTR(command_event_reg, 0644,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001655 show_command_event_reg, store_command_event_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001656
Andrew Mortonad3fee52005-06-20 14:30:36 -07001657static ssize_t show_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001658 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001659{
1660 u32 reg = 0;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001661 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001662
1663 reg = ipw_read_reg32(p, 0x301100);
1664 return sprintf(buf, "0x%08x\n", reg);
1665}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001666static ssize_t store_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001667 struct device_attribute *attr,
1668 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001669{
1670 u32 reg;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001671 struct ipw_priv *p = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001672
1673 sscanf(buf, "%x", &reg);
1674 ipw_write_reg32(p, 0x301100, reg);
1675 return strnlen(buf, count);
1676}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001677
Joe Perches2ef00c52018-03-23 15:54:37 -07001678static DEVICE_ATTR(mem_gpio_reg, 0644, show_mem_gpio_reg, store_mem_gpio_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001679
Andrew Mortonad3fee52005-06-20 14:30:36 -07001680static ssize_t show_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001681 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001682{
1683 u32 reg = 0;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001684 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosafbf30a2005-08-25 00:05:33 -05001685
Jeff Garzikbf794512005-07-31 13:07:26 -04001686 if (priv->status & STATUS_INDIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001687 reg = ipw_read_reg32(priv, priv->indirect_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001688 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001689 reg = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04001690
James Ketrenos43f66a62005-03-25 12:31:53 -06001691 return sprintf(buf, "0x%08x\n", reg);
1692}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001693static ssize_t store_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001694 struct device_attribute *attr,
1695 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001696{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001697 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001698
1699 sscanf(buf, "%x", &priv->indirect_dword);
1700 priv->status |= STATUS_INDIRECT_DWORD;
1701 return strnlen(buf, count);
1702}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001703
Joe Perches2ef00c52018-03-23 15:54:37 -07001704static DEVICE_ATTR(indirect_dword, 0644,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001705 show_indirect_dword, store_indirect_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001706
Andrew Mortonad3fee52005-06-20 14:30:36 -07001707static ssize_t show_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001708 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001709{
1710 u8 reg = 0;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001711 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosafbf30a2005-08-25 00:05:33 -05001712
Jeff Garzikbf794512005-07-31 13:07:26 -04001713 if (priv->status & STATUS_INDIRECT_BYTE)
James Ketrenos43f66a62005-03-25 12:31:53 -06001714 reg = ipw_read_reg8(priv, priv->indirect_byte);
Jeff Garzikbf794512005-07-31 13:07:26 -04001715 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001716 reg = 0;
1717
1718 return sprintf(buf, "0x%02x\n", reg);
1719}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001720static ssize_t store_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001721 struct device_attribute *attr,
1722 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001723{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001724 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001725
1726 sscanf(buf, "%x", &priv->indirect_byte);
1727 priv->status |= STATUS_INDIRECT_BYTE;
1728 return strnlen(buf, count);
1729}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001730
Joe Perches2ef00c52018-03-23 15:54:37 -07001731static DEVICE_ATTR(indirect_byte, 0644,
James Ketrenos43f66a62005-03-25 12:31:53 -06001732 show_indirect_byte, store_indirect_byte);
1733
Andrew Mortonad3fee52005-06-20 14:30:36 -07001734static ssize_t show_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001735 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001736{
1737 u32 reg = 0;
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001738 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001739
Jeff Garzikbf794512005-07-31 13:07:26 -04001740 if (priv->status & STATUS_DIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001741 reg = ipw_read32(priv, priv->direct_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001742 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001743 reg = 0;
1744
1745 return sprintf(buf, "0x%08x\n", reg);
1746}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001747static ssize_t store_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001748 struct device_attribute *attr,
1749 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001750{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001751 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001752
1753 sscanf(buf, "%x", &priv->direct_dword);
1754 priv->status |= STATUS_DIRECT_DWORD;
1755 return strnlen(buf, count);
1756}
James Ketrenos43f66a62005-03-25 12:31:53 -06001757
Joe Perches2ef00c52018-03-23 15:54:37 -07001758static DEVICE_ATTR(direct_dword, 0644, show_direct_dword, store_direct_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001759
Arjan van de Ven858119e2006-01-14 13:20:43 -08001760static int rf_kill_active(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06001761{
Matthew Garrett25f94ae2009-11-11 14:36:31 -05001762 if (0 == (ipw_read32(priv, 0x30) & 0x10000)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001763 priv->status |= STATUS_RF_KILL_HW;
Matthew Garrett25f94ae2009-11-11 14:36:31 -05001764 wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
1765 } else {
James Ketrenos43f66a62005-03-25 12:31:53 -06001766 priv->status &= ~STATUS_RF_KILL_HW;
Matthew Garrett25f94ae2009-11-11 14:36:31 -05001767 wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
1768 }
James Ketrenos43f66a62005-03-25 12:31:53 -06001769
1770 return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
1771}
1772
Andrew Mortonad3fee52005-06-20 14:30:36 -07001773static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001774 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001775{
1776 /* 0 - RF kill not enabled
Jeff Garzikbf794512005-07-31 13:07:26 -04001777 1 - SW based RF kill active (sysfs)
James Ketrenos43f66a62005-03-25 12:31:53 -06001778 2 - HW based RF kill active
1779 3 - Both HW and SW baed RF kill active */
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001780 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenos43f66a62005-03-25 12:31:53 -06001781 int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001782 (rf_kill_active(priv) ? 0x2 : 0x0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001783 return sprintf(buf, "%i\n", val);
1784}
1785
1786static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
1787{
Jeff Garzikbf794512005-07-31 13:07:26 -04001788 if ((disable_radio ? 1 : 0) ==
James Ketrenosea2b26e2005-08-24 21:25:16 -05001789 ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001790 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001791
1792 IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
1793 disable_radio ? "OFF" : "ON");
1794
1795 if (disable_radio) {
1796 priv->status |= STATUS_RF_KILL_SW;
1797
Tejun Heobcb6d912011-01-26 12:12:50 +01001798 cancel_delayed_work(&priv->request_scan);
1799 cancel_delayed_work(&priv->request_direct_scan);
1800 cancel_delayed_work(&priv->request_passive_scan);
1801 cancel_delayed_work(&priv->scan_event);
1802 schedule_work(&priv->down);
James Ketrenos43f66a62005-03-25 12:31:53 -06001803 } else {
1804 priv->status &= ~STATUS_RF_KILL_SW;
1805 if (rf_kill_active(priv)) {
1806 IPW_DEBUG_RF_KILL("Can not turn radio back on - "
1807 "disabled by HW switch\n");
1808 /* Make sure the RF_KILL check timer is running */
1809 cancel_delayed_work(&priv->rf_kill);
Tejun Heobcb6d912011-01-26 12:12:50 +01001810 schedule_delayed_work(&priv->rf_kill,
1811 round_jiffies_relative(2 * HZ));
Jeff Garzikbf794512005-07-31 13:07:26 -04001812 } else
Tejun Heobcb6d912011-01-26 12:12:50 +01001813 schedule_work(&priv->up);
James Ketrenos43f66a62005-03-25 12:31:53 -06001814 }
1815
1816 return 1;
1817}
1818
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001819static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
1820 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001821{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001822 struct ipw_priv *priv = dev_get_drvdata(d);
Jeff Garzikbf794512005-07-31 13:07:26 -04001823
James Ketrenos43f66a62005-03-25 12:31:53 -06001824 ipw_radio_kill_sw(priv, buf[0] == '1');
1825
1826 return count;
1827}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001828
Joe Perches2ef00c52018-03-23 15:54:37 -07001829static DEVICE_ATTR(rf_kill, 0644, show_rf_kill, store_rf_kill);
James Ketrenos43f66a62005-03-25 12:31:53 -06001830
James Ketrenosb095c382005-08-24 22:04:42 -05001831static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr,
1832 char *buf)
1833{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001834 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosb095c382005-08-24 22:04:42 -05001835 int pos = 0, len = 0;
1836 if (priv->config & CFG_SPEED_SCAN) {
1837 while (priv->speed_scan[pos] != 0)
1838 len += sprintf(&buf[len], "%d ",
1839 priv->speed_scan[pos++]);
1840 return len + sprintf(&buf[len], "\n");
1841 }
1842
1843 return sprintf(buf, "0\n");
1844}
1845
1846static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr,
1847 const char *buf, size_t count)
1848{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001849 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosb095c382005-08-24 22:04:42 -05001850 int channel, pos = 0;
1851 const char *p = buf;
1852
1853 /* list of space separated channels to scan, optionally ending with 0 */
1854 while ((channel = simple_strtol(p, NULL, 0))) {
1855 if (pos == MAX_SPEED_SCAN - 1) {
1856 priv->speed_scan[pos] = 0;
1857 break;
1858 }
1859
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001860 if (libipw_is_valid_channel(priv->ieee, channel))
James Ketrenosb095c382005-08-24 22:04:42 -05001861 priv->speed_scan[pos++] = channel;
1862 else
1863 IPW_WARNING("Skipping invalid channel request: %d\n",
1864 channel);
1865 p = strchr(p, ' ');
1866 if (!p)
1867 break;
1868 while (*p == ' ' || *p == '\t')
1869 p++;
1870 }
1871
1872 if (pos == 0)
1873 priv->config &= ~CFG_SPEED_SCAN;
1874 else {
1875 priv->speed_scan_pos = 0;
1876 priv->config |= CFG_SPEED_SCAN;
1877 }
1878
1879 return count;
1880}
1881
Joe Perches2ef00c52018-03-23 15:54:37 -07001882static DEVICE_ATTR(speed_scan, 0644, show_speed_scan, store_speed_scan);
James Ketrenosb095c382005-08-24 22:04:42 -05001883
1884static ssize_t show_net_stats(struct device *d, struct device_attribute *attr,
1885 char *buf)
1886{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001887 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosb095c382005-08-24 22:04:42 -05001888 return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0');
1889}
1890
1891static ssize_t store_net_stats(struct device *d, struct device_attribute *attr,
1892 const char *buf, size_t count)
1893{
Greg Kroah-Hartman928841b2009-04-30 23:02:47 -07001894 struct ipw_priv *priv = dev_get_drvdata(d);
James Ketrenosb095c382005-08-24 22:04:42 -05001895 if (buf[0] == '1')
1896 priv->config |= CFG_NET_STATS;
1897 else
1898 priv->config &= ~CFG_NET_STATS;
1899
1900 return count;
1901}
1902
Joe Perches2ef00c52018-03-23 15:54:37 -07001903static DEVICE_ATTR(net_stats, 0644, show_net_stats, store_net_stats);
James Ketrenosb095c382005-08-24 22:04:42 -05001904
Zhu Yi375dd242007-02-14 16:04:24 +08001905static ssize_t show_channels(struct device *d,
1906 struct device_attribute *attr,
1907 char *buf)
1908{
1909 struct ipw_priv *priv = dev_get_drvdata(d);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001910 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
Zhu Yi375dd242007-02-14 16:04:24 +08001911 int len = 0, i;
1912
1913 len = sprintf(&buf[len],
1914 "Displaying %d channels in 2.4Ghz band "
1915 "(802.11bg):\n", geo->bg_channels);
1916
1917 for (i = 0; i < geo->bg_channels; i++) {
1918 len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n",
1919 geo->bg[i].channel,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001920 geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT ?
Zhu Yi375dd242007-02-14 16:04:24 +08001921 " (radar spectrum)" : "",
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001922 ((geo->bg[i].flags & LIBIPW_CH_NO_IBSS) ||
1923 (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT))
Zhu Yi375dd242007-02-14 16:04:24 +08001924 ? "" : ", IBSS",
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001925 geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY ?
Zhu Yi375dd242007-02-14 16:04:24 +08001926 "passive only" : "active/passive",
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001927 geo->bg[i].flags & LIBIPW_CH_B_ONLY ?
Zhu Yi375dd242007-02-14 16:04:24 +08001928 "B" : "B/G");
1929 }
1930
1931 len += sprintf(&buf[len],
1932 "Displaying %d channels in 5.2Ghz band "
1933 "(802.11a):\n", geo->a_channels);
1934 for (i = 0; i < geo->a_channels; i++) {
1935 len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n",
1936 geo->a[i].channel,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001937 geo->a[i].flags & LIBIPW_CH_RADAR_DETECT ?
Zhu Yi375dd242007-02-14 16:04:24 +08001938 " (radar spectrum)" : "",
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001939 ((geo->a[i].flags & LIBIPW_CH_NO_IBSS) ||
1940 (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT))
Zhu Yi375dd242007-02-14 16:04:24 +08001941 ? "" : ", IBSS",
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04001942 geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY ?
Zhu Yi375dd242007-02-14 16:04:24 +08001943 "passive only" : "active/passive");
1944 }
1945
1946 return len;
1947}
1948
Joe Perches2ef00c52018-03-23 15:54:37 -07001949static DEVICE_ATTR(channels, 0400, show_channels, NULL);
Zhu Yi375dd242007-02-14 16:04:24 +08001950
James Ketrenosea2b26e2005-08-24 21:25:16 -05001951static void notify_wx_assoc_event(struct ipw_priv *priv)
1952{
1953 union iwreq_data wrqu;
1954 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1955 if (priv->status & STATUS_ASSOCIATED)
1956 memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
1957 else
Joe Perches93803b32015-03-02 19:54:49 -08001958 eth_zero_addr(wrqu.ap_addr.sa_data);
James Ketrenosea2b26e2005-08-24 21:25:16 -05001959 wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
1960}
1961
James Ketrenos43f66a62005-03-25 12:31:53 -06001962static void ipw_irq_tasklet(struct ipw_priv *priv)
1963{
1964 u32 inta, inta_mask, handled = 0;
1965 unsigned long flags;
1966 int rc = 0;
1967
Zhu Yi89c318e2006-06-08 22:19:49 -07001968 spin_lock_irqsave(&priv->irq_lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001969
James Ketrenosb095c382005-08-24 22:04:42 -05001970 inta = ipw_read32(priv, IPW_INTA_RW);
1971 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
Indan Zupancic3c4a8cc2011-01-08 12:17:27 +01001972
1973 if (inta == 0xFFFFFFFF) {
1974 /* Hardware disappeared */
1975 IPW_WARNING("TASKLET INTA == 0xFFFFFFFF\n");
1976 /* Only handle the cached INTA values */
1977 inta = 0;
1978 }
James Ketrenosb095c382005-08-24 22:04:42 -05001979 inta &= (IPW_INTA_MASK_ALL & inta_mask);
James Ketrenos43f66a62005-03-25 12:31:53 -06001980
1981 /* Add any cached INTA values that need to be handled */
1982 inta |= priv->isr_inta;
1983
Zhu Yi89c318e2006-06-08 22:19:49 -07001984 spin_unlock_irqrestore(&priv->irq_lock, flags);
1985
1986 spin_lock_irqsave(&priv->lock, flags);
1987
James Ketrenos43f66a62005-03-25 12:31:53 -06001988 /* handle all the justifications for the interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05001989 if (inta & IPW_INTA_BIT_RX_TRANSFER) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001990 ipw_rx(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05001991 handled |= IPW_INTA_BIT_RX_TRANSFER;
James Ketrenos43f66a62005-03-25 12:31:53 -06001992 }
1993
James Ketrenosb095c382005-08-24 22:04:42 -05001994 if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001995 IPW_DEBUG_HC("Command completed.\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001996 rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
James Ketrenos43f66a62005-03-25 12:31:53 -06001997 priv->status &= ~STATUS_HCMD_ACTIVE;
1998 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosb095c382005-08-24 22:04:42 -05001999 handled |= IPW_INTA_BIT_TX_CMD_QUEUE;
James Ketrenos43f66a62005-03-25 12:31:53 -06002000 }
2001
James Ketrenosb095c382005-08-24 22:04:42 -05002002 if (inta & IPW_INTA_BIT_TX_QUEUE_1) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002003 IPW_DEBUG_TX("TX_QUEUE_1\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002004 rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
James Ketrenosb095c382005-08-24 22:04:42 -05002005 handled |= IPW_INTA_BIT_TX_QUEUE_1;
James Ketrenos43f66a62005-03-25 12:31:53 -06002006 }
2007
James Ketrenosb095c382005-08-24 22:04:42 -05002008 if (inta & IPW_INTA_BIT_TX_QUEUE_2) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002009 IPW_DEBUG_TX("TX_QUEUE_2\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002010 rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
James Ketrenosb095c382005-08-24 22:04:42 -05002011 handled |= IPW_INTA_BIT_TX_QUEUE_2;
James Ketrenos43f66a62005-03-25 12:31:53 -06002012 }
2013
James Ketrenosb095c382005-08-24 22:04:42 -05002014 if (inta & IPW_INTA_BIT_TX_QUEUE_3) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002015 IPW_DEBUG_TX("TX_QUEUE_3\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002016 rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
James Ketrenosb095c382005-08-24 22:04:42 -05002017 handled |= IPW_INTA_BIT_TX_QUEUE_3;
James Ketrenos43f66a62005-03-25 12:31:53 -06002018 }
2019
James Ketrenosb095c382005-08-24 22:04:42 -05002020 if (inta & IPW_INTA_BIT_TX_QUEUE_4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002021 IPW_DEBUG_TX("TX_QUEUE_4\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002022 rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
James Ketrenosb095c382005-08-24 22:04:42 -05002023 handled |= IPW_INTA_BIT_TX_QUEUE_4;
James Ketrenos43f66a62005-03-25 12:31:53 -06002024 }
2025
James Ketrenosb095c382005-08-24 22:04:42 -05002026 if (inta & IPW_INTA_BIT_STATUS_CHANGE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002027 IPW_WARNING("STATUS_CHANGE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002028 handled |= IPW_INTA_BIT_STATUS_CHANGE;
James Ketrenos43f66a62005-03-25 12:31:53 -06002029 }
2030
James Ketrenosb095c382005-08-24 22:04:42 -05002031 if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002032 IPW_WARNING("TX_PERIOD_EXPIRED\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002033 handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED;
James Ketrenos43f66a62005-03-25 12:31:53 -06002034 }
2035
James Ketrenosb095c382005-08-24 22:04:42 -05002036 if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002037 IPW_WARNING("HOST_CMD_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002038 handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06002039 }
2040
James Ketrenosb095c382005-08-24 22:04:42 -05002041 if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002042 IPW_WARNING("FW_INITIALIZATION_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002043 handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06002044 }
2045
James Ketrenosb095c382005-08-24 22:04:42 -05002046 if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002047 IPW_WARNING("PHY_OFF_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002048 handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06002049 }
2050
James Ketrenosb095c382005-08-24 22:04:42 -05002051 if (inta & IPW_INTA_BIT_RF_KILL_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002052 IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
2053 priv->status |= STATUS_RF_KILL_HW;
Matthew Garrett25f94ae2009-11-11 14:36:31 -05002054 wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
James Ketrenos43f66a62005-03-25 12:31:53 -06002055 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosea2b26e2005-08-24 21:25:16 -05002056 priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
James Ketrenos43f66a62005-03-25 12:31:53 -06002057 cancel_delayed_work(&priv->request_scan);
Dan Williamsea177302008-06-02 17:51:23 -04002058 cancel_delayed_work(&priv->request_direct_scan);
2059 cancel_delayed_work(&priv->request_passive_scan);
Dan Williams0b531672007-10-09 13:55:24 -04002060 cancel_delayed_work(&priv->scan_event);
James Ketrenosa613bff2005-08-24 21:43:11 -05002061 schedule_work(&priv->link_down);
Tejun Heobcb6d912011-01-26 12:12:50 +01002062 schedule_delayed_work(&priv->rf_kill, 2 * HZ);
James Ketrenosb095c382005-08-24 22:04:42 -05002063 handled |= IPW_INTA_BIT_RF_KILL_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06002064 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002065
James Ketrenosb095c382005-08-24 22:04:42 -05002066 if (inta & IPW_INTA_BIT_FATAL_ERROR) {
Zhu Yi1d1b09e2006-03-02 06:40:59 +08002067 IPW_WARNING("Firmware error detected. Restarting.\n");
James Ketrenosb39860c2005-08-12 09:36:32 -05002068 if (priv->error) {
Zhu Yi1d1b09e2006-03-02 06:40:59 +08002069 IPW_DEBUG_FW("Sysfs 'error' log already exists.\n");
James Ketrenosb39860c2005-08-12 09:36:32 -05002070 if (ipw_debug_level & IPW_DL_FW_ERRORS) {
2071 struct ipw_fw_error *error =
2072 ipw_alloc_error_log(priv);
2073 ipw_dump_error_log(priv, error);
Jesper Juhl8f760782006-06-27 02:55:06 -07002074 kfree(error);
James Ketrenosb39860c2005-08-12 09:36:32 -05002075 }
James Ketrenosb39860c2005-08-12 09:36:32 -05002076 } else {
2077 priv->error = ipw_alloc_error_log(priv);
2078 if (priv->error)
Zhu Yi1d1b09e2006-03-02 06:40:59 +08002079 IPW_DEBUG_FW("Sysfs 'error' log captured.\n");
James Ketrenosb39860c2005-08-12 09:36:32 -05002080 else
Zhu Yi1d1b09e2006-03-02 06:40:59 +08002081 IPW_DEBUG_FW("Error allocating sysfs 'error' "
2082 "log.\n");
James Ketrenosb39860c2005-08-12 09:36:32 -05002083 if (ipw_debug_level & IPW_DL_FW_ERRORS)
2084 ipw_dump_error_log(priv, priv->error);
James Ketrenosb39860c2005-08-12 09:36:32 -05002085 }
2086
James Ketrenosb095c382005-08-24 22:04:42 -05002087 /* XXX: If hardware encryption is for WPA/WPA2,
2088 * we have to notify the supplicant. */
2089 if (priv->ieee->sec.encrypt) {
2090 priv->status &= ~STATUS_ASSOCIATED;
2091 notify_wx_assoc_event(priv);
2092 }
2093
2094 /* Keep the restart process from trying to send host
2095 * commands by clearing the INIT status bit */
2096 priv->status &= ~STATUS_INIT;
James Ketrenosafbf30a2005-08-25 00:05:33 -05002097
2098 /* Cancel currently queued command. */
2099 priv->status &= ~STATUS_HCMD_ACTIVE;
2100 wake_up_interruptible(&priv->wait_command_queue);
2101
Tejun Heobcb6d912011-01-26 12:12:50 +01002102 schedule_work(&priv->adapter_restart);
James Ketrenosb095c382005-08-24 22:04:42 -05002103 handled |= IPW_INTA_BIT_FATAL_ERROR;
James Ketrenos43f66a62005-03-25 12:31:53 -06002104 }
2105
James Ketrenosb095c382005-08-24 22:04:42 -05002106 if (inta & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002107 IPW_ERROR("Parity error\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002108 handled |= IPW_INTA_BIT_PARITY_ERROR;
James Ketrenos43f66a62005-03-25 12:31:53 -06002109 }
2110
2111 if (handled != inta) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002112 IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
James Ketrenos43f66a62005-03-25 12:31:53 -06002113 }
2114
Zhu Yi89c318e2006-06-08 22:19:49 -07002115 spin_unlock_irqrestore(&priv->lock, flags);
2116
James Ketrenos43f66a62005-03-25 12:31:53 -06002117 /* enable all interrupts */
2118 ipw_enable_interrupts(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06002119}
Jeff Garzikbf794512005-07-31 13:07:26 -04002120
James Ketrenos43f66a62005-03-25 12:31:53 -06002121#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
2122static char *get_cmd_string(u8 cmd)
2123{
2124 switch (cmd) {
2125 IPW_CMD(HOST_COMPLETE);
Jeff Garzikbf794512005-07-31 13:07:26 -04002126 IPW_CMD(POWER_DOWN);
2127 IPW_CMD(SYSTEM_CONFIG);
2128 IPW_CMD(MULTICAST_ADDRESS);
2129 IPW_CMD(SSID);
2130 IPW_CMD(ADAPTER_ADDRESS);
2131 IPW_CMD(PORT_TYPE);
2132 IPW_CMD(RTS_THRESHOLD);
2133 IPW_CMD(FRAG_THRESHOLD);
2134 IPW_CMD(POWER_MODE);
2135 IPW_CMD(WEP_KEY);
2136 IPW_CMD(TGI_TX_KEY);
2137 IPW_CMD(SCAN_REQUEST);
2138 IPW_CMD(SCAN_REQUEST_EXT);
2139 IPW_CMD(ASSOCIATE);
2140 IPW_CMD(SUPPORTED_RATES);
2141 IPW_CMD(SCAN_ABORT);
2142 IPW_CMD(TX_FLUSH);
2143 IPW_CMD(QOS_PARAMETERS);
2144 IPW_CMD(DINO_CONFIG);
2145 IPW_CMD(RSN_CAPABILITIES);
2146 IPW_CMD(RX_KEY);
2147 IPW_CMD(CARD_DISABLE);
2148 IPW_CMD(SEED_NUMBER);
2149 IPW_CMD(TX_POWER);
2150 IPW_CMD(COUNTRY_INFO);
2151 IPW_CMD(AIRONET_INFO);
2152 IPW_CMD(AP_TX_POWER);
2153 IPW_CMD(CCKM_INFO);
2154 IPW_CMD(CCX_VER_INFO);
2155 IPW_CMD(SET_CALIBRATION);
2156 IPW_CMD(SENSITIVITY_CALIB);
2157 IPW_CMD(RETRY_LIMIT);
2158 IPW_CMD(IPW_PRE_POWER_DOWN);
2159 IPW_CMD(VAP_BEACON_TEMPLATE);
2160 IPW_CMD(VAP_DTIM_PERIOD);
2161 IPW_CMD(EXT_SUPPORTED_RATES);
2162 IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
2163 IPW_CMD(VAP_QUIET_INTERVALS);
2164 IPW_CMD(VAP_CHANNEL_SWITCH);
2165 IPW_CMD(VAP_MANDATORY_CHANNELS);
2166 IPW_CMD(VAP_CELL_PWR_LIMIT);
2167 IPW_CMD(VAP_CF_PARAM_SET);
2168 IPW_CMD(VAP_SET_BEACONING_STATE);
2169 IPW_CMD(MEASUREMENT);
2170 IPW_CMD(POWER_CAPABILITY);
2171 IPW_CMD(SUPPORTED_CHANNELS);
2172 IPW_CMD(TPC_REPORT);
2173 IPW_CMD(WME_INFO);
2174 IPW_CMD(PRODUCTION_COMMAND);
2175 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06002176 return "UNKNOWN";
2177 }
2178}
James Ketrenos43f66a62005-03-25 12:31:53 -06002179
2180#define HOST_COMPLETE_TIMEOUT HZ
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002181
2182static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
James Ketrenos43f66a62005-03-25 12:31:53 -06002183{
2184 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05002185 unsigned long flags;
Stanislav Yakovlevdd447312012-04-19 15:55:09 -04002186 unsigned long now, end;
James Ketrenos43f66a62005-03-25 12:31:53 -06002187
James Ketrenosa613bff2005-08-24 21:43:11 -05002188 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002189 if (priv->status & STATUS_HCMD_ACTIVE) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002190 IPW_ERROR("Failed to send %s: Already sending a command.\n",
2191 get_cmd_string(cmd->cmd));
James Ketrenosa613bff2005-08-24 21:43:11 -05002192 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002193 return -EAGAIN;
James Ketrenos43f66a62005-03-25 12:31:53 -06002194 }
2195
2196 priv->status |= STATUS_HCMD_ACTIVE;
Jeff Garzikbf794512005-07-31 13:07:26 -04002197
James Ketrenosf6c5cb72005-08-25 00:39:09 -05002198 if (priv->cmdlog) {
2199 priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies;
2200 priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd;
2201 priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len;
2202 memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param,
2203 cmd->len);
2204 priv->cmdlog[priv->cmdlog_pos].retcode = -1;
2205 }
2206
James Ketrenosb095c382005-08-24 22:04:42 -05002207 IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
2208 get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
2209 priv->status);
Zhu Yif516dbc2006-01-24 16:36:44 +08002210
2211#ifndef DEBUG_CMD_WEP_KEY
2212 if (cmd->cmd == IPW_CMD_WEP_KEY)
2213 IPW_DEBUG_HC("WEP_KEY command masked out for secure.\n");
2214 else
2215#endif
2216 printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
2217
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002218 rc = ipw_queue_tx_hcmd(priv, cmd->cmd, cmd->param, cmd->len, 0);
James Ketrenosa613bff2005-08-24 21:43:11 -05002219 if (rc) {
2220 priv->status &= ~STATUS_HCMD_ACTIVE;
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002221 IPW_ERROR("Failed to send %s: Reason %d\n",
2222 get_cmd_string(cmd->cmd), rc);
James Ketrenosa613bff2005-08-24 21:43:11 -05002223 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenosf6c5cb72005-08-25 00:39:09 -05002224 goto exit;
James Ketrenosa613bff2005-08-24 21:43:11 -05002225 }
2226 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002227
Stanislav Yakovlevdd447312012-04-19 15:55:09 -04002228 now = jiffies;
2229 end = now + HOST_COMPLETE_TIMEOUT;
2230again:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002231 rc = wait_event_interruptible_timeout(priv->wait_command_queue,
2232 !(priv->
2233 status & STATUS_HCMD_ACTIVE),
Stanislav Yakovlevdd447312012-04-19 15:55:09 -04002234 end - now);
2235 if (rc < 0) {
2236 now = jiffies;
2237 if (time_before(now, end))
2238 goto again;
2239 rc = 0;
2240 }
2241
James Ketrenos43f66a62005-03-25 12:31:53 -06002242 if (rc == 0) {
James Ketrenosa613bff2005-08-24 21:43:11 -05002243 spin_lock_irqsave(&priv->lock, flags);
2244 if (priv->status & STATUS_HCMD_ACTIVE) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002245 IPW_ERROR("Failed to send %s: Command timed out.\n",
2246 get_cmd_string(cmd->cmd));
James Ketrenosa613bff2005-08-24 21:43:11 -05002247 priv->status &= ~STATUS_HCMD_ACTIVE;
2248 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenosf6c5cb72005-08-25 00:39:09 -05002249 rc = -EIO;
2250 goto exit;
James Ketrenosa613bff2005-08-24 21:43:11 -05002251 }
2252 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos3b9990c2005-08-19 13:18:55 -05002253 } else
2254 rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05002255
James Ketrenosb095c382005-08-24 22:04:42 -05002256 if (priv->status & STATUS_RF_KILL_HW) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002257 IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n",
2258 get_cmd_string(cmd->cmd));
James Ketrenosf6c5cb72005-08-25 00:39:09 -05002259 rc = -EIO;
2260 goto exit;
James Ketrenos43f66a62005-03-25 12:31:53 -06002261 }
2262
Zhu Yi2638bc32006-01-24 16:37:52 +08002263 exit:
James Ketrenosf6c5cb72005-08-25 00:39:09 -05002264 if (priv->cmdlog) {
2265 priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
2266 priv->cmdlog_pos %= priv->cmdlog_len;
2267 }
2268 return rc;
James Ketrenos43f66a62005-03-25 12:31:53 -06002269}
2270
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002271static int ipw_send_cmd_simple(struct ipw_priv *priv, u8 command)
James Ketrenos43f66a62005-03-25 12:31:53 -06002272{
2273 struct host_cmd cmd = {
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002274 .cmd = command,
James Ketrenos43f66a62005-03-25 12:31:53 -06002275 };
2276
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002277 return __ipw_send_cmd(priv, &cmd);
2278}
2279
2280static int ipw_send_cmd_pdu(struct ipw_priv *priv, u8 command, u8 len,
2281 void *data)
2282{
2283 struct host_cmd cmd = {
2284 .cmd = command,
2285 .len = len,
2286 .param = data,
2287 };
2288
2289 return __ipw_send_cmd(priv, &cmd);
2290}
2291
2292static int ipw_send_host_complete(struct ipw_priv *priv)
2293{
James Ketrenos43f66a62005-03-25 12:31:53 -06002294 if (!priv) {
2295 IPW_ERROR("Invalid args\n");
2296 return -1;
2297 }
2298
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002299 return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE);
James Ketrenos43f66a62005-03-25 12:31:53 -06002300}
2301
Zhu Yid685b8c2006-04-13 17:20:27 +08002302static int ipw_send_system_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002303{
Zhu Yid685b8c2006-04-13 17:20:27 +08002304 return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG,
2305 sizeof(priv->sys_config),
2306 &priv->sys_config);
James Ketrenos43f66a62005-03-25 12:31:53 -06002307}
2308
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002309static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002310{
James Ketrenos43f66a62005-03-25 12:31:53 -06002311 if (!priv || !ssid) {
2312 IPW_ERROR("Invalid args\n");
2313 return -1;
2314 }
2315
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002316 return ipw_send_cmd_pdu(priv, IPW_CMD_SSID, min(len, IW_ESSID_MAX_SIZE),
Zhu Yi2638bc32006-01-24 16:37:52 +08002317 ssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06002318}
2319
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002320static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06002321{
James Ketrenos43f66a62005-03-25 12:31:53 -06002322 if (!priv || !mac) {
2323 IPW_ERROR("Invalid args\n");
2324 return -1;
2325 }
2326
Johannes Berge1749612008-10-27 15:59:26 -07002327 IPW_DEBUG_INFO("%s: Setting MAC to %pM\n",
2328 priv->net_dev->name, mac);
James Ketrenos43f66a62005-03-25 12:31:53 -06002329
Zhu Yi2638bc32006-01-24 16:37:52 +08002330 return ipw_send_cmd_pdu(priv, IPW_CMD_ADAPTER_ADDRESS, ETH_ALEN, mac);
James Ketrenos43f66a62005-03-25 12:31:53 -06002331}
2332
2333static void ipw_adapter_restart(void *adapter)
2334{
2335 struct ipw_priv *priv = adapter;
2336
2337 if (priv->status & STATUS_RF_KILL_MASK)
2338 return;
2339
2340 ipw_down(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05002341
2342 if (priv->assoc_network &&
2343 (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS))
2344 ipw_remove_current_network(priv);
2345
James Ketrenos43f66a62005-03-25 12:31:53 -06002346 if (ipw_up(priv)) {
2347 IPW_ERROR("Failed to up device\n");
2348 return;
2349 }
2350}
2351
David Howellsc4028952006-11-22 14:57:56 +00002352static void ipw_bg_adapter_restart(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05002353{
David Howellsc4028952006-11-22 14:57:56 +00002354 struct ipw_priv *priv =
2355 container_of(work, struct ipw_priv, adapter_restart);
Zhu Yi46441512006-01-24 16:37:59 +08002356 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00002357 ipw_adapter_restart(priv);
Zhu Yi46441512006-01-24 16:37:59 +08002358 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05002359}
2360
Zhu Yie65054b2010-03-30 09:36:16 +08002361static void ipw_abort_scan(struct ipw_priv *priv);
2362
2363#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
James Ketrenos43f66a62005-03-25 12:31:53 -06002364
2365static void ipw_scan_check(void *data)
2366{
2367 struct ipw_priv *priv = data;
Zhu Yie65054b2010-03-30 09:36:16 +08002368
2369 if (priv->status & STATUS_SCAN_ABORTING) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002370 IPW_DEBUG_SCAN("Scan completion watchdog resetting "
Zhu Yic7b6a672006-01-24 16:37:05 +08002371 "adapter after (%dms).\n",
2372 jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG));
Tejun Heobcb6d912011-01-26 12:12:50 +01002373 schedule_work(&priv->adapter_restart);
Zhu Yie65054b2010-03-30 09:36:16 +08002374 } else if (priv->status & STATUS_SCANNING) {
2375 IPW_DEBUG_SCAN("Scan completion watchdog aborting scan "
2376 "after (%dms).\n",
2377 jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG));
2378 ipw_abort_scan(priv);
Tejun Heobcb6d912011-01-26 12:12:50 +01002379 schedule_delayed_work(&priv->scan_check, HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -06002380 }
2381}
2382
David Howellsc4028952006-11-22 14:57:56 +00002383static void ipw_bg_scan_check(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05002384{
David Howellsc4028952006-11-22 14:57:56 +00002385 struct ipw_priv *priv =
2386 container_of(work, struct ipw_priv, scan_check.work);
Zhu Yi46441512006-01-24 16:37:59 +08002387 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00002388 ipw_scan_check(priv);
Zhu Yi46441512006-01-24 16:37:59 +08002389 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05002390}
2391
James Ketrenos43f66a62005-03-25 12:31:53 -06002392static int ipw_send_scan_request_ext(struct ipw_priv *priv,
2393 struct ipw_scan_request_ext *request)
2394{
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002395 return ipw_send_cmd_pdu(priv, IPW_CMD_SCAN_REQUEST_EXT,
Zhu Yi2638bc32006-01-24 16:37:52 +08002396 sizeof(*request), request);
James Ketrenos43f66a62005-03-25 12:31:53 -06002397}
2398
2399static int ipw_send_scan_abort(struct ipw_priv *priv)
2400{
James Ketrenos43f66a62005-03-25 12:31:53 -06002401 if (!priv) {
2402 IPW_ERROR("Invalid args\n");
2403 return -1;
2404 }
2405
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002406 return ipw_send_cmd_simple(priv, IPW_CMD_SCAN_ABORT);
James Ketrenos43f66a62005-03-25 12:31:53 -06002407}
2408
2409static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
2410{
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002411 struct ipw_sensitivity_calib calib = {
Zhu Yi851ca262006-08-21 11:37:58 +08002412 .beacon_rssi_raw = cpu_to_le16(sens),
James Ketrenos43f66a62005-03-25 12:31:53 -06002413 };
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002414
2415 return ipw_send_cmd_pdu(priv, IPW_CMD_SENSITIVITY_CALIB, sizeof(calib),
Zhu Yi2638bc32006-01-24 16:37:52 +08002416 &calib);
James Ketrenos43f66a62005-03-25 12:31:53 -06002417}
2418
2419static int ipw_send_associate(struct ipw_priv *priv,
2420 struct ipw_associate *associate)
2421{
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002422 if (!priv || !associate) {
2423 IPW_ERROR("Invalid args\n");
2424 return -1;
2425 }
2426
Al Viro5b5e8072007-12-27 01:54:06 -05002427 return ipw_send_cmd_pdu(priv, IPW_CMD_ASSOCIATE, sizeof(*associate),
2428 associate);
James Ketrenos43f66a62005-03-25 12:31:53 -06002429}
2430
2431static int ipw_send_supported_rates(struct ipw_priv *priv,
2432 struct ipw_supported_rates *rates)
2433{
James Ketrenos43f66a62005-03-25 12:31:53 -06002434 if (!priv || !rates) {
2435 IPW_ERROR("Invalid args\n");
2436 return -1;
2437 }
2438
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002439 return ipw_send_cmd_pdu(priv, IPW_CMD_SUPPORTED_RATES, sizeof(*rates),
Zhu Yi2638bc32006-01-24 16:37:52 +08002440 rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06002441}
2442
2443static int ipw_set_random_seed(struct ipw_priv *priv)
2444{
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002445 u32 val;
James Ketrenos43f66a62005-03-25 12:31:53 -06002446
2447 if (!priv) {
2448 IPW_ERROR("Invalid args\n");
2449 return -1;
2450 }
2451
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002452 get_random_bytes(&val, sizeof(val));
James Ketrenos43f66a62005-03-25 12:31:53 -06002453
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002454 return ipw_send_cmd_pdu(priv, IPW_CMD_SEED_NUMBER, sizeof(val), &val);
James Ketrenos43f66a62005-03-25 12:31:53 -06002455}
2456
James Ketrenos43f66a62005-03-25 12:31:53 -06002457static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
2458{
Al Viroe62e1ee2007-12-27 01:36:46 -05002459 __le32 v = cpu_to_le32(phy_off);
James Ketrenos43f66a62005-03-25 12:31:53 -06002460 if (!priv) {
2461 IPW_ERROR("Invalid args\n");
2462 return -1;
2463 }
2464
Al Viroe62e1ee2007-12-27 01:36:46 -05002465 return ipw_send_cmd_pdu(priv, IPW_CMD_CARD_DISABLE, sizeof(v), &v);
James Ketrenos43f66a62005-03-25 12:31:53 -06002466}
James Ketrenos43f66a62005-03-25 12:31:53 -06002467
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002468static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
James Ketrenos43f66a62005-03-25 12:31:53 -06002469{
James Ketrenos43f66a62005-03-25 12:31:53 -06002470 if (!priv || !power) {
2471 IPW_ERROR("Invalid args\n");
2472 return -1;
2473 }
2474
Zhu Yi2638bc32006-01-24 16:37:52 +08002475 return ipw_send_cmd_pdu(priv, IPW_CMD_TX_POWER, sizeof(*power), power);
James Ketrenos43f66a62005-03-25 12:31:53 -06002476}
James Ketrenos43f66a62005-03-25 12:31:53 -06002477
Zhu Yi6de9f7f2005-08-11 14:39:33 +08002478static int ipw_set_tx_power(struct ipw_priv *priv)
2479{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04002480 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08002481 struct ipw_tx_power tx_power;
2482 s8 max_power;
2483 int i;
2484
2485 memset(&tx_power, 0, sizeof(tx_power));
2486
2487 /* configure device for 'G' band */
2488 tx_power.ieee_mode = IPW_G_MODE;
2489 tx_power.num_channels = geo->bg_channels;
2490 for (i = 0; i < geo->bg_channels; i++) {
2491 max_power = geo->bg[i].max_power;
2492 tx_power.channels_tx_power[i].channel_number =
2493 geo->bg[i].channel;
2494 tx_power.channels_tx_power[i].tx_power = max_power ?
2495 min(max_power, priv->tx_power) : priv->tx_power;
2496 }
2497 if (ipw_send_tx_power(priv, &tx_power))
2498 return -EIO;
2499
2500 /* configure device to also handle 'B' band */
2501 tx_power.ieee_mode = IPW_B_MODE;
2502 if (ipw_send_tx_power(priv, &tx_power))
2503 return -EIO;
2504
2505 /* configure device to also handle 'A' band */
2506 if (priv->ieee->abg_true) {
2507 tx_power.ieee_mode = IPW_A_MODE;
2508 tx_power.num_channels = geo->a_channels;
2509 for (i = 0; i < tx_power.num_channels; i++) {
2510 max_power = geo->a[i].max_power;
2511 tx_power.channels_tx_power[i].channel_number =
2512 geo->a[i].channel;
2513 tx_power.channels_tx_power[i].tx_power = max_power ?
2514 min(max_power, priv->tx_power) : priv->tx_power;
2515 }
2516 if (ipw_send_tx_power(priv, &tx_power))
2517 return -EIO;
2518 }
James Ketrenos43f66a62005-03-25 12:31:53 -06002519 return 0;
2520}
2521
2522static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
2523{
2524 struct ipw_rts_threshold rts_threshold = {
Zhu Yi851ca262006-08-21 11:37:58 +08002525 .rts_threshold = cpu_to_le16(rts),
James Ketrenos43f66a62005-03-25 12:31:53 -06002526 };
James Ketrenos43f66a62005-03-25 12:31:53 -06002527
2528 if (!priv) {
2529 IPW_ERROR("Invalid args\n");
2530 return -1;
2531 }
2532
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002533 return ipw_send_cmd_pdu(priv, IPW_CMD_RTS_THRESHOLD,
2534 sizeof(rts_threshold), &rts_threshold);
James Ketrenos43f66a62005-03-25 12:31:53 -06002535}
2536
2537static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
2538{
2539 struct ipw_frag_threshold frag_threshold = {
Zhu Yi851ca262006-08-21 11:37:58 +08002540 .frag_threshold = cpu_to_le16(frag),
James Ketrenos43f66a62005-03-25 12:31:53 -06002541 };
James Ketrenos43f66a62005-03-25 12:31:53 -06002542
2543 if (!priv) {
2544 IPW_ERROR("Invalid args\n");
2545 return -1;
2546 }
2547
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002548 return ipw_send_cmd_pdu(priv, IPW_CMD_FRAG_THRESHOLD,
2549 sizeof(frag_threshold), &frag_threshold);
James Ketrenos43f66a62005-03-25 12:31:53 -06002550}
2551
2552static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
2553{
Al Viroe62e1ee2007-12-27 01:36:46 -05002554 __le32 param;
James Ketrenos43f66a62005-03-25 12:31:53 -06002555
2556 if (!priv) {
2557 IPW_ERROR("Invalid args\n");
2558 return -1;
2559 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002560
James Ketrenos43f66a62005-03-25 12:31:53 -06002561 /* If on battery, set to 3, if AC set to CAM, else user
2562 * level */
2563 switch (mode) {
2564 case IPW_POWER_BATTERY:
Al Viroe62e1ee2007-12-27 01:36:46 -05002565 param = cpu_to_le32(IPW_POWER_INDEX_3);
James Ketrenos43f66a62005-03-25 12:31:53 -06002566 break;
2567 case IPW_POWER_AC:
Al Viroe62e1ee2007-12-27 01:36:46 -05002568 param = cpu_to_le32(IPW_POWER_MODE_CAM);
James Ketrenos43f66a62005-03-25 12:31:53 -06002569 break;
2570 default:
Al Viroe62e1ee2007-12-27 01:36:46 -05002571 param = cpu_to_le32(mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06002572 break;
2573 }
2574
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002575 return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param),
Zhu Yi2638bc32006-01-24 16:37:52 +08002576 &param);
James Ketrenos43f66a62005-03-25 12:31:53 -06002577}
2578
James Ketrenosafbf30a2005-08-25 00:05:33 -05002579static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit)
2580{
2581 struct ipw_retry_limit retry_limit = {
2582 .short_retry_limit = slimit,
2583 .long_retry_limit = llimit
2584 };
James Ketrenosafbf30a2005-08-25 00:05:33 -05002585
2586 if (!priv) {
2587 IPW_ERROR("Invalid args\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002588 return -1;
2589 }
2590
Zhu Yi0a7bcf22006-01-24 16:37:28 +08002591 return ipw_send_cmd_pdu(priv, IPW_CMD_RETRY_LIMIT, sizeof(retry_limit),
Zhu Yi2638bc32006-01-24 16:37:52 +08002592 &retry_limit);
James Ketrenos43f66a62005-03-25 12:31:53 -06002593}
2594
2595/*
2596 * The IPW device contains a Microwire compatible EEPROM that stores
2597 * various data like the MAC address. Usually the firmware has exclusive
2598 * access to the eeprom, but during device initialization (before the
2599 * device driver has sent the HostComplete command to the firmware) the
2600 * device driver has read access to the EEPROM by way of indirect addressing
2601 * through a couple of memory mapped registers.
2602 *
2603 * The following is a simplified implementation for pulling data out of the
2604 * the eeprom, along with some helper functions to find information in
2605 * the per device private data's copy of the eeprom.
2606 *
2607 * NOTE: To better understand how these functions work (i.e what is a chip
2608 * select and why do have to keep driving the eeprom clock?), read
2609 * just about any data sheet for a Microwire compatible EEPROM.
2610 */
2611
2612/* write a 32 bit value into the indirect accessor register */
2613static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
2614{
2615 ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
Jeff Garzikbf794512005-07-31 13:07:26 -04002616
James Ketrenos43f66a62005-03-25 12:31:53 -06002617 /* the eeprom requires some time to complete the operation */
2618 udelay(p->eeprom_delay);
James Ketrenos43f66a62005-03-25 12:31:53 -06002619}
2620
2621/* perform a chip select operation */
Arjan van de Ven858119e2006-01-14 13:20:43 -08002622static void eeprom_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002623{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002624 eeprom_write_reg(priv, 0);
2625 eeprom_write_reg(priv, EEPROM_BIT_CS);
2626 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2627 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002628}
2629
2630/* perform a chip select operation */
Arjan van de Ven858119e2006-01-14 13:20:43 -08002631static void eeprom_disable_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002632{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002633 eeprom_write_reg(priv, EEPROM_BIT_CS);
2634 eeprom_write_reg(priv, 0);
2635 eeprom_write_reg(priv, EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002636}
2637
2638/* push a single bit down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002639static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
James Ketrenos43f66a62005-03-25 12:31:53 -06002640{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002641 int d = (bit ? EEPROM_BIT_DI : 0);
2642 eeprom_write_reg(p, EEPROM_BIT_CS | d);
2643 eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002644}
2645
2646/* push an opcode followed by an address down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002647static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002648{
2649 int i;
2650
2651 eeprom_cs(priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002652 eeprom_write_bit(priv, 1);
2653 eeprom_write_bit(priv, op & 2);
2654 eeprom_write_bit(priv, op & 1);
2655 for (i = 7; i >= 0; i--) {
2656 eeprom_write_bit(priv, addr & (1 << i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002657 }
2658}
2659
2660/* pull 16 bits off the eeprom, one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002661static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002662{
2663 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002664 u16 r = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002665
James Ketrenos43f66a62005-03-25 12:31:53 -06002666 /* Send READ Opcode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002667 eeprom_op(priv, EEPROM_CMD_READ, addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06002668
2669 /* Send dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002670 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002671
2672 /* Read the byte off the eeprom one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002673 for (i = 0; i < 16; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002674 u32 data = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002675 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2676 eeprom_write_reg(priv, EEPROM_BIT_CS);
2677 data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
2678 r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002679 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002680
James Ketrenos43f66a62005-03-25 12:31:53 -06002681 /* Send another dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002682 eeprom_write_reg(priv, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002683 eeprom_disable_cs(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002684
James Ketrenos43f66a62005-03-25 12:31:53 -06002685 return r;
2686}
2687
2688/* helper function for pulling the mac address out of the private */
2689/* data's copy of the eeprom data */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002690static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06002691{
Joe Perchesd458cdf2013-10-01 19:04:40 -07002692 memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06002693}
2694
Stanislav Yakovlevfdbfff72012-05-17 15:56:35 -04002695static void ipw_read_eeprom(struct ipw_priv *priv)
2696{
2697 int i;
2698 __le16 *eeprom = (__le16 *) priv->eeprom;
2699
2700 IPW_DEBUG_TRACE(">>\n");
2701
2702 /* read entire contents of eeprom into private buffer */
2703 for (i = 0; i < 128; i++)
2704 eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i));
2705
2706 IPW_DEBUG_TRACE("<<\n");
2707}
2708
James Ketrenos43f66a62005-03-25 12:31:53 -06002709/*
2710 * Either the device driver (i.e. the host) or the firmware can
2711 * load eeprom data into the designated region in SRAM. If neither
2712 * happens then the FW will shutdown with a fatal error.
2713 *
2714 * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
2715 * bit needs region of shared SRAM needs to be non-zero.
2716 */
2717static void ipw_eeprom_init_sram(struct ipw_priv *priv)
2718{
2719 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04002720
James Ketrenos43f66a62005-03-25 12:31:53 -06002721 IPW_DEBUG_TRACE(">>\n");
2722
Jeff Garzikbf794512005-07-31 13:07:26 -04002723 /*
2724 If the data looks correct, then copy it to our private
James Ketrenos43f66a62005-03-25 12:31:53 -06002725 copy. Otherwise let the firmware know to perform the operation
Zhu Yic7b6a672006-01-24 16:37:05 +08002726 on its own.
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002727 */
Alexey Dobriyan386093e2006-02-01 03:04:57 -08002728 if (priv->eeprom[EEPROM_VERSION] != 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002729 IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
2730
2731 /* write the eeprom data to sram */
James Ketrenosb095c382005-08-24 22:04:42 -05002732 for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002733 ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002734
2735 /* Do not load eeprom data on fatal error or suspend */
2736 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
2737 } else {
2738 IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
2739
2740 /* Load eeprom data on fatal error or suspend */
2741 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
2742 }
2743
2744 IPW_DEBUG_TRACE("<<\n");
2745}
2746
Arjan van de Ven858119e2006-01-14 13:20:43 -08002747static void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
James Ketrenos43f66a62005-03-25 12:31:53 -06002748{
2749 count >>= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002750 if (!count)
2751 return;
James Ketrenosb095c382005-08-24 22:04:42 -05002752 _ipw_write32(priv, IPW_AUTOINC_ADDR, start);
Jeff Garzikbf794512005-07-31 13:07:26 -04002753 while (count--)
James Ketrenosb095c382005-08-24 22:04:42 -05002754 _ipw_write32(priv, IPW_AUTOINC_DATA, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002755}
2756
2757static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
2758{
James Ketrenosb095c382005-08-24 22:04:42 -05002759 ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL,
Jeff Garzikbf794512005-07-31 13:07:26 -04002760 CB_NUMBER_OF_ELEMENTS_SMALL *
James Ketrenos43f66a62005-03-25 12:31:53 -06002761 sizeof(struct command_block));
2762}
2763
2764static int ipw_fw_dma_enable(struct ipw_priv *priv)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002765{ /* start dma engine but no transfers yet */
James Ketrenos43f66a62005-03-25 12:31:53 -06002766
Frans Pop9fd1ea42010-03-24 19:46:31 +01002767 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002768
James Ketrenos43f66a62005-03-25 12:31:53 -06002769 /* Start the dma */
2770 ipw_fw_dma_reset_command_blocks(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002771
James Ketrenos43f66a62005-03-25 12:31:53 -06002772 /* Write CB base address */
James Ketrenosb095c382005-08-24 22:04:42 -05002773 ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL);
James Ketrenos43f66a62005-03-25 12:31:53 -06002774
Frans Pop9fd1ea42010-03-24 19:46:31 +01002775 IPW_DEBUG_FW("<< :\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002776 return 0;
2777}
2778
2779static void ipw_fw_dma_abort(struct ipw_priv *priv)
2780{
2781 u32 control = 0;
2782
2783 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002784
Pavel Machek67fd6b42006-07-11 15:34:05 +02002785 /* set the Stop and Abort bit */
James Ketrenos43f66a62005-03-25 12:31:53 -06002786 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
James Ketrenosb095c382005-08-24 22:04:42 -05002787 ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
James Ketrenos43f66a62005-03-25 12:31:53 -06002788 priv->sram_desc.last_cb_index = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002789
Frans Pop9fd1ea42010-03-24 19:46:31 +01002790 IPW_DEBUG_FW("<<\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002791}
2792
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002793static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
2794 struct command_block *cb)
James Ketrenos43f66a62005-03-25 12:31:53 -06002795{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002796 u32 address =
James Ketrenosb095c382005-08-24 22:04:42 -05002797 IPW_SHARED_SRAM_DMA_CONTROL +
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002798 (sizeof(struct command_block) * index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002799 IPW_DEBUG_FW(">> :\n");
2800
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002801 ipw_write_indirect(priv, address, (u8 *) cb,
2802 (int)sizeof(struct command_block));
James Ketrenos43f66a62005-03-25 12:31:53 -06002803
2804 IPW_DEBUG_FW("<< :\n");
2805 return 0;
2806
2807}
2808
2809static int ipw_fw_dma_kick(struct ipw_priv *priv)
2810{
2811 u32 control = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002812 u32 index = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002813
2814 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002815
James Ketrenos43f66a62005-03-25 12:31:53 -06002816 for (index = 0; index < priv->sram_desc.last_cb_index; index++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002817 ipw_fw_dma_write_command_block(priv, index,
2818 &priv->sram_desc.cb_list[index]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002819
2820 /* Enable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002821 ipw_clear_bit(priv, IPW_RESET_REG,
2822 IPW_RESET_REG_MASTER_DISABLED |
2823 IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04002824
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002825 /* Set the Start bit. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002826 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
James Ketrenosb095c382005-08-24 22:04:42 -05002827 ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
James Ketrenos43f66a62005-03-25 12:31:53 -06002828
2829 IPW_DEBUG_FW("<< :\n");
2830 return 0;
2831}
2832
2833static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
2834{
2835 u32 address;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002836 u32 register_value = 0;
2837 u32 cb_fields_address = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002838
2839 IPW_DEBUG_FW(">> :\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002840 address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
Frans Pop9fd1ea42010-03-24 19:46:31 +01002841 IPW_DEBUG_FW_INFO("Current CB is 0x%x\n", address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002842
2843 /* Read the DMA Controlor register */
James Ketrenosb095c382005-08-24 22:04:42 -05002844 register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL);
Frans Pop9fd1ea42010-03-24 19:46:31 +01002845 IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x\n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002846
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002847 /* Print the CB values */
James Ketrenos43f66a62005-03-25 12:31:53 -06002848 cb_fields_address = address;
2849 register_value = ipw_read_reg32(priv, cb_fields_address);
Frans Pop9fd1ea42010-03-24 19:46:31 +01002850 IPW_DEBUG_FW_INFO("Current CB Control Field is 0x%x\n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002851
2852 cb_fields_address += sizeof(u32);
2853 register_value = ipw_read_reg32(priv, cb_fields_address);
Frans Pop9fd1ea42010-03-24 19:46:31 +01002854 IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x\n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002855
2856 cb_fields_address += sizeof(u32);
2857 register_value = ipw_read_reg32(priv, cb_fields_address);
Frans Pop9fd1ea42010-03-24 19:46:31 +01002858 IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06002859 register_value);
2860
2861 cb_fields_address += sizeof(u32);
2862 register_value = ipw_read_reg32(priv, cb_fields_address);
Frans Pop9fd1ea42010-03-24 19:46:31 +01002863 IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x\n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002864
2865 IPW_DEBUG_FW(">> :\n");
2866}
2867
2868static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
2869{
2870 u32 current_cb_address = 0;
2871 u32 current_cb_index = 0;
2872
2873 IPW_DEBUG_FW("<< :\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002874 current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
Jeff Garzikbf794512005-07-31 13:07:26 -04002875
James Ketrenosb095c382005-08-24 22:04:42 -05002876 current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002877 sizeof(struct command_block);
Jeff Garzikbf794512005-07-31 13:07:26 -04002878
Frans Pop9fd1ea42010-03-24 19:46:31 +01002879 IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002880 current_cb_index, current_cb_address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002881
2882 IPW_DEBUG_FW(">> :\n");
2883 return current_cb_index;
2884
2885}
2886
2887static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
2888 u32 src_address,
2889 u32 dest_address,
2890 u32 length,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002891 int interrupt_enabled, int is_last)
James Ketrenos43f66a62005-03-25 12:31:53 -06002892{
2893
Jeff Garzikbf794512005-07-31 13:07:26 -04002894 u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002895 CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
2896 CB_DEST_SIZE_LONG;
James Ketrenos43f66a62005-03-25 12:31:53 -06002897 struct command_block *cb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002898 u32 last_cb_element = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002899
2900 IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
2901 src_address, dest_address, length);
2902
2903 if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
2904 return -1;
2905
2906 last_cb_element = priv->sram_desc.last_cb_index;
2907 cb = &priv->sram_desc.cb_list[last_cb_element];
2908 priv->sram_desc.last_cb_index++;
2909
2910 /* Calculate the new CB control word */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002911 if (interrupt_enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06002912 control |= CB_INT_ENABLED;
2913
2914 if (is_last)
2915 control |= CB_LAST_VALID;
Jeff Garzikbf794512005-07-31 13:07:26 -04002916
James Ketrenos43f66a62005-03-25 12:31:53 -06002917 control |= length;
2918
2919 /* Calculate the CB Element's checksum value */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002920 cb->status = control ^ src_address ^ dest_address;
James Ketrenos43f66a62005-03-25 12:31:53 -06002921
2922 /* Copy the Source and Destination addresses */
2923 cb->dest_addr = dest_address;
2924 cb->source_addr = src_address;
2925
2926 /* Copy the Control Word last */
2927 cb->control = control;
2928
2929 return 0;
2930}
2931
Zhu Yi11ebd1b2009-08-28 11:42:31 +08002932static int ipw_fw_dma_add_buffer(struct ipw_priv *priv, dma_addr_t *src_address,
2933 int nr, u32 dest_address, u32 len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002934{
Zhu Yi11ebd1b2009-08-28 11:42:31 +08002935 int ret, i;
2936 u32 size;
2937
Frans Pop9fd1ea42010-03-24 19:46:31 +01002938 IPW_DEBUG_FW(">>\n");
Zhu Yi11ebd1b2009-08-28 11:42:31 +08002939 IPW_DEBUG_FW_INFO("nr=%d dest_address=0x%x len=0x%x\n",
2940 nr, dest_address, len);
2941
2942 for (i = 0; i < nr; i++) {
2943 size = min_t(u32, len - i * CB_MAX_LENGTH, CB_MAX_LENGTH);
2944 ret = ipw_fw_dma_add_command_block(priv, src_address[i],
2945 dest_address +
2946 i * CB_MAX_LENGTH, size,
2947 0, 0);
2948 if (ret) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002949 IPW_DEBUG_FW_INFO(": Failed\n");
2950 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002951 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06002952 IPW_DEBUG_FW_INFO(": Added new cb\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002953 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002954
Frans Pop9fd1ea42010-03-24 19:46:31 +01002955 IPW_DEBUG_FW("<<\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002956 return 0;
2957}
2958
2959static int ipw_fw_dma_wait(struct ipw_priv *priv)
2960{
Zhu Yi397ae122006-01-24 16:37:22 +08002961 u32 current_index = 0, previous_index;
James Ketrenos43f66a62005-03-25 12:31:53 -06002962 u32 watchdog = 0;
2963
Frans Pop9fd1ea42010-03-24 19:46:31 +01002964 IPW_DEBUG_FW(">> :\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002965
2966 current_index = ipw_fw_dma_command_block_index(priv);
Zhu Yi397ae122006-01-24 16:37:22 +08002967 IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002968 (int)priv->sram_desc.last_cb_index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002969
2970 while (current_index < priv->sram_desc.last_cb_index) {
2971 udelay(50);
Zhu Yi397ae122006-01-24 16:37:22 +08002972 previous_index = current_index;
James Ketrenos43f66a62005-03-25 12:31:53 -06002973 current_index = ipw_fw_dma_command_block_index(priv);
2974
Zhu Yi397ae122006-01-24 16:37:22 +08002975 if (previous_index < current_index) {
2976 watchdog = 0;
2977 continue;
2978 }
2979 if (++watchdog > 400) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002980 IPW_DEBUG_FW_INFO("Timeout\n");
2981 ipw_fw_dma_dump_command_block(priv);
2982 ipw_fw_dma_abort(priv);
2983 return -1;
2984 }
2985 }
2986
2987 ipw_fw_dma_abort(priv);
2988
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002989 /*Disable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002990 ipw_set_bit(priv, IPW_RESET_REG,
2991 IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002992
Frans Pop9fd1ea42010-03-24 19:46:31 +01002993 IPW_DEBUG_FW("<< dmaWaitSync\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002994 return 0;
2995}
2996
Jeff Garzikbf794512005-07-31 13:07:26 -04002997static void ipw_remove_current_network(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002998{
2999 struct list_head *element, *safe;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04003000 struct libipw_network *network = NULL;
James Ketrenosa613bff2005-08-24 21:43:11 -05003001 unsigned long flags;
3002
3003 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003004 list_for_each_safe(element, safe, &priv->ieee->network_list) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04003005 network = list_entry(element, struct libipw_network, list);
dingtianhong36325f32013-12-26 19:41:23 +08003006 if (ether_addr_equal(network->bssid, priv->bssid)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003007 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04003008 list_add_tail(&network->list,
James Ketrenos43f66a62005-03-25 12:31:53 -06003009 &priv->ieee->network_free_list);
3010 }
3011 }
James Ketrenosa613bff2005-08-24 21:43:11 -05003012 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003013}
3014
3015/**
Jeff Garzikbf794512005-07-31 13:07:26 -04003016 * Check that card is still alive.
James Ketrenos43f66a62005-03-25 12:31:53 -06003017 * Reads debug register from domain0.
3018 * If card is present, pre-defined value should
3019 * be found there.
Jeff Garzikbf794512005-07-31 13:07:26 -04003020 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003021 * @param priv
3022 * @return 1 if card is present, 0 otherwise
3023 */
3024static inline int ipw_alive(struct ipw_priv *priv)
3025{
3026 return ipw_read32(priv, 0x90) == 0xd55555d5;
3027}
3028
Zhu Yic7b6a672006-01-24 16:37:05 +08003029/* timeout in msec, attempted in 10-msec quanta */
Arjan van de Ven858119e2006-01-14 13:20:43 -08003030static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
James Ketrenos43f66a62005-03-25 12:31:53 -06003031 int timeout)
3032{
3033 int i = 0;
3034
3035 do {
Jeff Garzikbf794512005-07-31 13:07:26 -04003036 if ((ipw_read32(priv, addr) & mask) == mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06003037 return i;
3038 mdelay(10);
3039 i += 10;
3040 } while (i < timeout);
Jeff Garzikbf794512005-07-31 13:07:26 -04003041
James Ketrenos43f66a62005-03-25 12:31:53 -06003042 return -ETIME;
3043}
3044
Jeff Garzikbf794512005-07-31 13:07:26 -04003045/* These functions load the firmware and micro code for the operation of
James Ketrenos43f66a62005-03-25 12:31:53 -06003046 * the ipw hardware. It assumes the buffer has all the bits for the
3047 * image and the caller is handling the memory allocation and clean up.
3048 */
3049
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003050static int ipw_stop_master(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06003051{
3052 int rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003053
Frans Pop9fd1ea42010-03-24 19:46:31 +01003054 IPW_DEBUG_TRACE(">>\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003055 /* stop master. typical delay - 0 */
James Ketrenosb095c382005-08-24 22:04:42 -05003056 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06003057
Zhu Yic7b6a672006-01-24 16:37:05 +08003058 /* timeout is in msec, polled in 10-msec quanta */
James Ketrenosb095c382005-08-24 22:04:42 -05003059 rc = ipw_poll_bit(priv, IPW_RESET_REG,
3060 IPW_RESET_REG_MASTER_DISABLED, 100);
James Ketrenos43f66a62005-03-25 12:31:53 -06003061 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08003062 IPW_ERROR("wait for stop master failed after 100ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003063 return -1;
3064 }
3065
3066 IPW_DEBUG_INFO("stop master %dms\n", rc);
3067
3068 return rc;
3069}
3070
3071static void ipw_arc_release(struct ipw_priv *priv)
3072{
Frans Pop9fd1ea42010-03-24 19:46:31 +01003073 IPW_DEBUG_TRACE(">>\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003074 mdelay(5);
3075
James Ketrenosb095c382005-08-24 22:04:42 -05003076 ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06003077
3078 /* no one knows timing, for safety add some delay */
3079 mdelay(5);
3080}
3081
James Ketrenos43f66a62005-03-25 12:31:53 -06003082struct fw_chunk {
Al Viroe62e1ee2007-12-27 01:36:46 -05003083 __le32 address;
3084 __le32 length;
James Ketrenos43f66a62005-03-25 12:31:53 -06003085};
3086
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003087static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06003088{
3089 int rc = 0, i, addr;
3090 u8 cr = 0;
Al Viroe62e1ee2007-12-27 01:36:46 -05003091 __le16 *image;
James Ketrenos43f66a62005-03-25 12:31:53 -06003092
Al Viroe62e1ee2007-12-27 01:36:46 -05003093 image = (__le16 *) data;
Jeff Garzikbf794512005-07-31 13:07:26 -04003094
Frans Pop9fd1ea42010-03-24 19:46:31 +01003095 IPW_DEBUG_TRACE(">>\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003096
3097 rc = ipw_stop_master(priv);
3098
3099 if (rc < 0)
3100 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003101
James Ketrenosb095c382005-08-24 22:04:42 -05003102 for (addr = IPW_SHARED_LOWER_BOUND;
3103 addr < IPW_REGISTER_DOMAIN1_END; addr += 4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003104 ipw_write32(priv, addr, 0);
3105 }
3106
3107 /* no ucode (yet) */
3108 memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
3109 /* destroy DMA queues */
3110 /* reset sequence */
3111
James Ketrenosb095c382005-08-24 22:04:42 -05003112 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON);
James Ketrenos43f66a62005-03-25 12:31:53 -06003113 ipw_arc_release(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05003114 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF);
James Ketrenos43f66a62005-03-25 12:31:53 -06003115 mdelay(1);
3116
3117 /* reset PHY */
James Ketrenosb095c382005-08-24 22:04:42 -05003118 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN);
James Ketrenos43f66a62005-03-25 12:31:53 -06003119 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04003120
James Ketrenosb095c382005-08-24 22:04:42 -05003121 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06003122 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04003123
James Ketrenos43f66a62005-03-25 12:31:53 -06003124 /* enable ucode store */
Zhu Yic8fe6672006-01-24 16:36:36 +08003125 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0);
3126 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06003127 mdelay(1);
3128
3129 /* write ucode */
3130 /**
3131 * @bug
3132 * Do NOT set indirect address register once and then
3133 * store data to indirect data register in the loop.
3134 * It seems very reasonable, but in this case DINO do not
3135 * accept ucode. It is essential to set address each time.
3136 */
3137 /* load new ipw uCode */
3138 for (i = 0; i < len / 2; i++)
James Ketrenosb095c382005-08-24 22:04:42 -05003139 ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE,
Al Viroe62e1ee2007-12-27 01:36:46 -05003140 le16_to_cpu(image[i]));
James Ketrenos43f66a62005-03-25 12:31:53 -06003141
James Ketrenos43f66a62005-03-25 12:31:53 -06003142 /* enable DINO */
James Ketrenosb095c382005-08-24 22:04:42 -05003143 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
3144 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
James Ketrenos43f66a62005-03-25 12:31:53 -06003145
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003146 /* this is where the igx / win driver deveates from the VAP driver. */
James Ketrenos43f66a62005-03-25 12:31:53 -06003147
3148 /* wait for alive response */
3149 for (i = 0; i < 100; i++) {
3150 /* poll for incoming data */
James Ketrenosb095c382005-08-24 22:04:42 -05003151 cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS);
James Ketrenos43f66a62005-03-25 12:31:53 -06003152 if (cr & DINO_RXFIFO_DATA)
3153 break;
3154 mdelay(1);
3155 }
3156
3157 if (cr & DINO_RXFIFO_DATA) {
3158 /* alive_command_responce size is NOT multiple of 4 */
Al Viroe62e1ee2007-12-27 01:36:46 -05003159 __le32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
Jeff Garzikbf794512005-07-31 13:07:26 -04003160
3161 for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06003162 response_buffer[i] =
Al Viroe62e1ee2007-12-27 01:36:46 -05003163 cpu_to_le32(ipw_read_reg32(priv,
James Ketrenosb095c382005-08-24 22:04:42 -05003164 IPW_BASEBAND_RX_FIFO_READ));
James Ketrenos43f66a62005-03-25 12:31:53 -06003165 memcpy(&priv->dino_alive, response_buffer,
3166 sizeof(priv->dino_alive));
3167 if (priv->dino_alive.alive_command == 1
3168 && priv->dino_alive.ucode_valid == 1) {
3169 rc = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003170 IPW_DEBUG_INFO
3171 ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
3172 "of %02d/%02d/%02d %02d:%02d\n",
3173 priv->dino_alive.software_revision,
3174 priv->dino_alive.software_revision,
3175 priv->dino_alive.device_identifier,
3176 priv->dino_alive.device_identifier,
3177 priv->dino_alive.time_stamp[0],
3178 priv->dino_alive.time_stamp[1],
3179 priv->dino_alive.time_stamp[2],
3180 priv->dino_alive.time_stamp[3],
3181 priv->dino_alive.time_stamp[4]);
James Ketrenos43f66a62005-03-25 12:31:53 -06003182 } else {
3183 IPW_DEBUG_INFO("Microcode is not alive\n");
3184 rc = -EINVAL;
3185 }
3186 } else {
3187 IPW_DEBUG_INFO("No alive response from DINO\n");
3188 rc = -ETIME;
3189 }
3190
3191 /* disable DINO, otherwise for some reason
3192 firmware have problem getting alive resp. */
James Ketrenosb095c382005-08-24 22:04:42 -05003193 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06003194
James Ketrenos43f66a62005-03-25 12:31:53 -06003195 return rc;
3196}
3197
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003198static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06003199{
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003200 int ret = -1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003201 int offset = 0;
3202 struct fw_chunk *chunk;
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003203 int total_nr = 0;
3204 int i;
Romain Perier28b75412017-08-23 09:16:51 +02003205 struct dma_pool *pool;
Zhu Yi41093162010-03-09 16:05:31 +08003206 void **virts;
3207 dma_addr_t *phys;
James Ketrenos43f66a62005-03-25 12:31:53 -06003208
Frans Pop9fd1ea42010-03-24 19:46:31 +01003209 IPW_DEBUG_TRACE("<< :\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003210
Kees Cook6da2ec52018-06-12 13:55:00 -07003211 virts = kmalloc_array(CB_NUMBER_OF_ELEMENTS_SMALL, sizeof(void *),
3212 GFP_KERNEL);
Zhu Yi41093162010-03-09 16:05:31 +08003213 if (!virts)
3214 return -ENOMEM;
3215
Kees Cook6da2ec52018-06-12 13:55:00 -07003216 phys = kmalloc_array(CB_NUMBER_OF_ELEMENTS_SMALL, sizeof(dma_addr_t),
3217 GFP_KERNEL);
Zhu Yi41093162010-03-09 16:05:31 +08003218 if (!phys) {
3219 kfree(virts);
3220 return -ENOMEM;
3221 }
Romain Perier28b75412017-08-23 09:16:51 +02003222 pool = dma_pool_create("ipw2200", &priv->pci_dev->dev, CB_MAX_LENGTH, 0,
3223 0);
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003224 if (!pool) {
Romain Perier28b75412017-08-23 09:16:51 +02003225 IPW_ERROR("dma_pool_create failed\n");
Zhu Yi41093162010-03-09 16:05:31 +08003226 kfree(phys);
3227 kfree(virts);
James Ketrenos43f66a62005-03-25 12:31:53 -06003228 return -ENOMEM;
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003229 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003230
3231 /* Start the Dma */
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003232 ret = ipw_fw_dma_enable(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003233
Alexander Beregalov0ee904c2009-04-11 14:50:23 +00003234 /* the DMA is already ready this would be a bug. */
3235 BUG_ON(priv->sram_desc.last_cb_index > 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06003236
3237 do {
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003238 u32 chunk_len;
3239 u8 *start;
3240 int size;
3241 int nr = 0;
3242
James Ketrenos43f66a62005-03-25 12:31:53 -06003243 chunk = (struct fw_chunk *)(data + offset);
3244 offset += sizeof(struct fw_chunk);
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003245 chunk_len = le32_to_cpu(chunk->length);
3246 start = data + offset;
3247
3248 nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH;
3249 for (i = 0; i < nr; i++) {
Romain Perier28b75412017-08-23 09:16:51 +02003250 virts[total_nr] = dma_pool_alloc(pool, GFP_KERNEL,
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003251 &phys[total_nr]);
3252 if (!virts[total_nr]) {
3253 ret = -ENOMEM;
3254 goto out;
3255 }
3256 size = min_t(u32, chunk_len - i * CB_MAX_LENGTH,
3257 CB_MAX_LENGTH);
3258 memcpy(virts[total_nr], start, size);
3259 start += size;
3260 total_nr++;
3261 /* We don't support fw chunk larger than 64*8K */
3262 BUG_ON(total_nr > CB_NUMBER_OF_ELEMENTS_SMALL);
3263 }
3264
James Ketrenos43f66a62005-03-25 12:31:53 -06003265 /* build DMA packet and queue up for sending */
Jeff Garzikbf794512005-07-31 13:07:26 -04003266 /* dma to chunk->address, the chunk->length bytes from data +
James Ketrenos43f66a62005-03-25 12:31:53 -06003267 * offeset*/
3268 /* Dma loading */
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003269 ret = ipw_fw_dma_add_buffer(priv, &phys[total_nr - nr],
3270 nr, le32_to_cpu(chunk->address),
3271 chunk_len);
3272 if (ret) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003273 IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
3274 goto out;
3275 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003276
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003277 offset += chunk_len;
James Ketrenos43f66a62005-03-25 12:31:53 -06003278 } while (offset < len);
3279
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003280 /* Run the DMA and wait for the answer */
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003281 ret = ipw_fw_dma_kick(priv);
3282 if (ret) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003283 IPW_ERROR("dmaKick Failed\n");
3284 goto out;
3285 }
3286
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003287 ret = ipw_fw_dma_wait(priv);
3288 if (ret) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003289 IPW_ERROR("dmaWaitSync Failed\n");
3290 goto out;
3291 }
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003292 out:
3293 for (i = 0; i < total_nr; i++)
Romain Perier28b75412017-08-23 09:16:51 +02003294 dma_pool_free(pool, virts[i], phys[i]);
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003295
Romain Perier28b75412017-08-23 09:16:51 +02003296 dma_pool_destroy(pool);
Zhu Yi41093162010-03-09 16:05:31 +08003297 kfree(phys);
3298 kfree(virts);
Zhu Yi11ebd1b2009-08-28 11:42:31 +08003299
3300 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06003301}
3302
3303/* stop nic */
3304static int ipw_stop_nic(struct ipw_priv *priv)
3305{
3306 int rc = 0;
3307
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003308 /* stop */
James Ketrenosb095c382005-08-24 22:04:42 -05003309 ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04003310
James Ketrenosb095c382005-08-24 22:04:42 -05003311 rc = ipw_poll_bit(priv, IPW_RESET_REG,
3312 IPW_RESET_REG_MASTER_DISABLED, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003313 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08003314 IPW_ERROR("wait for reg master disabled failed after 500ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003315 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003316 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003317
James Ketrenosb095c382005-08-24 22:04:42 -05003318 ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04003319
James Ketrenos43f66a62005-03-25 12:31:53 -06003320 return rc;
3321}
3322
3323static void ipw_start_nic(struct ipw_priv *priv)
3324{
3325 IPW_DEBUG_TRACE(">>\n");
3326
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003327 /* prvHwStartNic release ARC */
James Ketrenosb095c382005-08-24 22:04:42 -05003328 ipw_clear_bit(priv, IPW_RESET_REG,
3329 IPW_RESET_REG_MASTER_DISABLED |
3330 IPW_RESET_REG_STOP_MASTER |
James Ketrenos43f66a62005-03-25 12:31:53 -06003331 CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04003332
James Ketrenos43f66a62005-03-25 12:31:53 -06003333 /* enable power management */
James Ketrenosb095c382005-08-24 22:04:42 -05003334 ipw_set_bit(priv, IPW_GP_CNTRL_RW,
3335 IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
James Ketrenos43f66a62005-03-25 12:31:53 -06003336
3337 IPW_DEBUG_TRACE("<<\n");
3338}
Jeff Garzikbf794512005-07-31 13:07:26 -04003339
James Ketrenos43f66a62005-03-25 12:31:53 -06003340static int ipw_init_nic(struct ipw_priv *priv)
3341{
3342 int rc;
3343
3344 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04003345 /* reset */
James Ketrenos43f66a62005-03-25 12:31:53 -06003346 /*prvHwInitNic */
3347 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05003348 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003349
3350 /* low-level PLL activation */
James Ketrenosb095c382005-08-24 22:04:42 -05003351 ipw_write32(priv, IPW_READ_INT_REGISTER,
3352 IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06003353
3354 /* wait for clock stabilization */
James Ketrenosb095c382005-08-24 22:04:42 -05003355 rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
3356 IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003357 if (rc < 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06003358 IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
3359
3360 /* assert SW reset */
James Ketrenosb095c382005-08-24 22:04:42 -05003361 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06003362
3363 udelay(10);
3364
3365 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05003366 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003367
3368 IPW_DEBUG_TRACE(">>\n");
3369 return 0;
3370}
3371
Jeff Garzikbf794512005-07-31 13:07:26 -04003372/* Call this function from process context, it will sleep in request_firmware.
James Ketrenos43f66a62005-03-25 12:31:53 -06003373 * Probe is an ok place to call this from.
3374 */
3375static int ipw_reset_nic(struct ipw_priv *priv)
3376{
3377 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05003378 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06003379
3380 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04003381
James Ketrenos43f66a62005-03-25 12:31:53 -06003382 rc = ipw_init_nic(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04003383
James Ketrenosa613bff2005-08-24 21:43:11 -05003384 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003385 /* Clear the 'host command active' bit... */
3386 priv->status &= ~STATUS_HCMD_ACTIVE;
3387 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -05003388 priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
3389 wake_up_interruptible(&priv->wait_state);
James Ketrenosa613bff2005-08-24 21:43:11 -05003390 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003391
3392 IPW_DEBUG_TRACE("<<\n");
3393 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003394}
James Ketrenos43f66a62005-03-25 12:31:53 -06003395
James Ketrenos9006ea72006-03-08 03:22:28 +08003396
3397struct ipw_fw {
Zhu Yi0070f8c2006-04-13 17:20:12 +08003398 __le32 ver;
3399 __le32 boot_size;
3400 __le32 ucode_size;
3401 __le32 fw_size;
James Ketrenos9006ea72006-03-08 03:22:28 +08003402 u8 data[0];
3403};
3404
Jeff Garzikbf794512005-07-31 13:07:26 -04003405static int ipw_get_fw(struct ipw_priv *priv,
James Ketrenos9006ea72006-03-08 03:22:28 +08003406 const struct firmware **raw, const char *name)
James Ketrenos43f66a62005-03-25 12:31:53 -06003407{
James Ketrenos9006ea72006-03-08 03:22:28 +08003408 struct ipw_fw *fw;
James Ketrenos43f66a62005-03-25 12:31:53 -06003409 int rc;
3410
3411 /* ask firmware_class module to get the boot firmware off disk */
James Ketrenos9006ea72006-03-08 03:22:28 +08003412 rc = request_firmware(raw, name, &priv->pci_dev->dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06003413 if (rc < 0) {
James Ketrenos9006ea72006-03-08 03:22:28 +08003414 IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003415 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003416 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003417
James Ketrenos9006ea72006-03-08 03:22:28 +08003418 if ((*raw)->size < sizeof(*fw)) {
3419 IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size);
James Ketrenos43f66a62005-03-25 12:31:53 -06003420 return -EINVAL;
3421 }
3422
James Ketrenos9006ea72006-03-08 03:22:28 +08003423 fw = (void *)(*raw)->data;
3424
Zhu Yi0070f8c2006-04-13 17:20:12 +08003425 if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) +
3426 le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) {
James Ketrenos9006ea72006-03-08 03:22:28 +08003427 IPW_ERROR("%s is too small or corrupt (%zd)\n",
3428 name, (*raw)->size);
3429 return -EINVAL;
3430 }
3431
3432 IPW_DEBUG_INFO("Read firmware '%s' image v%d.%d (%zd bytes)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06003433 name,
James Ketrenos9006ea72006-03-08 03:22:28 +08003434 le32_to_cpu(fw->ver) >> 16,
3435 le32_to_cpu(fw->ver) & 0xff,
3436 (*raw)->size - sizeof(*fw));
James Ketrenos43f66a62005-03-25 12:31:53 -06003437 return 0;
3438}
3439
James Ketrenosb095c382005-08-24 22:04:42 -05003440#define IPW_RX_BUF_SIZE (3000)
James Ketrenos43f66a62005-03-25 12:31:53 -06003441
Arjan van de Ven858119e2006-01-14 13:20:43 -08003442static void ipw_rx_queue_reset(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003443 struct ipw_rx_queue *rxq)
3444{
3445 unsigned long flags;
3446 int i;
3447
3448 spin_lock_irqsave(&rxq->lock, flags);
3449
3450 INIT_LIST_HEAD(&rxq->rx_free);
3451 INIT_LIST_HEAD(&rxq->rx_used);
3452
3453 /* Fill the rx_used queue with _all_ of the Rx buffers */
3454 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
3455 /* In the reset function, these buffers may have been allocated
3456 * to an SKB, so we need to unmap and free potential storage */
3457 if (rxq->pool[i].skb != NULL) {
3458 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05003459 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003460 dev_kfree_skb(rxq->pool[i].skb);
James Ketrenosa613bff2005-08-24 21:43:11 -05003461 rxq->pool[i].skb = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003462 }
3463 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
3464 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003465
James Ketrenos43f66a62005-03-25 12:31:53 -06003466 /* Set us so that we have processed and used all buffers, but have
3467 * not restocked the Rx queue with fresh buffers */
3468 rxq->read = rxq->write = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003469 rxq->free_count = 0;
3470 spin_unlock_irqrestore(&rxq->lock, flags);
3471}
3472
3473#ifdef CONFIG_PM
3474static int fw_loaded = 0;
James Ketrenos9006ea72006-03-08 03:22:28 +08003475static const struct firmware *raw = NULL;
James Ketrenosafbf30a2005-08-25 00:05:33 -05003476
3477static void free_firmware(void)
3478{
3479 if (fw_loaded) {
James Ketrenos9006ea72006-03-08 03:22:28 +08003480 release_firmware(raw);
3481 raw = NULL;
James Ketrenosafbf30a2005-08-25 00:05:33 -05003482 fw_loaded = 0;
3483 }
3484}
3485#else
3486#define free_firmware() do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06003487#endif
3488
3489static int ipw_load(struct ipw_priv *priv)
3490{
3491#ifndef CONFIG_PM
James Ketrenos9006ea72006-03-08 03:22:28 +08003492 const struct firmware *raw = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003493#endif
James Ketrenos9006ea72006-03-08 03:22:28 +08003494 struct ipw_fw *fw;
3495 u8 *boot_img, *ucode_img, *fw_img;
3496 u8 *name = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003497 int rc = 0, retries = 3;
3498
Zhu Yi397ae122006-01-24 16:37:22 +08003499 switch (priv->ieee->iw_mode) {
3500 case IW_MODE_ADHOC:
James Ketrenos9006ea72006-03-08 03:22:28 +08003501 name = "ipw2200-ibss.fw";
Zhu Yi397ae122006-01-24 16:37:22 +08003502 break;
James Ketrenosb095c382005-08-24 22:04:42 -05003503#ifdef CONFIG_IPW2200_MONITOR
Zhu Yi397ae122006-01-24 16:37:22 +08003504 case IW_MODE_MONITOR:
James Ketrenos9006ea72006-03-08 03:22:28 +08003505 name = "ipw2200-sniffer.fw";
Zhu Yi397ae122006-01-24 16:37:22 +08003506 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06003507#endif
Zhu Yi397ae122006-01-24 16:37:22 +08003508 case IW_MODE_INFRA:
James Ketrenos9006ea72006-03-08 03:22:28 +08003509 name = "ipw2200-bss.fw";
Zhu Yi397ae122006-01-24 16:37:22 +08003510 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06003511 }
Zhu Yi397ae122006-01-24 16:37:22 +08003512
James Ketrenos9006ea72006-03-08 03:22:28 +08003513 if (!name) {
3514 rc = -EINVAL;
3515 goto error;
3516 }
3517
3518#ifdef CONFIG_PM
3519 if (!fw_loaded) {
3520#endif
3521 rc = ipw_get_fw(priv, &raw, name);
3522 if (rc < 0)
3523 goto error;
3524#ifdef CONFIG_PM
3525 }
3526#endif
3527
3528 fw = (void *)raw->data;
3529 boot_img = &fw->data[0];
Zhu Yi0070f8c2006-04-13 17:20:12 +08003530 ucode_img = &fw->data[le32_to_cpu(fw->boot_size)];
3531 fw_img = &fw->data[le32_to_cpu(fw->boot_size) +
3532 le32_to_cpu(fw->ucode_size)];
James Ketrenos9006ea72006-03-08 03:22:28 +08003533
James Ketrenos43f66a62005-03-25 12:31:53 -06003534 if (!priv->rxq)
3535 priv->rxq = ipw_rx_queue_alloc(priv);
3536 else
3537 ipw_rx_queue_reset(priv, priv->rxq);
3538 if (!priv->rxq) {
3539 IPW_ERROR("Unable to initialize Rx queue\n");
Wei Yongjun5d9e3bc2013-06-21 10:42:19 +08003540 rc = -ENOMEM;
James Ketrenos43f66a62005-03-25 12:31:53 -06003541 goto error;
3542 }
3543
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003544 retry:
James Ketrenos43f66a62005-03-25 12:31:53 -06003545 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003546 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003547 priv->status &= ~STATUS_INT_ENABLED;
3548
3549 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003550 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003551
James Ketrenos43f66a62005-03-25 12:31:53 -06003552 ipw_stop_nic(priv);
3553
3554 rc = ipw_reset_nic(priv);
Zhu Yi397ae122006-01-24 16:37:22 +08003555 if (rc < 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003556 IPW_ERROR("Unable to reset NIC\n");
3557 goto error;
3558 }
3559
James Ketrenosb095c382005-08-24 22:04:42 -05003560 ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
3561 IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
James Ketrenos43f66a62005-03-25 12:31:53 -06003562
3563 /* DMA the initial boot firmware into the device */
Zhu Yi0070f8c2006-04-13 17:20:12 +08003564 rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size));
James Ketrenos43f66a62005-03-25 12:31:53 -06003565 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003566 IPW_ERROR("Unable to load boot firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003567 goto error;
3568 }
3569
3570 /* kick start the device */
3571 ipw_start_nic(priv);
3572
Zhu Yic7b6a672006-01-24 16:37:05 +08003573 /* wait for the device to finish its initial startup sequence */
James Ketrenosb095c382005-08-24 22:04:42 -05003574 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3575 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003576 if (rc < 0) {
3577 IPW_ERROR("device failed to boot initial fw image\n");
3578 goto error;
3579 }
3580 IPW_DEBUG_INFO("initial device response after %dms\n", rc);
3581
Jeff Garzikbf794512005-07-31 13:07:26 -04003582 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003583 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003584
3585 /* DMA the ucode into the device */
Zhu Yi0070f8c2006-04-13 17:20:12 +08003586 rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size));
James Ketrenos43f66a62005-03-25 12:31:53 -06003587 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003588 IPW_ERROR("Unable to load ucode: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003589 goto error;
3590 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003591
James Ketrenos43f66a62005-03-25 12:31:53 -06003592 /* stop nic */
3593 ipw_stop_nic(priv);
3594
3595 /* DMA bss firmware into the device */
Zhu Yi0070f8c2006-04-13 17:20:12 +08003596 rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003597 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003598 IPW_ERROR("Unable to load firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003599 goto error;
3600 }
Zhu Yi397ae122006-01-24 16:37:22 +08003601#ifdef CONFIG_PM
3602 fw_loaded = 1;
3603#endif
3604
James Ketrenos43f66a62005-03-25 12:31:53 -06003605 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
3606
3607 rc = ipw_queue_reset(priv);
Zhu Yi397ae122006-01-24 16:37:22 +08003608 if (rc < 0) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003609 IPW_ERROR("Unable to initialize queues\n");
3610 goto error;
3611 }
3612
3613 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003614 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003615 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003616 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003617
James Ketrenos43f66a62005-03-25 12:31:53 -06003618 /* kick start the device */
3619 ipw_start_nic(priv);
3620
James Ketrenosb095c382005-08-24 22:04:42 -05003621 if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003622 if (retries > 0) {
3623 IPW_WARNING("Parity error. Retrying init.\n");
3624 retries--;
3625 goto retry;
3626 }
3627
3628 IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
3629 rc = -EIO;
3630 goto error;
3631 }
3632
3633 /* wait for the device */
James Ketrenosb095c382005-08-24 22:04:42 -05003634 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3635 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003636 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08003637 IPW_ERROR("device failed to start within 500ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003638 goto error;
3639 }
3640 IPW_DEBUG_INFO("device response after %dms\n", rc);
3641
3642 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003643 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003644
Stanislav Yakovlevfdbfff72012-05-17 15:56:35 -04003645 /* read eeprom data */
James Ketrenos43f66a62005-03-25 12:31:53 -06003646 priv->eeprom_delay = 1;
Stanislav Yakovlevfdbfff72012-05-17 15:56:35 -04003647 ipw_read_eeprom(priv);
3648 /* initialize the eeprom region of sram */
Jeff Garzikbf794512005-07-31 13:07:26 -04003649 ipw_eeprom_init_sram(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003650
3651 /* enable interrupts */
3652 ipw_enable_interrupts(priv);
3653
3654 /* Ensure our queue has valid packets */
3655 ipw_rx_queue_replenish(priv);
3656
James Ketrenosb095c382005-08-24 22:04:42 -05003657 ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read);
James Ketrenos43f66a62005-03-25 12:31:53 -06003658
3659 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003660 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003661
3662#ifndef CONFIG_PM
James Ketrenos9006ea72006-03-08 03:22:28 +08003663 release_firmware(raw);
James Ketrenos43f66a62005-03-25 12:31:53 -06003664#endif
3665 return 0;
3666
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003667 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06003668 if (priv->rxq) {
3669 ipw_rx_queue_free(priv, priv->rxq);
3670 priv->rxq = NULL;
3671 }
3672 ipw_tx_queue_free(priv);
Jesper Juhld144f532012-04-09 22:51:01 +02003673 release_firmware(raw);
James Ketrenos43f66a62005-03-25 12:31:53 -06003674#ifdef CONFIG_PM
3675 fw_loaded = 0;
James Ketrenos9006ea72006-03-08 03:22:28 +08003676 raw = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003677#endif
3678
3679 return rc;
3680}
3681
Jeff Garzikbf794512005-07-31 13:07:26 -04003682/**
James Ketrenos43f66a62005-03-25 12:31:53 -06003683 * DMA services
3684 *
3685 * Theory of operation
3686 *
3687 * A queue is a circular buffers with 'Read' and 'Write' pointers.
3688 * 2 empty entries always kept in the buffer to protect from overflow.
3689 *
3690 * For Tx queue, there are low mark and high mark limits. If, after queuing
Jeff Garzikbf794512005-07-31 13:07:26 -04003691 * the packet for Tx, free space become < low mark, Tx queue stopped. When
3692 * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
James Ketrenos43f66a62005-03-25 12:31:53 -06003693 * Tx queue resumed.
3694 *
3695 * The IPW operates with six queues, one receive queue in the device's
3696 * sram, one transmit queue for sending commands to the device firmware,
Jeff Garzikbf794512005-07-31 13:07:26 -04003697 * and four transmit queues for data.
James Ketrenos43f66a62005-03-25 12:31:53 -06003698 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003699 * The four transmit queues allow for performing quality of service (qos)
James Ketrenos43f66a62005-03-25 12:31:53 -06003700 * transmissions as per the 802.11 protocol. Currently Linux does not
Jeff Garzikbf794512005-07-31 13:07:26 -04003701 * provide a mechanism to the user for utilizing prioritized queues, so
James Ketrenos43f66a62005-03-25 12:31:53 -06003702 * we only utilize the first data transmit queue (queue1).
3703 */
3704
3705/**
3706 * Driver allocates buffers of this size for Rx
3707 */
3708
Dan Williams943dbef2008-02-14 17:49:41 -05003709/**
3710 * ipw_rx_queue_space - Return number of free slots available in queue.
3711 */
3712static int ipw_rx_queue_space(const struct ipw_rx_queue *q)
3713{
3714 int s = q->read - q->write;
3715 if (s <= 0)
3716 s += RX_QUEUE_SIZE;
3717 /* keep some buffer to not confuse full and empty queue */
3718 s -= 2;
3719 if (s < 0)
3720 s = 0;
3721 return s;
3722}
3723
3724static inline int ipw_tx_queue_space(const struct clx2_queue *q)
James Ketrenos43f66a62005-03-25 12:31:53 -06003725{
3726 int s = q->last_used - q->first_empty;
3727 if (s <= 0)
3728 s += q->n_bd;
3729 s -= 2; /* keep some reserve to not confuse empty and full situations */
3730 if (s < 0)
3731 s = 0;
3732 return s;
3733}
3734
3735static inline int ipw_queue_inc_wrap(int index, int n_bd)
3736{
3737 return (++index == n_bd) ? 0 : index;
3738}
3739
3740/**
3741 * Initialize common DMA queue structure
Jeff Garzikbf794512005-07-31 13:07:26 -04003742 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003743 * @param q queue to init
3744 * @param count Number of BD's to allocate. Should be power of 2
3745 * @param read_register Address for 'read' register
3746 * (not offset within BAR, full address)
3747 * @param write_register Address for 'write' register
3748 * (not offset within BAR, full address)
3749 * @param base_register Address for 'base' register
3750 * (not offset within BAR, full address)
3751 * @param size Address for 'size' register
3752 * (not offset within BAR, full address)
3753 */
Jeff Garzikbf794512005-07-31 13:07:26 -04003754static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003755 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003756{
3757 q->n_bd = count;
3758
3759 q->low_mark = q->n_bd / 4;
3760 if (q->low_mark < 4)
3761 q->low_mark = 4;
3762
3763 q->high_mark = q->n_bd / 8;
3764 if (q->high_mark < 2)
3765 q->high_mark = 2;
3766
3767 q->first_empty = q->last_used = 0;
3768 q->reg_r = read;
3769 q->reg_w = write;
3770
3771 ipw_write32(priv, base, q->dma_addr);
3772 ipw_write32(priv, size, count);
3773 ipw_write32(priv, read, 0);
3774 ipw_write32(priv, write, 0);
3775
3776 _ipw_read32(priv, 0x90);
3777}
3778
Jeff Garzikbf794512005-07-31 13:07:26 -04003779static int ipw_queue_tx_init(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003780 struct clx2_tx_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003781 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003782{
3783 struct pci_dev *dev = priv->pci_dev;
3784
Kees Cook6da2ec52018-06-12 13:55:00 -07003785 q->txb = kmalloc_array(count, sizeof(q->txb[0]), GFP_KERNEL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003786 if (!q->txb) {
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003787 IPW_ERROR("vmalloc for auxiliary BD structures failed\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003788 return -ENOMEM;
3789 }
3790
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003791 q->bd =
3792 pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06003793 if (!q->bd) {
Jiri Bencaaa4d302005-06-07 14:58:41 +02003794 IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003795 sizeof(q->bd[0]) * count);
James Ketrenos43f66a62005-03-25 12:31:53 -06003796 kfree(q->txb);
3797 q->txb = NULL;
3798 return -ENOMEM;
3799 }
3800
3801 ipw_queue_init(priv, &q->q, count, read, write, base, size);
3802 return 0;
3803}
3804
3805/**
3806 * Free one TFD, those at index [txq->q.last_used].
3807 * Do NOT advance any indexes
Jeff Garzikbf794512005-07-31 13:07:26 -04003808 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003809 * @param dev
3810 * @param txq
3811 */
3812static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
3813 struct clx2_tx_queue *txq)
3814{
3815 struct tfd_frame *bd = &txq->bd[txq->q.last_used];
3816 struct pci_dev *dev = priv->pci_dev;
3817 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003818
James Ketrenos43f66a62005-03-25 12:31:53 -06003819 /* classify bd */
3820 if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
3821 /* nothing to cleanup after for host commands */
3822 return;
3823
3824 /* sanity check */
James Ketrenosa613bff2005-08-24 21:43:11 -05003825 if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
3826 IPW_ERROR("Too many chunks: %i\n",
3827 le32_to_cpu(bd->u.data.num_chunks));
James Ketrenos43f66a62005-03-25 12:31:53 -06003828 /** @todo issue fatal error, it is quite serious situation */
3829 return;
3830 }
3831
3832 /* unmap chunks if any */
James Ketrenosa613bff2005-08-24 21:43:11 -05003833 for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
3834 pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]),
3835 le16_to_cpu(bd->u.data.chunk_len[i]),
3836 PCI_DMA_TODEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003837 if (txq->txb[txq->q.last_used]) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04003838 libipw_txb_free(txq->txb[txq->q.last_used]);
James Ketrenos43f66a62005-03-25 12:31:53 -06003839 txq->txb[txq->q.last_used] = NULL;
3840 }
3841 }
3842}
3843
3844/**
3845 * Deallocate DMA queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04003846 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003847 * Empty queue by removing and destroying all BD's.
3848 * Free all buffers.
Jeff Garzikbf794512005-07-31 13:07:26 -04003849 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003850 * @param dev
3851 * @param q
3852 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003853static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
James Ketrenos43f66a62005-03-25 12:31:53 -06003854{
3855 struct clx2_queue *q = &txq->q;
3856 struct pci_dev *dev = priv->pci_dev;
3857
Jeff Garzikbf794512005-07-31 13:07:26 -04003858 if (q->n_bd == 0)
3859 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06003860
3861 /* first, empty all BD's */
3862 for (; q->first_empty != q->last_used;
3863 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
3864 ipw_queue_tx_free_tfd(priv, txq);
3865 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003866
James Ketrenos43f66a62005-03-25 12:31:53 -06003867 /* free buffers belonging to queue itself */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003868 pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
James Ketrenos43f66a62005-03-25 12:31:53 -06003869 q->dma_addr);
3870 kfree(txq->txb);
3871
3872 /* 0 fill whole structure */
3873 memset(txq, 0, sizeof(*txq));
3874}
3875
James Ketrenos43f66a62005-03-25 12:31:53 -06003876/**
3877 * Destroy all DMA queues and structures
Jeff Garzikbf794512005-07-31 13:07:26 -04003878 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003879 * @param priv
3880 */
3881static void ipw_tx_queue_free(struct ipw_priv *priv)
3882{
3883 /* Tx CMD queue */
3884 ipw_queue_tx_free(priv, &priv->txq_cmd);
3885
3886 /* Tx queues */
3887 ipw_queue_tx_free(priv, &priv->txq[0]);
3888 ipw_queue_tx_free(priv, &priv->txq[1]);
3889 ipw_queue_tx_free(priv, &priv->txq[2]);
3890 ipw_queue_tx_free(priv, &priv->txq[3]);
3891}
3892
Arjan van de Ven858119e2006-01-14 13:20:43 -08003893static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003894{
3895 /* First 3 bytes are manufacturer */
3896 bssid[0] = priv->mac_addr[0];
3897 bssid[1] = priv->mac_addr[1];
3898 bssid[2] = priv->mac_addr[2];
3899
3900 /* Last bytes are random */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003901 get_random_bytes(&bssid[3], ETH_ALEN - 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06003902
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003903 bssid[0] &= 0xfe; /* clear multicast bit */
3904 bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
James Ketrenos43f66a62005-03-25 12:31:53 -06003905}
3906
Arjan van de Ven858119e2006-01-14 13:20:43 -08003907static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003908{
3909 struct ipw_station_entry entry;
3910 int i;
3911
3912 for (i = 0; i < priv->num_stations; i++) {
dingtianhong36325f32013-12-26 19:41:23 +08003913 if (ether_addr_equal(priv->stations[i], bssid)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003914 /* Another node is active in network */
3915 priv->missed_adhoc_beacons = 0;
3916 if (!(priv->config & CFG_STATIC_CHANNEL))
3917 /* when other nodes drop out, we drop out */
3918 priv->config &= ~CFG_ADHOC_PERSIST;
3919
3920 return i;
3921 }
3922 }
3923
3924 if (i == MAX_STATIONS)
3925 return IPW_INVALID_STATION;
3926
Johannes Berge1749612008-10-27 15:59:26 -07003927 IPW_DEBUG_SCAN("Adding AdHoc station: %pM\n", bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06003928
3929 entry.reserved = 0;
3930 entry.support_mode = 0;
3931 memcpy(entry.mac_addr, bssid, ETH_ALEN);
3932 memcpy(priv->stations[i], bssid, ETH_ALEN);
3933 ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003934 &entry, sizeof(entry));
James Ketrenos43f66a62005-03-25 12:31:53 -06003935 priv->num_stations++;
3936
3937 return i;
3938}
3939
Arjan van de Ven858119e2006-01-14 13:20:43 -08003940static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003941{
3942 int i;
3943
Jeff Garzikbf794512005-07-31 13:07:26 -04003944 for (i = 0; i < priv->num_stations; i++)
dingtianhong36325f32013-12-26 19:41:23 +08003945 if (ether_addr_equal(priv->stations[i], bssid))
James Ketrenos43f66a62005-03-25 12:31:53 -06003946 return i;
3947
3948 return IPW_INVALID_STATION;
3949}
3950
3951static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
3952{
3953 int err;
3954
Hong Liu7b996592005-08-25 17:36:13 +08003955 if (priv->status & STATUS_ASSOCIATING) {
3956 IPW_DEBUG_ASSOC("Disassociating while associating.\n");
Tejun Heobcb6d912011-01-26 12:12:50 +01003957 schedule_work(&priv->disassociate);
Hong Liu7b996592005-08-25 17:36:13 +08003958 return;
3959 }
3960
3961 if (!(priv->status & STATUS_ASSOCIATED)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003962 IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
3963 return;
3964 }
3965
Masahiro Yamada66f00442017-02-27 14:29:42 -08003966 IPW_DEBUG_ASSOC("Disassociation attempt from %pM "
James Ketrenos43f66a62005-03-25 12:31:53 -06003967 "on channel %d.\n",
Johannes Berge1749612008-10-27 15:59:26 -07003968 priv->assoc_request.bssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06003969 priv->assoc_request.channel);
3970
3971 priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
3972 priv->status |= STATUS_DISASSOCIATING;
3973
3974 if (quiet)
3975 priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
3976 else
3977 priv->assoc_request.assoc_type = HC_DISASSOCIATE;
Hong Liue6324722005-09-14 21:04:15 -05003978
James Ketrenos43f66a62005-03-25 12:31:53 -06003979 err = ipw_send_associate(priv, &priv->assoc_request);
3980 if (err) {
3981 IPW_DEBUG_HC("Attempt to send [dis]associate command "
3982 "failed.\n");
3983 return;
3984 }
3985
3986}
3987
James Ketrenosc848d0a2005-08-24 21:56:24 -05003988static int ipw_disassociate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06003989{
James Ketrenosc848d0a2005-08-24 21:56:24 -05003990 struct ipw_priv *priv = data;
3991 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
3992 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003993 ipw_send_disassociate(data, 0);
Zhu Yib8ddafd2008-11-27 13:42:20 +08003994 netif_carrier_off(priv->net_dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003995 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003996}
3997
David Howellsc4028952006-11-22 14:57:56 +00003998static void ipw_bg_disassociate(struct work_struct *work)
James Ketrenos43f66a62005-03-25 12:31:53 -06003999{
David Howellsc4028952006-11-22 14:57:56 +00004000 struct ipw_priv *priv =
4001 container_of(work, struct ipw_priv, disassociate);
Zhu Yi46441512006-01-24 16:37:59 +08004002 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00004003 ipw_disassociate(priv);
Zhu Yi46441512006-01-24 16:37:59 +08004004 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06004005}
4006
David Howellsc4028952006-11-22 14:57:56 +00004007static void ipw_system_config(struct work_struct *work)
Zhu Yid8bad6d2005-07-13 12:25:38 -05004008{
David Howellsc4028952006-11-22 14:57:56 +00004009 struct ipw_priv *priv =
4010 container_of(work, struct ipw_priv, system_config);
Zhu Yid685b8c2006-04-13 17:20:27 +08004011
4012#ifdef CONFIG_IPW2200_PROMISCUOUS
4013 if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
4014 priv->sys_config.accept_all_data_frames = 1;
4015 priv->sys_config.accept_non_directed_frames = 1;
4016 priv->sys_config.accept_all_mgmt_bcpr = 1;
4017 priv->sys_config.accept_all_mgmt_frames = 1;
4018 }
4019#endif
4020
4021 ipw_send_system_config(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06004022}
4023
4024struct ipw_status_code {
4025 u16 status;
4026 const char *reason;
4027};
4028
4029static const struct ipw_status_code ipw_status_codes[] = {
4030 {0x00, "Successful"},
4031 {0x01, "Unspecified failure"},
4032 {0x0A, "Cannot support all requested capabilities in the "
4033 "Capability information field"},
4034 {0x0B, "Reassociation denied due to inability to confirm that "
4035 "association exists"},
4036 {0x0C, "Association denied due to reason outside the scope of this "
4037 "standard"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004038 {0x0D,
4039 "Responding station does not support the specified authentication "
James Ketrenos43f66a62005-03-25 12:31:53 -06004040 "algorithm"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004041 {0x0E,
4042 "Received an Authentication frame with authentication sequence "
James Ketrenos43f66a62005-03-25 12:31:53 -06004043 "transaction sequence number out of expected sequence"},
4044 {0x0F, "Authentication rejected because of challenge failure"},
4045 {0x10, "Authentication rejected due to timeout waiting for next "
4046 "frame in sequence"},
4047 {0x11, "Association denied because AP is unable to handle additional "
4048 "associated stations"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004049 {0x12,
4050 "Association denied due to requesting station not supporting all "
James Ketrenos43f66a62005-03-25 12:31:53 -06004051 "of the datarates in the BSSBasicServiceSet Parameter"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004052 {0x13,
4053 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06004054 "short preamble operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004055 {0x14,
4056 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06004057 "PBCC encoding"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004058 {0x15,
4059 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06004060 "channel agility"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004061 {0x19,
4062 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06004063 "short slot operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004064 {0x1A,
4065 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06004066 "DSSS-OFDM operation"},
4067 {0x28, "Invalid Information Element"},
4068 {0x29, "Group Cipher is not valid"},
4069 {0x2A, "Pairwise Cipher is not valid"},
4070 {0x2B, "AKMP is not valid"},
4071 {0x2C, "Unsupported RSN IE version"},
4072 {0x2D, "Invalid RSN IE Capabilities"},
4073 {0x2E, "Cipher suite is rejected per security policy"},
4074};
4075
Jeff Garzikbf794512005-07-31 13:07:26 -04004076static const char *ipw_get_status_code(u16 status)
James Ketrenos43f66a62005-03-25 12:31:53 -06004077{
4078 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04004079 for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
James Ketrenosea2b26e2005-08-24 21:25:16 -05004080 if (ipw_status_codes[i].status == (status & 0xff))
James Ketrenos43f66a62005-03-25 12:31:53 -06004081 return ipw_status_codes[i].reason;
4082 return "Unknown status value.";
4083}
James Ketrenos43f66a62005-03-25 12:31:53 -06004084
Arnd Bergmann6f07e0f2016-06-16 15:52:10 +02004085static inline void average_init(struct average *avg)
James Ketrenos43f66a62005-03-25 12:31:53 -06004086{
4087 memset(avg, 0, sizeof(*avg));
4088}
4089
Zhu Yi00d21de2006-04-13 17:19:02 +08004090#define DEPTH_RSSI 8
4091#define DEPTH_NOISE 16
4092static s16 exponential_average(s16 prev_avg, s16 val, u8 depth)
4093{
4094 return ((depth-1)*prev_avg + val)/depth;
4095}
4096
Arjan van de Ven858119e2006-01-14 13:20:43 -08004097static void average_add(struct average *avg, s16 val)
James Ketrenos43f66a62005-03-25 12:31:53 -06004098{
4099 avg->sum -= avg->entries[avg->pos];
4100 avg->sum += val;
4101 avg->entries[avg->pos++] = val;
4102 if (unlikely(avg->pos == AVG_ENTRIES)) {
4103 avg->init = 1;
4104 avg->pos = 0;
4105 }
4106}
4107
Arjan van de Ven858119e2006-01-14 13:20:43 -08004108static s16 average_value(struct average *avg)
James Ketrenos43f66a62005-03-25 12:31:53 -06004109{
4110 if (!unlikely(avg->init)) {
4111 if (avg->pos)
4112 return avg->sum / avg->pos;
4113 return 0;
4114 }
4115
4116 return avg->sum / AVG_ENTRIES;
4117}
4118
4119static void ipw_reset_stats(struct ipw_priv *priv)
4120{
4121 u32 len = sizeof(u32);
4122
4123 priv->quality = 0;
4124
4125 average_init(&priv->average_missed_beacons);
Zhu Yi00d21de2006-04-13 17:19:02 +08004126 priv->exp_avg_rssi = -60;
4127 priv->exp_avg_noise = -85 + 0x100;
James Ketrenos43f66a62005-03-25 12:31:53 -06004128
4129 priv->last_rate = 0;
4130 priv->last_missed_beacons = 0;
4131 priv->last_rx_packets = 0;
4132 priv->last_tx_packets = 0;
4133 priv->last_tx_failures = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04004134
James Ketrenos43f66a62005-03-25 12:31:53 -06004135 /* Firmware managed, reset only when NIC is restarted, so we have to
4136 * normalize on the current value */
Jeff Garzikbf794512005-07-31 13:07:26 -04004137 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
James Ketrenos43f66a62005-03-25 12:31:53 -06004138 &priv->last_rx_err, &len);
Jeff Garzikbf794512005-07-31 13:07:26 -04004139 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
James Ketrenos43f66a62005-03-25 12:31:53 -06004140 &priv->last_tx_failures, &len);
4141
4142 /* Driver managed, reset with each association */
4143 priv->missed_adhoc_beacons = 0;
4144 priv->missed_beacons = 0;
4145 priv->tx_packets = 0;
4146 priv->rx_packets = 0;
4147
4148}
4149
Arjan van de Ven858119e2006-01-14 13:20:43 -08004150static u32 ipw_get_max_rate(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06004151{
4152 u32 i = 0x80000000;
4153 u32 mask = priv->rates_mask;
4154 /* If currently associated in B mode, restrict the maximum
4155 * rate match to B rates */
4156 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004157 mask &= LIBIPW_CCK_RATES_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06004158
4159 /* TODO: Verify that the rate is supported by the current rates
4160 * list. */
4161
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004162 while (i && !(mask & i))
4163 i >>= 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06004164 switch (i) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004165 case LIBIPW_CCK_RATE_1MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004166 return 1000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004167 case LIBIPW_CCK_RATE_2MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004168 return 2000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004169 case LIBIPW_CCK_RATE_5MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004170 return 5500000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004171 case LIBIPW_OFDM_RATE_6MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004172 return 6000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004173 case LIBIPW_OFDM_RATE_9MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004174 return 9000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004175 case LIBIPW_CCK_RATE_11MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004176 return 11000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004177 case LIBIPW_OFDM_RATE_12MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004178 return 12000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004179 case LIBIPW_OFDM_RATE_18MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004180 return 18000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004181 case LIBIPW_OFDM_RATE_24MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004182 return 24000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004183 case LIBIPW_OFDM_RATE_36MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004184 return 36000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004185 case LIBIPW_OFDM_RATE_48MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004186 return 48000000;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004187 case LIBIPW_OFDM_RATE_54MB_MASK:
James Ketrenosea2b26e2005-08-24 21:25:16 -05004188 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06004189 }
4190
Jeff Garzikbf794512005-07-31 13:07:26 -04004191 if (priv->ieee->mode == IEEE_B)
James Ketrenos43f66a62005-03-25 12:31:53 -06004192 return 11000000;
4193 else
4194 return 54000000;
4195}
4196
4197static u32 ipw_get_current_rate(struct ipw_priv *priv)
4198{
4199 u32 rate, len = sizeof(rate);
4200 int err;
4201
Jeff Garzikbf794512005-07-31 13:07:26 -04004202 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06004203 return 0;
4204
4205 if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
Jeff Garzikbf794512005-07-31 13:07:26 -04004206 err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
James Ketrenos43f66a62005-03-25 12:31:53 -06004207 &len);
4208 if (err) {
4209 IPW_DEBUG_INFO("failed querying ordinals.\n");
4210 return 0;
4211 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004212 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06004213 return ipw_get_max_rate(priv);
4214
4215 switch (rate) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05004216 case IPW_TX_RATE_1MB:
4217 return 1000000;
4218 case IPW_TX_RATE_2MB:
4219 return 2000000;
4220 case IPW_TX_RATE_5MB:
4221 return 5500000;
4222 case IPW_TX_RATE_6MB:
4223 return 6000000;
4224 case IPW_TX_RATE_9MB:
4225 return 9000000;
4226 case IPW_TX_RATE_11MB:
4227 return 11000000;
4228 case IPW_TX_RATE_12MB:
4229 return 12000000;
4230 case IPW_TX_RATE_18MB:
4231 return 18000000;
4232 case IPW_TX_RATE_24MB:
4233 return 24000000;
4234 case IPW_TX_RATE_36MB:
4235 return 36000000;
4236 case IPW_TX_RATE_48MB:
4237 return 48000000;
4238 case IPW_TX_RATE_54MB:
4239 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06004240 }
4241
4242 return 0;
4243}
4244
James Ketrenos43f66a62005-03-25 12:31:53 -06004245#define IPW_STATS_INTERVAL (2 * HZ)
4246static void ipw_gather_stats(struct ipw_priv *priv)
4247{
4248 u32 rx_err, rx_err_delta, rx_packets_delta;
4249 u32 tx_failures, tx_failures_delta, tx_packets_delta;
4250 u32 missed_beacons_percent, missed_beacons_delta;
4251 u32 quality = 0;
4252 u32 len = sizeof(u32);
4253 s16 rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04004254 u32 beacon_quality, signal_quality, tx_quality, rx_quality,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004255 rate_quality;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004256 u32 max_rate;
James Ketrenos43f66a62005-03-25 12:31:53 -06004257
4258 if (!(priv->status & STATUS_ASSOCIATED)) {
4259 priv->quality = 0;
4260 return;
4261 }
4262
4263 /* Update the statistics */
Jeff Garzikbf794512005-07-31 13:07:26 -04004264 ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
James Ketrenos43f66a62005-03-25 12:31:53 -06004265 &priv->missed_beacons, &len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004266 missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
James Ketrenos43f66a62005-03-25 12:31:53 -06004267 priv->last_missed_beacons = priv->missed_beacons;
4268 if (priv->assoc_request.beacon_interval) {
4269 missed_beacons_percent = missed_beacons_delta *
Al Viro5b5e8072007-12-27 01:54:06 -05004270 (HZ * le16_to_cpu(priv->assoc_request.beacon_interval)) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004271 (IPW_STATS_INTERVAL * 10);
James Ketrenos43f66a62005-03-25 12:31:53 -06004272 } else {
4273 missed_beacons_percent = 0;
4274 }
4275 average_add(&priv->average_missed_beacons, missed_beacons_percent);
4276
4277 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
4278 rx_err_delta = rx_err - priv->last_rx_err;
4279 priv->last_rx_err = rx_err;
4280
4281 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
4282 tx_failures_delta = tx_failures - priv->last_tx_failures;
4283 priv->last_tx_failures = tx_failures;
4284
4285 rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
4286 priv->last_rx_packets = priv->rx_packets;
4287
4288 tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
4289 priv->last_tx_packets = priv->tx_packets;
4290
4291 /* Calculate quality based on the following:
Jeff Garzikbf794512005-07-31 13:07:26 -04004292 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004293 * Missed beacon: 100% = 0, 0% = 70% missed
4294 * Rate: 60% = 1Mbs, 100% = Max
4295 * Rx and Tx errors represent a straight % of total Rx/Tx
4296 * RSSI: 100% = > -50, 0% = < -80
4297 * Rx errors: 100% = 0, 0% = 50% missed
Jeff Garzikbf794512005-07-31 13:07:26 -04004298 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004299 * The lowest computed quality is used.
4300 *
4301 */
4302#define BEACON_THRESHOLD 5
4303 beacon_quality = 100 - missed_beacons_percent;
4304 if (beacon_quality < BEACON_THRESHOLD)
4305 beacon_quality = 0;
4306 else
Jeff Garzikbf794512005-07-31 13:07:26 -04004307 beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004308 (100 - BEACON_THRESHOLD);
Jeff Garzikbf794512005-07-31 13:07:26 -04004309 IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06004310 beacon_quality, missed_beacons_percent);
Jeff Garzikbf794512005-07-31 13:07:26 -04004311
James Ketrenos43f66a62005-03-25 12:31:53 -06004312 priv->last_rate = ipw_get_current_rate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004313 max_rate = ipw_get_max_rate(priv);
4314 rate_quality = priv->last_rate * 40 / max_rate + 60;
James Ketrenos43f66a62005-03-25 12:31:53 -06004315 IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
4316 rate_quality, priv->last_rate / 1000000);
Jeff Garzikbf794512005-07-31 13:07:26 -04004317
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004318 if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04004319 rx_quality = 100 - (rx_err_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004320 (rx_packets_delta + rx_err_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06004321 else
4322 rx_quality = 100;
4323 IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
4324 rx_quality, rx_err_delta, rx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04004325
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004326 if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04004327 tx_quality = 100 - (tx_failures_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004328 (tx_packets_delta + tx_failures_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06004329 else
4330 tx_quality = 100;
4331 IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
4332 tx_quality, tx_failures_delta, tx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04004333
Zhu Yi00d21de2006-04-13 17:19:02 +08004334 rssi = priv->exp_avg_rssi;
James Ketrenosc848d0a2005-08-24 21:56:24 -05004335 signal_quality =
4336 (100 *
4337 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
4338 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
4339 (priv->ieee->perfect_rssi - rssi) *
4340 (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
4341 62 * (priv->ieee->perfect_rssi - rssi))) /
4342 ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
4343 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
4344 if (signal_quality > 100)
James Ketrenos43f66a62005-03-25 12:31:53 -06004345 signal_quality = 100;
James Ketrenosc848d0a2005-08-24 21:56:24 -05004346 else if (signal_quality < 1)
James Ketrenos43f66a62005-03-25 12:31:53 -06004347 signal_quality = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004348
Zhu Yi61fb9ed2006-04-13 17:19:55 +08004349 IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06004350 signal_quality, rssi);
Jeff Garzikbf794512005-07-31 13:07:26 -04004351
Reinette Chatre21f8a732009-08-18 10:25:05 -07004352 quality = min(rx_quality, signal_quality);
4353 quality = min(tx_quality, quality);
4354 quality = min(rate_quality, quality);
4355 quality = min(beacon_quality, quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004356 if (quality == beacon_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004357 IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
4358 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004359 if (quality == rate_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004360 IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
4361 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004362 if (quality == tx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004363 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
4364 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004365 if (quality == rx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004366 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
4367 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004368 if (quality == signal_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004369 IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
4370 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004371
4372 priv->quality = quality;
Jeff Garzikbf794512005-07-31 13:07:26 -04004373
Tejun Heobcb6d912011-01-26 12:12:50 +01004374 schedule_delayed_work(&priv->gather_stats, IPW_STATS_INTERVAL);
James Ketrenos43f66a62005-03-25 12:31:53 -06004375}
4376
David Howellsc4028952006-11-22 14:57:56 +00004377static void ipw_bg_gather_stats(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05004378{
David Howellsc4028952006-11-22 14:57:56 +00004379 struct ipw_priv *priv =
4380 container_of(work, struct ipw_priv, gather_stats.work);
Zhu Yi46441512006-01-24 16:37:59 +08004381 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00004382 ipw_gather_stats(priv);
Zhu Yi46441512006-01-24 16:37:59 +08004383 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05004384}
4385
Ben Cahille7582562005-10-06 15:34:41 -05004386/* Missed beacon behavior:
4387 * 1st missed -> roaming_threshold, just wait, don't do any scan/roam.
4388 * roaming_threshold -> disassociate_threshold, scan and roam for better signal.
4389 * Above disassociate threshold, give up and stop scanning.
4390 * Roaming is disabled if disassociate_threshold <= roaming_threshold */
Arjan van de Ven858119e2006-01-14 13:20:43 -08004391static void ipw_handle_missed_beacon(struct ipw_priv *priv,
James Ketrenosea2b26e2005-08-24 21:25:16 -05004392 int missed_count)
4393{
4394 priv->notif_missed_beacons = missed_count;
4395
James Ketrenosafbf30a2005-08-25 00:05:33 -05004396 if (missed_count > priv->disassociate_threshold &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05004397 priv->status & STATUS_ASSOCIATED) {
4398 /* If associated and we've hit the missed
4399 * beacon threshold, disassociate, turn
4400 * off roaming, and abort any active scans */
4401 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
James Ketrenosafbf30a2005-08-25 00:05:33 -05004402 IPW_DL_STATE | IPW_DL_ASSOC,
James Ketrenosea2b26e2005-08-24 21:25:16 -05004403 "Missed beacon: %d - disassociate\n", missed_count);
4404 priv->status &= ~STATUS_ROAMING;
James Ketrenosa613bff2005-08-24 21:43:11 -05004405 if (priv->status & STATUS_SCANNING) {
4406 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
4407 IPW_DL_STATE,
4408 "Aborting scan with missed beacon.\n");
Tejun Heobcb6d912011-01-26 12:12:50 +01004409 schedule_work(&priv->abort_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004410 }
4411
Tejun Heobcb6d912011-01-26 12:12:50 +01004412 schedule_work(&priv->disassociate);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004413 return;
4414 }
4415
4416 if (priv->status & STATUS_ROAMING) {
4417 /* If we are currently roaming, then just
4418 * print a debug statement... */
4419 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4420 "Missed beacon: %d - roam in progress\n",
4421 missed_count);
4422 return;
4423 }
4424
Zhu Yi4bfdb912006-01-24 16:37:16 +08004425 if (roaming &&
4426 (missed_count > priv->roaming_threshold &&
4427 missed_count <= priv->disassociate_threshold)) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05004428 /* If we are not already roaming, set the ROAM
Ben Cahille7582562005-10-06 15:34:41 -05004429 * bit in the status and kick off a scan.
4430 * This can happen several times before we reach
4431 * disassociate_threshold. */
James Ketrenosea2b26e2005-08-24 21:25:16 -05004432 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4433 "Missed beacon: %d - initiate "
4434 "roaming\n", missed_count);
4435 if (!(priv->status & STATUS_ROAMING)) {
4436 priv->status |= STATUS_ROAMING;
4437 if (!(priv->status & STATUS_SCANNING))
Tejun Heobcb6d912011-01-26 12:12:50 +01004438 schedule_delayed_work(&priv->request_scan, 0);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004439 }
4440 return;
4441 }
4442
Helmut Schaa14a4dfe2008-12-10 13:17:26 +01004443 if (priv->status & STATUS_SCANNING &&
4444 missed_count > IPW_MB_SCAN_CANCEL_THRESHOLD) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05004445 /* Stop scan to keep fw from getting
4446 * stuck (only if we aren't roaming --
4447 * otherwise we'll never scan more than 2 or 3
4448 * channels..) */
James Ketrenosb095c382005-08-24 22:04:42 -05004449 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE,
4450 "Aborting scan with missed beacon.\n");
Tejun Heobcb6d912011-01-26 12:12:50 +01004451 schedule_work(&priv->abort_scan);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004452 }
4453
4454 IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004455}
4456
Dan Williams0b531672007-10-09 13:55:24 -04004457static void ipw_scan_event(struct work_struct *work)
4458{
4459 union iwreq_data wrqu;
4460
4461 struct ipw_priv *priv =
4462 container_of(work, struct ipw_priv, scan_event.work);
4463
4464 wrqu.data.length = 0;
4465 wrqu.data.flags = 0;
4466 wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
4467}
4468
4469static void handle_scan_event(struct ipw_priv *priv)
4470{
4471 /* Only userspace-requested scan completion events go out immediately */
4472 if (!priv->user_requested_scan) {
Tejun Heo7c99e0b2012-12-21 17:56:54 -08004473 schedule_delayed_work(&priv->scan_event,
4474 round_jiffies_relative(msecs_to_jiffies(4000)));
Dan Williams0b531672007-10-09 13:55:24 -04004475 } else {
Dan Williams0b531672007-10-09 13:55:24 -04004476 priv->user_requested_scan = 0;
Tejun Heo7c99e0b2012-12-21 17:56:54 -08004477 mod_delayed_work(system_wq, &priv->scan_event, 0);
Dan Williams0b531672007-10-09 13:55:24 -04004478 }
4479}
4480
James Ketrenos43f66a62005-03-25 12:31:53 -06004481/**
4482 * Handle host notification packet.
4483 * Called from interrupt routine
4484 */
Arjan van de Ven858119e2006-01-14 13:20:43 -08004485static void ipw_rx_notification(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004486 struct ipw_rx_notification *notif)
4487{
Al Viroe62e1ee2007-12-27 01:36:46 -05004488 u16 size = le16_to_cpu(notif->size);
James Ketrenosa613bff2005-08-24 21:43:11 -05004489
Al Viroe62e1ee2007-12-27 01:36:46 -05004490 IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size);
Jeff Garzikbf794512005-07-31 13:07:26 -04004491
James Ketrenos43f66a62005-03-25 12:31:53 -06004492 switch (notif->subtype) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004493 case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
4494 struct notif_association *assoc = &notif->u.assoc;
Jeff Garzikbf794512005-07-31 13:07:26 -04004495
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004496 switch (assoc->state) {
4497 case CMAS_ASSOCIATED:{
4498 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4499 IPW_DL_ASSOC,
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07004500 "associated: '%*pE' %pM\n",
4501 priv->essid_len, priv->essid,
Johannes Berge1749612008-10-27 15:59:26 -07004502 priv->bssid);
Jeff Garzikbf794512005-07-31 13:07:26 -04004503
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004504 switch (priv->ieee->iw_mode) {
4505 case IW_MODE_INFRA:
4506 memcpy(priv->ieee->bssid,
4507 priv->bssid, ETH_ALEN);
4508 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004509
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004510 case IW_MODE_ADHOC:
4511 memcpy(priv->ieee->bssid,
4512 priv->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04004513
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004514 /* clear out the station table */
4515 priv->num_stations = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004516
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004517 IPW_DEBUG_ASSOC
4518 ("queueing adhoc check\n");
Tejun Heobcb6d912011-01-26 12:12:50 +01004519 schedule_delayed_work(
4520 &priv->adhoc_check,
4521 le16_to_cpu(priv->
4522 assoc_request.
4523 beacon_interval));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004524 break;
4525 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004526
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004527 priv->status &= ~STATUS_ASSOCIATING;
4528 priv->status |= STATUS_ASSOCIATED;
Tejun Heobcb6d912011-01-26 12:12:50 +01004529 schedule_work(&priv->system_config);
James Ketrenos43f66a62005-03-25 12:31:53 -06004530
Zhu Yie43e3c12006-04-13 17:20:45 +08004531#ifdef CONFIG_IPW2200_QOS
James Ketrenosafbf30a2005-08-25 00:05:33 -05004532#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
John W. Linville72118012008-09-30 21:43:03 -04004533 le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_control))
James Ketrenosafbf30a2005-08-25 00:05:33 -05004534 if ((priv->status & STATUS_AUTH) &&
4535 (IPW_GET_PACKET_STYPE(&notif->u.raw)
4536 == IEEE80211_STYPE_ASSOC_RESP)) {
James Ketrenosb095c382005-08-24 22:04:42 -05004537 if ((sizeof
4538 (struct
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004539 libipw_assoc_response)
Al Viroe62e1ee2007-12-27 01:36:46 -05004540 <= size)
4541 && (size <= 2314)) {
James Ketrenosb095c382005-08-24 22:04:42 -05004542 struct
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004543 libipw_rx_stats
James Ketrenosb095c382005-08-24 22:04:42 -05004544 stats = {
Al Viroe62e1ee2007-12-27 01:36:46 -05004545 .len = size - 1,
James Ketrenosb095c382005-08-24 22:04:42 -05004546 };
4547
4548 IPW_DEBUG_QOS
4549 ("QoS Associate "
Al Viroe62e1ee2007-12-27 01:36:46 -05004550 "size %d\n", size);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004551 libipw_rx_mgt(priv->
James Ketrenosb095c382005-08-24 22:04:42 -05004552 ieee,
4553 (struct
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004554 libipw_hdr_4addr
James Ketrenosb095c382005-08-24 22:04:42 -05004555 *)
4556 &notif->u.raw, &stats);
4557 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004558 }
James Ketrenosb095c382005-08-24 22:04:42 -05004559#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06004560
James Ketrenosa613bff2005-08-24 21:43:11 -05004561 schedule_work(&priv->link_up);
James Ketrenos43f66a62005-03-25 12:31:53 -06004562
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004563 break;
4564 }
4565
4566 case CMAS_AUTHENTICATED:{
4567 if (priv->
4568 status & (STATUS_ASSOCIATED |
4569 STATUS_AUTH)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004570 struct notif_authenticate *auth
4571 = &notif->u.auth;
4572 IPW_DEBUG(IPW_DL_NOTIF |
4573 IPW_DL_STATE |
4574 IPW_DL_ASSOC,
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07004575 "deauthenticated: '%*pE' %pM: (0x%04X) - %s\n",
4576 priv->essid_len,
4577 priv->essid,
Johannes Berge1749612008-10-27 15:59:26 -07004578 priv->bssid,
Al Viro83f7d572008-03-16 22:26:44 +00004579 le16_to_cpu(auth->status),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004580 ipw_get_status_code
Al Viro83f7d572008-03-16 22:26:44 +00004581 (le16_to_cpu
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004582 (auth->status)));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004583
4584 priv->status &=
4585 ~(STATUS_ASSOCIATING |
4586 STATUS_AUTH |
4587 STATUS_ASSOCIATED);
4588
James Ketrenosa613bff2005-08-24 21:43:11 -05004589 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004590 break;
4591 }
4592
4593 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4594 IPW_DL_ASSOC,
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07004595 "authenticated: '%*pE' %pM\n",
4596 priv->essid_len, priv->essid,
Johannes Berge1749612008-10-27 15:59:26 -07004597 priv->bssid);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004598 break;
4599 }
4600
4601 case CMAS_INIT:{
James Ketrenosea2b26e2005-08-24 21:25:16 -05004602 if (priv->status & STATUS_AUTH) {
4603 struct
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004604 libipw_assoc_response
James Ketrenosea2b26e2005-08-24 21:25:16 -05004605 *resp;
4606 resp =
4607 (struct
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04004608 libipw_assoc_response
James Ketrenosea2b26e2005-08-24 21:25:16 -05004609 *)&notif->u.raw;
4610 IPW_DEBUG(IPW_DL_NOTIF |
4611 IPW_DL_STATE |
4612 IPW_DL_ASSOC,
4613 "association failed (0x%04X): %s\n",
Al Viro83f7d572008-03-16 22:26:44 +00004614 le16_to_cpu(resp->status),
James Ketrenosea2b26e2005-08-24 21:25:16 -05004615 ipw_get_status_code
Al Viro83f7d572008-03-16 22:26:44 +00004616 (le16_to_cpu
James Ketrenosea2b26e2005-08-24 21:25:16 -05004617 (resp->status)));
4618 }
4619
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004620 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4621 IPW_DL_ASSOC,
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07004622 "disassociated: '%*pE' %pM\n",
4623 priv->essid_len, priv->essid,
Johannes Berge1749612008-10-27 15:59:26 -07004624 priv->bssid);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004625
4626 priv->status &=
4627 ~(STATUS_DISASSOCIATING |
4628 STATUS_ASSOCIATING |
4629 STATUS_ASSOCIATED | STATUS_AUTH);
James Ketrenosb095c382005-08-24 22:04:42 -05004630 if (priv->assoc_network
4631 && (priv->assoc_network->
4632 capability &
4633 WLAN_CAPABILITY_IBSS))
4634 ipw_remove_current_network
4635 (priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004636
James Ketrenosa613bff2005-08-24 21:43:11 -05004637 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004638
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004639 break;
4640 }
4641
James Ketrenosb095c382005-08-24 22:04:42 -05004642 case CMAS_RX_ASSOC_RESP:
4643 break;
4644
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004645 default:
4646 IPW_ERROR("assoc: unknown (%d)\n",
4647 assoc->state);
4648 break;
4649 }
4650
James Ketrenos43f66a62005-03-25 12:31:53 -06004651 break;
4652 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004653
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004654 case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
4655 struct notif_authenticate *auth = &notif->u.auth;
4656 switch (auth->state) {
4657 case CMAS_AUTHENTICATED:
4658 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07004659 "authenticated: '%*pE' %pM\n",
4660 priv->essid_len, priv->essid,
Johannes Berge1749612008-10-27 15:59:26 -07004661 priv->bssid);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004662 priv->status |= STATUS_AUTH;
4663 break;
4664
4665 case CMAS_INIT:
4666 if (priv->status & STATUS_AUTH) {
4667 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4668 IPW_DL_ASSOC,
4669 "authentication failed (0x%04X): %s\n",
Al Viro83f7d572008-03-16 22:26:44 +00004670 le16_to_cpu(auth->status),
4671 ipw_get_status_code(le16_to_cpu
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004672 (auth->
4673 status)));
4674 }
4675 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4676 IPW_DL_ASSOC,
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07004677 "deauthenticated: '%*pE' %pM\n",
4678 priv->essid_len, priv->essid,
Johannes Berge1749612008-10-27 15:59:26 -07004679 priv->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06004680
4681 priv->status &= ~(STATUS_ASSOCIATING |
4682 STATUS_AUTH |
4683 STATUS_ASSOCIATED);
4684
James Ketrenosa613bff2005-08-24 21:43:11 -05004685 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06004686 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004687
4688 case CMAS_TX_AUTH_SEQ_1:
4689 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4690 IPW_DL_ASSOC, "AUTH_SEQ_1\n");
4691 break;
4692 case CMAS_RX_AUTH_SEQ_2:
4693 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4694 IPW_DL_ASSOC, "AUTH_SEQ_2\n");
4695 break;
4696 case CMAS_AUTH_SEQ_1_PASS:
4697 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4698 IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
4699 break;
4700 case CMAS_AUTH_SEQ_1_FAIL:
4701 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4702 IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
4703 break;
4704 case CMAS_TX_AUTH_SEQ_3:
4705 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4706 IPW_DL_ASSOC, "AUTH_SEQ_3\n");
4707 break;
4708 case CMAS_RX_AUTH_SEQ_4:
4709 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4710 IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
4711 break;
4712 case CMAS_AUTH_SEQ_2_PASS:
4713 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4714 IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
4715 break;
4716 case CMAS_AUTH_SEQ_2_FAIL:
4717 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4718 IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
4719 break;
4720 case CMAS_TX_ASSOC:
4721 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4722 IPW_DL_ASSOC, "TX_ASSOC\n");
4723 break;
4724 case CMAS_RX_ASSOC_RESP:
4725 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4726 IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
James Ketrenosb095c382005-08-24 22:04:42 -05004727
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004728 break;
4729 case CMAS_ASSOCIATED:
4730 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4731 IPW_DL_ASSOC, "ASSOCIATED\n");
4732 break;
4733 default:
4734 IPW_DEBUG_NOTIF("auth: failure - %d\n",
4735 auth->state);
4736 break;
4737 }
4738 break;
4739 }
4740
4741 case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
4742 struct notif_channel_result *x =
4743 &notif->u.channel_result;
4744
Al Viroe62e1ee2007-12-27 01:36:46 -05004745 if (size == sizeof(*x)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004746 IPW_DEBUG_SCAN("Scan result for channel %d\n",
4747 x->channel_num);
4748 } else {
4749 IPW_DEBUG_SCAN("Scan result of wrong size %d "
4750 "(should be %zd)\n",
Al Viroe62e1ee2007-12-27 01:36:46 -05004751 size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004752 }
4753 break;
4754 }
4755
4756 case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
4757 struct notif_scan_complete *x = &notif->u.scan_complete;
Al Viroe62e1ee2007-12-27 01:36:46 -05004758 if (size == sizeof(*x)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004759 IPW_DEBUG_SCAN
4760 ("Scan completed: type %d, %d channels, "
4761 "%d status\n", x->scan_type,
4762 x->num_channels, x->status);
4763 } else {
4764 IPW_ERROR("Scan completed of wrong size %d "
4765 "(should be %zd)\n",
Al Viroe62e1ee2007-12-27 01:36:46 -05004766 size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004767 }
4768
4769 priv->status &=
4770 ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
4771
James Ketrenosa0e04ab2005-08-25 00:49:43 -05004772 wake_up_interruptible(&priv->wait_state);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004773 cancel_delayed_work(&priv->scan_check);
4774
James Ketrenosb095c382005-08-24 22:04:42 -05004775 if (priv->status & STATUS_EXIT_PENDING)
4776 break;
4777
4778 priv->ieee->scans++;
4779
4780#ifdef CONFIG_IPW2200_MONITOR
4781 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05004782 priv->status |= STATUS_SCAN_FORCED;
Tejun Heobcb6d912011-01-26 12:12:50 +01004783 schedule_delayed_work(&priv->request_scan, 0);
James Ketrenosb095c382005-08-24 22:04:42 -05004784 break;
4785 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05004786 priv->status &= ~STATUS_SCAN_FORCED;
James Ketrenosb095c382005-08-24 22:04:42 -05004787#endif /* CONFIG_IPW2200_MONITOR */
4788
Dan Williamsea177302008-06-02 17:51:23 -04004789 /* Do queued direct scans first */
Tejun Heobcb6d912011-01-26 12:12:50 +01004790 if (priv->status & STATUS_DIRECT_SCAN_PENDING)
4791 schedule_delayed_work(&priv->request_direct_scan, 0);
Dan Williamsea177302008-06-02 17:51:23 -04004792
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004793 if (!(priv->status & (STATUS_ASSOCIATED |
4794 STATUS_ASSOCIATING |
4795 STATUS_ROAMING |
4796 STATUS_DISASSOCIATING)))
Tejun Heobcb6d912011-01-26 12:12:50 +01004797 schedule_work(&priv->associate);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004798 else if (priv->status & STATUS_ROAMING) {
Ben Cahille7582562005-10-06 15:34:41 -05004799 if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
4800 /* If a scan completed and we are in roam mode, then
4801 * the scan that completed was the one requested as a
4802 * result of entering roam... so, schedule the
4803 * roam work */
Tejun Heobcb6d912011-01-26 12:12:50 +01004804 schedule_work(&priv->roam);
Ben Cahille7582562005-10-06 15:34:41 -05004805 else
4806 /* Don't schedule if we aborted the scan */
4807 priv->status &= ~STATUS_ROAMING;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004808 } else if (priv->status & STATUS_SCAN_PENDING)
Tejun Heobcb6d912011-01-26 12:12:50 +01004809 schedule_delayed_work(&priv->request_scan, 0);
James Ketrenosa613bff2005-08-24 21:43:11 -05004810 else if (priv->config & CFG_BACKGROUND_SCAN
4811 && priv->status & STATUS_ASSOCIATED)
Tejun Heobcb6d912011-01-26 12:12:50 +01004812 schedule_delayed_work(&priv->request_scan,
4813 round_jiffies_relative(HZ));
Zhu Yi07f02e42006-04-13 17:19:25 +08004814
4815 /* Send an empty event to user space.
4816 * We don't send the received data on the event because
4817 * it would require us to do complex transcoding, and
4818 * we want to minimise the work done in the irq handler
4819 * Use a request to extract the data.
4820 * Also, we generate this even for any scan, regardless
4821 * on how the scan was initiated. User space can just
4822 * sync on periodic scan to get fresh data...
4823 * Jean II */
Dan Williams0b531672007-10-09 13:55:24 -04004824 if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
4825 handle_scan_event(priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004826 break;
4827 }
4828
4829 case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
4830 struct notif_frag_length *x = &notif->u.frag_len;
4831
Al Viroe62e1ee2007-12-27 01:36:46 -05004832 if (size == sizeof(*x))
James Ketrenosa613bff2005-08-24 21:43:11 -05004833 IPW_ERROR("Frag length: %d\n",
4834 le16_to_cpu(x->frag_length));
4835 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004836 IPW_ERROR("Frag length of wrong size %d "
4837 "(should be %zd)\n",
Al Viroe62e1ee2007-12-27 01:36:46 -05004838 size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004839 break;
4840 }
4841
4842 case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
4843 struct notif_link_deterioration *x =
4844 &notif->u.link_deterioration;
James Ketrenosafbf30a2005-08-25 00:05:33 -05004845
Al Viroe62e1ee2007-12-27 01:36:46 -05004846 if (size == sizeof(*x)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004847 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
Cahill, Ben M12977152006-03-08 02:58:02 +08004848 "link deterioration: type %d, cnt %d\n",
4849 x->silence_notification_type,
4850 x->silence_count);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004851 memcpy(&priv->last_link_deterioration, x,
4852 sizeof(*x));
4853 } else {
4854 IPW_ERROR("Link Deterioration of wrong size %d "
4855 "(should be %zd)\n",
Al Viroe62e1ee2007-12-27 01:36:46 -05004856 size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004857 }
4858 break;
4859 }
4860
4861 case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
4862 IPW_ERROR("Dino config\n");
4863 if (priv->hcmd
James Ketrenosa613bff2005-08-24 21:43:11 -05004864 && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004865 IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05004866
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004867 break;
4868 }
4869
4870 case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
4871 struct notif_beacon_state *x = &notif->u.beacon_state;
Al Viroe62e1ee2007-12-27 01:36:46 -05004872 if (size != sizeof(*x)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004873 IPW_ERROR
4874 ("Beacon state of wrong size %d (should "
Al Viroe62e1ee2007-12-27 01:36:46 -05004875 "be %zd)\n", size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004876 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004877 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004878
James Ketrenosa613bff2005-08-24 21:43:11 -05004879 if (le32_to_cpu(x->state) ==
4880 HOST_NOTIFICATION_STATUS_BEACON_MISSING)
4881 ipw_handle_missed_beacon(priv,
4882 le32_to_cpu(x->
4883 number));
Jeff Garzikbf794512005-07-31 13:07:26 -04004884
James Ketrenos43f66a62005-03-25 12:31:53 -06004885 break;
4886 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004887
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004888 case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
4889 struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
Al Viroe62e1ee2007-12-27 01:36:46 -05004890 if (size == sizeof(*x)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004891 IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
4892 "0x%02x station %d\n",
4893 x->key_state, x->security_type,
4894 x->station_index);
4895 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004896 }
4897
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004898 IPW_ERROR
4899 ("TGi Tx Key of wrong size %d (should be %zd)\n",
Al Viroe62e1ee2007-12-27 01:36:46 -05004900 size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004901 break;
4902 }
4903
4904 case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
4905 struct notif_calibration *x = &notif->u.calibration;
4906
Al Viroe62e1ee2007-12-27 01:36:46 -05004907 if (size == sizeof(*x)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004908 memcpy(&priv->calib, x, sizeof(*x));
4909 IPW_DEBUG_INFO("TODO: Calibration\n");
4910 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004911 }
4912
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004913 IPW_ERROR
4914 ("Calibration of wrong size %d (should be %zd)\n",
Al Viroe62e1ee2007-12-27 01:36:46 -05004915 size, sizeof(*x));
James Ketrenos43f66a62005-03-25 12:31:53 -06004916 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004917 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004918
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004919 case HOST_NOTIFICATION_NOISE_STATS:{
Al Viroe62e1ee2007-12-27 01:36:46 -05004920 if (size == sizeof(u32)) {
Zhu Yi00d21de2006-04-13 17:19:02 +08004921 priv->exp_avg_noise =
4922 exponential_average(priv->exp_avg_noise,
4923 (u8) (le32_to_cpu(notif->u.noise.value) & 0xff),
4924 DEPTH_NOISE);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004925 break;
4926 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004927
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004928 IPW_ERROR
4929 ("Noise stat is wrong size %d (should be %zd)\n",
Al Viroe62e1ee2007-12-27 01:36:46 -05004930 size, sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -06004931 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004932 }
4933
James Ketrenos43f66a62005-03-25 12:31:53 -06004934 default:
Zhu Yi1dd31b62006-02-20 18:28:09 -08004935 IPW_DEBUG_NOTIF("Unknown notification: "
4936 "subtype=%d,flags=0x%2x,size=%d\n",
Al Viroe62e1ee2007-12-27 01:36:46 -05004937 notif->subtype, notif->flags, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06004938 }
4939}
4940
4941/**
4942 * Destroys all DMA structures and initialise them again
Jeff Garzikbf794512005-07-31 13:07:26 -04004943 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004944 * @param priv
4945 * @return error code
4946 */
4947static int ipw_queue_reset(struct ipw_priv *priv)
4948{
4949 int rc = 0;
4950 /** @todo customize queue sizes */
4951 int nTx = 64, nTxCmd = 8;
4952 ipw_tx_queue_free(priv);
4953 /* Tx CMD queue */
4954 rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
James Ketrenosb095c382005-08-24 22:04:42 -05004955 IPW_TX_CMD_QUEUE_READ_INDEX,
4956 IPW_TX_CMD_QUEUE_WRITE_INDEX,
4957 IPW_TX_CMD_QUEUE_BD_BASE,
4958 IPW_TX_CMD_QUEUE_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004959 if (rc) {
4960 IPW_ERROR("Tx Cmd queue init failed\n");
4961 goto error;
4962 }
4963 /* Tx queue(s) */
4964 rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004965 IPW_TX_QUEUE_0_READ_INDEX,
4966 IPW_TX_QUEUE_0_WRITE_INDEX,
4967 IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004968 if (rc) {
4969 IPW_ERROR("Tx 0 queue init failed\n");
4970 goto error;
4971 }
4972 rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004973 IPW_TX_QUEUE_1_READ_INDEX,
4974 IPW_TX_QUEUE_1_WRITE_INDEX,
4975 IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004976 if (rc) {
4977 IPW_ERROR("Tx 1 queue init failed\n");
4978 goto error;
4979 }
4980 rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004981 IPW_TX_QUEUE_2_READ_INDEX,
4982 IPW_TX_QUEUE_2_WRITE_INDEX,
4983 IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004984 if (rc) {
4985 IPW_ERROR("Tx 2 queue init failed\n");
4986 goto error;
4987 }
4988 rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004989 IPW_TX_QUEUE_3_READ_INDEX,
4990 IPW_TX_QUEUE_3_WRITE_INDEX,
4991 IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004992 if (rc) {
4993 IPW_ERROR("Tx 3 queue init failed\n");
4994 goto error;
4995 }
4996 /* statistics */
4997 priv->rx_bufs_min = 0;
4998 priv->rx_pend_max = 0;
4999 return rc;
5000
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005001 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06005002 ipw_tx_queue_free(priv);
5003 return rc;
5004}
5005
5006/**
5007 * Reclaim Tx queue entries no more used by NIC.
Jeff Garzikbf794512005-07-31 13:07:26 -04005008 *
Stefano Brivio8ff9d212008-01-12 23:12:26 +01005009 * When FW advances 'R' index, all entries between old and
James Ketrenos43f66a62005-03-25 12:31:53 -06005010 * new 'R' index need to be reclaimed. As result, some free space
5011 * forms. If there is enough free space (> low mark), wake Tx queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04005012 *
James Ketrenos43f66a62005-03-25 12:31:53 -06005013 * @note Need to protect against garbage in 'R' index
5014 * @param priv
5015 * @param txq
5016 * @param qindex
5017 * @return Number of used entries remains in the queue
5018 */
Jeff Garzikbf794512005-07-31 13:07:26 -04005019static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06005020 struct clx2_tx_queue *txq, int qindex)
5021{
5022 u32 hw_tail;
5023 int used;
5024 struct clx2_queue *q = &txq->q;
5025
5026 hw_tail = ipw_read32(priv, q->reg_r);
5027 if (hw_tail >= q->n_bd) {
5028 IPW_ERROR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005029 ("Read index for DMA queue (%d) is out of range [0-%d)\n",
5030 hw_tail, q->n_bd);
James Ketrenos43f66a62005-03-25 12:31:53 -06005031 goto done;
5032 }
5033 for (; q->last_used != hw_tail;
5034 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
5035 ipw_queue_tx_free_tfd(priv, txq);
5036 priv->tx_packets++;
5037 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005038 done:
Dan Williams943dbef2008-02-14 17:49:41 -05005039 if ((ipw_tx_queue_space(q) > q->low_mark) &&
David S. Miller521c4d92008-07-22 18:32:47 -07005040 (qindex >= 0))
James Ketrenos9ddf84f2005-08-16 17:07:11 -05005041 netif_wake_queue(priv->net_dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06005042 used = q->first_empty - q->last_used;
5043 if (used < 0)
5044 used += q->n_bd;
5045
5046 return used;
5047}
5048
5049static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
5050 int len, int sync)
5051{
5052 struct clx2_tx_queue *txq = &priv->txq_cmd;
5053 struct clx2_queue *q = &txq->q;
5054 struct tfd_frame *tfd;
5055
Dan Williams943dbef2008-02-14 17:49:41 -05005056 if (ipw_tx_queue_space(q) < (sync ? 1 : 2)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005057 IPW_ERROR("No space for Tx\n");
5058 return -EBUSY;
5059 }
5060
5061 tfd = &txq->bd[q->first_empty];
5062 txq->txb[q->first_empty] = NULL;
5063
5064 memset(tfd, 0, sizeof(*tfd));
5065 tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
5066 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
5067 priv->hcmd_seq++;
5068 tfd->u.cmd.index = hcmd;
5069 tfd->u.cmd.length = len;
5070 memcpy(tfd->u.cmd.payload, buf, len);
5071 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
5072 ipw_write32(priv, q->reg_w, q->first_empty);
5073 _ipw_read32(priv, 0x90);
5074
5075 return 0;
5076}
5077
Jeff Garzikbf794512005-07-31 13:07:26 -04005078/*
James Ketrenos43f66a62005-03-25 12:31:53 -06005079 * Rx theory of operation
5080 *
5081 * The host allocates 32 DMA target addresses and passes the host address
James Ketrenosb095c382005-08-24 22:04:42 -05005082 * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
James Ketrenos43f66a62005-03-25 12:31:53 -06005083 * 0 to 31
5084 *
5085 * Rx Queue Indexes
5086 * The host/firmware share two index registers for managing the Rx buffers.
5087 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005088 * The READ index maps to the first position that the firmware may be writing
5089 * to -- the driver can read up to (but not including) this position and get
5090 * good data.
James Ketrenos43f66a62005-03-25 12:31:53 -06005091 * The READ index is managed by the firmware once the card is enabled.
5092 *
5093 * The WRITE index maps to the last position the driver has read from -- the
5094 * position preceding WRITE is the last slot the firmware can place a packet.
5095 *
5096 * The queue is empty (no good data) if WRITE = READ - 1, and is full if
Jeff Garzikbf794512005-07-31 13:07:26 -04005097 * WRITE = READ.
James Ketrenos43f66a62005-03-25 12:31:53 -06005098 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005099 * During initialization the host sets up the READ queue position to the first
James Ketrenos43f66a62005-03-25 12:31:53 -06005100 * INDEX position, and WRITE to the last (READ - 1 wrapped)
5101 *
5102 * When the firmware places a packet in a buffer it will advance the READ index
5103 * and fire the RX interrupt. The driver can then query the READ index and
5104 * process as many packets as possible, moving the WRITE index forward as it
5105 * resets the Rx queue buffers with new memory.
Jeff Garzikbf794512005-07-31 13:07:26 -04005106 *
James Ketrenos43f66a62005-03-25 12:31:53 -06005107 * The management in the driver is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04005108 * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
James Ketrenos43f66a62005-03-25 12:31:53 -06005109 * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
Jeff Garzikbf794512005-07-31 13:07:26 -04005110 * to replensish the ipw->rxq->rx_free.
James Ketrenos43f66a62005-03-25 12:31:53 -06005111 * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
5112 * ipw->rxq is replenished and the READ INDEX is updated (updating the
5113 * 'processed' and 'read' driver indexes as well)
5114 * + A received packet is processed and handed to the kernel network stack,
5115 * detached from the ipw->rxq. The driver 'processed' index is updated.
5116 * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
Jeff Garzikbf794512005-07-31 13:07:26 -04005117 * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
5118 * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
James Ketrenos43f66a62005-03-25 12:31:53 -06005119 * were enough free buffers and RX_STALLED is set it is cleared.
5120 *
5121 *
5122 * Driver sequence:
5123 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005124 * ipw_rx_queue_alloc() Allocates rx_free
James Ketrenos43f66a62005-03-25 12:31:53 -06005125 * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
5126 * ipw_rx_queue_restock
5127 * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
5128 * queue, updates firmware pointers, and updates
5129 * the WRITE index. If insufficient rx_free buffers
5130 * are available, schedules ipw_rx_queue_replenish
5131 *
5132 * -- enable interrupts --
5133 * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
Jeff Garzikbf794512005-07-31 13:07:26 -04005134 * READ INDEX, detaching the SKB from the pool.
James Ketrenos43f66a62005-03-25 12:31:53 -06005135 * Moves the packet buffer from queue to rx_used.
5136 * Calls ipw_rx_queue_restock to refill any empty
5137 * slots.
5138 * ...
5139 *
5140 */
5141
Jeff Garzikbf794512005-07-31 13:07:26 -04005142/*
James Ketrenos43f66a62005-03-25 12:31:53 -06005143 * If there are slots in the RX queue that need to be restocked,
5144 * and we have free pre-allocated buffers, fill the ranks as much
5145 * as we can pulling from rx_free.
5146 *
5147 * This moves the 'write' index forward to catch up with 'processed', and
5148 * also updates the memory address in the firmware to reference the new
5149 * target buffer.
5150 */
5151static void ipw_rx_queue_restock(struct ipw_priv *priv)
5152{
5153 struct ipw_rx_queue *rxq = priv->rxq;
5154 struct list_head *element;
5155 struct ipw_rx_mem_buffer *rxb;
5156 unsigned long flags;
5157 int write;
5158
5159 spin_lock_irqsave(&rxq->lock, flags);
5160 write = rxq->write;
Dan Williams943dbef2008-02-14 17:49:41 -05005161 while ((ipw_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005162 element = rxq->rx_free.next;
5163 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
5164 list_del(element);
5165
James Ketrenosb095c382005-08-24 22:04:42 -05005166 ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06005167 rxb->dma_addr);
5168 rxq->queue[rxq->write] = rxb;
5169 rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
5170 rxq->free_count--;
5171 }
5172 spin_unlock_irqrestore(&rxq->lock, flags);
5173
Jeff Garzikbf794512005-07-31 13:07:26 -04005174 /* If the pre-allocated buffer pool is dropping low, schedule to
James Ketrenos43f66a62005-03-25 12:31:53 -06005175 * refill it */
5176 if (rxq->free_count <= RX_LOW_WATERMARK)
Tejun Heobcb6d912011-01-26 12:12:50 +01005177 schedule_work(&priv->rx_replenish);
James Ketrenos43f66a62005-03-25 12:31:53 -06005178
5179 /* If we've added more space for the firmware to place data, tell it */
Jeff Garzikbf794512005-07-31 13:07:26 -04005180 if (write != rxq->write)
James Ketrenosb095c382005-08-24 22:04:42 -05005181 ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
James Ketrenos43f66a62005-03-25 12:31:53 -06005182}
5183
5184/*
5185 * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
Jeff Garzikbf794512005-07-31 13:07:26 -04005186 * Also restock the Rx queue via ipw_rx_queue_restock.
5187 *
Masahiro Yamada183b8022017-02-27 14:29:20 -08005188 * This is called as a scheduled work item (except for during initialization)
James Ketrenos43f66a62005-03-25 12:31:53 -06005189 */
5190static void ipw_rx_queue_replenish(void *data)
5191{
5192 struct ipw_priv *priv = data;
5193 struct ipw_rx_queue *rxq = priv->rxq;
5194 struct list_head *element;
5195 struct ipw_rx_mem_buffer *rxb;
5196 unsigned long flags;
5197
5198 spin_lock_irqsave(&rxq->lock, flags);
5199 while (!list_empty(&rxq->rx_used)) {
5200 element = rxq->rx_used.next;
5201 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
James Ketrenosb095c382005-08-24 22:04:42 -05005202 rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC);
James Ketrenos43f66a62005-03-25 12:31:53 -06005203 if (!rxb->skb) {
5204 printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
5205 priv->net_dev->name);
5206 /* We don't reschedule replenish work here -- we will
5207 * call the restock method and if it still needs
5208 * more buffers it will schedule replenish */
5209 break;
5210 }
5211 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04005212
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005213 rxb->dma_addr =
5214 pci_map_single(priv->pci_dev, rxb->skb->data,
James Ketrenosb095c382005-08-24 22:04:42 -05005215 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
Jeff Garzikbf794512005-07-31 13:07:26 -04005216
James Ketrenos43f66a62005-03-25 12:31:53 -06005217 list_add_tail(&rxb->list, &rxq->rx_free);
5218 rxq->free_count++;
5219 }
5220 spin_unlock_irqrestore(&rxq->lock, flags);
5221
5222 ipw_rx_queue_restock(priv);
5223}
5224
David Howellsc4028952006-11-22 14:57:56 +00005225static void ipw_bg_rx_queue_replenish(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05005226{
David Howellsc4028952006-11-22 14:57:56 +00005227 struct ipw_priv *priv =
5228 container_of(work, struct ipw_priv, rx_replenish);
Zhu Yi46441512006-01-24 16:37:59 +08005229 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00005230 ipw_rx_queue_replenish(priv);
Zhu Yi46441512006-01-24 16:37:59 +08005231 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005232}
5233
James Ketrenos43f66a62005-03-25 12:31:53 -06005234/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
Zhu Yic7b6a672006-01-24 16:37:05 +08005235 * If an SKB has been detached, the POOL needs to have its SKB set to NULL
Jeff Garzikbf794512005-07-31 13:07:26 -04005236 * This free routine walks the list of POOL entries and if SKB is set to
James Ketrenos43f66a62005-03-25 12:31:53 -06005237 * non NULL it is unmapped and freed
5238 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005239static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
James Ketrenos43f66a62005-03-25 12:31:53 -06005240{
5241 int i;
5242
5243 if (!rxq)
5244 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04005245
James Ketrenos43f66a62005-03-25 12:31:53 -06005246 for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
5247 if (rxq->pool[i].skb != NULL) {
5248 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05005249 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06005250 dev_kfree_skb(rxq->pool[i].skb);
5251 }
5252 }
5253
5254 kfree(rxq);
5255}
5256
5257static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
5258{
5259 struct ipw_rx_queue *rxq;
5260 int i;
5261
Takisc75f4742005-12-01 01:41:45 -08005262 rxq = kzalloc(sizeof(*rxq), GFP_KERNEL);
Panagiotis Issarisad18b0e2005-09-05 04:14:10 +02005263 if (unlikely(!rxq)) {
5264 IPW_ERROR("memory allocation failed\n");
5265 return NULL;
5266 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005267 spin_lock_init(&rxq->lock);
5268 INIT_LIST_HEAD(&rxq->rx_free);
5269 INIT_LIST_HEAD(&rxq->rx_used);
5270
5271 /* Fill the rx_used queue with _all_ of the Rx buffers */
Jeff Garzikbf794512005-07-31 13:07:26 -04005272 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06005273 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
5274
5275 /* Set us so that we have processed and used all buffers, but have
5276 * not restocked the Rx queue with fresh buffers */
5277 rxq->read = rxq->write = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005278 rxq->free_count = 0;
5279
5280 return rxq;
5281}
5282
5283static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
5284{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005285 rate &= ~LIBIPW_BASIC_RATE_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06005286 if (ieee_mode == IEEE_A) {
5287 switch (rate) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005288 case LIBIPW_OFDM_RATE_6MB:
5289 return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005290 1 : 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005291 case LIBIPW_OFDM_RATE_9MB:
5292 return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005293 1 : 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005294 case LIBIPW_OFDM_RATE_12MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005295 return priv->
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005296 rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0;
5297 case LIBIPW_OFDM_RATE_18MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005298 return priv->
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005299 rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0;
5300 case LIBIPW_OFDM_RATE_24MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005301 return priv->
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005302 rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0;
5303 case LIBIPW_OFDM_RATE_36MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005304 return priv->
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005305 rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0;
5306 case LIBIPW_OFDM_RATE_48MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005307 return priv->
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005308 rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0;
5309 case LIBIPW_OFDM_RATE_54MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005310 return priv->
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005311 rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005312 default:
5313 return 0;
5314 }
5315 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005316
James Ketrenos43f66a62005-03-25 12:31:53 -06005317 /* B and G mixed */
5318 switch (rate) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005319 case LIBIPW_CCK_RATE_1MB:
5320 return priv->rates_mask & LIBIPW_CCK_RATE_1MB_MASK ? 1 : 0;
5321 case LIBIPW_CCK_RATE_2MB:
5322 return priv->rates_mask & LIBIPW_CCK_RATE_2MB_MASK ? 1 : 0;
5323 case LIBIPW_CCK_RATE_5MB:
5324 return priv->rates_mask & LIBIPW_CCK_RATE_5MB_MASK ? 1 : 0;
5325 case LIBIPW_CCK_RATE_11MB:
5326 return priv->rates_mask & LIBIPW_CCK_RATE_11MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005327 }
5328
5329 /* If we are limited to B modulations, bail at this point */
5330 if (ieee_mode == IEEE_B)
5331 return 0;
5332
5333 /* G */
5334 switch (rate) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005335 case LIBIPW_OFDM_RATE_6MB:
5336 return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? 1 : 0;
5337 case LIBIPW_OFDM_RATE_9MB:
5338 return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? 1 : 0;
5339 case LIBIPW_OFDM_RATE_12MB:
5340 return priv->rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0;
5341 case LIBIPW_OFDM_RATE_18MB:
5342 return priv->rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0;
5343 case LIBIPW_OFDM_RATE_24MB:
5344 return priv->rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0;
5345 case LIBIPW_OFDM_RATE_36MB:
5346 return priv->rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0;
5347 case LIBIPW_OFDM_RATE_48MB:
5348 return priv->rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0;
5349 case LIBIPW_OFDM_RATE_54MB:
5350 return priv->rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005351 }
5352
5353 return 0;
5354}
5355
Jeff Garzikbf794512005-07-31 13:07:26 -04005356static int ipw_compatible_rates(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005357 const struct libipw_network *network,
James Ketrenos43f66a62005-03-25 12:31:53 -06005358 struct ipw_supported_rates *rates)
5359{
5360 int num_rates, i;
5361
5362 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005363 num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06005364 rates->num_rates = 0;
5365 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005366 if (!ipw_is_rate_in_mask(priv, network->mode,
5367 network->rates[i])) {
5368
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005369 if (network->rates[i] & LIBIPW_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005370 IPW_DEBUG_SCAN("Adding masked mandatory "
5371 "rate %02X\n",
5372 network->rates[i]);
5373 rates->supported_rates[rates->num_rates++] =
5374 network->rates[i];
5375 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005376 }
5377
James Ketrenos43f66a62005-03-25 12:31:53 -06005378 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
5379 network->rates[i], priv->rates_mask);
5380 continue;
5381 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005382
James Ketrenos43f66a62005-03-25 12:31:53 -06005383 rates->supported_rates[rates->num_rates++] = network->rates[i];
5384 }
5385
James Ketrenosa613bff2005-08-24 21:43:11 -05005386 num_rates = min(network->rates_ex_len,
5387 (u8) (IPW_MAX_RATES - num_rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06005388 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005389 if (!ipw_is_rate_in_mask(priv, network->mode,
5390 network->rates_ex[i])) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005391 if (network->rates_ex[i] & LIBIPW_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005392 IPW_DEBUG_SCAN("Adding masked mandatory "
5393 "rate %02X\n",
5394 network->rates_ex[i]);
5395 rates->supported_rates[rates->num_rates++] =
5396 network->rates[i];
5397 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005398 }
5399
James Ketrenos43f66a62005-03-25 12:31:53 -06005400 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
5401 network->rates_ex[i], priv->rates_mask);
5402 continue;
5403 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005404
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005405 rates->supported_rates[rates->num_rates++] =
5406 network->rates_ex[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06005407 }
5408
James Ketrenosea2b26e2005-08-24 21:25:16 -05005409 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06005410}
5411
Arjan van de Ven858119e2006-01-14 13:20:43 -08005412static void ipw_copy_rates(struct ipw_supported_rates *dest,
James Ketrenos43f66a62005-03-25 12:31:53 -06005413 const struct ipw_supported_rates *src)
5414{
5415 u8 i;
5416 for (i = 0; i < src->num_rates; i++)
5417 dest->supported_rates[i] = src->supported_rates[i];
5418 dest->num_rates = src->num_rates;
5419}
5420
5421/* TODO: Look at sniffed packets in the air to determine if the basic rate
5422 * mask should ever be used -- right now all callers to add the scan rates are
5423 * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
5424static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005425 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06005426{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005427 u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ?
5428 LIBIPW_BASIC_RATE_MASK : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005429
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005430 if (rate_mask & LIBIPW_CCK_RATE_1MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005431 rates->supported_rates[rates->num_rates++] =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005432 LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_1MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005433
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005434 if (rate_mask & LIBIPW_CCK_RATE_2MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005435 rates->supported_rates[rates->num_rates++] =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005436 LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_2MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005437
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005438 if (rate_mask & LIBIPW_CCK_RATE_5MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005439 rates->supported_rates[rates->num_rates++] = basic_mask |
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005440 LIBIPW_CCK_RATE_5MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005441
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005442 if (rate_mask & LIBIPW_CCK_RATE_11MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005443 rates->supported_rates[rates->num_rates++] = basic_mask |
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005444 LIBIPW_CCK_RATE_11MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005445}
5446
5447static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005448 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06005449{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005450 u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ?
5451 LIBIPW_BASIC_RATE_MASK : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005452
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005453 if (rate_mask & LIBIPW_OFDM_RATE_6MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005454 rates->supported_rates[rates->num_rates++] = basic_mask |
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005455 LIBIPW_OFDM_RATE_6MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005456
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005457 if (rate_mask & LIBIPW_OFDM_RATE_9MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005458 rates->supported_rates[rates->num_rates++] =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005459 LIBIPW_OFDM_RATE_9MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005460
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005461 if (rate_mask & LIBIPW_OFDM_RATE_12MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005462 rates->supported_rates[rates->num_rates++] = basic_mask |
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005463 LIBIPW_OFDM_RATE_12MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005464
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005465 if (rate_mask & LIBIPW_OFDM_RATE_18MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005466 rates->supported_rates[rates->num_rates++] =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005467 LIBIPW_OFDM_RATE_18MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005468
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005469 if (rate_mask & LIBIPW_OFDM_RATE_24MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005470 rates->supported_rates[rates->num_rates++] = basic_mask |
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005471 LIBIPW_OFDM_RATE_24MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005472
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005473 if (rate_mask & LIBIPW_OFDM_RATE_36MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005474 rates->supported_rates[rates->num_rates++] =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005475 LIBIPW_OFDM_RATE_36MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005476
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005477 if (rate_mask & LIBIPW_OFDM_RATE_48MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005478 rates->supported_rates[rates->num_rates++] =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005479 LIBIPW_OFDM_RATE_48MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005480
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005481 if (rate_mask & LIBIPW_OFDM_RATE_54MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005482 rates->supported_rates[rates->num_rates++] =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005483 LIBIPW_OFDM_RATE_54MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005484}
5485
5486struct ipw_network_match {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005487 struct libipw_network *network;
James Ketrenos43f66a62005-03-25 12:31:53 -06005488 struct ipw_supported_rates rates;
5489};
5490
James Ketrenosc848d0a2005-08-24 21:56:24 -05005491static int ipw_find_adhoc_network(struct ipw_priv *priv,
5492 struct ipw_network_match *match,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005493 struct libipw_network *network,
James Ketrenosc848d0a2005-08-24 21:56:24 -05005494 int roaming)
5495{
5496 struct ipw_supported_rates rates;
5497
5498 /* Verify that this network's capability is compatible with the
5499 * current mode (AdHoc or Infrastructure) */
5500 if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
5501 !(network->capability & WLAN_CAPABILITY_IBSS))) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005502 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded due to capability mismatch.\n",
5503 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005504 network->bssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005505 return 0;
5506 }
5507
James Ketrenosc848d0a2005-08-24 21:56:24 -05005508 if (unlikely(roaming)) {
5509 /* If we are roaming, then ensure check if this is a valid
5510 * network to try and roam to */
5511 if ((network->ssid_len != match->network->ssid_len) ||
5512 memcmp(network->ssid, match->network->ssid,
5513 network->ssid_len)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005514 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of non-network ESSID.\n",
5515 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005516 network->bssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005517 return 0;
5518 }
5519 } else {
5520 /* If an ESSID has been configured then compare the broadcast
5521 * ESSID to ours */
5522 if ((priv->config & CFG_STATIC_ESSID) &&
5523 ((network->ssid_len != priv->essid_len) ||
5524 memcmp(network->ssid, priv->essid,
5525 min(network->ssid_len, priv->essid_len)))) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005526 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n",
5527 network->ssid_len, network->ssid,
5528 network->bssid, priv->essid_len,
5529 priv->essid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005530 return 0;
5531 }
5532 }
5533
5534 /* If the old network rate is better than this one, don't bother
5535 * testing everything else. */
5536
5537 if (network->time_stamp[0] < match->network->time_stamp[0]) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005538 IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n",
5539 match->network->ssid_len, match->network->ssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005540 return 0;
5541 } else if (network->time_stamp[1] < match->network->time_stamp[1]) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005542 IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n",
5543 match->network->ssid_len, match->network->ssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005544 return 0;
5545 }
5546
5547 /* Now go through and see if the requested network is valid... */
5548 if (priv->ieee->scan_age != 0 &&
5549 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005550 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of age: %ums.\n",
5551 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005552 network->bssid,
Zhu Yi2638bc32006-01-24 16:37:52 +08005553 jiffies_to_msecs(jiffies -
5554 network->last_scanned));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005555 return 0;
5556 }
5557
5558 if ((priv->config & CFG_STATIC_CHANNEL) &&
5559 (network->channel != priv->channel)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005560 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n",
5561 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005562 network->bssid,
James Ketrenosc848d0a2005-08-24 21:56:24 -05005563 network->channel, priv->channel);
5564 return 0;
5565 }
5566
Lucas De Marchi25985ed2011-03-30 22:57:33 -03005567 /* Verify privacy compatibility */
James Ketrenosc848d0a2005-08-24 21:56:24 -05005568 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
5569 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005570 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of privacy mismatch: %s != %s.\n",
5571 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005572 network->bssid,
James Ketrenosafbf30a2005-08-25 00:05:33 -05005573 priv->
5574 capability & CAP_PRIVACY_ON ? "on" : "off",
5575 network->
5576 capability & WLAN_CAPABILITY_PRIVACY ? "on" :
5577 "off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05005578 return 0;
5579 }
5580
dingtianhong36325f32013-12-26 19:41:23 +08005581 if (ether_addr_equal(network->bssid, priv->bssid)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005582 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of the same BSSID match: %pM.\n",
5583 network->ssid_len, network->ssid,
5584 network->bssid, priv->bssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005585 return 0;
5586 }
5587
5588 /* Filter out any incompatible freq / mode combinations */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005589 if (!libipw_is_valid_mode(priv->ieee, network->mode)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005590 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n",
5591 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005592 network->bssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005593 return 0;
5594 }
5595
5596 /* Ensure that the rates supported by the driver are compatible with
5597 * this AP, including verification of basic rates (mandatory) */
5598 if (!ipw_compatible_rates(priv, network, &rates)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005599 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n",
5600 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005601 network->bssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005602 return 0;
5603 }
5604
5605 if (rates.num_rates == 0) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005606 IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of no compatible rates.\n",
5607 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005608 network->bssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005609 return 0;
5610 }
5611
5612 /* TODO: Perform any further minimal comparititive tests. We do not
5613 * want to put too much policy logic here; intelligent scan selection
5614 * should occur within a generic IEEE 802.11 user space tool. */
5615
5616 /* Set up 'new' AP to this network */
5617 ipw_copy_rates(&match->rates, &rates);
5618 match->network = network;
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005619 IPW_DEBUG_MERGE("Network '%*pE (%pM)' is a viable match.\n",
5620 network->ssid_len, network->ssid, network->bssid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005621
5622 return 1;
5623}
5624
David Howellsc4028952006-11-22 14:57:56 +00005625static void ipw_merge_adhoc_network(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05005626{
David Howellsc4028952006-11-22 14:57:56 +00005627 struct ipw_priv *priv =
5628 container_of(work, struct ipw_priv, merge_networks);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005629 struct libipw_network *network = NULL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05005630 struct ipw_network_match match = {
5631 .network = priv->assoc_network
5632 };
5633
James Ketrenosafbf30a2005-08-25 00:05:33 -05005634 if ((priv->status & STATUS_ASSOCIATED) &&
5635 (priv->ieee->iw_mode == IW_MODE_ADHOC)) {
James Ketrenosc848d0a2005-08-24 21:56:24 -05005636 /* First pass through ROAM process -- look for a better
5637 * network */
5638 unsigned long flags;
5639
5640 spin_lock_irqsave(&priv->ieee->lock, flags);
5641 list_for_each_entry(network, &priv->ieee->network_list, list) {
5642 if (network != priv->assoc_network)
5643 ipw_find_adhoc_network(priv, &match, network,
5644 1);
5645 }
5646 spin_unlock_irqrestore(&priv->ieee->lock, flags);
5647
5648 if (match.network == priv->assoc_network) {
5649 IPW_DEBUG_MERGE("No better ADHOC in this network to "
5650 "merge to.\n");
5651 return;
5652 }
5653
Zhu Yi46441512006-01-24 16:37:59 +08005654 mutex_lock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005655 if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005656 IPW_DEBUG_MERGE("remove network %*pE\n",
5657 priv->essid_len, priv->essid);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005658 ipw_remove_current_network(priv);
5659 }
5660
5661 ipw_disassociate(priv);
5662 priv->assoc_network = match.network;
Zhu Yi46441512006-01-24 16:37:59 +08005663 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05005664 return;
5665 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05005666}
5667
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005668static int ipw_best_network(struct ipw_priv *priv,
5669 struct ipw_network_match *match,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005670 struct libipw_network *network, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06005671{
5672 struct ipw_supported_rates rates;
5673
5674 /* Verify that this network's capability is compatible with the
5675 * current mode (AdHoc or Infrastructure) */
5676 if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
Jouni Malinen24743852005-08-14 20:59:59 -07005677 !(network->capability & WLAN_CAPABILITY_ESS)) ||
James Ketrenos43f66a62005-03-25 12:31:53 -06005678 (priv->ieee->iw_mode == IW_MODE_ADHOC &&
5679 !(network->capability & WLAN_CAPABILITY_IBSS))) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005680 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded due to capability mismatch.\n",
5681 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005682 network->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06005683 return 0;
5684 }
5685
James Ketrenos43f66a62005-03-25 12:31:53 -06005686 if (unlikely(roaming)) {
5687 /* If we are roaming, then ensure check if this is a valid
5688 * network to try and roam to */
5689 if ((network->ssid_len != match->network->ssid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005690 memcmp(network->ssid, match->network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005691 network->ssid_len)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005692 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of non-network ESSID.\n",
5693 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005694 network->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06005695 return 0;
5696 }
5697 } else {
Jeff Garzikbf794512005-07-31 13:07:26 -04005698 /* If an ESSID has been configured then compare the broadcast
5699 * ESSID to ours */
5700 if ((priv->config & CFG_STATIC_ESSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005701 ((network->ssid_len != priv->essid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005702 memcmp(network->ssid, priv->essid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005703 min(network->ssid_len, priv->essid_len)))) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005704 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n",
5705 network->ssid_len, network->ssid,
5706 network->bssid, priv->essid_len,
5707 priv->essid);
James Ketrenos43f66a62005-03-25 12:31:53 -06005708 return 0;
5709 }
5710 }
5711
5712 /* If the old network rate is better than this one, don't bother
5713 * testing everything else. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005714 if (match->network && match->network->stats.rssi > network->stats.rssi) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005715 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because '%*pE (%pM)' has a stronger signal.\n",
5716 network->ssid_len, network->ssid,
5717 network->bssid, match->network->ssid_len,
5718 match->network->ssid, match->network->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06005719 return 0;
5720 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005721
James Ketrenos43f66a62005-03-25 12:31:53 -06005722 /* If this network has already had an association attempt within the
5723 * last 3 seconds, do not try and associate again... */
5724 if (network->last_associate &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005725 time_after(network->last_associate + (HZ * 3UL), jiffies)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005726 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of storming (%ums since last assoc attempt).\n",
5727 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005728 network->bssid,
Zhu Yi2638bc32006-01-24 16:37:52 +08005729 jiffies_to_msecs(jiffies -
5730 network->last_associate));
James Ketrenos43f66a62005-03-25 12:31:53 -06005731 return 0;
5732 }
5733
5734 /* Now go through and see if the requested network is valid... */
Jeff Garzikbf794512005-07-31 13:07:26 -04005735 if (priv->ieee->scan_age != 0 &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005736 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005737 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of age: %ums.\n",
5738 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005739 network->bssid,
Zhu Yi2638bc32006-01-24 16:37:52 +08005740 jiffies_to_msecs(jiffies -
5741 network->last_scanned));
James Ketrenos43f66a62005-03-25 12:31:53 -06005742 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005743 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005744
Jeff Garzikbf794512005-07-31 13:07:26 -04005745 if ((priv->config & CFG_STATIC_CHANNEL) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005746 (network->channel != priv->channel)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005747 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n",
5748 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005749 network->bssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005750 network->channel, priv->channel);
5751 return 0;
5752 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005753
Lucas De Marchi25985ed2011-03-30 22:57:33 -03005754 /* Verify privacy compatibility */
Jeff Garzikbf794512005-07-31 13:07:26 -04005755 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
James Ketrenos43f66a62005-03-25 12:31:53 -06005756 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005757 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of privacy mismatch: %s != %s.\n",
5758 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005759 network->bssid,
Jeff Garzikbf794512005-07-31 13:07:26 -04005760 priv->capability & CAP_PRIVACY_ON ? "on" :
James Ketrenos43f66a62005-03-25 12:31:53 -06005761 "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04005762 network->capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005763 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06005764 return 0;
5765 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005766
5767 if ((priv->config & CFG_STATIC_BSSID) &&
dingtianhong36325f32013-12-26 19:41:23 +08005768 !ether_addr_equal(network->bssid, priv->bssid)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005769 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of BSSID mismatch: %pM.\n",
5770 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005771 network->bssid, priv->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06005772 return 0;
5773 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005774
James Ketrenos43f66a62005-03-25 12:31:53 -06005775 /* Filter out any incompatible freq / mode combinations */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005776 if (!libipw_is_valid_mode(priv->ieee, network->mode)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005777 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n",
5778 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005779 network->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06005780 return 0;
5781 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005782
Liu Hong1fe0adb2005-08-19 09:33:10 -05005783 /* Filter out invalid channel in current GEO */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005784 if (!libipw_is_valid_channel(priv->ieee, network->channel)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005785 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid channel in current GEO\n",
5786 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005787 network->bssid);
Liu Hong1fe0adb2005-08-19 09:33:10 -05005788 return 0;
5789 }
5790
James Ketrenosea2b26e2005-08-24 21:25:16 -05005791 /* Ensure that the rates supported by the driver are compatible with
5792 * this AP, including verification of basic rates (mandatory) */
5793 if (!ipw_compatible_rates(priv, network, &rates)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005794 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n",
5795 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005796 network->bssid);
James Ketrenosea2b26e2005-08-24 21:25:16 -05005797 return 0;
5798 }
5799
James Ketrenos43f66a62005-03-25 12:31:53 -06005800 if (rates.num_rates == 0) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005801 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of no compatible rates.\n",
5802 network->ssid_len, network->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07005803 network->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06005804 return 0;
5805 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005806
James Ketrenos43f66a62005-03-25 12:31:53 -06005807 /* TODO: Perform any further minimal comparititive tests. We do not
5808 * want to put too much policy logic here; intelligent scan selection
5809 * should occur within a generic IEEE 802.11 user space tool. */
5810
5811 /* Set up 'new' AP to this network */
5812 ipw_copy_rates(&match->rates, &rates);
5813 match->network = network;
5814
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07005815 IPW_DEBUG_ASSOC("Network '%*pE (%pM)' is a viable match.\n",
5816 network->ssid_len, network->ssid, network->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06005817
5818 return 1;
5819}
5820
Jeff Garzikbf794512005-07-31 13:07:26 -04005821static void ipw_adhoc_create(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005822 struct libipw_network *network)
James Ketrenos43f66a62005-03-25 12:31:53 -06005823{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005824 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
James Ketrenosafbf30a2005-08-25 00:05:33 -05005825 int i;
5826
James Ketrenos43f66a62005-03-25 12:31:53 -06005827 /*
5828 * For the purposes of scanning, we can set our wireless mode
5829 * to trigger scans across combinations of bands, but when it
5830 * comes to creating a new ad-hoc network, we have tell the FW
5831 * exactly which band to use.
5832 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005833 * We also have the possibility of an invalid channel for the
James Ketrenos43f66a62005-03-25 12:31:53 -06005834 * chossen band. Attempting to create a new ad-hoc network
5835 * with an invalid channel for wireless mode will trigger a
5836 * FW fatal error.
James Ketrenosafbf30a2005-08-25 00:05:33 -05005837 *
James Ketrenos43f66a62005-03-25 12:31:53 -06005838 */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005839 switch (libipw_is_valid_channel(priv->ieee, priv->channel)) {
5840 case LIBIPW_52GHZ_BAND:
James Ketrenosafbf30a2005-08-25 00:05:33 -05005841 network->mode = IEEE_A;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005842 i = libipw_channel_to_index(priv->ieee, priv->channel);
Eric Sesterhenn5d9428d2006-04-02 13:52:48 +02005843 BUG_ON(i == -1);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005844 if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005845 IPW_WARNING("Overriding invalid channel\n");
5846 priv->channel = geo->a[0].channel;
5847 }
5848 break;
5849
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005850 case LIBIPW_24GHZ_BAND:
James Ketrenosafbf30a2005-08-25 00:05:33 -05005851 if (priv->ieee->mode & IEEE_G)
5852 network->mode = IEEE_G;
5853 else
5854 network->mode = IEEE_B;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005855 i = libipw_channel_to_index(priv->ieee, priv->channel);
Eric Sesterhenn5d9428d2006-04-02 13:52:48 +02005856 BUG_ON(i == -1);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04005857 if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) {
Liu Hong1fe0adb2005-08-19 09:33:10 -05005858 IPW_WARNING("Overriding invalid channel\n");
5859 priv->channel = geo->bg[0].channel;
5860 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05005861 break;
5862
5863 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06005864 IPW_WARNING("Overriding invalid channel\n");
5865 if (priv->ieee->mode & IEEE_A) {
5866 network->mode = IEEE_A;
James Ketrenosb095c382005-08-24 22:04:42 -05005867 priv->channel = geo->a[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005868 } else if (priv->ieee->mode & IEEE_G) {
5869 network->mode = IEEE_G;
James Ketrenosb095c382005-08-24 22:04:42 -05005870 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005871 } else {
5872 network->mode = IEEE_B;
James Ketrenosb095c382005-08-24 22:04:42 -05005873 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005874 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05005875 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06005876 }
5877
5878 network->channel = priv->channel;
5879 priv->config |= CFG_ADHOC_PERSIST;
5880 ipw_create_bssid(priv, network->bssid);
5881 network->ssid_len = priv->essid_len;
5882 memcpy(network->ssid, priv->essid, priv->essid_len);
5883 memset(&network->stats, 0, sizeof(network->stats));
5884 network->capability = WLAN_CAPABILITY_IBSS;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005885 if (!(priv->config & CFG_PREAMBLE_LONG))
5886 network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06005887 if (priv->capability & CAP_PRIVACY_ON)
5888 network->capability |= WLAN_CAPABILITY_PRIVACY;
5889 network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005890 memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06005891 network->rates_ex_len = priv->rates.num_rates - network->rates_len;
Jeff Garzikbf794512005-07-31 13:07:26 -04005892 memcpy(network->rates_ex,
James Ketrenos43f66a62005-03-25 12:31:53 -06005893 &priv->rates.supported_rates[network->rates_len],
5894 network->rates_ex_len);
5895 network->last_scanned = 0;
5896 network->flags = 0;
5897 network->last_associate = 0;
5898 network->time_stamp[0] = 0;
5899 network->time_stamp[1] = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005900 network->beacon_interval = 100; /* Default */
5901 network->listen_interval = 10; /* Default */
5902 network->atim_window = 0; /* Default */
James Ketrenos43f66a62005-03-25 12:31:53 -06005903 network->wpa_ie_len = 0;
5904 network->rsn_ie_len = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005905}
5906
James Ketrenosb095c382005-08-24 22:04:42 -05005907static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
5908{
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005909 struct ipw_tgi_tx_key key;
James Ketrenosb095c382005-08-24 22:04:42 -05005910
5911 if (!(priv->ieee->sec.flags & (1 << index)))
5912 return;
5913
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005914 key.key_id = index;
5915 memcpy(key.key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
5916 key.security_type = type;
5917 key.station_index = 0; /* always 0 for BSS */
5918 key.flags = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05005919 /* 0 for new key; previous value of counter (after fatal error) */
Zhu Yi851ca262006-08-21 11:37:58 +08005920 key.tx_counter[0] = cpu_to_le32(0);
5921 key.tx_counter[1] = cpu_to_le32(0);
James Ketrenosb095c382005-08-24 22:04:42 -05005922
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005923 ipw_send_cmd_pdu(priv, IPW_CMD_TGI_TX_KEY, sizeof(key), &key);
James Ketrenosb095c382005-08-24 22:04:42 -05005924}
5925
5926static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
James Ketrenos43f66a62005-03-25 12:31:53 -06005927{
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005928 struct ipw_wep_key key;
James Ketrenos43f66a62005-03-25 12:31:53 -06005929 int i;
James Ketrenos43f66a62005-03-25 12:31:53 -06005930
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005931 key.cmd_id = DINO_CMD_WEP_KEY;
5932 key.seq_num = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005933
James Ketrenosb095c382005-08-24 22:04:42 -05005934 /* Note: AES keys cannot be set for multiple times.
5935 * Only set it at the first time. */
Jeff Garzikbf794512005-07-31 13:07:26 -04005936 for (i = 0; i < 4; i++) {
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005937 key.key_index = i | type;
James Ketrenosb095c382005-08-24 22:04:42 -05005938 if (!(priv->ieee->sec.flags & (1 << i))) {
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005939 key.key_size = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05005940 continue;
James Ketrenos43f66a62005-03-25 12:31:53 -06005941 }
5942
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005943 key.key_size = priv->ieee->sec.key_sizes[i];
5944 memcpy(key.key, priv->ieee->sec.keys[i], key.key_size);
James Ketrenosb095c382005-08-24 22:04:42 -05005945
Zhu Yi0a7bcf22006-01-24 16:37:28 +08005946 ipw_send_cmd_pdu(priv, IPW_CMD_WEP_KEY, sizeof(key), &key);
Jeff Garzikbf794512005-07-31 13:07:26 -04005947 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005948}
5949
Zhu Yi1fbfea52005-08-05 17:22:56 +08005950static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level)
5951{
5952 if (priv->ieee->host_encrypt)
5953 return;
5954
5955 switch (level) {
5956 case SEC_LEVEL_3:
5957 priv->sys_config.disable_unicast_decryption = 0;
5958 priv->ieee->host_decrypt = 0;
5959 break;
5960 case SEC_LEVEL_2:
5961 priv->sys_config.disable_unicast_decryption = 1;
5962 priv->ieee->host_decrypt = 1;
5963 break;
5964 case SEC_LEVEL_1:
5965 priv->sys_config.disable_unicast_decryption = 0;
5966 priv->ieee->host_decrypt = 0;
5967 break;
5968 case SEC_LEVEL_0:
5969 priv->sys_config.disable_unicast_decryption = 1;
5970 break;
5971 default:
5972 break;
5973 }
5974}
5975
5976static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level)
5977{
5978 if (priv->ieee->host_encrypt)
5979 return;
5980
5981 switch (level) {
5982 case SEC_LEVEL_3:
5983 priv->sys_config.disable_multicast_decryption = 0;
5984 break;
5985 case SEC_LEVEL_2:
5986 priv->sys_config.disable_multicast_decryption = 1;
5987 break;
5988 case SEC_LEVEL_1:
5989 priv->sys_config.disable_multicast_decryption = 0;
5990 break;
5991 case SEC_LEVEL_0:
5992 priv->sys_config.disable_multicast_decryption = 1;
5993 break;
5994 default:
5995 break;
5996 }
5997}
5998
James Ketrenosb095c382005-08-24 22:04:42 -05005999static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
6000{
6001 switch (priv->ieee->sec.level) {
6002 case SEC_LEVEL_3:
Zhu Yid8bad6d2005-07-13 12:25:38 -05006003 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
6004 ipw_send_tgi_tx_key(priv,
6005 DCT_FLAG_EXT_SECURITY_CCM,
6006 priv->ieee->sec.active_key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006007
Hong Liu567deaf2005-08-31 18:07:22 +08006008 if (!priv->ieee->host_mc_decrypt)
6009 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
James Ketrenosb095c382005-08-24 22:04:42 -05006010 break;
6011 case SEC_LEVEL_2:
Zhu Yid8bad6d2005-07-13 12:25:38 -05006012 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
6013 ipw_send_tgi_tx_key(priv,
6014 DCT_FLAG_EXT_SECURITY_TKIP,
6015 priv->ieee->sec.active_key);
James Ketrenosb095c382005-08-24 22:04:42 -05006016 break;
6017 case SEC_LEVEL_1:
6018 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
Hong Liu29cb8432005-09-12 10:43:33 -05006019 ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
6020 ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
James Ketrenosb095c382005-08-24 22:04:42 -05006021 break;
6022 case SEC_LEVEL_0:
6023 default:
6024 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06006025 }
6026}
6027
6028static void ipw_adhoc_check(void *data)
6029{
6030 struct ipw_priv *priv = data;
Jeff Garzikbf794512005-07-31 13:07:26 -04006031
James Ketrenosafbf30a2005-08-25 00:05:33 -05006032 if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold &&
James Ketrenos43f66a62005-03-25 12:31:53 -06006033 !(priv->config & CFG_ADHOC_PERSIST)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006034 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
6035 IPW_DL_STATE | IPW_DL_ASSOC,
6036 "Missed beacon: %d - disassociate\n",
6037 priv->missed_adhoc_beacons);
James Ketrenos43f66a62005-03-25 12:31:53 -06006038 ipw_remove_current_network(priv);
6039 ipw_disassociate(priv);
6040 return;
6041 }
6042
Tejun Heobcb6d912011-01-26 12:12:50 +01006043 schedule_delayed_work(&priv->adhoc_check,
6044 le16_to_cpu(priv->assoc_request.beacon_interval));
James Ketrenos43f66a62005-03-25 12:31:53 -06006045}
6046
David Howellsc4028952006-11-22 14:57:56 +00006047static void ipw_bg_adhoc_check(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05006048{
David Howellsc4028952006-11-22 14:57:56 +00006049 struct ipw_priv *priv =
6050 container_of(work, struct ipw_priv, adhoc_check.work);
Zhu Yi46441512006-01-24 16:37:59 +08006051 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00006052 ipw_adhoc_check(priv);
Zhu Yi46441512006-01-24 16:37:59 +08006053 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006054}
6055
James Ketrenos43f66a62005-03-25 12:31:53 -06006056static void ipw_debug_config(struct ipw_priv *priv)
6057{
6058 IPW_DEBUG_INFO("Scan completed, no valid APs matched "
6059 "[CFG 0x%08X]\n", priv->config);
6060 if (priv->config & CFG_STATIC_CHANNEL)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006061 IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06006062 else
6063 IPW_DEBUG_INFO("Channel unlocked.\n");
6064 if (priv->config & CFG_STATIC_ESSID)
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07006065 IPW_DEBUG_INFO("ESSID locked to '%*pE'\n",
6066 priv->essid_len, priv->essid);
James Ketrenos43f66a62005-03-25 12:31:53 -06006067 else
6068 IPW_DEBUG_INFO("ESSID unlocked.\n");
6069 if (priv->config & CFG_STATIC_BSSID)
Johannes Berge1749612008-10-27 15:59:26 -07006070 IPW_DEBUG_INFO("BSSID locked to %pM\n", priv->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06006071 else
6072 IPW_DEBUG_INFO("BSSID unlocked.\n");
6073 if (priv->capability & CAP_PRIVACY_ON)
6074 IPW_DEBUG_INFO("PRIVACY on\n");
6075 else
6076 IPW_DEBUG_INFO("PRIVACY off\n");
6077 IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
6078}
James Ketrenos43f66a62005-03-25 12:31:53 -06006079
Arjan van de Ven858119e2006-01-14 13:20:43 -08006080static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode)
James Ketrenos43f66a62005-03-25 12:31:53 -06006081{
6082 /* TODO: Verify that this works... */
Reinette Chatre21f8a732009-08-18 10:25:05 -07006083 struct ipw_fixed_rate fr;
James Ketrenos43f66a62005-03-25 12:31:53 -06006084 u32 reg;
6085 u16 mask = 0;
Reinette Chatre21f8a732009-08-18 10:25:05 -07006086 u16 new_tx_rates = priv->rates_mask;
James Ketrenos43f66a62005-03-25 12:31:53 -06006087
Jeff Garzikbf794512005-07-31 13:07:26 -04006088 /* Identify 'current FW band' and match it with the fixed
James Ketrenos43f66a62005-03-25 12:31:53 -06006089 * Tx rates */
Jeff Garzikbf794512005-07-31 13:07:26 -04006090
James Ketrenos43f66a62005-03-25 12:31:53 -06006091 switch (priv->ieee->freq_band) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006092 case LIBIPW_52GHZ_BAND: /* A only */
James Ketrenos43f66a62005-03-25 12:31:53 -06006093 /* IEEE_A */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006094 if (priv->rates_mask & ~LIBIPW_OFDM_RATES_MASK) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006095 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05006096 IPW_DEBUG_WX
6097 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
Reinette Chatre21f8a732009-08-18 10:25:05 -07006098 new_tx_rates = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006099 break;
6100 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006101
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006102 new_tx_rates >>= LIBIPW_OFDM_SHIFT_MASK_A;
James Ketrenos43f66a62005-03-25 12:31:53 -06006103 break;
6104
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006105 default: /* 2.4Ghz or Mixed */
James Ketrenos43f66a62005-03-25 12:31:53 -06006106 /* IEEE_B */
James Ketrenosb095c382005-08-24 22:04:42 -05006107 if (mode == IEEE_B) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006108 if (new_tx_rates & ~LIBIPW_CCK_RATES_MASK) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006109 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05006110 IPW_DEBUG_WX
6111 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
Reinette Chatre21f8a732009-08-18 10:25:05 -07006112 new_tx_rates = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006113 }
6114 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04006115 }
James Ketrenos43f66a62005-03-25 12:31:53 -06006116
6117 /* IEEE_G */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006118 if (new_tx_rates & ~(LIBIPW_CCK_RATES_MASK |
6119 LIBIPW_OFDM_RATES_MASK)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06006120 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05006121 IPW_DEBUG_WX
6122 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
Reinette Chatre21f8a732009-08-18 10:25:05 -07006123 new_tx_rates = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06006124 break;
6125 }
6126
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006127 if (LIBIPW_OFDM_RATE_6MB_MASK & new_tx_rates) {
6128 mask |= (LIBIPW_OFDM_RATE_6MB_MASK >> 1);
6129 new_tx_rates &= ~LIBIPW_OFDM_RATE_6MB_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06006130 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006131
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006132 if (LIBIPW_OFDM_RATE_9MB_MASK & new_tx_rates) {
6133 mask |= (LIBIPW_OFDM_RATE_9MB_MASK >> 1);
6134 new_tx_rates &= ~LIBIPW_OFDM_RATE_9MB_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06006135 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006136
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006137 if (LIBIPW_OFDM_RATE_12MB_MASK & new_tx_rates) {
6138 mask |= (LIBIPW_OFDM_RATE_12MB_MASK >> 1);
6139 new_tx_rates &= ~LIBIPW_OFDM_RATE_12MB_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06006140 }
Jeff Garzikbf794512005-07-31 13:07:26 -04006141
Reinette Chatre21f8a732009-08-18 10:25:05 -07006142 new_tx_rates |= mask;
James Ketrenos43f66a62005-03-25 12:31:53 -06006143 break;
6144 }
6145
Reinette Chatre21f8a732009-08-18 10:25:05 -07006146 fr.tx_rates = cpu_to_le16(new_tx_rates);
6147
James Ketrenos43f66a62005-03-25 12:31:53 -06006148 reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04006149 ipw_write_reg32(priv, reg, *(u32 *) & fr);
James Ketrenos43f66a62005-03-25 12:31:53 -06006150}
6151
James Ketrenosea2b26e2005-08-24 21:25:16 -05006152static void ipw_abort_scan(struct ipw_priv *priv)
6153{
6154 int err;
6155
6156 if (priv->status & STATUS_SCAN_ABORTING) {
6157 IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
6158 return;
6159 }
6160 priv->status |= STATUS_SCAN_ABORTING;
6161
6162 err = ipw_send_scan_abort(priv);
6163 if (err)
6164 IPW_DEBUG_HC("Request to abort scan failed.\n");
6165}
6166
James Ketrenosafbf30a2005-08-25 00:05:33 -05006167static void ipw_add_scan_channels(struct ipw_priv *priv,
6168 struct ipw_scan_request_ext *scan,
6169 int scan_type)
6170{
6171 int channel_index = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006172 const struct libipw_geo *geo;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006173 int i;
6174
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006175 geo = libipw_get_geo(priv->ieee);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006176
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006177 if (priv->ieee->freq_band & LIBIPW_52GHZ_BAND) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006178 int start = channel_index;
6179 for (i = 0; i < geo->a_channels; i++) {
6180 if ((priv->status & STATUS_ASSOCIATED) &&
6181 geo->a[i].channel == priv->channel)
6182 continue;
6183 channel_index++;
6184 scan->channels_list[channel_index] = geo->a[i].channel;
Liu Hong1fe0adb2005-08-19 09:33:10 -05006185 ipw_set_scan_type(scan, channel_index,
6186 geo->a[i].
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006187 flags & LIBIPW_CH_PASSIVE_ONLY ?
Liu Hong1fe0adb2005-08-19 09:33:10 -05006188 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN :
6189 scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006190 }
6191
6192 if (start != channel_index) {
6193 scan->channels_list[start] = (u8) (IPW_A_MODE << 6) |
6194 (channel_index - start);
6195 channel_index++;
6196 }
6197 }
6198
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006199 if (priv->ieee->freq_band & LIBIPW_24GHZ_BAND) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006200 int start = channel_index;
6201 if (priv->config & CFG_SPEED_SCAN) {
Liu Hong1fe0adb2005-08-19 09:33:10 -05006202 int index;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006203 u8 channels[LIBIPW_24GHZ_CHANNELS] = {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006204 /* nop out the list */
6205 [0] = 0
6206 };
6207
6208 u8 channel;
Zhu Yi7dd24592009-07-27 10:10:20 +08006209 while (channel_index < IPW_SCAN_CHANNELS - 1) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006210 channel =
6211 priv->speed_scan[priv->speed_scan_pos];
6212 if (channel == 0) {
6213 priv->speed_scan_pos = 0;
6214 channel = priv->speed_scan[0];
6215 }
6216 if ((priv->status & STATUS_ASSOCIATED) &&
6217 channel == priv->channel) {
6218 priv->speed_scan_pos++;
6219 continue;
6220 }
6221
6222 /* If this channel has already been
6223 * added in scan, break from loop
6224 * and this will be the first channel
6225 * in the next scan.
6226 */
6227 if (channels[channel - 1] != 0)
6228 break;
6229
6230 channels[channel - 1] = 1;
6231 priv->speed_scan_pos++;
6232 channel_index++;
6233 scan->channels_list[channel_index] = channel;
Liu Hong1fe0adb2005-08-19 09:33:10 -05006234 index =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006235 libipw_channel_to_index(priv->ieee, channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006236 ipw_set_scan_type(scan, channel_index,
Liu Hong1fe0adb2005-08-19 09:33:10 -05006237 geo->bg[index].
6238 flags &
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006239 LIBIPW_CH_PASSIVE_ONLY ?
Liu Hong1fe0adb2005-08-19 09:33:10 -05006240 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
6241 : scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006242 }
6243 } else {
6244 for (i = 0; i < geo->bg_channels; i++) {
6245 if ((priv->status & STATUS_ASSOCIATED) &&
6246 geo->bg[i].channel == priv->channel)
6247 continue;
6248 channel_index++;
6249 scan->channels_list[channel_index] =
6250 geo->bg[i].channel;
6251 ipw_set_scan_type(scan, channel_index,
Liu Hong1fe0adb2005-08-19 09:33:10 -05006252 geo->bg[i].
6253 flags &
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006254 LIBIPW_CH_PASSIVE_ONLY ?
Liu Hong1fe0adb2005-08-19 09:33:10 -05006255 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
6256 : scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006257 }
6258 }
6259
6260 if (start != channel_index) {
6261 scan->channels_list[start] = (u8) (IPW_B_MODE << 6) |
6262 (channel_index - start);
6263 }
6264 }
6265}
6266
Helmut Schaa14a4dfe2008-12-10 13:17:26 +01006267static int ipw_passive_dwell_time(struct ipw_priv *priv)
6268{
6269 /* staying on passive channels longer than the DTIM interval during a
6270 * scan, while associated, causes the firmware to cancel the scan
6271 * without notification. Hence, don't stay on passive channels longer
6272 * than the beacon interval.
6273 */
6274 if (priv->status & STATUS_ASSOCIATED
6275 && priv->assoc_network->beacon_interval > 10)
6276 return priv->assoc_network->beacon_interval - 10;
6277 else
6278 return 120;
6279}
6280
Dan Williamsea177302008-06-02 17:51:23 -04006281static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct)
James Ketrenosea2b26e2005-08-24 21:25:16 -05006282{
6283 struct ipw_scan_request_ext scan;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006284 int err = 0, scan_type;
6285
6286 if (!(priv->status & STATUS_INIT) ||
6287 (priv->status & STATUS_EXIT_PENDING))
6288 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006289
Zhu Yi46441512006-01-24 16:37:59 +08006290 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05006291
Dan Williamsea177302008-06-02 17:51:23 -04006292 if (direct && (priv->direct_scan_ssid_len == 0)) {
6293 IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n");
6294 priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
6295 goto done;
6296 }
6297
James Ketrenosea2b26e2005-08-24 21:25:16 -05006298 if (priv->status & STATUS_SCANNING) {
Dan Williamsea177302008-06-02 17:51:23 -04006299 IPW_DEBUG_HC("Concurrent scan requested. Queuing.\n");
6300 priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
6301 STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006302 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006303 }
6304
James Ketrenosafbf30a2005-08-25 00:05:33 -05006305 if (!(priv->status & STATUS_SCAN_FORCED) &&
6306 priv->status & STATUS_SCAN_ABORTING) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006307 IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
Dan Williamsea177302008-06-02 17:51:23 -04006308 priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
6309 STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006310 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006311 }
6312
6313 if (priv->status & STATUS_RF_KILL_MASK) {
Dan Williamsea177302008-06-02 17:51:23 -04006314 IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n");
6315 priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
6316 STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006317 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006318 }
6319
6320 memset(&scan, 0, sizeof(scan));
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006321 scan.full_scan_index = cpu_to_le32(libipw_get_scans(priv->ieee));
James Ketrenosea2b26e2005-08-24 21:25:16 -05006322
Zhu Yi094c4d22006-08-21 11:39:03 +08006323 if (type == IW_SCAN_TYPE_PASSIVE) {
Helmut Schaa14a4dfe2008-12-10 13:17:26 +01006324 IPW_DEBUG_WX("use passive scanning\n");
6325 scan_type = IPW_SCAN_PASSIVE_FULL_DWELL_SCAN;
Zhu Yi094c4d22006-08-21 11:39:03 +08006326 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
Helmut Schaa14a4dfe2008-12-10 13:17:26 +01006327 cpu_to_le16(ipw_passive_dwell_time(priv));
Zhu Yi094c4d22006-08-21 11:39:03 +08006328 ipw_add_scan_channels(priv, &scan, scan_type);
6329 goto send_request;
6330 }
6331
6332 /* Use active scan by default. */
Helmut Schaa14a4dfe2008-12-10 13:17:26 +01006333 if (priv->config & CFG_SPEED_SCAN)
James Ketrenosb095c382005-08-24 22:04:42 -05006334 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
Zhu Yi094c4d22006-08-21 11:39:03 +08006335 cpu_to_le16(30);
James Ketrenosb095c382005-08-24 22:04:42 -05006336 else
6337 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
Zhu Yi094c4d22006-08-21 11:39:03 +08006338 cpu_to_le16(20);
James Ketrenosb095c382005-08-24 22:04:42 -05006339
James Ketrenosa613bff2005-08-24 21:43:11 -05006340 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
Zhu Yi094c4d22006-08-21 11:39:03 +08006341 cpu_to_le16(20);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006342
Helmut Schaa14a4dfe2008-12-10 13:17:26 +01006343 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
6344 cpu_to_le16(ipw_passive_dwell_time(priv));
Dan Williamsea177302008-06-02 17:51:23 -04006345 scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006346
James Ketrenosb095c382005-08-24 22:04:42 -05006347#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05006348 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006349 u8 channel;
James Ketrenosb095c382005-08-24 22:04:42 -05006350 u8 band = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006351
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006352 switch (libipw_is_valid_channel(priv->ieee, priv->channel)) {
6353 case LIBIPW_52GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006354 band = (u8) (IPW_A_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006355 channel = priv->channel;
6356 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006357
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006358 case LIBIPW_24GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006359 band = (u8) (IPW_B_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006360 channel = priv->channel;
6361 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006362
James Ketrenosb095c382005-08-24 22:04:42 -05006363 default:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006364 band = (u8) (IPW_B_MODE << 6) | 1;
6365 channel = 9;
James Ketrenosb095c382005-08-24 22:04:42 -05006366 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006367 }
6368
James Ketrenosb095c382005-08-24 22:04:42 -05006369 scan.channels_list[0] = band;
6370 scan.channels_list[1] = channel;
6371 ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006372
James Ketrenosb095c382005-08-24 22:04:42 -05006373 /* NOTE: The card will sit on this channel for this time
6374 * period. Scan aborts are timing sensitive and frequently
6375 * result in firmware restarts. As such, it is best to
6376 * set a small dwell_time here and just keep re-issuing
6377 * scans. Otherwise fast channel hopping will not actually
6378 * hop channels.
6379 *
6380 * TODO: Move SPEED SCAN support to all modes and bands */
James Ketrenosa613bff2005-08-24 21:43:11 -05006381 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
Zhu Yi094c4d22006-08-21 11:39:03 +08006382 cpu_to_le16(2000);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006383 } else {
James Ketrenosb095c382005-08-24 22:04:42 -05006384#endif /* CONFIG_IPW2200_MONITOR */
Dan Williamsea177302008-06-02 17:51:23 -04006385 /* Honor direct scans first, otherwise if we are roaming make
6386 * this a direct scan for the current network. Finally,
6387 * ensure that every other scan is a fast channel hop scan */
6388 if (direct) {
6389 err = ipw_send_ssid(priv, priv->direct_scan_ssid,
6390 priv->direct_scan_ssid_len);
6391 if (err) {
6392 IPW_DEBUG_HC("Attempt to send SSID command "
6393 "failed\n");
6394 goto done;
6395 }
6396
6397 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
6398 } else if ((priv->status & STATUS_ROAMING)
6399 || (!(priv->status & STATUS_ASSOCIATED)
6400 && (priv->config & CFG_STATIC_ESSID)
6401 && (le32_to_cpu(scan.full_scan_index) % 2))) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006402 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
6403 if (err) {
James Ketrenosb095c382005-08-24 22:04:42 -05006404 IPW_DEBUG_HC("Attempt to send SSID command "
6405 "failed.\n");
6406 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006407 }
6408
6409 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006410 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05006411 scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006412
James Ketrenosafbf30a2005-08-25 00:05:33 -05006413 ipw_add_scan_channels(priv, &scan, scan_type);
James Ketrenosb095c382005-08-24 22:04:42 -05006414#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05006415 }
6416#endif
6417
Zhu Yi094c4d22006-08-21 11:39:03 +08006418send_request:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006419 err = ipw_send_scan_request_ext(priv, &scan);
6420 if (err) {
6421 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
James Ketrenosb095c382005-08-24 22:04:42 -05006422 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006423 }
6424
6425 priv->status |= STATUS_SCANNING;
Dan Williamsea177302008-06-02 17:51:23 -04006426 if (direct) {
6427 priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
6428 priv->direct_scan_ssid_len = 0;
6429 } else
6430 priv->status &= ~STATUS_SCAN_PENDING;
6431
Tejun Heobcb6d912011-01-26 12:12:50 +01006432 schedule_delayed_work(&priv->scan_check, IPW_SCAN_CHECK_WATCHDOG);
Zhu Yi094c4d22006-08-21 11:39:03 +08006433done:
Zhu Yi46441512006-01-24 16:37:59 +08006434 mutex_unlock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05006435 return err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006436}
6437
David Howellsc4028952006-11-22 14:57:56 +00006438static void ipw_request_passive_scan(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05006439{
David Howellsc4028952006-11-22 14:57:56 +00006440 struct ipw_priv *priv =
Dan Williamsea177302008-06-02 17:51:23 -04006441 container_of(work, struct ipw_priv, request_passive_scan.work);
6442 ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0);
David Howellsc4028952006-11-22 14:57:56 +00006443}
6444
6445static void ipw_request_scan(struct work_struct *work)
6446{
6447 struct ipw_priv *priv =
6448 container_of(work, struct ipw_priv, request_scan.work);
Dan Williamsea177302008-06-02 17:51:23 -04006449 ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0);
6450}
6451
6452static void ipw_request_direct_scan(struct work_struct *work)
6453{
6454 struct ipw_priv *priv =
6455 container_of(work, struct ipw_priv, request_direct_scan.work);
6456 ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1);
David Howellsc4028952006-11-22 14:57:56 +00006457}
6458
6459static void ipw_bg_abort_scan(struct work_struct *work)
6460{
6461 struct ipw_priv *priv =
6462 container_of(work, struct ipw_priv, abort_scan);
Zhu Yi46441512006-01-24 16:37:59 +08006463 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00006464 ipw_abort_scan(priv);
Zhu Yi46441512006-01-24 16:37:59 +08006465 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05006466}
6467
James Ketrenosea2b26e2005-08-24 21:25:16 -05006468static int ipw_wpa_enable(struct ipw_priv *priv, int value)
6469{
James Ketrenosb095c382005-08-24 22:04:42 -05006470 /* This is called when wpa_supplicant loads and closes the driver
6471 * interface. */
Hong Liucdd1fa12005-08-31 18:14:27 +08006472 priv->ieee->wpa_enabled = value;
James Ketrenosb095c382005-08-24 22:04:42 -05006473 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006474}
6475
James Ketrenosea2b26e2005-08-24 21:25:16 -05006476static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
6477{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006478 struct libipw_device *ieee = priv->ieee;
6479 struct libipw_security sec = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006480 .flags = SEC_AUTH_MODE,
6481 };
6482 int ret = 0;
6483
James Ketrenosafbf30a2005-08-25 00:05:33 -05006484 if (value & IW_AUTH_ALG_SHARED_KEY) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006485 sec.auth_mode = WLAN_AUTH_SHARED_KEY;
6486 ieee->open_wep = 0;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006487 } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006488 sec.auth_mode = WLAN_AUTH_OPEN;
6489 ieee->open_wep = 1;
Zhu Yi3e234b42006-01-24 16:36:52 +08006490 } else if (value & IW_AUTH_ALG_LEAP) {
6491 sec.auth_mode = WLAN_AUTH_LEAP;
6492 ieee->open_wep = 1;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006493 } else
6494 return -EINVAL;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006495
6496 if (ieee->set_security)
6497 ieee->set_security(ieee->dev, &sec);
6498 else
6499 ret = -EOPNOTSUPP;
6500
6501 return ret;
6502}
6503
Adrian Bunka73e22b2006-01-21 01:39:42 +01006504static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie,
6505 int wpa_ie_len)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006506{
6507 /* make sure WPA is enabled */
6508 ipw_wpa_enable(priv, 1);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006509}
6510
6511static int ipw_set_rsn_capa(struct ipw_priv *priv,
6512 char *capabilities, int length)
6513{
James Ketrenosafbf30a2005-08-25 00:05:33 -05006514 IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
6515
Zhu Yi0a7bcf22006-01-24 16:37:28 +08006516 return ipw_send_cmd_pdu(priv, IPW_CMD_RSN_CAPABILITIES, length,
Zhu Yi2638bc32006-01-24 16:37:52 +08006517 capabilities);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006518}
6519
James Ketrenosafbf30a2005-08-25 00:05:33 -05006520/*
6521 * WE-18 support
6522 */
6523
6524/* SIOCSIWGENIE */
6525static int ipw_wx_set_genie(struct net_device *dev,
6526 struct iw_request_info *info,
6527 union iwreq_data *wrqu, char *extra)
6528{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006529 struct ipw_priv *priv = libipw_priv(dev);
6530 struct libipw_device *ieee = priv->ieee;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006531 u8 *buf;
6532 int err = 0;
6533
6534 if (wrqu->data.length > MAX_WPA_IE_LEN ||
6535 (wrqu->data.length && extra == NULL))
6536 return -EINVAL;
6537
James Ketrenosafbf30a2005-08-25 00:05:33 -05006538 if (wrqu->data.length) {
Julia Lawalld3e50332010-05-15 23:21:01 +02006539 buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006540 if (buf == NULL) {
6541 err = -ENOMEM;
6542 goto out;
6543 }
6544
James Ketrenosafbf30a2005-08-25 00:05:33 -05006545 kfree(ieee->wpa_ie);
6546 ieee->wpa_ie = buf;
6547 ieee->wpa_ie_len = wrqu->data.length;
6548 } else {
6549 kfree(ieee->wpa_ie);
6550 ieee->wpa_ie = NULL;
6551 ieee->wpa_ie_len = 0;
6552 }
6553
6554 ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
6555 out:
James Ketrenosafbf30a2005-08-25 00:05:33 -05006556 return err;
6557}
6558
6559/* SIOCGIWGENIE */
6560static int ipw_wx_get_genie(struct net_device *dev,
6561 struct iw_request_info *info,
6562 union iwreq_data *wrqu, char *extra)
6563{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006564 struct ipw_priv *priv = libipw_priv(dev);
6565 struct libipw_device *ieee = priv->ieee;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006566 int err = 0;
6567
James Ketrenosafbf30a2005-08-25 00:05:33 -05006568 if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
6569 wrqu->data.length = 0;
6570 goto out;
6571 }
6572
6573 if (wrqu->data.length < ieee->wpa_ie_len) {
6574 err = -E2BIG;
6575 goto out;
6576 }
6577
6578 wrqu->data.length = ieee->wpa_ie_len;
6579 memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
6580
6581 out:
James Ketrenosafbf30a2005-08-25 00:05:33 -05006582 return err;
6583}
6584
Zhu Yi1fbfea52005-08-05 17:22:56 +08006585static int wext_cipher2level(int cipher)
6586{
6587 switch (cipher) {
6588 case IW_AUTH_CIPHER_NONE:
6589 return SEC_LEVEL_0;
6590 case IW_AUTH_CIPHER_WEP40:
6591 case IW_AUTH_CIPHER_WEP104:
6592 return SEC_LEVEL_1;
6593 case IW_AUTH_CIPHER_TKIP:
6594 return SEC_LEVEL_2;
6595 case IW_AUTH_CIPHER_CCMP:
6596 return SEC_LEVEL_3;
6597 default:
6598 return -1;
6599 }
6600}
6601
James Ketrenosafbf30a2005-08-25 00:05:33 -05006602/* SIOCSIWAUTH */
6603static int ipw_wx_set_auth(struct net_device *dev,
6604 struct iw_request_info *info,
6605 union iwreq_data *wrqu, char *extra)
6606{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006607 struct ipw_priv *priv = libipw_priv(dev);
6608 struct libipw_device *ieee = priv->ieee;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006609 struct iw_param *param = &wrqu->param;
John W. Linville274bfb82008-10-29 11:35:05 -04006610 struct lib80211_crypt_data *crypt;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006611 unsigned long flags;
6612 int ret = 0;
6613
6614 switch (param->flags & IW_AUTH_INDEX) {
6615 case IW_AUTH_WPA_VERSION:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006616 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006617 case IW_AUTH_CIPHER_PAIRWISE:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006618 ipw_set_hw_decrypt_unicast(priv,
6619 wext_cipher2level(param->value));
6620 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006621 case IW_AUTH_CIPHER_GROUP:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006622 ipw_set_hw_decrypt_multicast(priv,
6623 wext_cipher2level(param->value));
6624 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006625 case IW_AUTH_KEY_MGMT:
6626 /*
6627 * ipw2200 does not use these parameters
6628 */
6629 break;
6630
6631 case IW_AUTH_TKIP_COUNTERMEASURES:
John W. Linville274bfb82008-10-29 11:35:05 -04006632 crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
James Ketrenos991d1cc2005-10-13 09:26:48 +00006633 if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006634 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006635
6636 flags = crypt->ops->get_flags(crypt->priv);
6637
6638 if (param->value)
6639 flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6640 else
6641 flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6642
6643 crypt->ops->set_flags(flags, crypt->priv);
6644
6645 break;
6646
6647 case IW_AUTH_DROP_UNENCRYPTED:{
6648 /* HACK:
6649 *
6650 * wpa_supplicant calls set_wpa_enabled when the driver
6651 * is loaded and unloaded, regardless of if WPA is being
6652 * used. No other calls are made which can be used to
6653 * determine if encryption will be used or not prior to
6654 * association being expected. If encryption is not being
6655 * used, drop_unencrypted is set to false, else true -- we
6656 * can use this to determine if the CAP_PRIVACY_ON bit should
6657 * be set.
6658 */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006659 struct libipw_security sec = {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006660 .flags = SEC_ENABLED,
6661 .enabled = param->value,
6662 };
6663 priv->ieee->drop_unencrypted = param->value;
6664 /* We only change SEC_LEVEL for open mode. Others
6665 * are set by ipw_wpa_set_encryption.
6666 */
6667 if (!param->value) {
6668 sec.flags |= SEC_LEVEL;
6669 sec.level = SEC_LEVEL_0;
6670 } else {
6671 sec.flags |= SEC_LEVEL;
6672 sec.level = SEC_LEVEL_1;
6673 }
6674 if (priv->ieee->set_security)
6675 priv->ieee->set_security(priv->ieee->dev, &sec);
6676 break;
6677 }
6678
6679 case IW_AUTH_80211_AUTH_ALG:
6680 ret = ipw_wpa_set_auth_algs(priv, param->value);
6681 break;
6682
6683 case IW_AUTH_WPA_ENABLED:
6684 ret = ipw_wpa_enable(priv, param->value);
Zhu Yie3c5a642006-04-13 17:21:13 +08006685 ipw_disassociate(priv);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006686 break;
6687
6688 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6689 ieee->ieee802_1x = param->value;
6690 break;
6691
James Ketrenosafbf30a2005-08-25 00:05:33 -05006692 case IW_AUTH_PRIVACY_INVOKED:
6693 ieee->privacy_invoked = param->value;
6694 break;
6695
6696 default:
6697 return -EOPNOTSUPP;
6698 }
6699 return ret;
6700}
6701
6702/* SIOCGIWAUTH */
6703static int ipw_wx_get_auth(struct net_device *dev,
6704 struct iw_request_info *info,
6705 union iwreq_data *wrqu, char *extra)
6706{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006707 struct ipw_priv *priv = libipw_priv(dev);
6708 struct libipw_device *ieee = priv->ieee;
John W. Linville274bfb82008-10-29 11:35:05 -04006709 struct lib80211_crypt_data *crypt;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006710 struct iw_param *param = &wrqu->param;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006711
6712 switch (param->flags & IW_AUTH_INDEX) {
6713 case IW_AUTH_WPA_VERSION:
6714 case IW_AUTH_CIPHER_PAIRWISE:
6715 case IW_AUTH_CIPHER_GROUP:
6716 case IW_AUTH_KEY_MGMT:
6717 /*
6718 * wpa_supplicant will control these internally
6719 */
Wei Yongjun8df0f1e2012-12-05 03:08:11 -05006720 return -EOPNOTSUPP;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006721
6722 case IW_AUTH_TKIP_COUNTERMEASURES:
John W. Linville274bfb82008-10-29 11:35:05 -04006723 crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
James Ketrenos991d1cc2005-10-13 09:26:48 +00006724 if (!crypt || !crypt->ops->get_flags)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006725 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006726
6727 param->value = (crypt->ops->get_flags(crypt->priv) &
6728 IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
6729
6730 break;
6731
6732 case IW_AUTH_DROP_UNENCRYPTED:
6733 param->value = ieee->drop_unencrypted;
6734 break;
6735
6736 case IW_AUTH_80211_AUTH_ALG:
6737 param->value = ieee->sec.auth_mode;
6738 break;
6739
6740 case IW_AUTH_WPA_ENABLED:
6741 param->value = ieee->wpa_enabled;
6742 break;
6743
6744 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6745 param->value = ieee->ieee802_1x;
6746 break;
6747
6748 case IW_AUTH_ROAMING_CONTROL:
6749 case IW_AUTH_PRIVACY_INVOKED:
6750 param->value = ieee->privacy_invoked;
6751 break;
6752
6753 default:
6754 return -EOPNOTSUPP;
6755 }
6756 return 0;
6757}
6758
6759/* SIOCSIWENCODEEXT */
6760static int ipw_wx_set_encodeext(struct net_device *dev,
6761 struct iw_request_info *info,
6762 union iwreq_data *wrqu, char *extra)
6763{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006764 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006765 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
6766
6767 if (hwcrypto) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006768 if (ext->alg == IW_ENCODE_ALG_TKIP) {
Hong Liu567deaf2005-08-31 18:07:22 +08006769 /* IPW HW can't build TKIP MIC,
6770 host decryption still needed */
6771 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
6772 priv->ieee->host_mc_decrypt = 1;
6773 else {
6774 priv->ieee->host_encrypt = 0;
6775 priv->ieee->host_encrypt_msdu = 1;
6776 priv->ieee->host_decrypt = 1;
6777 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006778 } else {
6779 priv->ieee->host_encrypt = 0;
6780 priv->ieee->host_encrypt_msdu = 0;
6781 priv->ieee->host_decrypt = 0;
Hong Liu567deaf2005-08-31 18:07:22 +08006782 priv->ieee->host_mc_decrypt = 0;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006783 }
6784 }
6785
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006786 return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006787}
6788
6789/* SIOCGIWENCODEEXT */
6790static int ipw_wx_get_encodeext(struct net_device *dev,
6791 struct iw_request_info *info,
6792 union iwreq_data *wrqu, char *extra)
6793{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006794 struct ipw_priv *priv = libipw_priv(dev);
6795 return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006796}
6797
6798/* SIOCSIWMLME */
6799static int ipw_wx_set_mlme(struct net_device *dev,
6800 struct iw_request_info *info,
6801 union iwreq_data *wrqu, char *extra)
6802{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006803 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006804 struct iw_mlme *mlme = (struct iw_mlme *)extra;
Al Viroe62e1ee2007-12-27 01:36:46 -05006805 __le16 reason;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006806
6807 reason = cpu_to_le16(mlme->reason_code);
6808
6809 switch (mlme->cmd) {
6810 case IW_MLME_DEAUTH:
Pavel Machek67fd6b42006-07-11 15:34:05 +02006811 /* silently ignore */
James Ketrenosafbf30a2005-08-25 00:05:33 -05006812 break;
6813
6814 case IW_MLME_DISASSOC:
6815 ipw_disassociate(priv);
6816 break;
6817
6818 default:
6819 return -EOPNOTSUPP;
6820 }
6821 return 0;
6822}
James Ketrenosea2b26e2005-08-24 21:25:16 -05006823
Zhu Yie43e3c12006-04-13 17:20:45 +08006824#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -05006825
6826/* QoS */
6827/*
6828* get the modulation type of the current network or
6829* the card current mode
6830*/
Adrian Bunk53d0bcf2006-03-04 13:14:31 +01006831static u8 ipw_qos_current_mode(struct ipw_priv * priv)
James Ketrenosb095c382005-08-24 22:04:42 -05006832{
6833 u8 mode = 0;
6834
6835 if (priv->status & STATUS_ASSOCIATED) {
6836 unsigned long flags;
6837
6838 spin_lock_irqsave(&priv->ieee->lock, flags);
6839 mode = priv->assoc_network->mode;
6840 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6841 } else {
6842 mode = priv->ieee->mode;
6843 }
Frans Pop9fd1ea42010-03-24 19:46:31 +01006844 IPW_DEBUG_QOS("QoS network/card mode %d\n", mode);
James Ketrenosb095c382005-08-24 22:04:42 -05006845 return mode;
6846}
6847
6848/*
6849* Handle management frame beacon and probe response
6850*/
James Ketrenos3b9990c2005-08-19 13:18:55 -05006851static int ipw_qos_handle_probe_response(struct ipw_priv *priv,
6852 int active_network,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006853 struct libipw_network *network)
James Ketrenosb095c382005-08-24 22:04:42 -05006854{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006855 u32 size = sizeof(struct libipw_qos_parameters);
James Ketrenosb095c382005-08-24 22:04:42 -05006856
James Ketrenosafbf30a2005-08-25 00:05:33 -05006857 if (network->capability & WLAN_CAPABILITY_IBSS)
James Ketrenosb095c382005-08-24 22:04:42 -05006858 network->qos_data.active = network->qos_data.supported;
6859
6860 if (network->flags & NETWORK_HAS_QOS_MASK) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006861 if (active_network &&
6862 (network->flags & NETWORK_HAS_QOS_PARAMETERS))
James Ketrenosb095c382005-08-24 22:04:42 -05006863 network->qos_data.active = network->qos_data.supported;
6864
6865 if ((network->qos_data.active == 1) && (active_network == 1) &&
6866 (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
6867 (network->qos_data.old_param_count !=
6868 network->qos_data.param_count)) {
6869 network->qos_data.old_param_count =
6870 network->qos_data.param_count;
6871 schedule_work(&priv->qos_activate);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006872 IPW_DEBUG_QOS("QoS parameters change call "
6873 "qos_activate\n");
James Ketrenosb095c382005-08-24 22:04:42 -05006874 }
6875 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006876 if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B))
6877 memcpy(&network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006878 &def_parameters_CCK, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006879 else
6880 memcpy(&network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006881 &def_parameters_OFDM, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006882
James Ketrenosb095c382005-08-24 22:04:42 -05006883 if ((network->qos_data.active == 1) && (active_network == 1)) {
Frans Pop9fd1ea42010-03-24 19:46:31 +01006884 IPW_DEBUG_QOS("QoS was disabled call qos_activate\n");
James Ketrenosb095c382005-08-24 22:04:42 -05006885 schedule_work(&priv->qos_activate);
6886 }
6887
6888 network->qos_data.active = 0;
6889 network->qos_data.supported = 0;
6890 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006891 if ((priv->status & STATUS_ASSOCIATED) &&
6892 (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) {
dingtianhong36325f32013-12-26 19:41:23 +08006893 if (!ether_addr_equal(network->bssid, priv->bssid))
John W. Linvillec5d3dce2008-09-30 17:17:26 -04006894 if (network->capability & WLAN_CAPABILITY_IBSS)
James Ketrenosb095c382005-08-24 22:04:42 -05006895 if ((network->ssid_len ==
James Ketrenosafbf30a2005-08-25 00:05:33 -05006896 priv->assoc_network->ssid_len) &&
6897 !memcmp(network->ssid,
6898 priv->assoc_network->ssid,
6899 network->ssid_len)) {
Tejun Heobcb6d912011-01-26 12:12:50 +01006900 schedule_work(&priv->merge_networks);
James Ketrenosb095c382005-08-24 22:04:42 -05006901 }
James Ketrenosb095c382005-08-24 22:04:42 -05006902 }
6903
6904 return 0;
6905}
6906
6907/*
6908* This function set up the firmware to support QoS. It sends
6909* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO
6910*/
6911static int ipw_qos_activate(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006912 struct libipw_qos_data *qos_network_data)
James Ketrenosb095c382005-08-24 22:04:42 -05006913{
6914 int err;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006915 struct libipw_qos_parameters qos_parameters[QOS_QOS_SETS];
6916 struct libipw_qos_parameters *active_one = NULL;
6917 u32 size = sizeof(struct libipw_qos_parameters);
James Ketrenosb095c382005-08-24 22:04:42 -05006918 u32 burst_duration;
6919 int i;
6920 u8 type;
6921
6922 type = ipw_qos_current_mode(priv);
6923
6924 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]);
6925 memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size);
6926 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]);
6927 memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size);
6928
6929 if (qos_network_data == NULL) {
6930 if (type == IEEE_B) {
6931 IPW_DEBUG_QOS("QoS activate network mode %d\n", type);
6932 active_one = &def_parameters_CCK;
6933 } else
6934 active_one = &def_parameters_OFDM;
6935
James Ketrenosafbf30a2005-08-25 00:05:33 -05006936 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006937 burst_duration = ipw_qos_get_burst_duration(priv);
6938 for (i = 0; i < QOS_QUEUE_NUM; i++)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006939 qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] =
Al Viro8fffc152007-12-27 01:25:40 -05006940 cpu_to_le16(burst_duration);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006941 } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
James Ketrenosb095c382005-08-24 22:04:42 -05006942 if (type == IEEE_B) {
Masanari Iidafd9071e2012-04-13 04:33:20 +00006943 IPW_DEBUG_QOS("QoS activate IBSS network mode %d\n",
James Ketrenosb095c382005-08-24 22:04:42 -05006944 type);
6945 if (priv->qos_data.qos_enable == 0)
6946 active_one = &def_parameters_CCK;
6947 else
6948 active_one = priv->qos_data.def_qos_parm_CCK;
6949 } else {
6950 if (priv->qos_data.qos_enable == 0)
6951 active_one = &def_parameters_OFDM;
6952 else
6953 active_one = priv->qos_data.def_qos_parm_OFDM;
6954 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006955 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006956 } else {
6957 unsigned long flags;
6958 int active;
6959
6960 spin_lock_irqsave(&priv->ieee->lock, flags);
6961 active_one = &(qos_network_data->parameters);
6962 qos_network_data->old_param_count =
6963 qos_network_data->param_count;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006964 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006965 active = qos_network_data->supported;
6966 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6967
6968 if (active == 0) {
6969 burst_duration = ipw_qos_get_burst_duration(priv);
6970 for (i = 0; i < QOS_QUEUE_NUM; i++)
6971 qos_parameters[QOS_PARAM_SET_ACTIVE].
Al Viro8fffc152007-12-27 01:25:40 -05006972 tx_op_limit[i] = cpu_to_le16(burst_duration);
James Ketrenosb095c382005-08-24 22:04:42 -05006973 }
6974 }
6975
6976 IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n");
Joe Perches2c208892012-06-04 12:44:17 +00006977 err = ipw_send_qos_params_command(priv, &qos_parameters[0]);
James Ketrenosb095c382005-08-24 22:04:42 -05006978 if (err)
6979 IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n");
6980
6981 return err;
6982}
6983
6984/*
6985* send IPW_CMD_WME_INFO to the firmware
6986*/
6987static int ipw_qos_set_info_element(struct ipw_priv *priv)
6988{
6989 int ret = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006990 struct libipw_qos_information_element qos_info;
James Ketrenosb095c382005-08-24 22:04:42 -05006991
6992 if (priv == NULL)
6993 return -1;
6994
6995 qos_info.elementID = QOS_ELEMENT_ID;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04006996 qos_info.length = sizeof(struct libipw_qos_information_element) - 2;
James Ketrenosb095c382005-08-24 22:04:42 -05006997
6998 qos_info.version = QOS_VERSION_1;
6999 qos_info.ac_info = 0;
7000
7001 memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN);
7002 qos_info.qui_type = QOS_OUI_TYPE;
7003 qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE;
7004
7005 ret = ipw_send_qos_info_command(priv, &qos_info);
7006 if (ret != 0) {
7007 IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n");
7008 }
7009 return ret;
7010}
7011
7012/*
7013* Set the QoS parameter with the association request structure
7014*/
7015static int ipw_qos_association(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007016 struct libipw_network *network)
James Ketrenosb095c382005-08-24 22:04:42 -05007017{
7018 int err = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007019 struct libipw_qos_data *qos_data = NULL;
7020 struct libipw_qos_data ibss_data = {
James Ketrenosb095c382005-08-24 22:04:42 -05007021 .supported = 1,
7022 .active = 1,
7023 };
7024
7025 switch (priv->ieee->iw_mode) {
7026 case IW_MODE_ADHOC:
Eric Sesterhenn5d9428d2006-04-02 13:52:48 +02007027 BUG_ON(!(network->capability & WLAN_CAPABILITY_IBSS));
James Ketrenosb095c382005-08-24 22:04:42 -05007028
7029 qos_data = &ibss_data;
7030 break;
7031
7032 case IW_MODE_INFRA:
7033 qos_data = &network->qos_data;
7034 break;
7035
7036 default:
7037 BUG();
7038 break;
7039 }
7040
7041 err = ipw_qos_activate(priv, qos_data);
7042 if (err) {
7043 priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC;
7044 return err;
7045 }
7046
7047 if (priv->qos_data.qos_enable && qos_data->supported) {
7048 IPW_DEBUG_QOS("QoS will be enabled for this association\n");
7049 priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC;
7050 return ipw_qos_set_info_element(priv);
7051 }
7052
7053 return 0;
7054}
7055
7056/*
Matt LaPlante0779bf22006-11-30 05:24:39 +01007057* handling the beaconing responses. if we get different QoS setting
7058* off the network from the associated setting, adjust the QoS
James Ketrenosb095c382005-08-24 22:04:42 -05007059* setting
7060*/
7061static int ipw_qos_association_resp(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007062 struct libipw_network *network)
James Ketrenosb095c382005-08-24 22:04:42 -05007063{
7064 int ret = 0;
7065 unsigned long flags;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007066 u32 size = sizeof(struct libipw_qos_parameters);
James Ketrenosb095c382005-08-24 22:04:42 -05007067 int set_qos_param = 0;
7068
James Ketrenosafbf30a2005-08-25 00:05:33 -05007069 if ((priv == NULL) || (network == NULL) ||
7070 (priv->assoc_network == NULL))
James Ketrenosb095c382005-08-24 22:04:42 -05007071 return ret;
7072
7073 if (!(priv->status & STATUS_ASSOCIATED))
7074 return ret;
7075
James Ketrenosafbf30a2005-08-25 00:05:33 -05007076 if ((priv->ieee->iw_mode != IW_MODE_INFRA))
James Ketrenosb095c382005-08-24 22:04:42 -05007077 return ret;
James Ketrenosb095c382005-08-24 22:04:42 -05007078
7079 spin_lock_irqsave(&priv->ieee->lock, flags);
7080 if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007081 memcpy(&priv->assoc_network->qos_data, &network->qos_data,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007082 sizeof(struct libipw_qos_data));
James Ketrenosb095c382005-08-24 22:04:42 -05007083 priv->assoc_network->qos_data.active = 1;
7084 if ((network->qos_data.old_param_count !=
7085 network->qos_data.param_count)) {
7086 set_qos_param = 1;
7087 network->qos_data.old_param_count =
7088 network->qos_data.param_count;
7089 }
7090
7091 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007092 if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B))
7093 memcpy(&priv->assoc_network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05007094 &def_parameters_CCK, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05007095 else
7096 memcpy(&priv->assoc_network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05007097 &def_parameters_OFDM, size);
James Ketrenosb095c382005-08-24 22:04:42 -05007098 priv->assoc_network->qos_data.active = 0;
7099 priv->assoc_network->qos_data.supported = 0;
7100 set_qos_param = 1;
7101 }
7102
7103 spin_unlock_irqrestore(&priv->ieee->lock, flags);
7104
7105 if (set_qos_param == 1)
7106 schedule_work(&priv->qos_activate);
7107
7108 return ret;
7109}
7110
7111static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv)
7112{
7113 u32 ret = 0;
7114
7115 if ((priv == NULL))
7116 return 0;
7117
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007118 if (!(priv->ieee->modulation & LIBIPW_OFDM_MODULATION))
James Ketrenosb095c382005-08-24 22:04:42 -05007119 ret = priv->qos_data.burst_duration_CCK;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007120 else
James Ketrenosb095c382005-08-24 22:04:42 -05007121 ret = priv->qos_data.burst_duration_OFDM;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007122
James Ketrenosb095c382005-08-24 22:04:42 -05007123 return ret;
7124}
7125
7126/*
7127* Initialize the setting of QoS global
7128*/
7129static void ipw_qos_init(struct ipw_priv *priv, int enable,
7130 int burst_enable, u32 burst_duration_CCK,
7131 u32 burst_duration_OFDM)
7132{
7133 priv->qos_data.qos_enable = enable;
7134
7135 if (priv->qos_data.qos_enable) {
7136 priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK;
7137 priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM;
7138 IPW_DEBUG_QOS("QoS is enabled\n");
7139 } else {
7140 priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK;
7141 priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM;
7142 IPW_DEBUG_QOS("QoS is not enabled\n");
7143 }
7144
7145 priv->qos_data.burst_enable = burst_enable;
7146
7147 if (burst_enable) {
7148 priv->qos_data.burst_duration_CCK = burst_duration_CCK;
7149 priv->qos_data.burst_duration_OFDM = burst_duration_OFDM;
7150 } else {
7151 priv->qos_data.burst_duration_CCK = 0;
7152 priv->qos_data.burst_duration_OFDM = 0;
7153 }
7154}
7155
7156/*
7157* map the packet priority to the right TX Queue
7158*/
7159static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority)
7160{
7161 if (priority > 7 || !priv->qos_data.qos_enable)
7162 priority = 0;
7163
7164 return from_priority_to_tx_queue[priority] - 1;
7165}
7166
Zhu Yia5cf4fe2006-04-13 17:19:11 +08007167static int ipw_is_qos_active(struct net_device *dev,
7168 struct sk_buff *skb)
James Ketrenosb095c382005-08-24 22:04:42 -05007169{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007170 struct ipw_priv *priv = libipw_priv(dev);
7171 struct libipw_qos_data *qos_data = NULL;
James Ketrenosb095c382005-08-24 22:04:42 -05007172 int active, supported;
Zhu Yia5cf4fe2006-04-13 17:19:11 +08007173 u8 *daddr = skb->data + ETH_ALEN;
7174 int unicast = !is_multicast_ether_addr(daddr);
James Ketrenosb095c382005-08-24 22:04:42 -05007175
7176 if (!(priv->status & STATUS_ASSOCIATED))
7177 return 0;
7178
7179 qos_data = &priv->assoc_network->qos_data;
7180
James Ketrenosb095c382005-08-24 22:04:42 -05007181 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
7182 if (unicast == 0)
7183 qos_data->active = 0;
7184 else
7185 qos_data->active = qos_data->supported;
7186 }
James Ketrenosb095c382005-08-24 22:04:42 -05007187 active = qos_data->active;
7188 supported = qos_data->supported;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007189 IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d "
7190 "unicast %d\n",
7191 priv->qos_data.qos_enable, active, supported, unicast);
Zhu Yia5cf4fe2006-04-13 17:19:11 +08007192 if (active && priv->qos_data.qos_enable)
7193 return 1;
James Ketrenosb095c382005-08-24 22:04:42 -05007194
Zhu Yia5cf4fe2006-04-13 17:19:11 +08007195 return 0;
7196
7197}
7198/*
7199* add QoS parameter to the TX command
7200*/
7201static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
7202 u16 priority,
7203 struct tfd_data *tfd)
7204{
7205 int tx_queue_id = 0;
7206
7207
7208 tx_queue_id = from_priority_to_tx_queue[priority] - 1;
7209 tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
7210
7211 if (priv->qos_data.qos_no_ack_mask & (1UL << tx_queue_id)) {
7212 tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
Zhu Yi851ca262006-08-21 11:37:58 +08007213 tfd->tfd.tfd_26.mchdr.qos_ctrl |= cpu_to_le16(CTRL_QOS_NO_ACK);
James Ketrenosb095c382005-08-24 22:04:42 -05007214 }
Zhu Yia5cf4fe2006-04-13 17:19:11 +08007215 return 0;
James Ketrenosb095c382005-08-24 22:04:42 -05007216}
7217
7218/*
7219* background support to run QoS activate functionality
7220*/
David Howellsc4028952006-11-22 14:57:56 +00007221static void ipw_bg_qos_activate(struct work_struct *work)
James Ketrenosb095c382005-08-24 22:04:42 -05007222{
David Howellsc4028952006-11-22 14:57:56 +00007223 struct ipw_priv *priv =
7224 container_of(work, struct ipw_priv, qos_activate);
James Ketrenosb095c382005-08-24 22:04:42 -05007225
Zhu Yi46441512006-01-24 16:37:59 +08007226 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05007227
7228 if (priv->status & STATUS_ASSOCIATED)
7229 ipw_qos_activate(priv, &(priv->assoc_network->qos_data));
7230
Zhu Yi46441512006-01-24 16:37:59 +08007231 mutex_unlock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05007232}
7233
James Ketrenos3b9990c2005-08-19 13:18:55 -05007234static int ipw_handle_probe_response(struct net_device *dev,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007235 struct libipw_probe_response *resp,
7236 struct libipw_network *network)
James Ketrenosb095c382005-08-24 22:04:42 -05007237{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007238 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos3b9990c2005-08-19 13:18:55 -05007239 int active_network = ((priv->status & STATUS_ASSOCIATED) &&
7240 (network == priv->assoc_network));
James Ketrenosb095c382005-08-24 22:04:42 -05007241
James Ketrenos3b9990c2005-08-19 13:18:55 -05007242 ipw_qos_handle_probe_response(priv, active_network, network);
James Ketrenosb095c382005-08-24 22:04:42 -05007243
James Ketrenos3b9990c2005-08-19 13:18:55 -05007244 return 0;
7245}
James Ketrenosb095c382005-08-24 22:04:42 -05007246
James Ketrenos3b9990c2005-08-19 13:18:55 -05007247static int ipw_handle_beacon(struct net_device *dev,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007248 struct libipw_beacon *resp,
7249 struct libipw_network *network)
James Ketrenos3b9990c2005-08-19 13:18:55 -05007250{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007251 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos3b9990c2005-08-19 13:18:55 -05007252 int active_network = ((priv->status & STATUS_ASSOCIATED) &&
7253 (network == priv->assoc_network));
7254
7255 ipw_qos_handle_probe_response(priv, active_network, network);
7256
7257 return 0;
7258}
7259
7260static int ipw_handle_assoc_response(struct net_device *dev,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007261 struct libipw_assoc_response *resp,
7262 struct libipw_network *network)
James Ketrenos3b9990c2005-08-19 13:18:55 -05007263{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007264 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos3b9990c2005-08-19 13:18:55 -05007265 ipw_qos_association_resp(priv, network);
James Ketrenosb095c382005-08-24 22:04:42 -05007266 return 0;
7267}
7268
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007269static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters
James Ketrenosb095c382005-08-24 22:04:42 -05007270 *qos_param)
7271{
Zhu Yi4e226992006-01-24 16:37:36 +08007272 return ipw_send_cmd_pdu(priv, IPW_CMD_QOS_PARAMETERS,
7273 sizeof(*qos_param) * 3, qos_param);
James Ketrenosb095c382005-08-24 22:04:42 -05007274}
7275
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007276static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element
James Ketrenosb095c382005-08-24 22:04:42 -05007277 *qos_param)
7278{
Zhu Yi4e226992006-01-24 16:37:36 +08007279 return ipw_send_cmd_pdu(priv, IPW_CMD_WME_INFO, sizeof(*qos_param),
7280 qos_param);
James Ketrenosb095c382005-08-24 22:04:42 -05007281}
7282
Zhu Yie43e3c12006-04-13 17:20:45 +08007283#endif /* CONFIG_IPW2200_QOS */
James Ketrenosb095c382005-08-24 22:04:42 -05007284
James Ketrenos43f66a62005-03-25 12:31:53 -06007285static int ipw_associate_network(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007286 struct libipw_network *network,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007287 struct ipw_supported_rates *rates, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06007288{
7289 int err;
7290
7291 if (priv->config & CFG_FIXED_RATE)
James Ketrenosb095c382005-08-24 22:04:42 -05007292 ipw_set_fixed_rate(priv, network->mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06007293
7294 if (!(priv->config & CFG_STATIC_ESSID)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007295 priv->essid_len = min(network->ssid_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007296 (u8) IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06007297 memcpy(priv->essid, network->ssid, priv->essid_len);
7298 }
7299
7300 network->last_associate = jiffies;
7301
7302 memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
7303 priv->assoc_request.channel = network->channel;
Zhu Yi3e234b42006-01-24 16:36:52 +08007304 priv->assoc_request.auth_key = 0;
7305
James Ketrenos43f66a62005-03-25 12:31:53 -06007306 if ((priv->capability & CAP_PRIVACY_ON) &&
Zhu Yi3e234b42006-01-24 16:36:52 +08007307 (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007308 priv->assoc_request.auth_type = AUTH_SHARED_KEY;
James Ketrenosb095c382005-08-24 22:04:42 -05007309 priv->assoc_request.auth_key = priv->ieee->sec.active_key;
7310
Zhu Yi1ba61e02006-02-15 13:00:55 +08007311 if (priv->ieee->sec.level == SEC_LEVEL_1)
James Ketrenosb095c382005-08-24 22:04:42 -05007312 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
Zhu Yi3e234b42006-01-24 16:36:52 +08007313
7314 } else if ((priv->capability & CAP_PRIVACY_ON) &&
7315 (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP))
7316 priv->assoc_request.auth_type = AUTH_LEAP;
7317 else
James Ketrenos43f66a62005-03-25 12:31:53 -06007318 priv->assoc_request.auth_type = AUTH_OPEN;
James Ketrenos43f66a62005-03-25 12:31:53 -06007319
James Ketrenosa613bff2005-08-24 21:43:11 -05007320 if (priv->ieee->wpa_ie_len) {
Al Viro5b5e8072007-12-27 01:54:06 -05007321 priv->assoc_request.policy_support = cpu_to_le16(0x02); /* RSN active */
James Ketrenosea2b26e2005-08-24 21:25:16 -05007322 ipw_set_rsn_capa(priv, priv->ieee->wpa_ie,
7323 priv->ieee->wpa_ie_len);
7324 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007325
Jeff Garzikbf794512005-07-31 13:07:26 -04007326 /*
7327 * It is valid for our ieee device to support multiple modes, but
7328 * when it comes to associating to a given network we have to choose
James Ketrenos43f66a62005-03-25 12:31:53 -06007329 * just one mode.
7330 */
7331 if (network->mode & priv->ieee->mode & IEEE_A)
7332 priv->assoc_request.ieee_mode = IPW_A_MODE;
7333 else if (network->mode & priv->ieee->mode & IEEE_G)
7334 priv->assoc_request.ieee_mode = IPW_G_MODE;
7335 else if (network->mode & priv->ieee->mode & IEEE_B)
7336 priv->assoc_request.ieee_mode = IPW_B_MODE;
7337
Al Viro5b5e8072007-12-27 01:54:06 -05007338 priv->assoc_request.capability = cpu_to_le16(network->capability);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007339 if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
7340 && !(priv->config & CFG_PREAMBLE_LONG)) {
7341 priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE;
7342 } else {
7343 priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE;
7344
7345 /* Clear the short preamble if we won't be supporting it */
7346 priv->assoc_request.capability &=
Al Viro5b5e8072007-12-27 01:54:06 -05007347 ~cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007348 }
7349
James Ketrenosafbf30a2005-08-25 00:05:33 -05007350 /* Clear capability bits that aren't used in Ad Hoc */
7351 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
7352 priv->assoc_request.capability &=
Al Viro5b5e8072007-12-27 01:54:06 -05007353 ~cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME);
James Ketrenosafbf30a2005-08-25 00:05:33 -05007354
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07007355 IPW_DEBUG_ASSOC("%ssociation attempt: '%*pE', channel %d, 802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007356 roaming ? "Rea" : "A",
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07007357 priv->essid_len, priv->essid,
Jeff Garzikbf794512005-07-31 13:07:26 -04007358 network->channel,
7359 ipw_modes[priv->assoc_request.ieee_mode],
7360 rates->num_rates,
James Ketrenosea2b26e2005-08-24 21:25:16 -05007361 (priv->assoc_request.preamble_length ==
7362 DCT_FLAG_LONG_PREAMBLE) ? "long" : "short",
7363 network->capability &
7364 WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long",
James Ketrenos43f66a62005-03-25 12:31:53 -06007365 priv->capability & CAP_PRIVACY_ON ? "on " : "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04007366 priv->capability & CAP_PRIVACY_ON ?
7367 (priv->capability & CAP_SHARED_KEY ? "(shared)" :
James Ketrenos43f66a62005-03-25 12:31:53 -06007368 "(open)") : "",
7369 priv->capability & CAP_PRIVACY_ON ? " key=" : "",
Jeff Garzikbf794512005-07-31 13:07:26 -04007370 priv->capability & CAP_PRIVACY_ON ?
James Ketrenosb095c382005-08-24 22:04:42 -05007371 '1' + priv->ieee->sec.active_key : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007372 priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
James Ketrenos43f66a62005-03-25 12:31:53 -06007373
Al Viro5b5e8072007-12-27 01:54:06 -05007374 priv->assoc_request.beacon_interval = cpu_to_le16(network->beacon_interval);
James Ketrenos43f66a62005-03-25 12:31:53 -06007375 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007376 (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007377 priv->assoc_request.assoc_type = HC_IBSS_START;
7378 priv->assoc_request.assoc_tsf_msw = 0;
7379 priv->assoc_request.assoc_tsf_lsw = 0;
7380 } else {
7381 if (unlikely(roaming))
7382 priv->assoc_request.assoc_type = HC_REASSOCIATE;
7383 else
7384 priv->assoc_request.assoc_type = HC_ASSOCIATE;
Al Viro5b5e8072007-12-27 01:54:06 -05007385 priv->assoc_request.assoc_tsf_msw = cpu_to_le32(network->time_stamp[1]);
7386 priv->assoc_request.assoc_tsf_lsw = cpu_to_le32(network->time_stamp[0]);
James Ketrenos43f66a62005-03-25 12:31:53 -06007387 }
7388
James Ketrenosafbf30a2005-08-25 00:05:33 -05007389 memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06007390
7391 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
Joe Perches93803b32015-03-02 19:54:49 -08007392 eth_broadcast_addr(priv->assoc_request.dest);
Al Viro5b5e8072007-12-27 01:54:06 -05007393 priv->assoc_request.atim_window = cpu_to_le16(network->atim_window);
James Ketrenos43f66a62005-03-25 12:31:53 -06007394 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007395 memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06007396 priv->assoc_request.atim_window = 0;
7397 }
7398
Al Viro5b5e8072007-12-27 01:54:06 -05007399 priv->assoc_request.listen_interval = cpu_to_le16(network->listen_interval);
Jeff Garzikbf794512005-07-31 13:07:26 -04007400
James Ketrenos43f66a62005-03-25 12:31:53 -06007401 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
7402 if (err) {
7403 IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
7404 return err;
7405 }
7406
7407 rates->ieee_mode = priv->assoc_request.ieee_mode;
7408 rates->purpose = IPW_RATE_CONNECT;
7409 ipw_send_supported_rates(priv, rates);
Jeff Garzikbf794512005-07-31 13:07:26 -04007410
James Ketrenos43f66a62005-03-25 12:31:53 -06007411 if (priv->assoc_request.ieee_mode == IPW_G_MODE)
7412 priv->sys_config.dot11g_auto_detection = 1;
7413 else
7414 priv->sys_config.dot11g_auto_detection = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007415
7416 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
7417 priv->sys_config.answer_broadcast_ssid_probe = 1;
7418 else
7419 priv->sys_config.answer_broadcast_ssid_probe = 0;
7420
Zhu Yid685b8c2006-04-13 17:20:27 +08007421 err = ipw_send_system_config(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06007422 if (err) {
7423 IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
7424 return err;
7425 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007426
James Ketrenos43f66a62005-03-25 12:31:53 -06007427 IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007428 err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM);
James Ketrenos43f66a62005-03-25 12:31:53 -06007429 if (err) {
7430 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7431 return err;
7432 }
7433
7434 /*
7435 * If preemption is enabled, it is possible for the association
7436 * to complete before we return from ipw_send_associate. Therefore
7437 * we have to be sure and update our priviate data first.
7438 */
7439 priv->channel = network->channel;
7440 memcpy(priv->bssid, network->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04007441 priv->status |= STATUS_ASSOCIATING;
James Ketrenos43f66a62005-03-25 12:31:53 -06007442 priv->status &= ~STATUS_SECURITY_UPDATED;
7443
7444 priv->assoc_network = network;
7445
Zhu Yie43e3c12006-04-13 17:20:45 +08007446#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -05007447 ipw_qos_association(priv, network);
7448#endif
7449
James Ketrenos43f66a62005-03-25 12:31:53 -06007450 err = ipw_send_associate(priv, &priv->assoc_request);
7451 if (err) {
7452 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7453 return err;
7454 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007455
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07007456 IPW_DEBUG(IPW_DL_STATE, "associating: '%*pE' %pM\n",
7457 priv->essid_len, priv->essid, priv->bssid);
James Ketrenos43f66a62005-03-25 12:31:53 -06007458
7459 return 0;
7460}
7461
7462static void ipw_roam(void *data)
7463{
7464 struct ipw_priv *priv = data;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007465 struct libipw_network *network = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06007466 struct ipw_network_match match = {
7467 .network = priv->assoc_network
7468 };
7469
7470 /* The roaming process is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04007471 *
7472 * 1. Missed beacon threshold triggers the roaming process by
James Ketrenos43f66a62005-03-25 12:31:53 -06007473 * setting the status ROAM bit and requesting a scan.
7474 * 2. When the scan completes, it schedules the ROAM work
7475 * 3. The ROAM work looks at all of the known networks for one that
7476 * is a better network than the currently associated. If none
7477 * found, the ROAM process is over (ROAM bit cleared)
7478 * 4. If a better network is found, a disassociation request is
7479 * sent.
7480 * 5. When the disassociation completes, the roam work is again
7481 * scheduled. The second time through, the driver is no longer
7482 * associated, and the newly selected network is sent an
Jeff Garzikbf794512005-07-31 13:07:26 -04007483 * association request.
James Ketrenos43f66a62005-03-25 12:31:53 -06007484 * 6. At this point ,the roaming process is complete and the ROAM
7485 * status bit is cleared.
7486 */
7487
7488 /* If we are no longer associated, and the roaming bit is no longer
7489 * set, then we are not actively roaming, so just return */
7490 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
7491 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007492
James Ketrenos43f66a62005-03-25 12:31:53 -06007493 if (priv->status & STATUS_ASSOCIATED) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007494 /* First pass through ROAM process -- look for a better
James Ketrenos43f66a62005-03-25 12:31:53 -06007495 * network */
James Ketrenosa613bff2005-08-24 21:43:11 -05007496 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007497 u8 rssi = priv->assoc_network->stats.rssi;
7498 priv->assoc_network->stats.rssi = -128;
James Ketrenosa613bff2005-08-24 21:43:11 -05007499 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007500 list_for_each_entry(network, &priv->ieee->network_list, list) {
7501 if (network != priv->assoc_network)
7502 ipw_best_network(priv, &match, network, 1);
7503 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007504 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007505 priv->assoc_network->stats.rssi = rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04007506
James Ketrenos43f66a62005-03-25 12:31:53 -06007507 if (match.network == priv->assoc_network) {
7508 IPW_DEBUG_ASSOC("No better APs in this network to "
7509 "roam to.\n");
7510 priv->status &= ~STATUS_ROAMING;
7511 ipw_debug_config(priv);
7512 return;
7513 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007514
James Ketrenos43f66a62005-03-25 12:31:53 -06007515 ipw_send_disassociate(priv, 1);
7516 priv->assoc_network = match.network;
7517
7518 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007519 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007520
7521 /* Second pass through ROAM process -- request association */
7522 ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
7523 ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
7524 priv->status &= ~STATUS_ROAMING;
7525}
7526
David Howellsc4028952006-11-22 14:57:56 +00007527static void ipw_bg_roam(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -05007528{
David Howellsc4028952006-11-22 14:57:56 +00007529 struct ipw_priv *priv =
7530 container_of(work, struct ipw_priv, roam);
Zhu Yi46441512006-01-24 16:37:59 +08007531 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00007532 ipw_roam(priv);
Zhu Yi46441512006-01-24 16:37:59 +08007533 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007534}
7535
7536static int ipw_associate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06007537{
7538 struct ipw_priv *priv = data;
7539
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007540 struct libipw_network *network = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06007541 struct ipw_network_match match = {
7542 .network = NULL
7543 };
7544 struct ipw_supported_rates *rates;
7545 struct list_head *element;
James Ketrenosa613bff2005-08-24 21:43:11 -05007546 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007547
James Ketrenosb095c382005-08-24 22:04:42 -05007548 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
7549 IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n");
7550 return 0;
7551 }
7552
James Ketrenosc848d0a2005-08-24 21:56:24 -05007553 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007554 IPW_DEBUG_ASSOC("Not attempting association (already in "
7555 "progress)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007556 return 0;
7557 }
7558
Hong Liue6324722005-09-14 21:04:15 -05007559 if (priv->status & STATUS_DISASSOCIATING) {
Joe Perchesd602de8e2018-05-28 19:51:57 -07007560 IPW_DEBUG_ASSOC("Not attempting association (in disassociating)\n");
Tejun Heobcb6d912011-01-26 12:12:50 +01007561 schedule_work(&priv->associate);
Hong Liue6324722005-09-14 21:04:15 -05007562 return 0;
7563 }
7564
James Ketrenosc848d0a2005-08-24 21:56:24 -05007565 if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007566 IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
7567 "initialized)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007568 return 0;
7569 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007570
7571 if (!(priv->config & CFG_ASSOCIATE) &&
Alexey Fisher3e4127f2008-11-06 09:51:16 +01007572 !(priv->config & (CFG_STATIC_ESSID | CFG_STATIC_BSSID))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007573 IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007574 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007575 }
7576
James Ketrenosa613bff2005-08-24 21:43:11 -05007577 /* Protect our use of the network_list */
7578 spin_lock_irqsave(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007579 list_for_each_entry(network, &priv->ieee->network_list, list)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007580 ipw_best_network(priv, &match, network, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06007581
7582 network = match.network;
7583 rates = &match.rates;
7584
7585 if (network == NULL &&
7586 priv->ieee->iw_mode == IW_MODE_ADHOC &&
7587 priv->config & CFG_ADHOC_CREATE &&
7588 priv->config & CFG_STATIC_ESSID &&
Dan Williamsa6d4eae2008-05-29 14:38:28 -04007589 priv->config & CFG_STATIC_CHANNEL) {
7590 /* Use oldest network if the free list is empty */
7591 if (list_empty(&priv->ieee->network_free_list)) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007592 struct libipw_network *oldest = NULL;
7593 struct libipw_network *target;
Dan Williamsa6d4eae2008-05-29 14:38:28 -04007594
7595 list_for_each_entry(target, &priv->ieee->network_list, list) {
7596 if ((oldest == NULL) ||
7597 (target->last_scanned < oldest->last_scanned))
7598 oldest = target;
7599 }
7600
7601 /* If there are no more slots, expire the oldest */
7602 list_del(&oldest->list);
7603 target = oldest;
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07007604 IPW_DEBUG_ASSOC("Expired '%*pE' (%pM) from network list.\n",
7605 target->ssid_len, target->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07007606 target->bssid);
Dan Williamsa6d4eae2008-05-29 14:38:28 -04007607 list_add_tail(&target->list,
7608 &priv->ieee->network_free_list);
7609 }
7610
James Ketrenos43f66a62005-03-25 12:31:53 -06007611 element = priv->ieee->network_free_list.next;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007612 network = list_entry(element, struct libipw_network, list);
James Ketrenos43f66a62005-03-25 12:31:53 -06007613 ipw_adhoc_create(priv, network);
7614 rates = &priv->rates;
7615 list_del(element);
7616 list_add_tail(&network->list, &priv->ieee->network_list);
7617 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007618 spin_unlock_irqrestore(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007619
James Ketrenos43f66a62005-03-25 12:31:53 -06007620 /* If we reached the end of the list, then we don't have any valid
7621 * matching APs */
7622 if (!network) {
7623 ipw_debug_config(priv);
7624
James Ketrenosb095c382005-08-24 22:04:42 -05007625 if (!(priv->status & STATUS_SCANNING)) {
7626 if (!(priv->config & CFG_SPEED_SCAN))
Tejun Heobcb6d912011-01-26 12:12:50 +01007627 schedule_delayed_work(&priv->request_scan,
7628 SCAN_INTERVAL);
James Ketrenosb095c382005-08-24 22:04:42 -05007629 else
Tejun Heobcb6d912011-01-26 12:12:50 +01007630 schedule_delayed_work(&priv->request_scan, 0);
James Ketrenosb095c382005-08-24 22:04:42 -05007631 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007632
James Ketrenosc848d0a2005-08-24 21:56:24 -05007633 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007634 }
7635
7636 ipw_associate_network(priv, network, rates, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007637
7638 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06007639}
Jeff Garzikbf794512005-07-31 13:07:26 -04007640
David Howellsc4028952006-11-22 14:57:56 +00007641static void ipw_bg_associate(struct work_struct *work)
James Ketrenos43f66a62005-03-25 12:31:53 -06007642{
David Howellsc4028952006-11-22 14:57:56 +00007643 struct ipw_priv *priv =
7644 container_of(work, struct ipw_priv, associate);
Zhu Yi46441512006-01-24 16:37:59 +08007645 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00007646 ipw_associate(priv);
Zhu Yi46441512006-01-24 16:37:59 +08007647 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06007648}
7649
James Ketrenosb095c382005-08-24 22:04:42 -05007650static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
7651 struct sk_buff *skb)
7652{
7653 struct ieee80211_hdr *hdr;
7654 u16 fc;
7655
7656 hdr = (struct ieee80211_hdr *)skb->data;
John W. Linville72118012008-09-30 21:43:03 -04007657 fc = le16_to_cpu(hdr->frame_control);
James Ketrenosb095c382005-08-24 22:04:42 -05007658 if (!(fc & IEEE80211_FCTL_PROTECTED))
7659 return;
7660
7661 fc &= ~IEEE80211_FCTL_PROTECTED;
John W. Linville72118012008-09-30 21:43:03 -04007662 hdr->frame_control = cpu_to_le16(fc);
James Ketrenosb095c382005-08-24 22:04:42 -05007663 switch (priv->ieee->sec.level) {
7664 case SEC_LEVEL_3:
7665 /* Remove CCMP HDR */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007666 memmove(skb->data + LIBIPW_3ADDR_LEN,
7667 skb->data + LIBIPW_3ADDR_LEN + 8,
7668 skb->len - LIBIPW_3ADDR_LEN - 8);
Zhu Yif4ff4972005-09-12 10:48:48 -05007669 skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */
James Ketrenosb095c382005-08-24 22:04:42 -05007670 break;
7671 case SEC_LEVEL_2:
7672 break;
7673 case SEC_LEVEL_1:
7674 /* Remove IV */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007675 memmove(skb->data + LIBIPW_3ADDR_LEN,
7676 skb->data + LIBIPW_3ADDR_LEN + 4,
7677 skb->len - LIBIPW_3ADDR_LEN - 4);
Zhu Yif4ff4972005-09-12 10:48:48 -05007678 skb_trim(skb, skb->len - 8); /* IV + ICV */
James Ketrenosb095c382005-08-24 22:04:42 -05007679 break;
7680 case SEC_LEVEL_0:
7681 break;
7682 default:
André Goddard Rosaaf901ca2009-11-14 13:09:05 -02007683 printk(KERN_ERR "Unknown security level %d\n",
James Ketrenosb095c382005-08-24 22:04:42 -05007684 priv->ieee->sec.level);
7685 break;
7686 }
7687}
7688
7689static void ipw_handle_data_packet(struct ipw_priv *priv,
7690 struct ipw_rx_mem_buffer *rxb,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007691 struct libipw_rx_stats *stats)
James Ketrenos43f66a62005-03-25 12:31:53 -06007692{
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007693 struct net_device *dev = priv->net_dev;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007694 struct libipw_hdr_4addr *hdr;
James Ketrenos43f66a62005-03-25 12:31:53 -06007695 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7696
7697 /* We received data from the HW, so stop the watchdog */
Florian Westphal860e9532016-05-03 16:33:13 +02007698 netif_trans_update(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06007699
Jeff Garzikbf794512005-07-31 13:07:26 -04007700 /* We only process data packets if the
James Ketrenos43f66a62005-03-25 12:31:53 -06007701 * interface is open */
James Ketrenosa613bff2005-08-24 21:43:11 -05007702 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
James Ketrenos43f66a62005-03-25 12:31:53 -06007703 skb_tailroom(rxb->skb))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007704 dev->stats.rx_errors++;
James Ketrenos43f66a62005-03-25 12:31:53 -06007705 priv->wstats.discard.misc++;
7706 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7707 return;
7708 } else if (unlikely(!netif_running(priv->net_dev))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007709 dev->stats.rx_dropped++;
James Ketrenos43f66a62005-03-25 12:31:53 -06007710 priv->wstats.discard.misc++;
7711 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7712 return;
7713 }
7714
7715 /* Advance skb->data to the start of the actual payload */
Jiri Bencaaa4d302005-06-07 14:58:41 +02007716 skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
James Ketrenos43f66a62005-03-25 12:31:53 -06007717
7718 /* Set the size of the skb to the size of the frame */
James Ketrenosa613bff2005-08-24 21:43:11 -05007719 skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
James Ketrenos43f66a62005-03-25 12:31:53 -06007720
7721 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7722
James Ketrenosb095c382005-08-24 22:04:42 -05007723 /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007724 hdr = (struct libipw_hdr_4addr *)rxb->skb->data;
Hong Liu567deaf2005-08-31 18:07:22 +08007725 if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
Stephen Hemminger3c190652006-01-03 15:27:38 -08007726 (is_multicast_ether_addr(hdr->addr1) ?
Hong Liu567deaf2005-08-31 18:07:22 +08007727 !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
James Ketrenosb095c382005-08-24 22:04:42 -05007728 ipw_rebuild_decrypted_skb(priv, rxb->skb);
7729
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007730 if (!libipw_rx(priv->ieee, rxb->skb, stats))
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007731 dev->stats.rx_errors++;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007732 else { /* libipw_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007733 rxb->skb = NULL;
James Ketrenosb095c382005-08-24 22:04:42 -05007734 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05007735 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007736}
7737
Zhu Yi459d4082006-04-13 17:21:00 +08007738#ifdef CONFIG_IPW2200_RADIOTAP
Mike Kershaw24a47db2005-08-26 00:41:54 -05007739static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
7740 struct ipw_rx_mem_buffer *rxb,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007741 struct libipw_rx_stats *stats)
Mike Kershaw24a47db2005-08-26 00:41:54 -05007742{
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007743 struct net_device *dev = priv->net_dev;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007744 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7745 struct ipw_rx_frame *frame = &pkt->u.frame;
7746
7747 /* initial pull of some data */
7748 u16 received_channel = frame->received_channel;
7749 u8 antennaAndPhy = frame->antennaAndPhy;
7750 s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */
7751 u16 pktrate = frame->rate;
7752
7753 /* Magic struct that slots into the radiotap header -- no reason
7754 * to build this manually element by element, we can write it much
7755 * more efficiently than we can parse it. ORDER MATTERS HERE */
Zhu Yid685b8c2006-04-13 17:20:27 +08007756 struct ipw_rt_hdr *ipw_rt;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007757
Dan Carpenter92c1ff12012-01-12 09:34:50 +03007758 unsigned short len = le16_to_cpu(pkt->u.frame.length);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007759
7760 /* We received data from the HW, so stop the watchdog */
Florian Westphal860e9532016-05-03 16:33:13 +02007761 netif_trans_update(dev);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007762
7763 /* We only process data packets if the
7764 * interface is open */
7765 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
7766 skb_tailroom(rxb->skb))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007767 dev->stats.rx_errors++;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007768 priv->wstats.discard.misc++;
7769 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7770 return;
7771 } else if (unlikely(!netif_running(priv->net_dev))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007772 dev->stats.rx_dropped++;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007773 priv->wstats.discard.misc++;
7774 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7775 return;
7776 }
7777
7778 /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
7779 * that now */
7780 if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
7781 /* FIXME: Should alloc bigger skb instead */
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007782 dev->stats.rx_dropped++;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007783 priv->wstats.discard.misc++;
7784 IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
7785 return;
7786 }
7787
7788 /* copy the frame itself */
7789 memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr),
7790 rxb->skb->data + IPW_RX_FRAME_SIZE, len);
7791
Mike Kershaw24a47db2005-08-26 00:41:54 -05007792 ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data;
7793
7794 ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
7795 ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
Al Viro743b84d2007-12-27 01:43:16 -05007796 ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total header+data */
Mike Kershaw24a47db2005-08-26 00:41:54 -05007797
7798 /* Big bitfield of all the fields we provide in radiotap */
Al Viro743b84d2007-12-27 01:43:16 -05007799 ipw_rt->rt_hdr.it_present = cpu_to_le32(
7800 (1 << IEEE80211_RADIOTAP_TSFT) |
Zhu Yi4b1f8a92006-12-05 14:42:14 +08007801 (1 << IEEE80211_RADIOTAP_FLAGS) |
Mike Kershaw24a47db2005-08-26 00:41:54 -05007802 (1 << IEEE80211_RADIOTAP_RATE) |
7803 (1 << IEEE80211_RADIOTAP_CHANNEL) |
7804 (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
Zhu Yid685b8c2006-04-13 17:20:27 +08007805 (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
Mike Kershaw24a47db2005-08-26 00:41:54 -05007806 (1 << IEEE80211_RADIOTAP_ANTENNA));
7807
7808 /* Zero the flags, we'll add to them as we go */
7809 ipw_rt->rt_flags = 0;
Zhu Yi4b1f8a92006-12-05 14:42:14 +08007810 ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
7811 frame->parent_tsf[2] << 16 |
7812 frame->parent_tsf[1] << 8 |
7813 frame->parent_tsf[0]);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007814
7815 /* Convert signal to DBM */
7816 ipw_rt->rt_dbmsignal = antsignal;
Reinette Chatre21f8a732009-08-18 10:25:05 -07007817 ipw_rt->rt_dbmnoise = (s8) le16_to_cpu(frame->noise);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007818
7819 /* Convert the channel data and set the flags */
7820 ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
7821 if (received_channel > 14) { /* 802.11a */
7822 ipw_rt->rt_chbitmask =
7823 cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
7824 } else if (antennaAndPhy & 32) { /* 802.11b */
7825 ipw_rt->rt_chbitmask =
7826 cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
7827 } else { /* 802.11g */
7828 ipw_rt->rt_chbitmask =
Al Viro472caf82007-12-27 01:50:54 -05007829 cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007830 }
7831
7832 /* set the rate in multiples of 500k/s */
7833 switch (pktrate) {
7834 case IPW_TX_RATE_1MB:
7835 ipw_rt->rt_rate = 2;
7836 break;
7837 case IPW_TX_RATE_2MB:
7838 ipw_rt->rt_rate = 4;
7839 break;
7840 case IPW_TX_RATE_5MB:
7841 ipw_rt->rt_rate = 10;
7842 break;
7843 case IPW_TX_RATE_6MB:
7844 ipw_rt->rt_rate = 12;
7845 break;
7846 case IPW_TX_RATE_9MB:
7847 ipw_rt->rt_rate = 18;
7848 break;
7849 case IPW_TX_RATE_11MB:
7850 ipw_rt->rt_rate = 22;
7851 break;
7852 case IPW_TX_RATE_12MB:
7853 ipw_rt->rt_rate = 24;
7854 break;
7855 case IPW_TX_RATE_18MB:
7856 ipw_rt->rt_rate = 36;
7857 break;
7858 case IPW_TX_RATE_24MB:
7859 ipw_rt->rt_rate = 48;
7860 break;
7861 case IPW_TX_RATE_36MB:
7862 ipw_rt->rt_rate = 72;
7863 break;
7864 case IPW_TX_RATE_48MB:
7865 ipw_rt->rt_rate = 96;
7866 break;
7867 case IPW_TX_RATE_54MB:
7868 ipw_rt->rt_rate = 108;
7869 break;
7870 default:
7871 ipw_rt->rt_rate = 0;
7872 break;
7873 }
7874
7875 /* antenna number */
7876 ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */
7877
7878 /* set the preamble flag if we have it */
7879 if ((antennaAndPhy & 64))
7880 ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
7881
7882 /* Set the size of the skb to the size of the frame */
7883 skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr));
James Ketrenos43f66a62005-03-25 12:31:53 -06007884
7885 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7886
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007887 if (!libipw_rx(priv->ieee, rxb->skb, stats))
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007888 dev->stats.rx_errors++;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007889 else { /* libipw_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007890 rxb->skb = NULL;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007891 /* no LED during capture */
7892 }
7893}
7894#endif
7895
Zhu Yid685b8c2006-04-13 17:20:27 +08007896#ifdef CONFIG_IPW2200_PROMISCUOUS
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007897#define libipw_is_probe_response(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007898 ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \
7899 (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP )
7900
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007901#define libipw_is_management(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007902 ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
7903
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007904#define libipw_is_control(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007905 ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
7906
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007907#define libipw_is_data(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007908 ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
7909
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007910#define libipw_is_assoc_request(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007911 ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ)
7912
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007913#define libipw_is_reassoc_request(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007914 ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ)
7915
7916static void ipw_handle_promiscuous_rx(struct ipw_priv *priv,
7917 struct ipw_rx_mem_buffer *rxb,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007918 struct libipw_rx_stats *stats)
Zhu Yid685b8c2006-04-13 17:20:27 +08007919{
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007920 struct net_device *dev = priv->prom_net_dev;
Zhu Yid685b8c2006-04-13 17:20:27 +08007921 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7922 struct ipw_rx_frame *frame = &pkt->u.frame;
7923 struct ipw_rt_hdr *ipw_rt;
7924
7925 /* First cache any information we need before we overwrite
7926 * the information provided in the skb from the hardware */
7927 struct ieee80211_hdr *hdr;
7928 u16 channel = frame->received_channel;
7929 u8 phy_flags = frame->antennaAndPhy;
7930 s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM;
Reinette Chatre21f8a732009-08-18 10:25:05 -07007931 s8 noise = (s8) le16_to_cpu(frame->noise);
Zhu Yid685b8c2006-04-13 17:20:27 +08007932 u8 rate = frame->rate;
Dan Carpenter92c1ff12012-01-12 09:34:50 +03007933 unsigned short len = le16_to_cpu(pkt->u.frame.length);
Zhu Yid685b8c2006-04-13 17:20:27 +08007934 struct sk_buff *skb;
7935 int hdr_only = 0;
7936 u16 filter = priv->prom_priv->filter;
7937
7938 /* If the filter is set to not include Rx frames then return */
7939 if (filter & IPW_PROM_NO_RX)
7940 return;
7941
Zhu Yid685b8c2006-04-13 17:20:27 +08007942 /* We received data from the HW, so stop the watchdog */
Florian Westphal860e9532016-05-03 16:33:13 +02007943 netif_trans_update(dev);
Zhu Yid685b8c2006-04-13 17:20:27 +08007944
7945 if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007946 dev->stats.rx_errors++;
Zhu Yid685b8c2006-04-13 17:20:27 +08007947 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7948 return;
7949 }
7950
7951 /* We only process data packets if the interface is open */
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007952 if (unlikely(!netif_running(dev))) {
7953 dev->stats.rx_dropped++;
Zhu Yid685b8c2006-04-13 17:20:27 +08007954 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7955 return;
7956 }
7957
7958 /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
7959 * that now */
7960 if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
7961 /* FIXME: Should alloc bigger skb instead */
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007962 dev->stats.rx_dropped++;
Zhu Yid685b8c2006-04-13 17:20:27 +08007963 IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
7964 return;
7965 }
7966
7967 hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007968 if (libipw_is_management(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +08007969 if (filter & IPW_PROM_NO_MGMT)
7970 return;
7971 if (filter & IPW_PROM_MGMT_HEADER_ONLY)
7972 hdr_only = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007973 } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +08007974 if (filter & IPW_PROM_NO_CTL)
7975 return;
7976 if (filter & IPW_PROM_CTL_HEADER_ONLY)
7977 hdr_only = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007978 } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +08007979 if (filter & IPW_PROM_NO_DATA)
7980 return;
7981 if (filter & IPW_PROM_DATA_HEADER_ONLY)
7982 hdr_only = 1;
7983 }
7984
7985 /* Copy the SKB since this is for the promiscuous side */
7986 skb = skb_copy(rxb->skb, GFP_ATOMIC);
7987 if (skb == NULL) {
7988 IPW_ERROR("skb_clone failed for promiscuous copy.\n");
7989 return;
7990 }
7991
7992 /* copy the frame data to write after where the radiotap header goes */
7993 ipw_rt = (void *)skb->data;
7994
7995 if (hdr_only)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007996 len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control));
Zhu Yid685b8c2006-04-13 17:20:27 +08007997
7998 memcpy(ipw_rt->payload, hdr, len);
7999
Zhu Yid685b8c2006-04-13 17:20:27 +08008000 ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
8001 ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
Al Viro743b84d2007-12-27 01:43:16 -05008002 ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*ipw_rt)); /* total header+data */
Zhu Yid685b8c2006-04-13 17:20:27 +08008003
8004 /* Set the size of the skb to the size of the frame */
Al Viro743b84d2007-12-27 01:43:16 -05008005 skb_put(skb, sizeof(*ipw_rt) + len);
Zhu Yid685b8c2006-04-13 17:20:27 +08008006
8007 /* Big bitfield of all the fields we provide in radiotap */
Al Viro743b84d2007-12-27 01:43:16 -05008008 ipw_rt->rt_hdr.it_present = cpu_to_le32(
8009 (1 << IEEE80211_RADIOTAP_TSFT) |
Zhu Yi4b1f8a92006-12-05 14:42:14 +08008010 (1 << IEEE80211_RADIOTAP_FLAGS) |
Zhu Yid685b8c2006-04-13 17:20:27 +08008011 (1 << IEEE80211_RADIOTAP_RATE) |
8012 (1 << IEEE80211_RADIOTAP_CHANNEL) |
8013 (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
8014 (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
8015 (1 << IEEE80211_RADIOTAP_ANTENNA));
8016
8017 /* Zero the flags, we'll add to them as we go */
8018 ipw_rt->rt_flags = 0;
Zhu Yi4b1f8a92006-12-05 14:42:14 +08008019 ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
8020 frame->parent_tsf[2] << 16 |
8021 frame->parent_tsf[1] << 8 |
8022 frame->parent_tsf[0]);
Zhu Yid685b8c2006-04-13 17:20:27 +08008023
8024 /* Convert to DBM */
8025 ipw_rt->rt_dbmsignal = signal;
8026 ipw_rt->rt_dbmnoise = noise;
8027
8028 /* Convert the channel data and set the flags */
8029 ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel));
8030 if (channel > 14) { /* 802.11a */
8031 ipw_rt->rt_chbitmask =
8032 cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
8033 } else if (phy_flags & (1 << 5)) { /* 802.11b */
8034 ipw_rt->rt_chbitmask =
8035 cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
8036 } else { /* 802.11g */
8037 ipw_rt->rt_chbitmask =
Al Viro472caf82007-12-27 01:50:54 -05008038 cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
Zhu Yid685b8c2006-04-13 17:20:27 +08008039 }
8040
8041 /* set the rate in multiples of 500k/s */
8042 switch (rate) {
8043 case IPW_TX_RATE_1MB:
8044 ipw_rt->rt_rate = 2;
8045 break;
8046 case IPW_TX_RATE_2MB:
8047 ipw_rt->rt_rate = 4;
8048 break;
8049 case IPW_TX_RATE_5MB:
8050 ipw_rt->rt_rate = 10;
8051 break;
8052 case IPW_TX_RATE_6MB:
8053 ipw_rt->rt_rate = 12;
8054 break;
8055 case IPW_TX_RATE_9MB:
8056 ipw_rt->rt_rate = 18;
8057 break;
8058 case IPW_TX_RATE_11MB:
8059 ipw_rt->rt_rate = 22;
8060 break;
8061 case IPW_TX_RATE_12MB:
8062 ipw_rt->rt_rate = 24;
8063 break;
8064 case IPW_TX_RATE_18MB:
8065 ipw_rt->rt_rate = 36;
8066 break;
8067 case IPW_TX_RATE_24MB:
8068 ipw_rt->rt_rate = 48;
8069 break;
8070 case IPW_TX_RATE_36MB:
8071 ipw_rt->rt_rate = 72;
8072 break;
8073 case IPW_TX_RATE_48MB:
8074 ipw_rt->rt_rate = 96;
8075 break;
8076 case IPW_TX_RATE_54MB:
8077 ipw_rt->rt_rate = 108;
8078 break;
8079 default:
8080 ipw_rt->rt_rate = 0;
8081 break;
8082 }
8083
8084 /* antenna number */
8085 ipw_rt->rt_antenna = (phy_flags & 3);
8086
8087 /* set the preamble flag if we have it */
8088 if (phy_flags & (1 << 6))
8089 ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
8090
8091 IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len);
8092
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008093 if (!libipw_rx(priv->prom_priv->ieee, skb, stats)) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00008094 dev->stats.rx_errors++;
Zhu Yid685b8c2006-04-13 17:20:27 +08008095 dev_kfree_skb_any(skb);
8096 }
8097}
8098#endif
8099
Arjan van de Ven858119e2006-01-14 13:20:43 -08008100static int is_network_packet(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008101 struct libipw_hdr_4addr *header)
James Ketrenosea2b26e2005-08-24 21:25:16 -05008102{
Lucas De Marchi25985ed2011-03-30 22:57:33 -03008103 /* Filter incoming packets to determine if they are targeted toward
James Ketrenosea2b26e2005-08-24 21:25:16 -05008104 * this network, discarding packets coming from ourselves */
8105 switch (priv->ieee->iw_mode) {
James Ketrenosa613bff2005-08-24 21:43:11 -05008106 case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008107 /* packets from our adapter are dropped (echo) */
dingtianhong36325f32013-12-26 19:41:23 +08008108 if (ether_addr_equal(header->addr2, priv->net_dev->dev_addr))
James Ketrenosc848d0a2005-08-24 21:56:24 -05008109 return 0;
8110
Peter Jones90700fd2005-08-26 16:51:06 -05008111 /* {broad,multi}cast packets to our BSSID go through */
Stephen Hemminger3c190652006-01-03 15:27:38 -08008112 if (is_multicast_ether_addr(header->addr1))
dingtianhong36325f32013-12-26 19:41:23 +08008113 return ether_addr_equal(header->addr3, priv->bssid);
James Ketrenosa613bff2005-08-24 21:43:11 -05008114
8115 /* packets to our adapter go through */
dingtianhong36325f32013-12-26 19:41:23 +08008116 return ether_addr_equal(header->addr1,
8117 priv->net_dev->dev_addr);
James Ketrenosa613bff2005-08-24 21:43:11 -05008118
Peter Jones90700fd2005-08-26 16:51:06 -05008119 case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008120 /* packets from our adapter are dropped (echo) */
dingtianhong36325f32013-12-26 19:41:23 +08008121 if (ether_addr_equal(header->addr3, priv->net_dev->dev_addr))
James Ketrenosc848d0a2005-08-24 21:56:24 -05008122 return 0;
8123
Peter Jones90700fd2005-08-26 16:51:06 -05008124 /* {broad,multi}cast packets to our BSS go through */
Stephen Hemminger3c190652006-01-03 15:27:38 -08008125 if (is_multicast_ether_addr(header->addr1))
dingtianhong36325f32013-12-26 19:41:23 +08008126 return ether_addr_equal(header->addr2, priv->bssid);
James Ketrenosa613bff2005-08-24 21:43:11 -05008127
8128 /* packets to our adapter go through */
dingtianhong36325f32013-12-26 19:41:23 +08008129 return ether_addr_equal(header->addr1,
8130 priv->net_dev->dev_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008131 }
James Ketrenosa613bff2005-08-24 21:43:11 -05008132
James Ketrenosea2b26e2005-08-24 21:25:16 -05008133 return 1;
8134}
8135
James Ketrenosafbf30a2005-08-25 00:05:33 -05008136#define IPW_PACKET_RETRY_TIME HZ
8137
Arjan van de Ven858119e2006-01-14 13:20:43 -08008138static int is_duplicate_packet(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008139 struct libipw_hdr_4addr *header)
James Ketrenosafbf30a2005-08-25 00:05:33 -05008140{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008141 u16 sc = le16_to_cpu(header->seq_ctl);
8142 u16 seq = WLAN_GET_SEQ_SEQ(sc);
8143 u16 frag = WLAN_GET_SEQ_FRAG(sc);
8144 u16 *last_seq, *last_frag;
8145 unsigned long *last_time;
8146
8147 switch (priv->ieee->iw_mode) {
8148 case IW_MODE_ADHOC:
8149 {
8150 struct list_head *p;
8151 struct ipw_ibss_seq *entry = NULL;
8152 u8 *mac = header->addr2;
8153 int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE;
8154
Dave Jones5cb06562013-07-03 15:05:18 -07008155 list_for_each(p, &priv->ibss_mac_hash[index]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008156 entry =
8157 list_entry(p, struct ipw_ibss_seq, list);
dingtianhong36325f32013-12-26 19:41:23 +08008158 if (ether_addr_equal(entry->mac, mac))
James Ketrenosafbf30a2005-08-25 00:05:33 -05008159 break;
8160 }
8161 if (p == &priv->ibss_mac_hash[index]) {
8162 entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
8163 if (!entry) {
8164 IPW_ERROR
8165 ("Cannot malloc new mac entry\n");
8166 return 0;
8167 }
8168 memcpy(entry->mac, mac, ETH_ALEN);
8169 entry->seq_num = seq;
8170 entry->frag_num = frag;
8171 entry->packet_time = jiffies;
8172 list_add(&entry->list,
8173 &priv->ibss_mac_hash[index]);
8174 return 0;
8175 }
8176 last_seq = &entry->seq_num;
8177 last_frag = &entry->frag_num;
8178 last_time = &entry->packet_time;
8179 break;
8180 }
8181 case IW_MODE_INFRA:
8182 last_seq = &priv->last_seq_num;
8183 last_frag = &priv->last_frag_num;
8184 last_time = &priv->last_packet_time;
8185 break;
8186 default:
8187 return 0;
8188 }
8189 if ((*last_seq == seq) &&
8190 time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) {
8191 if (*last_frag == frag)
8192 goto drop;
8193 if (*last_frag + 1 != frag)
8194 /* out-of-order fragment */
8195 goto drop;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008196 } else
8197 *last_seq = seq;
8198
Zhu Yif57ce7c2005-07-13 12:22:15 -05008199 *last_frag = frag;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008200 *last_time = jiffies;
8201 return 0;
8202
8203 drop:
Zhu Yi87b016c2005-08-05 17:17:35 +08008204 /* Comment this line now since we observed the card receives
8205 * duplicate packets but the FCTL_RETRY bit is not set in the
8206 * IBSS mode with fragmentation enabled.
John W. Linville72118012008-09-30 21:43:03 -04008207 BUG_ON(!(le16_to_cpu(header->frame_control) & IEEE80211_FCTL_RETRY)); */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008208 return 1;
8209}
8210
James Ketrenosb095c382005-08-24 22:04:42 -05008211static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
8212 struct ipw_rx_mem_buffer *rxb,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008213 struct libipw_rx_stats *stats)
James Ketrenosb095c382005-08-24 22:04:42 -05008214{
8215 struct sk_buff *skb = rxb->skb;
8216 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008217 struct libipw_hdr_4addr *header = (struct libipw_hdr_4addr *)
James Ketrenosb095c382005-08-24 22:04:42 -05008218 (skb->data + IPW_RX_FRAME_SIZE);
8219
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008220 libipw_rx_mgt(priv->ieee, header, stats);
James Ketrenosb095c382005-08-24 22:04:42 -05008221
8222 if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
8223 ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
8224 IEEE80211_STYPE_PROBE_RESP) ||
8225 (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
8226 IEEE80211_STYPE_BEACON))) {
dingtianhong36325f32013-12-26 19:41:23 +08008227 if (ether_addr_equal(header->addr3, priv->bssid))
James Ketrenosb095c382005-08-24 22:04:42 -05008228 ipw_add_station(priv, header->addr2);
8229 }
8230
8231 if (priv->config & CFG_NET_STATS) {
8232 IPW_DEBUG_HC("sending stat packet\n");
8233
8234 /* Set the size of the skb to the size of the full
8235 * ipw header and 802.11 frame */
8236 skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
8237 IPW_RX_FRAME_SIZE);
8238
8239 /* Advance past the ipw packet header to the 802.11 frame */
8240 skb_pull(skb, IPW_RX_FRAME_SIZE);
8241
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008242 /* Push the libipw_rx_stats before the 802.11 frame */
James Ketrenosb095c382005-08-24 22:04:42 -05008243 memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
8244
8245 skb->dev = priv->ieee->dev;
8246
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008247 /* Point raw at the libipw_stats */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07008248 skb_reset_mac_header(skb);
James Ketrenosb095c382005-08-24 22:04:42 -05008249
8250 skb->pkt_type = PACKET_OTHERHOST;
Harvey Harrisonc1b4aa32009-01-29 13:26:44 -08008251 skb->protocol = cpu_to_be16(ETH_P_80211_STATS);
James Ketrenosb095c382005-08-24 22:04:42 -05008252 memset(skb->cb, 0, sizeof(rxb->skb->cb));
8253 netif_rx(skb);
8254 rxb->skb = NULL;
8255 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008256}
8257
James Ketrenos43f66a62005-03-25 12:31:53 -06008258/*
Lucas De Marchi25985ed2011-03-30 22:57:33 -03008259 * Main entry function for receiving a packet with 80211 headers. This
James Ketrenos43f66a62005-03-25 12:31:53 -06008260 * should be called when ever the FW has notified us that there is a new
Lucas De Marchi25985ed2011-03-30 22:57:33 -03008261 * skb in the receive queue.
James Ketrenos43f66a62005-03-25 12:31:53 -06008262 */
8263static void ipw_rx(struct ipw_priv *priv)
8264{
8265 struct ipw_rx_mem_buffer *rxb;
8266 struct ipw_rx_packet *pkt;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008267 struct libipw_hdr_4addr *header;
James Ketrenos43f66a62005-03-25 12:31:53 -06008268 u32 r, w, i;
8269 u8 network_packet;
Dan Williams943dbef2008-02-14 17:49:41 -05008270 u8 fill_rx = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008271
James Ketrenosb095c382005-08-24 22:04:42 -05008272 r = ipw_read32(priv, IPW_RX_READ_INDEX);
8273 w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
Dan Williams943dbef2008-02-14 17:49:41 -05008274 i = priv->rxq->read;
8275
8276 if (ipw_rx_queue_space (priv->rxq) > (RX_QUEUE_SIZE / 2))
8277 fill_rx = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06008278
8279 while (i != r) {
8280 rxb = priv->rxq->queue[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06008281 if (unlikely(rxb == NULL)) {
8282 printk(KERN_CRIT "Queue not allocated!\n");
8283 break;
8284 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008285 priv->rxq->queue[i] = NULL;
8286
8287 pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05008288 IPW_RX_BUF_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06008289 PCI_DMA_FROMDEVICE);
8290
8291 pkt = (struct ipw_rx_packet *)rxb->skb->data;
8292 IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
8293 pkt->header.message_type,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008294 pkt->header.rx_seq_num, pkt->header.control_bits);
James Ketrenos43f66a62005-03-25 12:31:53 -06008295
8296 switch (pkt->header.message_type) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008297 case RX_FRAME_TYPE: /* 802.11 frame */ {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008298 struct libipw_rx_stats stats = {
Zhu Yi851ca262006-08-21 11:37:58 +08008299 .rssi = pkt->u.frame.rssi_dbm -
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008300 IPW_RSSI_TO_DBM,
James Ketrenosc848d0a2005-08-24 21:56:24 -05008301 .signal =
Reinette Chatre21f8a732009-08-18 10:25:05 -07008302 pkt->u.frame.rssi_dbm -
Bill Mossb1916082006-02-15 08:50:18 +08008303 IPW_RSSI_TO_DBM + 0x100,
James Ketrenosc848d0a2005-08-24 21:56:24 -05008304 .noise =
8305 le16_to_cpu(pkt->u.frame.noise),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008306 .rate = pkt->u.frame.rate,
8307 .mac_time = jiffies,
8308 .received_channel =
8309 pkt->u.frame.received_channel,
8310 .freq =
8311 (pkt->u.frame.
8312 control & (1 << 0)) ?
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008313 LIBIPW_24GHZ_BAND :
8314 LIBIPW_52GHZ_BAND,
James Ketrenosa613bff2005-08-24 21:43:11 -05008315 .len = le16_to_cpu(pkt->u.frame.length),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008316 };
James Ketrenos43f66a62005-03-25 12:31:53 -06008317
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008318 if (stats.rssi != 0)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008319 stats.mask |= LIBIPW_STATMASK_RSSI;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008320 if (stats.signal != 0)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008321 stats.mask |= LIBIPW_STATMASK_SIGNAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008322 if (stats.noise != 0)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008323 stats.mask |= LIBIPW_STATMASK_NOISE;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008324 if (stats.rate != 0)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008325 stats.mask |= LIBIPW_STATMASK_RATE;
James Ketrenos43f66a62005-03-25 12:31:53 -06008326
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008327 priv->rx_packets++;
James Ketrenos43f66a62005-03-25 12:31:53 -06008328
Zhu Yid685b8c2006-04-13 17:20:27 +08008329#ifdef CONFIG_IPW2200_PROMISCUOUS
8330 if (priv->prom_net_dev && netif_running(priv->prom_net_dev))
8331 ipw_handle_promiscuous_rx(priv, rxb, &stats);
8332#endif
8333
James Ketrenosb095c382005-08-24 22:04:42 -05008334#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008335 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
Zhu Yi459d4082006-04-13 17:21:00 +08008336#ifdef CONFIG_IPW2200_RADIOTAP
Zhu Yid685b8c2006-04-13 17:20:27 +08008337
8338 ipw_handle_data_packet_monitor(priv,
8339 rxb,
8340 &stats);
Mike Kershaw24a47db2005-08-26 00:41:54 -05008341#else
Zhu Yid685b8c2006-04-13 17:20:27 +08008342 ipw_handle_data_packet(priv, rxb,
8343 &stats);
Mike Kershaw24a47db2005-08-26 00:41:54 -05008344#endif
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008345 break;
8346 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008347#endif
Jeff Garzikbf794512005-07-31 13:07:26 -04008348
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008349 header =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008350 (struct libipw_hdr_4addr *)(rxb->skb->
James Ketrenos0dacca12005-09-21 12:23:41 -05008351 data +
8352 IPW_RX_FRAME_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06008353 /* TODO: Check Ad-Hoc dest/source and make sure
8354 * that we are actually parsing these packets
Jeff Garzikbf794512005-07-31 13:07:26 -04008355 * correctly -- we should probably use the
James Ketrenos43f66a62005-03-25 12:31:53 -06008356 * frame control of the packet and disregard
8357 * the current iw_mode */
James Ketrenos43f66a62005-03-25 12:31:53 -06008358
James Ketrenosea2b26e2005-08-24 21:25:16 -05008359 network_packet =
8360 is_network_packet(priv, header);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008361 if (network_packet && priv->assoc_network) {
8362 priv->assoc_network->stats.rssi =
8363 stats.rssi;
Zhu Yi00d21de2006-04-13 17:19:02 +08008364 priv->exp_avg_rssi =
8365 exponential_average(priv->exp_avg_rssi,
8366 stats.rssi, DEPTH_RSSI);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008367 }
8368
8369 IPW_DEBUG_RX("Frame: len=%u\n",
James Ketrenosa613bff2005-08-24 21:43:11 -05008370 le16_to_cpu(pkt->u.frame.length));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008371
James Ketrenosa613bff2005-08-24 21:43:11 -05008372 if (le16_to_cpu(pkt->u.frame.length) <
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008373 libipw_get_hdrlen(le16_to_cpu(
Zhu Yi9d0be032006-02-15 06:18:19 +08008374 header->frame_ctl))) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008375 IPW_DEBUG_DROP
8376 ("Received packet is too small. "
8377 "Dropping.\n");
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00008378 priv->net_dev->stats.rx_errors++;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008379 priv->wstats.discard.misc++;
8380 break;
8381 }
8382
James Ketrenosa613bff2005-08-24 21:43:11 -05008383 switch (WLAN_FC_GET_TYPE
8384 (le16_to_cpu(header->frame_ctl))) {
James Ketrenosb095c382005-08-24 22:04:42 -05008385
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008386 case IEEE80211_FTYPE_MGMT:
James Ketrenosb095c382005-08-24 22:04:42 -05008387 ipw_handle_mgmt_packet(priv, rxb,
8388 &stats);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008389 break;
8390
8391 case IEEE80211_FTYPE_CTL:
8392 break;
8393
8394 case IEEE80211_FTYPE_DATA:
James Ketrenosafbf30a2005-08-25 00:05:33 -05008395 if (unlikely(!network_packet ||
8396 is_duplicate_packet(priv,
8397 header)))
8398 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008399 IPW_DEBUG_DROP("Dropping: "
Johannes Berge1749612008-10-27 15:59:26 -07008400 "%pM, "
8401 "%pM, "
8402 "%pM\n",
8403 header->addr1,
8404 header->addr2,
8405 header->addr3);
James Ketrenosb095c382005-08-24 22:04:42 -05008406 break;
8407 }
8408
8409 ipw_handle_data_packet(priv, rxb,
8410 &stats);
8411
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008412 break;
8413 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008414 break;
8415 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008416
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008417 case RX_HOST_NOTIFICATION_TYPE:{
8418 IPW_DEBUG_RX
8419 ("Notification: subtype=%02X flags=%02X size=%d\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008420 pkt->u.notification.subtype,
8421 pkt->u.notification.flags,
Zhu Yi720eeb42006-12-05 14:41:40 +08008422 le16_to_cpu(pkt->u.notification.size));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008423 ipw_rx_notification(priv, &pkt->u.notification);
8424 break;
8425 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008426
8427 default:
8428 IPW_DEBUG_RX("Bad Rx packet of type %d\n",
8429 pkt->header.message_type);
8430 break;
8431 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008432
8433 /* For now we just don't re-use anything. We can tweak this
8434 * later to try and re-use notification packets and SKBs that
James Ketrenos43f66a62005-03-25 12:31:53 -06008435 * fail to Rx correctly */
8436 if (rxb->skb != NULL) {
8437 dev_kfree_skb_any(rxb->skb);
8438 rxb->skb = NULL;
8439 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008440
James Ketrenos43f66a62005-03-25 12:31:53 -06008441 pci_unmap_single(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05008442 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06008443 list_add_tail(&rxb->list, &priv->rxq->rx_used);
Jeff Garzikbf794512005-07-31 13:07:26 -04008444
James Ketrenos43f66a62005-03-25 12:31:53 -06008445 i = (i + 1) % RX_QUEUE_SIZE;
Dan Williams943dbef2008-02-14 17:49:41 -05008446
8447 /* If there are a lot of unsued frames, restock the Rx queue
8448 * so the ucode won't assert */
8449 if (fill_rx) {
8450 priv->rxq->read = i;
8451 ipw_rx_queue_replenish(priv);
8452 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008453 }
8454
8455 /* Backtrack one entry */
Dan Williams943dbef2008-02-14 17:49:41 -05008456 priv->rxq->read = i;
James Ketrenos43f66a62005-03-25 12:31:53 -06008457 ipw_rx_queue_restock(priv);
8458}
8459
James Ketrenosafbf30a2005-08-25 00:05:33 -05008460#define DEFAULT_RTS_THRESHOLD 2304U
8461#define MIN_RTS_THRESHOLD 1U
8462#define MAX_RTS_THRESHOLD 2304U
8463#define DEFAULT_BEACON_INTERVAL 100U
8464#define DEFAULT_SHORT_RETRY_LIMIT 7U
8465#define DEFAULT_LONG_RETRY_LIMIT 4U
8466
Zhu Yid6d5b5c2006-02-16 16:21:09 +08008467/**
8468 * ipw_sw_reset
8469 * @option: options to control different reset behaviour
8470 * 0 = reset everything except the 'disable' module_param
8471 * 1 = reset everything and print out driver info (for probe only)
8472 * 2 = reset everything
8473 */
8474static int ipw_sw_reset(struct ipw_priv *priv, int option)
James Ketrenos43f66a62005-03-25 12:31:53 -06008475{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008476 int band, modulation;
8477 int old_mode = priv->ieee->iw_mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06008478
James Ketrenosafbf30a2005-08-25 00:05:33 -05008479 /* Initialize module parameter values here */
8480 priv->config = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008481
James Ketrenosafbf30a2005-08-25 00:05:33 -05008482 /* We default to disabling the LED code as right now it causes
8483 * too many systems to lock up... */
Reinette Chatre21f8a732009-08-18 10:25:05 -07008484 if (!led_support)
James Ketrenosafbf30a2005-08-25 00:05:33 -05008485 priv->config |= CFG_NO_LED;
James Ketrenos43f66a62005-03-25 12:31:53 -06008486
James Ketrenosafbf30a2005-08-25 00:05:33 -05008487 if (associate)
8488 priv->config |= CFG_ASSOCIATE;
8489 else
8490 IPW_DEBUG_INFO("Auto associate disabled.\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04008491
James Ketrenosafbf30a2005-08-25 00:05:33 -05008492 if (auto_create)
8493 priv->config |= CFG_ADHOC_CREATE;
8494 else
8495 IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
8496
Zhu Yi17ed0812006-01-24 16:36:31 +08008497 priv->config &= ~CFG_STATIC_ESSID;
8498 priv->essid_len = 0;
8499 memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
8500
Zhu Yid6d5b5c2006-02-16 16:21:09 +08008501 if (disable && option) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008502 priv->status |= STATUS_RF_KILL_SW;
8503 IPW_DEBUG_INFO("Radio disabled.\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06008504 }
8505
Reinette Chatre21f8a732009-08-18 10:25:05 -07008506 if (default_channel != 0) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008507 priv->config |= CFG_STATIC_CHANNEL;
Reinette Chatre21f8a732009-08-18 10:25:05 -07008508 priv->channel = default_channel;
8509 IPW_DEBUG_INFO("Bind to static channel %d\n", default_channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008510 /* TODO: Validate that provided channel is in range */
8511 }
Zhu Yie43e3c12006-04-13 17:20:45 +08008512#ifdef CONFIG_IPW2200_QOS
James Ketrenosafbf30a2005-08-25 00:05:33 -05008513 ipw_qos_init(priv, qos_enable, qos_burst_enable,
8514 burst_duration_CCK, burst_duration_OFDM);
Zhu Yie43e3c12006-04-13 17:20:45 +08008515#endif /* CONFIG_IPW2200_QOS */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008516
Reinette Chatre21f8a732009-08-18 10:25:05 -07008517 switch (network_mode) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008518 case 1:
8519 priv->ieee->iw_mode = IW_MODE_ADHOC;
8520 priv->net_dev->type = ARPHRD_ETHER;
8521
8522 break;
8523#ifdef CONFIG_IPW2200_MONITOR
8524 case 2:
8525 priv->ieee->iw_mode = IW_MODE_MONITOR;
Zhu Yi459d4082006-04-13 17:21:00 +08008526#ifdef CONFIG_IPW2200_RADIOTAP
Mike Kershaw24a47db2005-08-26 00:41:54 -05008527 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
8528#else
James Ketrenosafbf30a2005-08-25 00:05:33 -05008529 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008530#endif
James Ketrenosafbf30a2005-08-25 00:05:33 -05008531 break;
8532#endif
8533 default:
8534 case 0:
8535 priv->net_dev->type = ARPHRD_ETHER;
8536 priv->ieee->iw_mode = IW_MODE_INFRA;
8537 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06008538 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008539
James Ketrenosafbf30a2005-08-25 00:05:33 -05008540 if (hwcrypto) {
8541 priv->ieee->host_encrypt = 0;
8542 priv->ieee->host_encrypt_msdu = 0;
8543 priv->ieee->host_decrypt = 0;
Hong Liu567deaf2005-08-31 18:07:22 +08008544 priv->ieee->host_mc_decrypt = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008545 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05008546 IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06008547
Zhu Yie402c932005-08-05 17:20:40 +08008548 /* IPW2200/2915 is abled to do hardware fragmentation. */
8549 priv->ieee->host_open_frag = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008550
James Ketrenosafbf30a2005-08-25 00:05:33 -05008551 if ((priv->pci_dev->device == 0x4223) ||
8552 (priv->pci_dev->device == 0x4224)) {
Zhu Yie8c69e22006-02-17 08:25:12 +08008553 if (option == 1)
James Ketrenosafbf30a2005-08-25 00:05:33 -05008554 printk(KERN_INFO DRV_NAME
8555 ": Detected Intel PRO/Wireless 2915ABG Network "
8556 "Connection\n");
8557 priv->ieee->abg_true = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008558 band = LIBIPW_52GHZ_BAND | LIBIPW_24GHZ_BAND;
8559 modulation = LIBIPW_OFDM_MODULATION |
8560 LIBIPW_CCK_MODULATION;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008561 priv->adapter = IPW_2915ABG;
8562 priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008563 } else {
Zhu Yie8c69e22006-02-17 08:25:12 +08008564 if (option == 1)
James Ketrenosafbf30a2005-08-25 00:05:33 -05008565 printk(KERN_INFO DRV_NAME
8566 ": Detected Intel PRO/Wireless 2200BG Network "
8567 "Connection\n");
8568
8569 priv->ieee->abg_true = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008570 band = LIBIPW_24GHZ_BAND;
8571 modulation = LIBIPW_OFDM_MODULATION |
8572 LIBIPW_CCK_MODULATION;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008573 priv->adapter = IPW_2200BG;
8574 priv->ieee->mode = IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008575 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008576
James Ketrenosafbf30a2005-08-25 00:05:33 -05008577 priv->ieee->freq_band = band;
8578 priv->ieee->modulation = modulation;
Jeff Garzikbf794512005-07-31 13:07:26 -04008579
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008580 priv->rates_mask = LIBIPW_DEFAULT_RATES_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06008581
James Ketrenosafbf30a2005-08-25 00:05:33 -05008582 priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
8583 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008584
James Ketrenosafbf30a2005-08-25 00:05:33 -05008585 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8586 priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
8587 priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
Jeff Garzikbf794512005-07-31 13:07:26 -04008588
James Ketrenosafbf30a2005-08-25 00:05:33 -05008589 /* If power management is turned on, default to AC mode */
8590 priv->power_mode = IPW_POWER_AC;
8591 priv->tx_power = IPW_TX_POWER_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008592
Zhu Yi0ece35b2005-08-05 17:26:51 +08008593 return old_mode == priv->ieee->iw_mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06008594}
8595
8596/*
8597 * This file defines the Wireless Extension handlers. It does not
8598 * define any methods of hardware manipulation and relies on the
8599 * functions defined in ipw_main to provide the HW interaction.
Jeff Garzikbf794512005-07-31 13:07:26 -04008600 *
8601 * The exception to this is the use of the ipw_get_ordinal()
Lucas De Marchi25985ed2011-03-30 22:57:33 -03008602 * function used to poll the hardware vs. making unnecessary calls.
James Ketrenos43f66a62005-03-25 12:31:53 -06008603 *
8604 */
8605
James Ketrenos43f66a62005-03-25 12:31:53 -06008606static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
8607{
8608 if (channel == 0) {
8609 IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
8610 priv->config &= ~CFG_STATIC_CHANNEL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008611 IPW_DEBUG_ASSOC("Attempting to associate with new "
8612 "parameters.\n");
8613 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008614 return 0;
8615 }
8616
8617 priv->config |= CFG_STATIC_CHANNEL;
8618
8619 if (priv->channel == channel) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008620 IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
8621 channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06008622 return 0;
8623 }
8624
8625 IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
8626 priv->channel = channel;
8627
James Ketrenosb095c382005-08-24 22:04:42 -05008628#ifdef CONFIG_IPW2200_MONITOR
8629 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008630 int i;
James Ketrenosb095c382005-08-24 22:04:42 -05008631 if (priv->status & STATUS_SCANNING) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008632 IPW_DEBUG_SCAN("Scan abort triggered due to "
James Ketrenosb095c382005-08-24 22:04:42 -05008633 "channel change.\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008634 ipw_abort_scan(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05008635 }
8636
8637 for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
8638 udelay(10);
8639
8640 if (priv->status & STATUS_SCANNING)
8641 IPW_DEBUG_SCAN("Still scanning...\n");
8642 else
8643 IPW_DEBUG_SCAN("Took %dms to abort current scan\n",
8644 1000 - i);
8645
8646 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008647 }
James Ketrenosb095c382005-08-24 22:04:42 -05008648#endif /* CONFIG_IPW2200_MONITOR */
8649
James Ketrenosc848d0a2005-08-24 21:56:24 -05008650 /* Network configuration changed -- force [re]association */
8651 IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
8652 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008653 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008654
8655 return 0;
8656}
8657
Jeff Garzikbf794512005-07-31 13:07:26 -04008658static int ipw_wx_set_freq(struct net_device *dev,
8659 struct iw_request_info *info,
8660 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008661{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008662 struct ipw_priv *priv = libipw_priv(dev);
8663 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
James Ketrenos43f66a62005-03-25 12:31:53 -06008664 struct iw_freq *fwrq = &wrqu->freq;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008665 int ret = 0, i;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008666 u8 channel, flags;
8667 int band;
Jeff Garzikbf794512005-07-31 13:07:26 -04008668
James Ketrenosb095c382005-08-24 22:04:42 -05008669 if (fwrq->m == 0) {
8670 IPW_DEBUG_WX("SET Freq/Channel -> any\n");
Zhu Yi46441512006-01-24 16:37:59 +08008671 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05008672 ret = ipw_set_channel(priv, 0);
Zhu Yi46441512006-01-24 16:37:59 +08008673 mutex_unlock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05008674 return ret;
8675 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008676 /* if setting by freq convert to channel */
8677 if (fwrq->e == 1) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008678 channel = libipw_freq_to_channel(priv->ieee, fwrq->m);
James Ketrenosb095c382005-08-24 22:04:42 -05008679 if (channel == 0)
8680 return -EINVAL;
8681 } else
8682 channel = fwrq->m;
Jeff Garzikbf794512005-07-31 13:07:26 -04008683
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008684 if (!(band = libipw_is_valid_channel(priv->ieee, channel)))
James Ketrenosb095c382005-08-24 22:04:42 -05008685 return -EINVAL;
Jeff Garzikbf794512005-07-31 13:07:26 -04008686
Liu Hong1fe0adb2005-08-19 09:33:10 -05008687 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008688 i = libipw_channel_to_index(priv->ieee, channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008689 if (i == -1)
8690 return -EINVAL;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008691
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008692 flags = (band == LIBIPW_24GHZ_BAND) ?
Liu Hong1fe0adb2005-08-19 09:33:10 -05008693 geo->bg[i].flags : geo->a[i].flags;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008694 if (flags & LIBIPW_CH_PASSIVE_ONLY) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008695 IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
8696 return -EINVAL;
James Ketrenos43f66a62005-03-25 12:31:53 -06008697 }
8698 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008699
Frans Pop9fd1ea42010-03-24 19:46:31 +01008700 IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m);
Zhu Yi46441512006-01-24 16:37:59 +08008701 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05008702 ret = ipw_set_channel(priv, channel);
Zhu Yi46441512006-01-24 16:37:59 +08008703 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008704 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06008705}
8706
Jeff Garzikbf794512005-07-31 13:07:26 -04008707static int ipw_wx_get_freq(struct net_device *dev,
8708 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008709 union iwreq_data *wrqu, char *extra)
8710{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008711 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008712
8713 wrqu->freq.e = 0;
8714
8715 /* If we are associated, trying to associate, or have a statically
8716 * configured CHANNEL then return that; otherwise return ANY */
Zhu Yi46441512006-01-24 16:37:59 +08008717 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008718 if (priv->config & CFG_STATIC_CHANNEL ||
Zhu Yic580f672006-08-21 11:37:01 +08008719 priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) {
8720 int i;
8721
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008722 i = libipw_channel_to_index(priv->ieee, priv->channel);
Zhu Yic580f672006-08-21 11:37:01 +08008723 BUG_ON(i == -1);
8724 wrqu->freq.e = 1;
8725
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008726 switch (libipw_is_valid_channel(priv->ieee, priv->channel)) {
8727 case LIBIPW_52GHZ_BAND:
Zhu Yic580f672006-08-21 11:37:01 +08008728 wrqu->freq.m = priv->ieee->geo.a[i].freq * 100000;
8729 break;
8730
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008731 case LIBIPW_24GHZ_BAND:
Zhu Yic580f672006-08-21 11:37:01 +08008732 wrqu->freq.m = priv->ieee->geo.bg[i].freq * 100000;
8733 break;
8734
8735 default:
8736 BUG();
8737 }
8738 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06008739 wrqu->freq.m = 0;
8740
Zhu Yi46441512006-01-24 16:37:59 +08008741 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01008742 IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06008743 return 0;
8744}
8745
Jeff Garzikbf794512005-07-31 13:07:26 -04008746static int ipw_wx_set_mode(struct net_device *dev,
8747 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008748 union iwreq_data *wrqu, char *extra)
8749{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008750 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008751 int err = 0;
8752
8753 IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
8754
James Ketrenos43f66a62005-03-25 12:31:53 -06008755 switch (wrqu->mode) {
James Ketrenosb095c382005-08-24 22:04:42 -05008756#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06008757 case IW_MODE_MONITOR:
8758#endif
8759 case IW_MODE_ADHOC:
8760 case IW_MODE_INFRA:
8761 break;
8762 case IW_MODE_AUTO:
8763 wrqu->mode = IW_MODE_INFRA;
8764 break;
8765 default:
8766 return -EINVAL;
8767 }
James Ketrenosb095c382005-08-24 22:04:42 -05008768 if (wrqu->mode == priv->ieee->iw_mode)
8769 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008770
Zhu Yi46441512006-01-24 16:37:59 +08008771 mutex_lock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008772
8773 ipw_sw_reset(priv, 0);
8774
James Ketrenosb095c382005-08-24 22:04:42 -05008775#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzikbf794512005-07-31 13:07:26 -04008776 if (priv->ieee->iw_mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06008777 priv->net_dev->type = ARPHRD_ETHER;
Jeff Garzikbf794512005-07-31 13:07:26 -04008778
8779 if (wrqu->mode == IW_MODE_MONITOR)
Zhu Yi459d4082006-04-13 17:21:00 +08008780#ifdef CONFIG_IPW2200_RADIOTAP
Mike Kershaw24a47db2005-08-26 00:41:54 -05008781 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
8782#else
James Ketrenos43f66a62005-03-25 12:31:53 -06008783 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008784#endif
James Ketrenosb095c382005-08-24 22:04:42 -05008785#endif /* CONFIG_IPW2200_MONITOR */
Jeff Garzikbf794512005-07-31 13:07:26 -04008786
Jeff Garzikbf794512005-07-31 13:07:26 -04008787 /* Free the existing firmware and reset the fw_loaded
Nick Andrew877d0312009-01-26 11:06:57 +01008788 * flag so ipw_load() will bring in the new firmware */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008789 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -06008790
8791 priv->ieee->iw_mode = wrqu->mode;
Jeff Garzikbf794512005-07-31 13:07:26 -04008792
Tejun Heobcb6d912011-01-26 12:12:50 +01008793 schedule_work(&priv->adapter_restart);
Zhu Yi46441512006-01-24 16:37:59 +08008794 mutex_unlock(&priv->mutex);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008795 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06008796}
8797
Jeff Garzikbf794512005-07-31 13:07:26 -04008798static int ipw_wx_get_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008799 struct iw_request_info *info,
8800 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008801{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008802 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08008803 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008804 wrqu->mode = priv->ieee->iw_mode;
8805 IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
Zhu Yi46441512006-01-24 16:37:59 +08008806 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008807 return 0;
8808}
8809
James Ketrenos43f66a62005-03-25 12:31:53 -06008810/* Values are in microsecond */
8811static const s32 timeout_duration[] = {
8812 350000,
8813 250000,
8814 75000,
8815 37000,
8816 25000,
8817};
8818
8819static const s32 period_duration[] = {
8820 400000,
8821 700000,
8822 1000000,
8823 1000000,
8824 1000000
8825};
8826
Jeff Garzikbf794512005-07-31 13:07:26 -04008827static int ipw_wx_get_range(struct net_device *dev,
8828 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008829 union iwreq_data *wrqu, char *extra)
8830{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008831 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008832 struct iw_range *range = (struct iw_range *)extra;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008833 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
James Ketrenosb095c382005-08-24 22:04:42 -05008834 int i = 0, j;
James Ketrenos43f66a62005-03-25 12:31:53 -06008835
8836 wrqu->data.length = sizeof(*range);
8837 memset(range, 0, sizeof(*range));
8838
8839 /* 54Mbs == ~27 Mb/s real (802.11g) */
Jeff Garzikbf794512005-07-31 13:07:26 -04008840 range->throughput = 27 * 1000 * 1000;
James Ketrenos43f66a62005-03-25 12:31:53 -06008841
8842 range->max_qual.qual = 100;
8843 /* TODO: Find real max RSSI and stick here */
8844 range->max_qual.level = 0;
Bill Mossb1916082006-02-15 08:50:18 +08008845 range->max_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008846 range->max_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06008847
8848 range->avg_qual.qual = 70;
André Goddard Rosaaf901ca2009-11-14 13:09:05 -02008849 /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008850 range->avg_qual.level = 0; /* FIXME to real average level */
James Ketrenos43f66a62005-03-25 12:31:53 -06008851 range->avg_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008852 range->avg_qual.updated = 7; /* Updated all three */
Zhu Yi46441512006-01-24 16:37:59 +08008853 mutex_lock(&priv->mutex);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008854 range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06008855
Jeff Garzikbf794512005-07-31 13:07:26 -04008856 for (i = 0; i < range->num_bitrates; i++)
8857 range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008858 500000;
Jeff Garzikbf794512005-07-31 13:07:26 -04008859
James Ketrenos43f66a62005-03-25 12:31:53 -06008860 range->max_rts = DEFAULT_RTS_THRESHOLD;
8861 range->min_frag = MIN_FRAG_THRESHOLD;
8862 range->max_frag = MAX_FRAG_THRESHOLD;
8863
8864 range->encoding_size[0] = 5;
Jeff Garzikbf794512005-07-31 13:07:26 -04008865 range->encoding_size[1] = 13;
James Ketrenos43f66a62005-03-25 12:31:53 -06008866 range->num_encoding_sizes = 2;
8867 range->max_encoding_tokens = WEP_KEYS;
8868
8869 /* Set the Wireless Extension versions */
8870 range->we_version_compiled = WIRELESS_EXT;
Dan Williamsf1b50862006-01-30 13:58:56 -05008871 range->we_version_source = 18;
James Ketrenos43f66a62005-03-25 12:31:53 -06008872
James Ketrenosb095c382005-08-24 22:04:42 -05008873 i = 0;
8874 if (priv->ieee->mode & (IEEE_B | IEEE_G)) {
Zhu Yie815de42006-03-02 05:55:51 +08008875 for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES; j++) {
8876 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008877 (geo->bg[j].flags & LIBIPW_CH_PASSIVE_ONLY))
Zhu Yie815de42006-03-02 05:55:51 +08008878 continue;
8879
James Ketrenosb095c382005-08-24 22:04:42 -05008880 range->freq[i].i = geo->bg[j].channel;
8881 range->freq[i].m = geo->bg[j].freq * 100000;
8882 range->freq[i].e = 1;
Zhu Yie815de42006-03-02 05:55:51 +08008883 i++;
James Ketrenosb095c382005-08-24 22:04:42 -05008884 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008885 }
James Ketrenosb095c382005-08-24 22:04:42 -05008886
8887 if (priv->ieee->mode & IEEE_A) {
Zhu Yie815de42006-03-02 05:55:51 +08008888 for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES; j++) {
8889 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008890 (geo->a[j].flags & LIBIPW_CH_PASSIVE_ONLY))
Zhu Yie815de42006-03-02 05:55:51 +08008891 continue;
8892
James Ketrenosb095c382005-08-24 22:04:42 -05008893 range->freq[i].i = geo->a[j].channel;
8894 range->freq[i].m = geo->a[j].freq * 100000;
8895 range->freq[i].e = 1;
Zhu Yie815de42006-03-02 05:55:51 +08008896 i++;
James Ketrenosb095c382005-08-24 22:04:42 -05008897 }
8898 }
8899
8900 range->num_channels = i;
8901 range->num_frequency = i;
8902
Zhu Yi46441512006-01-24 16:37:59 +08008903 mutex_unlock(&priv->mutex);
Benoit Boissinot97a78ca2005-09-15 17:30:28 +00008904
8905 /* Event capability (kernel + driver) */
8906 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
8907 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
Zhu Yi07f02e42006-04-13 17:19:25 +08008908 IW_EVENT_CAPA_MASK(SIOCGIWAP) |
8909 IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
Benoit Boissinot97a78ca2005-09-15 17:30:28 +00008910 range->event_capa[1] = IW_EVENT_CAPA_K_1;
James Ketrenos43f66a62005-03-25 12:31:53 -06008911
Dan Williamsf1b50862006-01-30 13:58:56 -05008912 range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
8913 IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
8914
Dan Williams374fdfb2007-12-12 10:25:07 -05008915 range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE;
8916
James Ketrenos43f66a62005-03-25 12:31:53 -06008917 IPW_DEBUG_WX("GET Range\n");
8918 return 0;
8919}
8920
Jeff Garzikbf794512005-07-31 13:07:26 -04008921static int ipw_wx_set_wap(struct net_device *dev,
8922 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008923 union iwreq_data *wrqu, char *extra)
8924{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008925 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008926
Jeff Garzikbf794512005-07-31 13:07:26 -04008927 if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
James Ketrenos43f66a62005-03-25 12:31:53 -06008928 return -EINVAL;
Zhu Yi46441512006-01-24 16:37:59 +08008929 mutex_lock(&priv->mutex);
Wei Yongjun7949d202012-08-23 14:54:05 +08008930 if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) ||
8931 is_zero_ether_addr(wrqu->ap_addr.sa_data)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008932 /* we disable mandatory BSSID association */
8933 IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
8934 priv->config &= ~CFG_STATIC_BSSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008935 IPW_DEBUG_ASSOC("Attempting to associate with new "
8936 "parameters.\n");
8937 ipw_associate(priv);
Zhu Yi46441512006-01-24 16:37:59 +08008938 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008939 return 0;
8940 }
8941
8942 priv->config |= CFG_STATIC_BSSID;
dingtianhong36325f32013-12-26 19:41:23 +08008943 if (ether_addr_equal(priv->bssid, wrqu->ap_addr.sa_data)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008944 IPW_DEBUG_WX("BSSID set to current BSSID.\n");
Zhu Yi46441512006-01-24 16:37:59 +08008945 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008946 return 0;
8947 }
8948
Johannes Berge1749612008-10-27 15:59:26 -07008949 IPW_DEBUG_WX("Setting mandatory BSSID to %pM\n",
8950 wrqu->ap_addr.sa_data);
James Ketrenos43f66a62005-03-25 12:31:53 -06008951
8952 memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
8953
James Ketrenosc848d0a2005-08-24 21:56:24 -05008954 /* Network configuration changed -- force [re]association */
8955 IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
8956 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008957 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008958
Zhu Yi46441512006-01-24 16:37:59 +08008959 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008960 return 0;
8961}
8962
Jeff Garzikbf794512005-07-31 13:07:26 -04008963static int ipw_wx_get_wap(struct net_device *dev,
8964 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008965 union iwreq_data *wrqu, char *extra)
8966{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008967 struct ipw_priv *priv = libipw_priv(dev);
Joe Perches0795af52007-10-03 17:59:30 -07008968
James Ketrenos43f66a62005-03-25 12:31:53 -06008969 /* If we are associated, trying to associate, or have a statically
8970 * configured BSSID then return that; otherwise return ANY */
Zhu Yi46441512006-01-24 16:37:59 +08008971 mutex_lock(&priv->mutex);
Jeff Garzikbf794512005-07-31 13:07:26 -04008972 if (priv->config & CFG_STATIC_BSSID ||
James Ketrenos43f66a62005-03-25 12:31:53 -06008973 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
8974 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008975 memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06008976 } else
Joe Perches93803b32015-03-02 19:54:49 -08008977 eth_zero_addr(wrqu->ap_addr.sa_data);
James Ketrenos43f66a62005-03-25 12:31:53 -06008978
Johannes Berge1749612008-10-27 15:59:26 -07008979 IPW_DEBUG_WX("Getting WAP BSSID: %pM\n",
8980 wrqu->ap_addr.sa_data);
Zhu Yi46441512006-01-24 16:37:59 +08008981 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008982 return 0;
8983}
8984
Jeff Garzikbf794512005-07-31 13:07:26 -04008985static int ipw_wx_set_essid(struct net_device *dev,
8986 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008987 union iwreq_data *wrqu, char *extra)
8988{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008989 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yiab644b02006-08-21 11:37:13 +08008990 int length;
James Ketrenos43f66a62005-03-25 12:31:53 -06008991
Zhu Yiab644b02006-08-21 11:37:13 +08008992 mutex_lock(&priv->mutex);
8993
8994 if (!wrqu->essid.flags)
8995 {
8996 IPW_DEBUG_WX("Setting ESSID to ANY\n");
8997 ipw_disassociate(priv);
8998 priv->config &= ~CFG_STATIC_ESSID;
8999 ipw_associate(priv);
9000 mutex_unlock(&priv->mutex);
9001 return 0;
9002 }
9003
Zhu Yia9f0d422006-08-21 11:37:26 +08009004 length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06009005
9006 priv->config |= CFG_STATIC_ESSID;
9007
Zhu Yia9f0d422006-08-21 11:37:26 +08009008 if (priv->essid_len == length && !memcmp(priv->essid, extra, length)
9009 && (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009010 IPW_DEBUG_WX("ESSID set to current ESSID.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009011 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009012 return 0;
9013 }
9014
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07009015 IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, extra, length);
James Ketrenos43f66a62005-03-25 12:31:53 -06009016
9017 priv->essid_len = length;
Zhu Yia9f0d422006-08-21 11:37:26 +08009018 memcpy(priv->essid, extra, priv->essid_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009019
James Ketrenosc848d0a2005-08-24 21:56:24 -05009020 /* Network configuration changed -- force [re]association */
9021 IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
9022 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06009023 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009024
Zhu Yi46441512006-01-24 16:37:59 +08009025 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009026 return 0;
9027}
9028
Jeff Garzikbf794512005-07-31 13:07:26 -04009029static int ipw_wx_get_essid(struct net_device *dev,
9030 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009031 union iwreq_data *wrqu, char *extra)
9032{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009033 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009034
9035 /* If we are associated, trying to associate, or have a statically
9036 * configured ESSID then return that; otherwise return ANY */
Zhu Yi46441512006-01-24 16:37:59 +08009037 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009038 if (priv->config & CFG_STATIC_ESSID ||
Jeff Garzikbf794512005-07-31 13:07:26 -04009039 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07009040 IPW_DEBUG_WX("Getting essid: '%*pE'\n",
9041 priv->essid_len, priv->essid);
Jeff Garzikbf794512005-07-31 13:07:26 -04009042 memcpy(extra, priv->essid, priv->essid_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06009043 wrqu->essid.length = priv->essid_len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009044 wrqu->essid.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06009045 } else {
9046 IPW_DEBUG_WX("Getting essid: ANY\n");
9047 wrqu->essid.length = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009048 wrqu->essid.flags = 0; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06009049 }
Zhu Yi46441512006-01-24 16:37:59 +08009050 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009051 return 0;
9052}
9053
Jeff Garzikbf794512005-07-31 13:07:26 -04009054static int ipw_wx_set_nick(struct net_device *dev,
9055 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009056 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009057{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009058 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009059
9060 IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
9061 if (wrqu->data.length > IW_ESSID_MAX_SIZE)
9062 return -E2BIG;
Zhu Yi46441512006-01-24 16:37:59 +08009063 mutex_lock(&priv->mutex);
Silvan Jegenc8e49552014-02-25 18:12:52 +01009064 wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick));
James Ketrenos43f66a62005-03-25 12:31:53 -06009065 memset(priv->nick, 0, sizeof(priv->nick));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009066 memcpy(priv->nick, extra, wrqu->data.length);
James Ketrenos43f66a62005-03-25 12:31:53 -06009067 IPW_DEBUG_TRACE("<<\n");
Zhu Yi46441512006-01-24 16:37:59 +08009068 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009069 return 0;
9070
9071}
9072
Jeff Garzikbf794512005-07-31 13:07:26 -04009073static int ipw_wx_get_nick(struct net_device *dev,
9074 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009075 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009076{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009077 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009078 IPW_DEBUG_WX("Getting nick\n");
Zhu Yi46441512006-01-24 16:37:59 +08009079 mutex_lock(&priv->mutex);
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009080 wrqu->data.length = strlen(priv->nick);
James Ketrenos43f66a62005-03-25 12:31:53 -06009081 memcpy(extra, priv->nick, wrqu->data.length);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009082 wrqu->data.flags = 1; /* active */
Zhu Yi46441512006-01-24 16:37:59 +08009083 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009084 return 0;
9085}
9086
Olivier Hochreutiner651be262006-03-08 03:13:55 +08009087static int ipw_wx_set_sens(struct net_device *dev,
9088 struct iw_request_info *info,
9089 union iwreq_data *wrqu, char *extra)
9090{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009091 struct ipw_priv *priv = libipw_priv(dev);
Olivier Hochreutiner651be262006-03-08 03:13:55 +08009092 int err = 0;
9093
9094 IPW_DEBUG_WX("Setting roaming threshold to %d\n", wrqu->sens.value);
9095 IPW_DEBUG_WX("Setting disassociate threshold to %d\n", 3*wrqu->sens.value);
9096 mutex_lock(&priv->mutex);
9097
9098 if (wrqu->sens.fixed == 0)
9099 {
9100 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
9101 priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
9102 goto out;
9103 }
9104 if ((wrqu->sens.value > IPW_MB_ROAMING_THRESHOLD_MAX) ||
9105 (wrqu->sens.value < IPW_MB_ROAMING_THRESHOLD_MIN)) {
9106 err = -EINVAL;
9107 goto out;
9108 }
9109
9110 priv->roaming_threshold = wrqu->sens.value;
9111 priv->disassociate_threshold = 3*wrqu->sens.value;
9112 out:
9113 mutex_unlock(&priv->mutex);
9114 return err;
9115}
9116
9117static int ipw_wx_get_sens(struct net_device *dev,
9118 struct iw_request_info *info,
9119 union iwreq_data *wrqu, char *extra)
9120{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009121 struct ipw_priv *priv = libipw_priv(dev);
Olivier Hochreutiner651be262006-03-08 03:13:55 +08009122 mutex_lock(&priv->mutex);
9123 wrqu->sens.fixed = 1;
9124 wrqu->sens.value = priv->roaming_threshold;
9125 mutex_unlock(&priv->mutex);
9126
Frans Pop9fd1ea42010-03-24 19:46:31 +01009127 IPW_DEBUG_WX("GET roaming threshold -> %s %d\n",
Olivier Hochreutiner651be262006-03-08 03:13:55 +08009128 wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
9129
9130 return 0;
9131}
9132
James Ketrenos43f66a62005-03-25 12:31:53 -06009133static int ipw_wx_set_rate(struct net_device *dev,
9134 struct iw_request_info *info,
9135 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009136{
James Ketrenosea2b26e2005-08-24 21:25:16 -05009137 /* TODO: We should use semaphores or locks for access to priv */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009138 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009139 u32 target_rate = wrqu->bitrate.value;
9140 u32 fixed, mask;
9141
9142 /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
9143 /* value = X, fixed = 1 means only rate X */
9144 /* value = X, fixed = 0 means all rates lower equal X */
9145
9146 if (target_rate == -1) {
9147 fixed = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009148 mask = LIBIPW_DEFAULT_RATES_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009149 /* Now we should reassociate */
9150 goto apply;
9151 }
9152
9153 mask = 0;
9154 fixed = wrqu->bitrate.fixed;
9155
9156 if (target_rate == 1000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009157 mask |= LIBIPW_CCK_RATE_1MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009158 if (target_rate == 1000000)
9159 goto apply;
9160
9161 if (target_rate == 2000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009162 mask |= LIBIPW_CCK_RATE_2MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009163 if (target_rate == 2000000)
9164 goto apply;
9165
9166 if (target_rate == 5500000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009167 mask |= LIBIPW_CCK_RATE_5MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009168 if (target_rate == 5500000)
9169 goto apply;
9170
9171 if (target_rate == 6000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009172 mask |= LIBIPW_OFDM_RATE_6MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009173 if (target_rate == 6000000)
9174 goto apply;
9175
9176 if (target_rate == 9000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009177 mask |= LIBIPW_OFDM_RATE_9MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009178 if (target_rate == 9000000)
9179 goto apply;
9180
9181 if (target_rate == 11000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009182 mask |= LIBIPW_CCK_RATE_11MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009183 if (target_rate == 11000000)
9184 goto apply;
9185
9186 if (target_rate == 12000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009187 mask |= LIBIPW_OFDM_RATE_12MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009188 if (target_rate == 12000000)
9189 goto apply;
9190
9191 if (target_rate == 18000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009192 mask |= LIBIPW_OFDM_RATE_18MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009193 if (target_rate == 18000000)
9194 goto apply;
9195
9196 if (target_rate == 24000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009197 mask |= LIBIPW_OFDM_RATE_24MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009198 if (target_rate == 24000000)
9199 goto apply;
9200
9201 if (target_rate == 36000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009202 mask |= LIBIPW_OFDM_RATE_36MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009203 if (target_rate == 36000000)
9204 goto apply;
9205
9206 if (target_rate == 48000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009207 mask |= LIBIPW_OFDM_RATE_48MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009208 if (target_rate == 48000000)
9209 goto apply;
9210
9211 if (target_rate == 54000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009212 mask |= LIBIPW_OFDM_RATE_54MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009213 if (target_rate == 54000000)
9214 goto apply;
9215
9216 IPW_DEBUG_WX("invalid rate specified, returning error\n");
9217 return -EINVAL;
9218
9219 apply:
9220 IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
9221 mask, fixed ? "fixed" : "sub-rates");
Zhu Yi46441512006-01-24 16:37:59 +08009222 mutex_lock(&priv->mutex);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009223 if (mask == LIBIPW_DEFAULT_RATES_MASK) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009224 priv->config &= ~CFG_FIXED_RATE;
James Ketrenosb095c382005-08-24 22:04:42 -05009225 ipw_set_fixed_rate(priv, priv->ieee->mode);
9226 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05009227 priv->config |= CFG_FIXED_RATE;
9228
James Ketrenosc848d0a2005-08-24 21:56:24 -05009229 if (priv->rates_mask == mask) {
9230 IPW_DEBUG_WX("Mask set to current mask.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009231 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009232 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009233 }
9234
James Ketrenosc848d0a2005-08-24 21:56:24 -05009235 priv->rates_mask = mask;
9236
9237 /* Network configuration changed -- force [re]association */
9238 IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
9239 if (!ipw_disassociate(priv))
9240 ipw_associate(priv);
9241
Zhu Yi46441512006-01-24 16:37:59 +08009242 mutex_unlock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009243 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009244}
9245
Jeff Garzikbf794512005-07-31 13:07:26 -04009246static int ipw_wx_get_rate(struct net_device *dev,
9247 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009248 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009249{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009250 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009251 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009252 wrqu->bitrate.value = priv->last_rate;
Zhu Yi455936c2006-04-13 17:20:05 +08009253 wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0;
Zhu Yi46441512006-01-24 16:37:59 +08009254 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009255 IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009256 return 0;
9257}
9258
Jeff Garzikbf794512005-07-31 13:07:26 -04009259static int ipw_wx_set_rts(struct net_device *dev,
9260 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009261 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009262{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009263 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009264 mutex_lock(&priv->mutex);
Zhu Yiea8862d2007-01-11 17:32:54 +08009265 if (wrqu->rts.disabled || !wrqu->rts.fixed)
James Ketrenos43f66a62005-03-25 12:31:53 -06009266 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
9267 else {
9268 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
James Ketrenosc848d0a2005-08-24 21:56:24 -05009269 wrqu->rts.value > MAX_RTS_THRESHOLD) {
Zhu Yi46441512006-01-24 16:37:59 +08009270 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009271 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009272 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009273 priv->rts_threshold = wrqu->rts.value;
9274 }
9275
9276 ipw_send_rts_threshold(priv, priv->rts_threshold);
Zhu Yi46441512006-01-24 16:37:59 +08009277 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009278 IPW_DEBUG_WX("SET RTS Threshold -> %d\n", priv->rts_threshold);
James Ketrenos43f66a62005-03-25 12:31:53 -06009279 return 0;
9280}
9281
Jeff Garzikbf794512005-07-31 13:07:26 -04009282static int ipw_wx_get_rts(struct net_device *dev,
9283 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009284 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009285{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009286 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009287 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009288 wrqu->rts.value = priv->rts_threshold;
9289 wrqu->rts.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009290 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
Zhu Yi46441512006-01-24 16:37:59 +08009291 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009292 IPW_DEBUG_WX("GET RTS Threshold -> %d\n", wrqu->rts.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009293 return 0;
9294}
9295
Jeff Garzikbf794512005-07-31 13:07:26 -04009296static int ipw_wx_set_txpow(struct net_device *dev,
9297 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009298 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009299{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009300 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009301 int err = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009302
Zhu Yi46441512006-01-24 16:37:59 +08009303 mutex_lock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009304 if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009305 err = -EINPROGRESS;
9306 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009307 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009308
James Ketrenosb095c382005-08-24 22:04:42 -05009309 if (!wrqu->power.fixed)
9310 wrqu->power.value = IPW_TX_POWER_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06009311
James Ketrenosc848d0a2005-08-24 21:56:24 -05009312 if (wrqu->power.flags != IW_TXPOW_DBM) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009313 err = -EINVAL;
9314 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009315 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009316
James Ketrenosb095c382005-08-24 22:04:42 -05009317 if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
James Ketrenosafbf30a2005-08-25 00:05:33 -05009318 (wrqu->power.value < IPW_TX_POWER_MIN)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009319 err = -EINVAL;
9320 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009321 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009322
9323 priv->tx_power = wrqu->power.value;
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009324 err = ipw_set_tx_power(priv);
9325 out:
Zhu Yi46441512006-01-24 16:37:59 +08009326 mutex_unlock(&priv->mutex);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009327 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06009328}
9329
Jeff Garzikbf794512005-07-31 13:07:26 -04009330static int ipw_wx_get_txpow(struct net_device *dev,
9331 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009332 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009333{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009334 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009335 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009336 wrqu->power.value = priv->tx_power;
9337 wrqu->power.fixed = 1;
9338 wrqu->power.flags = IW_TXPOW_DBM;
9339 wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
Zhu Yi46441512006-01-24 16:37:59 +08009340 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009341
Frans Pop9fd1ea42010-03-24 19:46:31 +01009342 IPW_DEBUG_WX("GET TX Power -> %s %d\n",
Zhu Yi22501c82005-08-11 10:49:17 +08009343 wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009344
9345 return 0;
9346}
9347
Jeff Garzikbf794512005-07-31 13:07:26 -04009348static int ipw_wx_set_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009349 struct iw_request_info *info,
9350 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009351{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009352 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009353 mutex_lock(&priv->mutex);
Zhu Yiea8862d2007-01-11 17:32:54 +08009354 if (wrqu->frag.disabled || !wrqu->frag.fixed)
James Ketrenos43f66a62005-03-25 12:31:53 -06009355 priv->ieee->fts = DEFAULT_FTS;
9356 else {
9357 if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
James Ketrenosb095c382005-08-24 22:04:42 -05009358 wrqu->frag.value > MAX_FRAG_THRESHOLD) {
Zhu Yi46441512006-01-24 16:37:59 +08009359 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009360 return -EINVAL;
James Ketrenosb095c382005-08-24 22:04:42 -05009361 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009362
James Ketrenos43f66a62005-03-25 12:31:53 -06009363 priv->ieee->fts = wrqu->frag.value & ~0x1;
9364 }
9365
9366 ipw_send_frag_threshold(priv, wrqu->frag.value);
Zhu Yi46441512006-01-24 16:37:59 +08009367 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009368 IPW_DEBUG_WX("SET Frag Threshold -> %d\n", wrqu->frag.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009369 return 0;
9370}
9371
Jeff Garzikbf794512005-07-31 13:07:26 -04009372static int ipw_wx_get_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009373 struct iw_request_info *info,
9374 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009375{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009376 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009377 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009378 wrqu->frag.value = priv->ieee->fts;
9379 wrqu->frag.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009380 wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
Zhu Yi46441512006-01-24 16:37:59 +08009381 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009382 IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009383
9384 return 0;
9385}
9386
Jeff Garzikbf794512005-07-31 13:07:26 -04009387static int ipw_wx_set_retry(struct net_device *dev,
9388 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009389 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009390{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009391 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009392
9393 if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
9394 return -EINVAL;
9395
9396 if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
9397 return 0;
9398
Zhu Yid5f7ac22006-08-21 11:38:17 +08009399 if (wrqu->retry.value < 0 || wrqu->retry.value >= 255)
James Ketrenosafbf30a2005-08-25 00:05:33 -05009400 return -EINVAL;
9401
Zhu Yi46441512006-01-24 16:37:59 +08009402 mutex_lock(&priv->mutex);
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009403 if (wrqu->retry.flags & IW_RETRY_SHORT)
James Ketrenosafbf30a2005-08-25 00:05:33 -05009404 priv->short_retry_limit = (u8) wrqu->retry.value;
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009405 else if (wrqu->retry.flags & IW_RETRY_LONG)
James Ketrenosafbf30a2005-08-25 00:05:33 -05009406 priv->long_retry_limit = (u8) wrqu->retry.value;
9407 else {
9408 priv->short_retry_limit = (u8) wrqu->retry.value;
9409 priv->long_retry_limit = (u8) wrqu->retry.value;
9410 }
9411
9412 ipw_send_retry_limit(priv, priv->short_retry_limit,
9413 priv->long_retry_limit);
Zhu Yi46441512006-01-24 16:37:59 +08009414 mutex_unlock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009415 IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n",
9416 priv->short_retry_limit, priv->long_retry_limit);
9417 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009418}
9419
Jeff Garzikbf794512005-07-31 13:07:26 -04009420static int ipw_wx_get_retry(struct net_device *dev,
9421 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009422 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009423{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009424 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009425
Zhu Yi46441512006-01-24 16:37:59 +08009426 mutex_lock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009427 wrqu->retry.disabled = 0;
9428
9429 if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
Zhu Yi46441512006-01-24 16:37:59 +08009430 mutex_unlock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009431 return -EINVAL;
9432 }
9433
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009434 if (wrqu->retry.flags & IW_RETRY_LONG) {
9435 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009436 wrqu->retry.value = priv->long_retry_limit;
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009437 } else if (wrqu->retry.flags & IW_RETRY_SHORT) {
9438 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009439 wrqu->retry.value = priv->short_retry_limit;
9440 } else {
9441 wrqu->retry.flags = IW_RETRY_LIMIT;
9442 wrqu->retry.value = priv->short_retry_limit;
9443 }
Zhu Yi46441512006-01-24 16:37:59 +08009444 mutex_unlock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009445
Frans Pop9fd1ea42010-03-24 19:46:31 +01009446 IPW_DEBUG_WX("GET retry -> %d\n", wrqu->retry.value);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009447
9448 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009449}
9450
Jeff Garzikbf794512005-07-31 13:07:26 -04009451static int ipw_wx_set_scan(struct net_device *dev,
9452 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009453 union iwreq_data *wrqu, char *extra)
9454{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009455 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi094c4d22006-08-21 11:39:03 +08009456 struct iw_scan_req *req = (struct iw_scan_req *)extra;
Dan Williamsea177302008-06-02 17:51:23 -04009457 struct delayed_work *work = NULL;
Zhu Yi094c4d22006-08-21 11:39:03 +08009458
Dan Williams0b531672007-10-09 13:55:24 -04009459 mutex_lock(&priv->mutex);
Dan Williamsea177302008-06-02 17:51:23 -04009460
Dan Williams0b531672007-10-09 13:55:24 -04009461 priv->user_requested_scan = 1;
Dan Williams0b531672007-10-09 13:55:24 -04009462
Zhu Yi094c4d22006-08-21 11:39:03 +08009463 if (wrqu->data.length == sizeof(struct iw_scan_req)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05009464 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
Dan Williamsea177302008-06-02 17:51:23 -04009465 int len = min((int)req->essid_len,
9466 (int)sizeof(priv->direct_scan_ssid));
9467 memcpy(priv->direct_scan_ssid, req->essid, len);
9468 priv->direct_scan_ssid_len = len;
9469 work = &priv->request_direct_scan;
9470 } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
9471 work = &priv->request_passive_scan;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009472 }
Dan Williamsea177302008-06-02 17:51:23 -04009473 } else {
9474 /* Normal active broadcast scan */
9475 work = &priv->request_scan;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009476 }
James Ketrenos8935f392005-10-05 15:59:08 -05009477
Dan Williamsea177302008-06-02 17:51:23 -04009478 mutex_unlock(&priv->mutex);
9479
James Ketrenos43f66a62005-03-25 12:31:53 -06009480 IPW_DEBUG_WX("Start scan\n");
James Ketrenosb095c382005-08-24 22:04:42 -05009481
Tejun Heobcb6d912011-01-26 12:12:50 +01009482 schedule_delayed_work(work, 0);
James Ketrenosb095c382005-08-24 22:04:42 -05009483
James Ketrenos43f66a62005-03-25 12:31:53 -06009484 return 0;
9485}
9486
Jeff Garzikbf794512005-07-31 13:07:26 -04009487static int ipw_wx_get_scan(struct net_device *dev,
9488 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009489 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009490{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009491 struct ipw_priv *priv = libipw_priv(dev);
9492 return libipw_wx_get_scan(priv->ieee, info, wrqu, extra);
James Ketrenos43f66a62005-03-25 12:31:53 -06009493}
9494
Jeff Garzikbf794512005-07-31 13:07:26 -04009495static int ipw_wx_set_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009496 struct iw_request_info *info,
9497 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009498{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009499 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009500 int ret;
Hong Liucaeff812005-08-05 17:25:50 +08009501 u32 cap = priv->capability;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009502
Zhu Yi46441512006-01-24 16:37:59 +08009503 mutex_lock(&priv->mutex);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009504 ret = libipw_wx_set_encode(priv->ieee, info, wrqu, key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009505
Hong Liucaeff812005-08-05 17:25:50 +08009506 /* In IBSS mode, we need to notify the firmware to update
9507 * the beacon info after we changed the capability. */
9508 if (cap != priv->capability &&
9509 priv->ieee->iw_mode == IW_MODE_ADHOC &&
9510 priv->status & STATUS_ASSOCIATED)
9511 ipw_disassociate(priv);
9512
Zhu Yi46441512006-01-24 16:37:59 +08009513 mutex_unlock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009514 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009515}
9516
Jeff Garzikbf794512005-07-31 13:07:26 -04009517static int ipw_wx_get_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009518 struct iw_request_info *info,
9519 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009520{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009521 struct ipw_priv *priv = libipw_priv(dev);
9522 return libipw_wx_get_encode(priv->ieee, info, wrqu, key);
James Ketrenos43f66a62005-03-25 12:31:53 -06009523}
9524
Jeff Garzikbf794512005-07-31 13:07:26 -04009525static int ipw_wx_set_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009526 struct iw_request_info *info,
9527 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009528{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009529 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009530 int err;
Zhu Yi46441512006-01-24 16:37:59 +08009531 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009532 if (wrqu->power.disabled) {
9533 priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
9534 err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
9535 if (err) {
9536 IPW_DEBUG_WX("failed setting power mode.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009537 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009538 return err;
9539 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009540 IPW_DEBUG_WX("SET Power Management Mode -> off\n");
Zhu Yi46441512006-01-24 16:37:59 +08009541 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009542 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009543 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009544
9545 switch (wrqu->power.flags & IW_POWER_MODE) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009546 case IW_POWER_ON: /* If not specified */
9547 case IW_POWER_MODE: /* If set all mask */
Jean Delvarec03983a2007-10-19 23:22:55 +02009548 case IW_POWER_ALL_R: /* If explicitly state all */
James Ketrenos43f66a62005-03-25 12:31:53 -06009549 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009550 default: /* Otherwise we don't support it */
James Ketrenos43f66a62005-03-25 12:31:53 -06009551 IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
9552 wrqu->power.flags);
Zhu Yi46441512006-01-24 16:37:59 +08009553 mutex_unlock(&priv->mutex);
Jeff Garzikbf794512005-07-31 13:07:26 -04009554 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06009555 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009556
James Ketrenos43f66a62005-03-25 12:31:53 -06009557 /* If the user hasn't specified a power management mode yet, default
9558 * to BATTERY */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009559 if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
James Ketrenos43f66a62005-03-25 12:31:53 -06009560 priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
Jeff Garzikbf794512005-07-31 13:07:26 -04009561 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009562 priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
Zhu Yi4e157f02007-07-12 16:09:32 +08009563
James Ketrenos43f66a62005-03-25 12:31:53 -06009564 err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
9565 if (err) {
9566 IPW_DEBUG_WX("failed setting power mode.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009567 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009568 return err;
9569 }
9570
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009571 IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
Zhu Yi46441512006-01-24 16:37:59 +08009572 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009573 return 0;
9574}
9575
Jeff Garzikbf794512005-07-31 13:07:26 -04009576static int ipw_wx_get_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009577 struct iw_request_info *info,
9578 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009579{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009580 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009581 mutex_lock(&priv->mutex);
James Ketrenosa613bff2005-08-24 21:43:11 -05009582 if (!(priv->power_mode & IPW_POWER_ENABLED))
James Ketrenos43f66a62005-03-25 12:31:53 -06009583 wrqu->power.disabled = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -05009584 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009585 wrqu->power.disabled = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009586
Zhu Yi46441512006-01-24 16:37:59 +08009587 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009588 IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04009589
James Ketrenos43f66a62005-03-25 12:31:53 -06009590 return 0;
9591}
9592
Jeff Garzikbf794512005-07-31 13:07:26 -04009593static int ipw_wx_set_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009594 struct iw_request_info *info,
9595 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009596{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009597 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009598 int mode = *(int *)extra;
9599 int err;
Zhu Yi4e157f02007-07-12 16:09:32 +08009600
Zhu Yi46441512006-01-24 16:37:59 +08009601 mutex_lock(&priv->mutex);
Zhu Yi4e157f02007-07-12 16:09:32 +08009602 if ((mode < 1) || (mode > IPW_POWER_LIMIT))
James Ketrenos43f66a62005-03-25 12:31:53 -06009603 mode = IPW_POWER_AC;
Jeff Garzikbf794512005-07-31 13:07:26 -04009604
Zhu Yi4e157f02007-07-12 16:09:32 +08009605 if (IPW_POWER_LEVEL(priv->power_mode) != mode) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009606 err = ipw_send_power_mode(priv, mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06009607 if (err) {
9608 IPW_DEBUG_WX("failed setting power mode.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009609 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009610 return err;
9611 }
Zhu Yi4e157f02007-07-12 16:09:32 +08009612 priv->power_mode = IPW_POWER_ENABLED | mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06009613 }
Zhu Yi46441512006-01-24 16:37:59 +08009614 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009615 return 0;
9616}
9617
9618#define MAX_WX_STRING 80
Jeff Garzikbf794512005-07-31 13:07:26 -04009619static int ipw_wx_get_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009620 struct iw_request_info *info,
9621 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009622{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009623 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009624 int level = IPW_POWER_LEVEL(priv->power_mode);
9625 char *p = extra;
9626
9627 p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
9628
9629 switch (level) {
9630 case IPW_POWER_AC:
9631 p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
9632 break;
9633 case IPW_POWER_BATTERY:
9634 p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
9635 break;
9636 default:
9637 p += snprintf(p, MAX_WX_STRING - (p - extra),
Jeff Garzikbf794512005-07-31 13:07:26 -04009638 "(Timeout %dms, Period %dms)",
James Ketrenos43f66a62005-03-25 12:31:53 -06009639 timeout_duration[level - 1] / 1000,
9640 period_duration[level - 1] / 1000);
9641 }
9642
9643 if (!(priv->power_mode & IPW_POWER_ENABLED))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009644 p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
James Ketrenos43f66a62005-03-25 12:31:53 -06009645
9646 wrqu->data.length = p - extra + 1;
9647
9648 return 0;
9649}
9650
9651static int ipw_wx_set_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009652 struct iw_request_info *info,
9653 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009654{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009655 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009656 int mode = *(int *)extra;
9657 u8 band = 0, modulation = 0;
9658
9659 if (mode == 0 || mode & ~IEEE_MODE_MASK) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009660 IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06009661 return -EINVAL;
9662 }
Zhu Yi46441512006-01-24 16:37:59 +08009663 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009664 if (priv->adapter == IPW_2915ABG) {
James Ketrenosa33a1982005-09-14 14:28:59 -05009665 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06009666 if (mode & IEEE_A) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009667 band |= LIBIPW_52GHZ_BAND;
9668 modulation |= LIBIPW_OFDM_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06009669 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009670 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009671 } else {
9672 if (mode & IEEE_A) {
9673 IPW_WARNING("Attempt to set 2200BG into "
9674 "802.11a mode\n");
Zhu Yi46441512006-01-24 16:37:59 +08009675 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009676 return -EINVAL;
9677 }
9678
James Ketrenosa33a1982005-09-14 14:28:59 -05009679 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009680 }
9681
9682 if (mode & IEEE_B) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009683 band |= LIBIPW_24GHZ_BAND;
9684 modulation |= LIBIPW_CCK_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06009685 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009686 priv->ieee->abg_true = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009687
James Ketrenos43f66a62005-03-25 12:31:53 -06009688 if (mode & IEEE_G) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009689 band |= LIBIPW_24GHZ_BAND;
9690 modulation |= LIBIPW_OFDM_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06009691 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009692 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009693
9694 priv->ieee->mode = mode;
9695 priv->ieee->freq_band = band;
9696 priv->ieee->modulation = modulation;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009697 init_supported_rates(priv, &priv->rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06009698
James Ketrenosc848d0a2005-08-24 21:56:24 -05009699 /* Network configuration changed -- force [re]association */
9700 IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
9701 if (!ipw_disassociate(priv)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009702 ipw_send_supported_rates(priv, &priv->rates);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009703 ipw_associate(priv);
9704 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009705
James Ketrenosa613bff2005-08-24 21:43:11 -05009706 /* Update the band LEDs */
9707 ipw_led_band_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009708
Jeff Garzikbf794512005-07-31 13:07:26 -04009709 IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009710 mode & IEEE_A ? 'a' : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009711 mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
Zhu Yi46441512006-01-24 16:37:59 +08009712 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009713 return 0;
9714}
9715
9716static int ipw_wx_get_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009717 struct iw_request_info *info,
9718 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009719{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009720 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009721 mutex_lock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009722 switch (priv->ieee->mode) {
9723 case IEEE_A:
James Ketrenos43f66a62005-03-25 12:31:53 -06009724 strncpy(extra, "802.11a (1)", MAX_WX_STRING);
9725 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009726 case IEEE_B:
9727 strncpy(extra, "802.11b (2)", MAX_WX_STRING);
9728 break;
9729 case IEEE_A | IEEE_B:
9730 strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
9731 break;
9732 case IEEE_G:
9733 strncpy(extra, "802.11g (4)", MAX_WX_STRING);
9734 break;
9735 case IEEE_A | IEEE_G:
9736 strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
9737 break;
9738 case IEEE_B | IEEE_G:
9739 strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
9740 break;
9741 case IEEE_A | IEEE_B | IEEE_G:
9742 strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
9743 break;
9744 default:
9745 strncpy(extra, "unknown", MAX_WX_STRING);
James Ketrenos43f66a62005-03-25 12:31:53 -06009746 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04009747 }
Rickard Strandqvist2da2c582014-08-10 01:39:28 +02009748 extra[MAX_WX_STRING - 1] = '\0';
Jeff Garzikbf794512005-07-31 13:07:26 -04009749
James Ketrenos43f66a62005-03-25 12:31:53 -06009750 IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
9751
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009752 wrqu->data.length = strlen(extra) + 1;
Zhu Yi46441512006-01-24 16:37:59 +08009753 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009754
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009755 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009756}
9757
James Ketrenosea2b26e2005-08-24 21:25:16 -05009758static int ipw_wx_set_preamble(struct net_device *dev,
9759 struct iw_request_info *info,
9760 union iwreq_data *wrqu, char *extra)
9761{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009762 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009763 int mode = *(int *)extra;
Zhu Yi46441512006-01-24 16:37:59 +08009764 mutex_lock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009765 /* Switching from SHORT -> LONG requires a disassociation */
9766 if (mode == 1) {
9767 if (!(priv->config & CFG_PREAMBLE_LONG)) {
9768 priv->config |= CFG_PREAMBLE_LONG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009769
9770 /* Network configuration changed -- force [re]association */
9771 IPW_DEBUG_ASSOC
9772 ("[re]association triggered due to preamble change.\n");
9773 if (!ipw_disassociate(priv))
9774 ipw_associate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009775 }
9776 goto done;
9777 }
9778
9779 if (mode == 0) {
9780 priv->config &= ~CFG_PREAMBLE_LONG;
9781 goto done;
9782 }
Zhu Yi46441512006-01-24 16:37:59 +08009783 mutex_unlock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009784 return -EINVAL;
9785
9786 done:
Zhu Yi46441512006-01-24 16:37:59 +08009787 mutex_unlock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009788 return 0;
9789}
9790
9791static int ipw_wx_get_preamble(struct net_device *dev,
9792 struct iw_request_info *info,
9793 union iwreq_data *wrqu, char *extra)
9794{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009795 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009796 mutex_lock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009797 if (priv->config & CFG_PREAMBLE_LONG)
9798 snprintf(wrqu->name, IFNAMSIZ, "long (1)");
9799 else
9800 snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
Zhu Yi46441512006-01-24 16:37:59 +08009801 mutex_unlock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009802 return 0;
9803}
9804
James Ketrenosb095c382005-08-24 22:04:42 -05009805#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05009806static int ipw_wx_set_monitor(struct net_device *dev,
Jeff Garzikbf794512005-07-31 13:07:26 -04009807 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009808 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009809{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009810 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009811 int *parms = (int *)extra;
9812 int enable = (parms[0] > 0);
Zhu Yi46441512006-01-24 16:37:59 +08009813 mutex_lock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009814 IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
James Ketrenos43f66a62005-03-25 12:31:53 -06009815 if (enable) {
9816 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
Zhu Yi459d4082006-04-13 17:21:00 +08009817#ifdef CONFIG_IPW2200_RADIOTAP
Mike Kershaw24a47db2005-08-26 00:41:54 -05009818 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
9819#else
James Ketrenos43f66a62005-03-25 12:31:53 -06009820 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05009821#endif
Tejun Heobcb6d912011-01-26 12:12:50 +01009822 schedule_work(&priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009823 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009824
James Ketrenos43f66a62005-03-25 12:31:53 -06009825 ipw_set_channel(priv, parms[1]);
9826 } else {
James Ketrenosc848d0a2005-08-24 21:56:24 -05009827 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
Zhu Yi46441512006-01-24 16:37:59 +08009828 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009829 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009830 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009831 priv->net_dev->type = ARPHRD_ETHER;
Tejun Heobcb6d912011-01-26 12:12:50 +01009832 schedule_work(&priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009833 }
Zhu Yi46441512006-01-24 16:37:59 +08009834 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009835 return 0;
9836}
9837
Pavel Machek67fd6b42006-07-11 15:34:05 +02009838#endif /* CONFIG_IPW2200_MONITOR */
James Ketrenosb095c382005-08-24 22:04:42 -05009839
Jeff Garzikbf794512005-07-31 13:07:26 -04009840static int ipw_wx_reset(struct net_device *dev,
9841 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009842 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009843{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009844 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009845 IPW_DEBUG_WX("RESET\n");
Tejun Heobcb6d912011-01-26 12:12:50 +01009846 schedule_work(&priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009847 return 0;
9848}
James Ketrenosb095c382005-08-24 22:04:42 -05009849
James Ketrenosb095c382005-08-24 22:04:42 -05009850static int ipw_wx_sw_reset(struct net_device *dev,
9851 struct iw_request_info *info,
9852 union iwreq_data *wrqu, char *extra)
9853{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009854 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosb095c382005-08-24 22:04:42 -05009855 union iwreq_data wrqu_sec = {
9856 .encoding = {
9857 .flags = IW_ENCODE_DISABLED,
9858 },
9859 };
James Ketrenosafbf30a2005-08-25 00:05:33 -05009860 int ret;
James Ketrenosb095c382005-08-24 22:04:42 -05009861
9862 IPW_DEBUG_WX("SW_RESET\n");
9863
Zhu Yi46441512006-01-24 16:37:59 +08009864 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05009865
Zhu Yid6d5b5c2006-02-16 16:21:09 +08009866 ret = ipw_sw_reset(priv, 2);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009867 if (!ret) {
9868 free_firmware();
9869 ipw_adapter_restart(priv);
9870 }
James Ketrenosb095c382005-08-24 22:04:42 -05009871
9872 /* The SW reset bit might have been toggled on by the 'disable'
9873 * module parameter, so take appropriate action */
9874 ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
9875
Zhu Yi46441512006-01-24 16:37:59 +08009876 mutex_unlock(&priv->mutex);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009877 libipw_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL);
Zhu Yi46441512006-01-24 16:37:59 +08009878 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05009879
9880 if (!(priv->status & STATUS_RF_KILL_MASK)) {
9881 /* Configuration likely changed -- force [re]association */
9882 IPW_DEBUG_ASSOC("[re]association triggered due to sw "
9883 "reset.\n");
9884 if (!ipw_disassociate(priv))
9885 ipw_associate(priv);
9886 }
9887
Zhu Yi46441512006-01-24 16:37:59 +08009888 mutex_unlock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05009889
9890 return 0;
9891}
James Ketrenos43f66a62005-03-25 12:31:53 -06009892
9893/* Rebase the WE IOCTLs to zero for the handler array */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009894static iw_handler ipw_wx_handlers[] = {
Joe Perches56b632e2010-03-18 18:29:38 -07009895 IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname),
9896 IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq),
9897 IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq),
9898 IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode),
9899 IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode),
9900 IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens),
9901 IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens),
9902 IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range),
9903 IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap),
9904 IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap),
9905 IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan),
9906 IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan),
9907 IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid),
9908 IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid),
9909 IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick),
9910 IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick),
9911 IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate),
9912 IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate),
9913 IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts),
9914 IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts),
9915 IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag),
9916 IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag),
9917 IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow),
9918 IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow),
9919 IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry),
9920 IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry),
9921 IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode),
9922 IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode),
9923 IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power),
9924 IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power),
9925 IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
9926 IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
9927 IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
9928 IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
9929 IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie),
9930 IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie),
9931 IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme),
9932 IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth),
9933 IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth),
9934 IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext),
9935 IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext),
James Ketrenos43f66a62005-03-25 12:31:53 -06009936};
9937
James Ketrenosb095c382005-08-24 22:04:42 -05009938enum {
9939 IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV,
9940 IPW_PRIV_GET_POWER,
9941 IPW_PRIV_SET_MODE,
9942 IPW_PRIV_GET_MODE,
9943 IPW_PRIV_SET_PREAMBLE,
9944 IPW_PRIV_GET_PREAMBLE,
9945 IPW_PRIV_RESET,
9946 IPW_PRIV_SW_RESET,
9947#ifdef CONFIG_IPW2200_MONITOR
9948 IPW_PRIV_SET_MONITOR,
9949#endif
9950};
James Ketrenos43f66a62005-03-25 12:31:53 -06009951
Jeff Garzikbf794512005-07-31 13:07:26 -04009952static struct iw_priv_args ipw_priv_args[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -06009953 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009954 .cmd = IPW_PRIV_SET_POWER,
9955 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9956 .name = "set_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009957 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009958 .cmd = IPW_PRIV_GET_POWER,
9959 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9960 .name = "get_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009961 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009962 .cmd = IPW_PRIV_SET_MODE,
9963 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9964 .name = "set_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009965 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009966 .cmd = IPW_PRIV_GET_MODE,
9967 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9968 .name = "get_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009969 {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009970 .cmd = IPW_PRIV_SET_PREAMBLE,
9971 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9972 .name = "set_preamble"},
9973 {
9974 .cmd = IPW_PRIV_GET_PREAMBLE,
9975 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
9976 .name = "get_preamble"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009977 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009978 IPW_PRIV_RESET,
9979 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
James Ketrenosb095c382005-08-24 22:04:42 -05009980 {
9981 IPW_PRIV_SW_RESET,
9982 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"},
9983#ifdef CONFIG_IPW2200_MONITOR
9984 {
9985 IPW_PRIV_SET_MONITOR,
9986 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
9987#endif /* CONFIG_IPW2200_MONITOR */
James Ketrenos43f66a62005-03-25 12:31:53 -06009988};
9989
9990static iw_handler ipw_priv_handler[] = {
9991 ipw_wx_set_powermode,
9992 ipw_wx_get_powermode,
9993 ipw_wx_set_wireless_mode,
9994 ipw_wx_get_wireless_mode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05009995 ipw_wx_set_preamble,
9996 ipw_wx_get_preamble,
Jeff Garzikbf794512005-07-31 13:07:26 -04009997 ipw_wx_reset,
James Ketrenosb095c382005-08-24 22:04:42 -05009998 ipw_wx_sw_reset,
9999#ifdef CONFIG_IPW2200_MONITOR
10000 ipw_wx_set_monitor,
James Ketrenos43f66a62005-03-25 12:31:53 -060010001#endif
10002};
10003
Bhumika Goyal2c1dca32017-08-23 18:06:42 +053010004static const struct iw_handler_def ipw_wx_handler_def = {
James Ketrenosea2b26e2005-08-24 21:25:16 -050010005 .standard = ipw_wx_handlers,
10006 .num_standard = ARRAY_SIZE(ipw_wx_handlers),
10007 .num_private = ARRAY_SIZE(ipw_priv_handler),
10008 .num_private_args = ARRAY_SIZE(ipw_priv_args),
10009 .private = ipw_priv_handler,
10010 .private_args = ipw_priv_args,
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000010011 .get_wireless_stats = ipw_get_wireless_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -060010012};
10013
James Ketrenos43f66a62005-03-25 12:31:53 -060010014/*
10015 * Get wireless statistics.
10016 * Called by /proc/net/wireless
10017 * Also called by SIOCGIWSTATS
10018 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010019static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
James Ketrenos43f66a62005-03-25 12:31:53 -060010020{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010021 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010022 struct iw_statistics *wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -040010023
James Ketrenos43f66a62005-03-25 12:31:53 -060010024 wstats = &priv->wstats;
10025
James Ketrenosea2b26e2005-08-24 21:25:16 -050010026 /* if hw is disabled, then ipw_get_ordinal() can't be called.
James Ketrenosafbf30a2005-08-25 00:05:33 -050010027 * netdev->get_wireless_stats seems to be called before fw is
James Ketrenos43f66a62005-03-25 12:31:53 -060010028 * initialized. STATUS_ASSOCIATED will only be set if the hw is up
10029 * and associated; if not associcated, the values are all meaningless
10030 * anyway, so set them all to NULL and INVALID */
10031 if (!(priv->status & STATUS_ASSOCIATED)) {
10032 wstats->miss.beacon = 0;
10033 wstats->discard.retries = 0;
10034 wstats->qual.qual = 0;
10035 wstats->qual.level = 0;
10036 wstats->qual.noise = 0;
10037 wstats->qual.updated = 7;
10038 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010039 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
James Ketrenos43f66a62005-03-25 12:31:53 -060010040 return wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -040010041 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010042
10043 wstats->qual.qual = priv->quality;
Zhu Yi00d21de2006-04-13 17:19:02 +080010044 wstats->qual.level = priv->exp_avg_rssi;
10045 wstats->qual.noise = priv->exp_avg_noise;
James Ketrenos43f66a62005-03-25 12:31:53 -060010046 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
Bill Mossb1916082006-02-15 08:50:18 +080010047 IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM;
James Ketrenos43f66a62005-03-25 12:31:53 -060010048
10049 wstats->miss.beacon = average_value(&priv->average_missed_beacons);
10050 wstats->discard.retries = priv->last_tx_failures;
10051 wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
Jeff Garzikbf794512005-07-31 13:07:26 -040010052
James Ketrenos43f66a62005-03-25 12:31:53 -060010053/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
10054 goto fail_get_ordinal;
10055 wstats->discard.retries += tx_retry; */
Jeff Garzikbf794512005-07-31 13:07:26 -040010056
James Ketrenos43f66a62005-03-25 12:31:53 -060010057 return wstats;
10058}
10059
James Ketrenos43f66a62005-03-25 12:31:53 -060010060/* net device stuff */
10061
Arjan van de Ven858119e2006-01-14 13:20:43 -080010062static void init_sys_config(struct ipw_sys_config *sys_config)
James Ketrenos43f66a62005-03-25 12:31:53 -060010063{
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010064 memset(sys_config, 0, sizeof(struct ipw_sys_config));
Zhu Yi810dabd2006-01-24 16:36:59 +080010065 sys_config->bt_coexistence = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010066 sys_config->answer_broadcast_ssid_probe = 0;
10067 sys_config->accept_all_data_frames = 0;
10068 sys_config->accept_non_directed_frames = 1;
10069 sys_config->exclude_unicast_unencrypted = 0;
10070 sys_config->disable_unicast_decryption = 1;
10071 sys_config->exclude_multicast_unencrypted = 0;
10072 sys_config->disable_multicast_decryption = 1;
Zhu Yid2b83e12006-04-13 17:19:36 +080010073 if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B)
10074 antenna = CFG_SYS_ANTENNA_BOTH;
10075 sys_config->antenna_diversity = antenna;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010076 sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010077 sys_config->dot11g_auto_detection = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -040010078 sys_config->enable_cts_to_self = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010079 sys_config->bt_coexist_collision_thr = 0;
Pavel Machek67fd6b42006-07-11 15:34:05 +020010080 sys_config->pass_noise_stats_to_host = 1; /* 1 -- fix for 256 */
Cahill, Ben M12977152006-03-08 02:58:02 +080010081 sys_config->silence_threshold = 0x1e;
James Ketrenos43f66a62005-03-25 12:31:53 -060010082}
10083
10084static int ipw_net_open(struct net_device *dev)
10085{
James Ketrenos43f66a62005-03-25 12:31:53 -060010086 IPW_DEBUG_INFO("dev->open\n");
David S. Miller521c4d92008-07-22 18:32:47 -070010087 netif_start_queue(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010088 return 0;
10089}
10090
10091static int ipw_net_stop(struct net_device *dev)
10092{
10093 IPW_DEBUG_INFO("dev->close\n");
10094 netif_stop_queue(dev);
10095 return 0;
10096}
10097
10098/*
10099todo:
10100
10101modify to send one tfd per fragment instead of using chunking. otherwise
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010102we need to heavily modify the libipw_skb_to_txb.
James Ketrenos43f66a62005-03-25 12:31:53 -060010103*/
10104
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010105static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb,
James Ketrenos227d2dc2005-07-28 16:25:55 -050010106 int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -060010107{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010108 struct libipw_hdr_3addrqos *hdr = (struct libipw_hdr_3addrqos *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010109 txb->fragments[0]->data;
James Ketrenos43f66a62005-03-25 12:31:53 -060010110 int i = 0;
10111 struct tfd_frame *tfd;
Zhu Yie43e3c12006-04-13 17:20:45 +080010112#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -050010113 int tx_id = ipw_get_tx_queue_number(priv, pri);
10114 struct clx2_tx_queue *txq = &priv->txq[tx_id];
10115#else
James Ketrenos43f66a62005-03-25 12:31:53 -060010116 struct clx2_tx_queue *txq = &priv->txq[0];
James Ketrenosb095c382005-08-24 22:04:42 -050010117#endif
James Ketrenos43f66a62005-03-25 12:31:53 -060010118 struct clx2_queue *q = &txq->q;
10119 u8 id, hdr_len, unicast;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010120 int fc;
James Ketrenos43f66a62005-03-25 12:31:53 -060010121
Zhu Yib8ddafd2008-11-27 13:42:20 +080010122 if (!(priv->status & STATUS_ASSOCIATED))
10123 goto drop;
10124
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010125 hdr_len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
James Ketrenos43f66a62005-03-25 12:31:53 -060010126 switch (priv->ieee->iw_mode) {
10127 case IW_MODE_ADHOC:
Stephen Hemminger3c190652006-01-03 15:27:38 -080010128 unicast = !is_multicast_ether_addr(hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -060010129 id = ipw_find_station(priv, hdr->addr1);
10130 if (id == IPW_INVALID_STATION) {
10131 id = ipw_add_station(priv, hdr->addr1);
10132 if (id == IPW_INVALID_STATION) {
10133 IPW_WARNING("Attempt to send data to "
Johannes Berge1749612008-10-27 15:59:26 -070010134 "invalid cell: %pM\n",
10135 hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -060010136 goto drop;
10137 }
10138 }
10139 break;
10140
10141 case IW_MODE_INFRA:
10142 default:
Stephen Hemminger3c190652006-01-03 15:27:38 -080010143 unicast = !is_multicast_ether_addr(hdr->addr3);
James Ketrenos43f66a62005-03-25 12:31:53 -060010144 id = 0;
10145 break;
10146 }
10147
10148 tfd = &txq->bd[q->first_empty];
10149 txq->txb[q->first_empty] = txb;
10150 memset(tfd, 0, sizeof(*tfd));
10151 tfd->u.data.station_number = id;
10152
10153 tfd->control_flags.message_type = TX_FRAME_TYPE;
10154 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
10155
10156 tfd->u.data.cmd_id = DINO_CMD_TX;
James Ketrenosa613bff2005-08-24 21:43:11 -050010157 tfd->u.data.len = cpu_to_le16(txb->payload_size);
Jeff Garzikbf794512005-07-31 13:07:26 -040010158
James Ketrenos43f66a62005-03-25 12:31:53 -060010159 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
James Ketrenosb095c382005-08-24 22:04:42 -050010160 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK;
James Ketrenos43f66a62005-03-25 12:31:53 -060010161 else
James Ketrenosb095c382005-08-24 22:04:42 -050010162 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM;
James Ketrenos43f66a62005-03-25 12:31:53 -060010163
James Ketrenosea2b26e2005-08-24 21:25:16 -050010164 if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
10165 tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010166
James Ketrenosc848d0a2005-08-24 21:56:24 -050010167 fc = le16_to_cpu(hdr->frame_ctl);
10168 hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
James Ketrenos43f66a62005-03-25 12:31:53 -060010169
10170 memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
10171
James Ketrenosb095c382005-08-24 22:04:42 -050010172 if (likely(unicast))
10173 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
10174
10175 if (txb->encrypted && !priv->ieee->host_encrypt) {
10176 switch (priv->ieee->sec.level) {
10177 case SEC_LEVEL_3:
10178 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
Zhu Yi851ca262006-08-21 11:37:58 +080010179 cpu_to_le16(IEEE80211_FCTL_PROTECTED);
James Ketrenosb095c382005-08-24 22:04:42 -050010180 /* XXX: ACK flag must be set for CCMP even if it
10181 * is a multicast/broadcast packet, because CCMP
10182 * group communication encrypted by GTK is
10183 * actually done by the AP. */
10184 if (!unicast)
10185 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
10186
10187 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
10188 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM;
10189 tfd->u.data.key_index = 0;
10190 tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE;
10191 break;
10192 case SEC_LEVEL_2:
10193 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
Zhu Yi851ca262006-08-21 11:37:58 +080010194 cpu_to_le16(IEEE80211_FCTL_PROTECTED);
James Ketrenosb095c382005-08-24 22:04:42 -050010195 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
10196 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP;
10197 tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE;
10198 break;
10199 case SEC_LEVEL_1:
10200 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
Zhu Yi851ca262006-08-21 11:37:58 +080010201 cpu_to_le16(IEEE80211_FCTL_PROTECTED);
John W. Linville274bfb82008-10-29 11:35:05 -040010202 tfd->u.data.key_index = priv->ieee->crypt_info.tx_keyidx;
10203 if (priv->ieee->sec.key_sizes[priv->ieee->crypt_info.tx_keyidx] <=
James Ketrenosb095c382005-08-24 22:04:42 -050010204 40)
10205 tfd->u.data.key_index |= DCT_WEP_KEY_64Bit;
10206 else
10207 tfd->u.data.key_index |= DCT_WEP_KEY_128Bit;
10208 break;
10209 case SEC_LEVEL_0:
10210 break;
10211 default:
André Goddard Rosaaf901ca2009-11-14 13:09:05 -020010212 printk(KERN_ERR "Unknown security level %d\n",
James Ketrenosb095c382005-08-24 22:04:42 -050010213 priv->ieee->sec.level);
10214 break;
10215 }
10216 } else
10217 /* No hardware encryption */
10218 tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
10219
Zhu Yie43e3c12006-04-13 17:20:45 +080010220#ifdef CONFIG_IPW2200_QOS
Zhu Yia5cf4fe2006-04-13 17:19:11 +080010221 if (fc & IEEE80211_STYPE_QOS_DATA)
10222 ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data));
Zhu Yie43e3c12006-04-13 17:20:45 +080010223#endif /* CONFIG_IPW2200_QOS */
James Ketrenosb095c382005-08-24 22:04:42 -050010224
James Ketrenos43f66a62005-03-25 12:31:53 -060010225 /* payload */
James Ketrenosa613bff2005-08-24 21:43:11 -050010226 tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
10227 txb->nr_frags));
10228 IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
10229 txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
10230 for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
10231 IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
10232 i, le32_to_cpu(tfd->u.data.num_chunks),
10233 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -040010234 IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
James Ketrenos43f66a62005-03-25 12:31:53 -060010235 i, tfd->u.data.num_chunks,
10236 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -040010237 printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
James Ketrenos43f66a62005-03-25 12:31:53 -060010238 txb->fragments[i]->len - hdr_len);
10239
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010240 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -050010241 cpu_to_le32(pci_map_single
10242 (priv->pci_dev,
10243 txb->fragments[i]->data + hdr_len,
10244 txb->fragments[i]->len - hdr_len,
10245 PCI_DMA_TODEVICE));
10246 tfd->u.data.chunk_len[i] =
10247 cpu_to_le16(txb->fragments[i]->len - hdr_len);
James Ketrenos43f66a62005-03-25 12:31:53 -060010248 }
10249
10250 if (i != txb->nr_frags) {
10251 struct sk_buff *skb;
10252 u16 remaining_bytes = 0;
10253 int j;
10254
10255 for (j = i; j < txb->nr_frags; j++)
10256 remaining_bytes += txb->fragments[j]->len - hdr_len;
10257
10258 printk(KERN_INFO "Trying to reallocate for %d bytes\n",
10259 remaining_bytes);
10260 skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
10261 if (skb != NULL) {
James Ketrenosa613bff2005-08-24 21:43:11 -050010262 tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
James Ketrenos43f66a62005-03-25 12:31:53 -060010263 for (j = i; j < txb->nr_frags; j++) {
10264 int size = txb->fragments[j]->len - hdr_len;
James Ketrenosafbf30a2005-08-25 00:05:33 -050010265
James Ketrenos43f66a62005-03-25 12:31:53 -060010266 printk(KERN_INFO "Adding frag %d %d...\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010267 j, size);
Johannes Berg59ae1d12017-06-16 14:29:20 +020010268 skb_put_data(skb,
10269 txb->fragments[j]->data + hdr_len,
10270 size);
James Ketrenos43f66a62005-03-25 12:31:53 -060010271 }
10272 dev_kfree_skb_any(txb->fragments[i]);
10273 txb->fragments[i] = skb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010274 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -050010275 cpu_to_le32(pci_map_single
10276 (priv->pci_dev, skb->data,
Al Viro49587302007-12-27 01:57:47 -050010277 remaining_bytes,
James Ketrenosa613bff2005-08-24 21:43:11 -050010278 PCI_DMA_TODEVICE));
10279
Marcin Slusarz5c058632008-02-13 00:06:12 +010010280 le32_add_cpu(&tfd->u.data.num_chunks, 1);
Jeff Garzikbf794512005-07-31 13:07:26 -040010281 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010282 }
10283
10284 /* kick DMA */
10285 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
10286 ipw_write32(priv, q->reg_w, q->first_empty);
10287
Dan Williams943dbef2008-02-14 17:49:41 -050010288 if (ipw_tx_queue_space(q) < q->high_mark)
James Ketrenosf6970142006-02-14 09:10:51 +080010289 netif_stop_queue(priv->net_dev);
10290
James Ketrenos227d2dc2005-07-28 16:25:55 -050010291 return NETDEV_TX_OK;
James Ketrenos43f66a62005-03-25 12:31:53 -060010292
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010293 drop:
James Ketrenos43f66a62005-03-25 12:31:53 -060010294 IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010295 libipw_txb_free(txb);
James Ketrenos227d2dc2005-07-28 16:25:55 -050010296 return NETDEV_TX_OK;
10297}
10298
10299static int ipw_net_is_queue_full(struct net_device *dev, int pri)
10300{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010301 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yie43e3c12006-04-13 17:20:45 +080010302#ifdef CONFIG_IPW2200_QOS
James Ketrenos227d2dc2005-07-28 16:25:55 -050010303 int tx_id = ipw_get_tx_queue_number(priv, pri);
10304 struct clx2_tx_queue *txq = &priv->txq[tx_id];
10305#else
10306 struct clx2_tx_queue *txq = &priv->txq[0];
Zhu Yie43e3c12006-04-13 17:20:45 +080010307#endif /* CONFIG_IPW2200_QOS */
James Ketrenos227d2dc2005-07-28 16:25:55 -050010308
Dan Williams943dbef2008-02-14 17:49:41 -050010309 if (ipw_tx_queue_space(&txq->q) < txq->q.high_mark)
James Ketrenos227d2dc2005-07-28 16:25:55 -050010310 return 1;
10311
10312 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010313}
10314
Zhu Yid685b8c2006-04-13 17:20:27 +080010315#ifdef CONFIG_IPW2200_PROMISCUOUS
10316static void ipw_handle_promiscuous_tx(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010317 struct libipw_txb *txb)
Zhu Yid685b8c2006-04-13 17:20:27 +080010318{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010319 struct libipw_rx_stats dummystats;
Zhu Yid685b8c2006-04-13 17:20:27 +080010320 struct ieee80211_hdr *hdr;
10321 u8 n;
10322 u16 filter = priv->prom_priv->filter;
10323 int hdr_only = 0;
10324
10325 if (filter & IPW_PROM_NO_TX)
10326 return;
10327
10328 memset(&dummystats, 0, sizeof(dummystats));
10329
Lucas De Marchi25985ed2011-03-30 22:57:33 -030010330 /* Filtering of fragment chains is done against the first fragment */
Zhu Yid685b8c2006-04-13 17:20:27 +080010331 hdr = (void *)txb->fragments[0]->data;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010332 if (libipw_is_management(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +080010333 if (filter & IPW_PROM_NO_MGMT)
10334 return;
10335 if (filter & IPW_PROM_MGMT_HEADER_ONLY)
10336 hdr_only = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010337 } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +080010338 if (filter & IPW_PROM_NO_CTL)
10339 return;
10340 if (filter & IPW_PROM_CTL_HEADER_ONLY)
10341 hdr_only = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010342 } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +080010343 if (filter & IPW_PROM_NO_DATA)
10344 return;
10345 if (filter & IPW_PROM_DATA_HEADER_ONLY)
10346 hdr_only = 1;
10347 }
10348
10349 for(n=0; n<txb->nr_frags; ++n) {
10350 struct sk_buff *src = txb->fragments[n];
10351 struct sk_buff *dst;
10352 struct ieee80211_radiotap_header *rt_hdr;
10353 int len;
10354
10355 if (hdr_only) {
10356 hdr = (void *)src->data;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010357 len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control));
Zhu Yid685b8c2006-04-13 17:20:27 +080010358 } else
10359 len = src->len;
10360
Stanislav Yakovlevbf113152012-10-15 14:14:32 +000010361 dst = alloc_skb(len + sizeof(*rt_hdr) + sizeof(u16)*2, GFP_ATOMIC);
Johannes Berg007e5dd2008-11-27 23:13:38 +010010362 if (!dst)
10363 continue;
Zhu Yid685b8c2006-04-13 17:20:27 +080010364
Johannes Berg4df864c2017-06-16 14:29:21 +020010365 rt_hdr = skb_put(dst, sizeof(*rt_hdr));
Zhu Yid685b8c2006-04-13 17:20:27 +080010366
10367 rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION;
10368 rt_hdr->it_pad = 0;
10369 rt_hdr->it_present = 0; /* after all, it's just an idea */
Al Viro743b84d2007-12-27 01:43:16 -050010370 rt_hdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
Zhu Yid685b8c2006-04-13 17:20:27 +080010371
Al Viroe62e1ee2007-12-27 01:36:46 -050010372 *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(
Zhu Yid685b8c2006-04-13 17:20:27 +080010373 ieee80211chan2mhz(priv->channel));
10374 if (priv->channel > 14) /* 802.11a */
Al Viroe62e1ee2007-12-27 01:36:46 -050010375 *(__le16*)skb_put(dst, sizeof(u16)) =
Zhu Yid685b8c2006-04-13 17:20:27 +080010376 cpu_to_le16(IEEE80211_CHAN_OFDM |
10377 IEEE80211_CHAN_5GHZ);
10378 else if (priv->ieee->mode == IEEE_B) /* 802.11b */
Al Viroe62e1ee2007-12-27 01:36:46 -050010379 *(__le16*)skb_put(dst, sizeof(u16)) =
Zhu Yid685b8c2006-04-13 17:20:27 +080010380 cpu_to_le16(IEEE80211_CHAN_CCK |
10381 IEEE80211_CHAN_2GHZ);
10382 else /* 802.11g */
Al Viroe62e1ee2007-12-27 01:36:46 -050010383 *(__le16*)skb_put(dst, sizeof(u16)) =
Zhu Yid685b8c2006-04-13 17:20:27 +080010384 cpu_to_le16(IEEE80211_CHAN_OFDM |
10385 IEEE80211_CHAN_2GHZ);
10386
Al Viro743b84d2007-12-27 01:43:16 -050010387 rt_hdr->it_len = cpu_to_le16(dst->len);
Zhu Yid685b8c2006-04-13 17:20:27 +080010388
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -030010389 skb_copy_from_linear_data(src, skb_put(dst, len), len);
Zhu Yid685b8c2006-04-13 17:20:27 +080010390
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010391 if (!libipw_rx(priv->prom_priv->ieee, dst, &dummystats))
Zhu Yid685b8c2006-04-13 17:20:27 +080010392 dev_kfree_skb_any(dst);
10393 }
10394}
10395#endif
10396
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +000010397static netdev_tx_t ipw_net_hard_start_xmit(struct libipw_txb *txb,
10398 struct net_device *dev, int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -060010399{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010400 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010401 unsigned long flags;
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +000010402 netdev_tx_t ret;
James Ketrenos43f66a62005-03-25 12:31:53 -060010403
10404 IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -060010405 spin_lock_irqsave(&priv->lock, flags);
10406
Zhu Yid685b8c2006-04-13 17:20:27 +080010407#ifdef CONFIG_IPW2200_PROMISCUOUS
10408 if (rtap_iface && netif_running(priv->prom_net_dev))
10409 ipw_handle_promiscuous_tx(priv, txb);
10410#endif
10411
James Ketrenos227d2dc2005-07-28 16:25:55 -050010412 ret = ipw_tx_skb(priv, txb, pri);
10413 if (ret == NETDEV_TX_OK)
10414 __ipw_led_activity_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010415 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -060010416
James Ketrenos227d2dc2005-07-28 16:25:55 -050010417 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -060010418}
10419
James Ketrenos43f66a62005-03-25 12:31:53 -060010420static void ipw_net_set_multicast_list(struct net_device *dev)
10421{
10422
10423}
10424
10425static int ipw_net_set_mac_address(struct net_device *dev, void *p)
10426{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010427 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010428 struct sockaddr *addr = p;
Joe Perches0795af52007-10-03 17:59:30 -070010429
James Ketrenos43f66a62005-03-25 12:31:53 -060010430 if (!is_valid_ether_addr(addr->sa_data))
10431 return -EADDRNOTAVAIL;
Zhu Yi46441512006-01-24 16:37:59 +080010432 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060010433 priv->config |= CFG_CUSTOM_MAC;
10434 memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
Johannes Berge1749612008-10-27 15:59:26 -070010435 printk(KERN_INFO "%s: Setting MAC to %pM\n",
10436 priv->net_dev->name, priv->mac_addr);
Tejun Heobcb6d912011-01-26 12:12:50 +010010437 schedule_work(&priv->adapter_restart);
Zhu Yi46441512006-01-24 16:37:59 +080010438 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060010439 return 0;
10440}
10441
Jeff Garzikbf794512005-07-31 13:07:26 -040010442static void ipw_ethtool_get_drvinfo(struct net_device *dev,
James Ketrenos43f66a62005-03-25 12:31:53 -060010443 struct ethtool_drvinfo *info)
10444{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010445 struct ipw_priv *p = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010446 char vers[64];
10447 char date[32];
10448 u32 len;
10449
Rick Jones1f80c232011-11-15 10:40:49 -080010450 strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
10451 strlcpy(info->version, DRV_VERSION, sizeof(info->version));
James Ketrenos43f66a62005-03-25 12:31:53 -060010452
10453 len = sizeof(vers);
10454 ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
10455 len = sizeof(date);
10456 ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
10457
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010458 snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
James Ketrenos43f66a62005-03-25 12:31:53 -060010459 vers, date);
Rick Jones1f80c232011-11-15 10:40:49 -080010460 strlcpy(info->bus_info, pci_name(p->pci_dev),
10461 sizeof(info->bus_info));
James Ketrenos43f66a62005-03-25 12:31:53 -060010462}
10463
10464static u32 ipw_ethtool_get_link(struct net_device *dev)
10465{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010466 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010467 return (priv->status & STATUS_ASSOCIATED) != 0;
10468}
10469
10470static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
10471{
James Ketrenosb095c382005-08-24 22:04:42 -050010472 return IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010473}
10474
10475static int ipw_ethtool_get_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010476 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010477{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010478 struct ipw_priv *p = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010479
James Ketrenosb095c382005-08-24 22:04:42 -050010480 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010481 return -EINVAL;
Zhu Yi46441512006-01-24 16:37:59 +080010482 mutex_lock(&p->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010483 memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len);
Zhu Yi46441512006-01-24 16:37:59 +080010484 mutex_unlock(&p->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060010485 return 0;
10486}
10487
10488static int ipw_ethtool_set_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010489 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010490{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010491 struct ipw_priv *p = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010492 int i;
10493
James Ketrenosb095c382005-08-24 22:04:42 -050010494 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010495 return -EINVAL;
Zhu Yi46441512006-01-24 16:37:59 +080010496 mutex_lock(&p->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010497 memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len);
Adrian Bunk71e585f2006-03-11 04:42:58 +010010498 for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
10499 ipw_write8(p, i + IPW_EEPROM_DATA, p->eeprom[i]);
Zhu Yi46441512006-01-24 16:37:59 +080010500 mutex_unlock(&p->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060010501 return 0;
10502}
10503
Jeff Garzik7282d492006-09-13 14:30:00 -040010504static const struct ethtool_ops ipw_ethtool_ops = {
James Ketrenosea2b26e2005-08-24 21:25:16 -050010505 .get_link = ipw_ethtool_get_link,
10506 .get_drvinfo = ipw_ethtool_get_drvinfo,
10507 .get_eeprom_len = ipw_ethtool_get_eeprom_len,
10508 .get_eeprom = ipw_ethtool_get_eeprom,
10509 .set_eeprom = ipw_ethtool_set_eeprom,
James Ketrenos43f66a62005-03-25 12:31:53 -060010510};
10511
David Howells7d12e782006-10-05 14:55:46 +010010512static irqreturn_t ipw_isr(int irq, void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -060010513{
10514 struct ipw_priv *priv = data;
10515 u32 inta, inta_mask;
Jeff Garzikbf794512005-07-31 13:07:26 -040010516
James Ketrenos43f66a62005-03-25 12:31:53 -060010517 if (!priv)
10518 return IRQ_NONE;
10519
Zhu Yi89c318e2006-06-08 22:19:49 -070010520 spin_lock(&priv->irq_lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010521
10522 if (!(priv->status & STATUS_INT_ENABLED)) {
Zhu Yid00d0122007-07-12 16:09:40 +080010523 /* IRQ is disabled */
James Ketrenos43f66a62005-03-25 12:31:53 -060010524 goto none;
10525 }
10526
James Ketrenosb095c382005-08-24 22:04:42 -050010527 inta = ipw_read32(priv, IPW_INTA_RW);
10528 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
Jeff Garzikbf794512005-07-31 13:07:26 -040010529
James Ketrenos43f66a62005-03-25 12:31:53 -060010530 if (inta == 0xFFFFFFFF) {
10531 /* Hardware disappeared */
10532 IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
10533 goto none;
10534 }
10535
James Ketrenosb095c382005-08-24 22:04:42 -050010536 if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010537 /* Shared interrupt */
10538 goto none;
10539 }
10540
10541 /* tell the device to stop sending interrupts */
Zhu Yi89c318e2006-06-08 22:19:49 -070010542 __ipw_disable_interrupts(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010543
James Ketrenos43f66a62005-03-25 12:31:53 -060010544 /* ack current interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -050010545 inta &= (IPW_INTA_MASK_ALL & inta_mask);
10546 ipw_write32(priv, IPW_INTA_RW, inta);
Jeff Garzikbf794512005-07-31 13:07:26 -040010547
James Ketrenos43f66a62005-03-25 12:31:53 -060010548 /* Cache INTA value for our tasklet */
10549 priv->isr_inta = inta;
10550
10551 tasklet_schedule(&priv->irq_tasklet);
10552
Zhu Yi89c318e2006-06-08 22:19:49 -070010553 spin_unlock(&priv->irq_lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010554
10555 return IRQ_HANDLED;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010556 none:
Zhu Yi89c318e2006-06-08 22:19:49 -070010557 spin_unlock(&priv->irq_lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010558 return IRQ_NONE;
10559}
10560
10561static void ipw_rf_kill(void *adapter)
10562{
10563 struct ipw_priv *priv = adapter;
10564 unsigned long flags;
Jeff Garzikbf794512005-07-31 13:07:26 -040010565
James Ketrenos43f66a62005-03-25 12:31:53 -060010566 spin_lock_irqsave(&priv->lock, flags);
10567
10568 if (rf_kill_active(priv)) {
10569 IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
Tejun Heobcb6d912011-01-26 12:12:50 +010010570 schedule_delayed_work(&priv->rf_kill, 2 * HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -060010571 goto exit_unlock;
10572 }
10573
10574 /* RF Kill is now disabled, so bring the device back up */
10575
10576 if (!(priv->status & STATUS_RF_KILL_MASK)) {
10577 IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
10578 "device\n");
10579
10580 /* we can not do an adapter restart while inside an irq lock */
Tejun Heobcb6d912011-01-26 12:12:50 +010010581 schedule_work(&priv->adapter_restart);
Jeff Garzikbf794512005-07-31 13:07:26 -040010582 } else
James Ketrenos43f66a62005-03-25 12:31:53 -060010583 IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
10584 "enabled\n");
10585
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010586 exit_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -060010587 spin_unlock_irqrestore(&priv->lock, flags);
10588}
10589
David Howellsc4028952006-11-22 14:57:56 +000010590static void ipw_bg_rf_kill(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050010591{
David Howellsc4028952006-11-22 14:57:56 +000010592 struct ipw_priv *priv =
10593 container_of(work, struct ipw_priv, rf_kill.work);
Zhu Yi46441512006-01-24 16:37:59 +080010594 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000010595 ipw_rf_kill(priv);
Zhu Yi46441512006-01-24 16:37:59 +080010596 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010597}
10598
Adrian Bunka73e22b2006-01-21 01:39:42 +010010599static void ipw_link_up(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -050010600{
James Ketrenosafbf30a2005-08-25 00:05:33 -050010601 priv->last_seq_num = -1;
10602 priv->last_frag_num = -1;
10603 priv->last_packet_time = 0;
10604
James Ketrenosa613bff2005-08-24 21:43:11 -050010605 netif_carrier_on(priv->net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050010606
James Ketrenosc848d0a2005-08-24 21:56:24 -050010607 cancel_delayed_work(&priv->request_scan);
Dan Williamsea177302008-06-02 17:51:23 -040010608 cancel_delayed_work(&priv->request_direct_scan);
10609 cancel_delayed_work(&priv->request_passive_scan);
Dan Williams0b531672007-10-09 13:55:24 -040010610 cancel_delayed_work(&priv->scan_event);
James Ketrenosa613bff2005-08-24 21:43:11 -050010611 ipw_reset_stats(priv);
10612 /* Ensure the rate is updated immediately */
10613 priv->last_rate = ipw_get_current_rate(priv);
10614 ipw_gather_stats(priv);
10615 ipw_led_link_up(priv);
10616 notify_wx_assoc_event(priv);
10617
10618 if (priv->config & CFG_BACKGROUND_SCAN)
Tejun Heobcb6d912011-01-26 12:12:50 +010010619 schedule_delayed_work(&priv->request_scan, HZ);
James Ketrenosa613bff2005-08-24 21:43:11 -050010620}
10621
David Howellsc4028952006-11-22 14:57:56 +000010622static void ipw_bg_link_up(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050010623{
David Howellsc4028952006-11-22 14:57:56 +000010624 struct ipw_priv *priv =
10625 container_of(work, struct ipw_priv, link_up);
Zhu Yi46441512006-01-24 16:37:59 +080010626 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000010627 ipw_link_up(priv);
Zhu Yi46441512006-01-24 16:37:59 +080010628 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010629}
10630
Adrian Bunka73e22b2006-01-21 01:39:42 +010010631static void ipw_link_down(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -050010632{
10633 ipw_led_link_down(priv);
10634 netif_carrier_off(priv->net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050010635 notify_wx_assoc_event(priv);
10636
10637 /* Cancel any queued work ... */
10638 cancel_delayed_work(&priv->request_scan);
Dan Williamsea177302008-06-02 17:51:23 -040010639 cancel_delayed_work(&priv->request_direct_scan);
10640 cancel_delayed_work(&priv->request_passive_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -050010641 cancel_delayed_work(&priv->adhoc_check);
10642 cancel_delayed_work(&priv->gather_stats);
10643
10644 ipw_reset_stats(priv);
10645
James Ketrenosafbf30a2005-08-25 00:05:33 -050010646 if (!(priv->status & STATUS_EXIT_PENDING)) {
10647 /* Queue up another scan... */
Tejun Heobcb6d912011-01-26 12:12:50 +010010648 schedule_delayed_work(&priv->request_scan, 0);
Dan Williams0b531672007-10-09 13:55:24 -040010649 } else
10650 cancel_delayed_work(&priv->scan_event);
James Ketrenosa613bff2005-08-24 21:43:11 -050010651}
10652
David Howellsc4028952006-11-22 14:57:56 +000010653static void ipw_bg_link_down(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050010654{
David Howellsc4028952006-11-22 14:57:56 +000010655 struct ipw_priv *priv =
10656 container_of(work, struct ipw_priv, link_down);
Zhu Yi46441512006-01-24 16:37:59 +080010657 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000010658 ipw_link_down(priv);
Zhu Yi46441512006-01-24 16:37:59 +080010659 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010660}
10661
Bill Pembertoneb9248e2012-12-03 09:56:32 -050010662static int ipw_setup_deferred_work(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010663{
10664 int ret = 0;
10665
James Ketrenos43f66a62005-03-25 12:31:53 -060010666 init_waitqueue_head(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010667 init_waitqueue_head(&priv->wait_state);
James Ketrenos43f66a62005-03-25 12:31:53 -060010668
David Howellsc4028952006-11-22 14:57:56 +000010669 INIT_DELAYED_WORK(&priv->adhoc_check, ipw_bg_adhoc_check);
10670 INIT_WORK(&priv->associate, ipw_bg_associate);
10671 INIT_WORK(&priv->disassociate, ipw_bg_disassociate);
10672 INIT_WORK(&priv->system_config, ipw_system_config);
10673 INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish);
10674 INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart);
10675 INIT_DELAYED_WORK(&priv->rf_kill, ipw_bg_rf_kill);
10676 INIT_WORK(&priv->up, ipw_bg_up);
10677 INIT_WORK(&priv->down, ipw_bg_down);
10678 INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
Dan Williamsea177302008-06-02 17:51:23 -040010679 INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan);
10680 INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
Dan Williams0b531672007-10-09 13:55:24 -040010681 INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event);
David Howellsc4028952006-11-22 14:57:56 +000010682 INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
10683 INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
10684 INIT_WORK(&priv->roam, ipw_bg_roam);
10685 INIT_DELAYED_WORK(&priv->scan_check, ipw_bg_scan_check);
10686 INIT_WORK(&priv->link_up, ipw_bg_link_up);
10687 INIT_WORK(&priv->link_down, ipw_bg_link_down);
10688 INIT_DELAYED_WORK(&priv->led_link_on, ipw_bg_led_link_on);
10689 INIT_DELAYED_WORK(&priv->led_link_off, ipw_bg_led_link_off);
10690 INIT_DELAYED_WORK(&priv->led_act_off, ipw_bg_led_activity_off);
10691 INIT_WORK(&priv->merge_networks, ipw_merge_adhoc_network);
James Ketrenos43f66a62005-03-25 12:31:53 -060010692
Zhu Yie43e3c12006-04-13 17:20:45 +080010693#ifdef CONFIG_IPW2200_QOS
David Howellsc4028952006-11-22 14:57:56 +000010694 INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate);
Zhu Yie43e3c12006-04-13 17:20:45 +080010695#endif /* CONFIG_IPW2200_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010696
10697 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
10698 ipw_irq_tasklet, (unsigned long)priv);
10699
10700 return ret;
10701}
10702
James Ketrenos43f66a62005-03-25 12:31:53 -060010703static void shim__set_security(struct net_device *dev,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010704 struct libipw_security *sec)
James Ketrenos43f66a62005-03-25 12:31:53 -060010705{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010706 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010707 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -040010708 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010709 if (sec->flags & (1 << i)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050010710 priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
James Ketrenosb095c382005-08-24 22:04:42 -050010711 priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
James Ketrenos43f66a62005-03-25 12:31:53 -060010712 if (sec->key_sizes[i] == 0)
James Ketrenosb095c382005-08-24 22:04:42 -050010713 priv->ieee->sec.flags &= ~(1 << i);
10714 else {
10715 memcpy(priv->ieee->sec.keys[i], sec->keys[i],
James Ketrenos43f66a62005-03-25 12:31:53 -060010716 sec->key_sizes[i]);
James Ketrenosb095c382005-08-24 22:04:42 -050010717 priv->ieee->sec.flags |= (1 << i);
10718 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010719 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010720 } else if (sec->level != SEC_LEVEL_1)
10721 priv->ieee->sec.flags &= ~(1 << i);
James Ketrenos43f66a62005-03-25 12:31:53 -060010722 }
10723
James Ketrenosb095c382005-08-24 22:04:42 -050010724 if (sec->flags & SEC_ACTIVE_KEY) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010725 if (sec->active_key <= 3) {
James Ketrenosb095c382005-08-24 22:04:42 -050010726 priv->ieee->sec.active_key = sec->active_key;
10727 priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikbf794512005-07-31 13:07:26 -040010728 } else
James Ketrenosb095c382005-08-24 22:04:42 -050010729 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010730 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010731 } else
10732 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010733
10734 if ((sec->flags & SEC_AUTH_MODE) &&
James Ketrenosb095c382005-08-24 22:04:42 -050010735 (priv->ieee->sec.auth_mode != sec->auth_mode)) {
10736 priv->ieee->sec.auth_mode = sec->auth_mode;
10737 priv->ieee->sec.flags |= SEC_AUTH_MODE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010738 if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
10739 priv->capability |= CAP_SHARED_KEY;
10740 else
10741 priv->capability &= ~CAP_SHARED_KEY;
10742 priv->status |= STATUS_SECURITY_UPDATED;
10743 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010744
James Ketrenosb095c382005-08-24 22:04:42 -050010745 if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
10746 priv->ieee->sec.flags |= SEC_ENABLED;
10747 priv->ieee->sec.enabled = sec->enabled;
James Ketrenos43f66a62005-03-25 12:31:53 -060010748 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -040010749 if (sec->enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -060010750 priv->capability |= CAP_PRIVACY_ON;
10751 else
10752 priv->capability &= ~CAP_PRIVACY_ON;
10753 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010754
James Ketrenosafbf30a2005-08-25 00:05:33 -050010755 if (sec->flags & SEC_ENCRYPT)
10756 priv->ieee->sec.encrypt = sec->encrypt;
James Ketrenos43f66a62005-03-25 12:31:53 -060010757
James Ketrenosb095c382005-08-24 22:04:42 -050010758 if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
10759 priv->ieee->sec.level = sec->level;
10760 priv->ieee->sec.flags |= SEC_LEVEL;
James Ketrenos43f66a62005-03-25 12:31:53 -060010761 priv->status |= STATUS_SECURITY_UPDATED;
10762 }
10763
Zhu Yi1fbfea52005-08-05 17:22:56 +080010764 if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
10765 ipw_set_hwcrypto_keys(priv);
10766
Jeff Garzikbf794512005-07-31 13:07:26 -040010767 /* To match current functionality of ipw2100 (which works well w/
10768 * various supplicants, we don't force a disassociate if the
James Ketrenos43f66a62005-03-25 12:31:53 -060010769 * privacy capability changes ... */
10770#if 0
10771 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
Jeff Garzikbf794512005-07-31 13:07:26 -040010772 (((priv->assoc_request.capability &
Al Viro5b5e8072007-12-27 01:54:06 -050010773 cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && !sec->enabled) ||
Jeff Garzikbf794512005-07-31 13:07:26 -040010774 (!(priv->assoc_request.capability &
Al Viro5b5e8072007-12-27 01:54:06 -050010775 cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && sec->enabled))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010776 IPW_DEBUG_ASSOC("Disassociating due to capability "
10777 "change.\n");
10778 ipw_disassociate(priv);
10779 }
10780#endif
10781}
10782
Jeff Garzikbf794512005-07-31 13:07:26 -040010783static int init_supported_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -060010784 struct ipw_supported_rates *rates)
10785{
10786 /* TODO: Mask out rates based on priv->rates_mask */
10787
10788 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010789 /* configure supported rates */
James Ketrenos43f66a62005-03-25 12:31:53 -060010790 switch (priv->ieee->freq_band) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010791 case LIBIPW_52GHZ_BAND:
James Ketrenos43f66a62005-03-25 12:31:53 -060010792 rates->ieee_mode = IPW_A_MODE;
10793 rates->purpose = IPW_RATE_CAPABILITIES;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010794 ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION,
10795 LIBIPW_OFDM_DEFAULT_RATES_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -060010796 break;
10797
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010798 default: /* Mixed or 2.4Ghz */
James Ketrenos43f66a62005-03-25 12:31:53 -060010799 rates->ieee_mode = IPW_G_MODE;
10800 rates->purpose = IPW_RATE_CAPABILITIES;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010801 ipw_add_cck_scan_rates(rates, LIBIPW_CCK_MODULATION,
10802 LIBIPW_CCK_DEFAULT_RATES_MASK);
10803 if (priv->ieee->modulation & LIBIPW_OFDM_MODULATION) {
10804 ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION,
10805 LIBIPW_OFDM_DEFAULT_RATES_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -060010806 }
10807 break;
10808 }
10809
10810 return 0;
10811}
10812
Jeff Garzikbf794512005-07-31 13:07:26 -040010813static int ipw_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010814{
James Ketrenos43f66a62005-03-25 12:31:53 -060010815 /* This is only called from ipw_up, which resets/reloads the firmware
10816 so, we don't need to first disable the card before we configure
10817 it */
Zhu Yi6de9f7f2005-08-11 14:39:33 +080010818 if (ipw_set_tx_power(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -060010819 goto error;
10820
10821 /* initialize adapter address */
10822 if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
10823 goto error;
10824
10825 /* set basic system config settings */
10826 init_sys_config(&priv->sys_config);
Zhu Yi810dabd2006-01-24 16:36:59 +080010827
10828 /* Support Bluetooth if we have BT h/w on board, and user wants to.
10829 * Does not support BT priority yet (don't abort or defer our Tx) */
10830 if (bt_coexist) {
Zhu Yi2638bc32006-01-24 16:37:52 +080010831 unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY];
Zhu Yi810dabd2006-01-24 16:36:59 +080010832
10833 if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG)
10834 priv->sys_config.bt_coexistence
Zhu Yi2638bc32006-01-24 16:37:52 +080010835 |= CFG_BT_COEXISTENCE_SIGNAL_CHNL;
Zhu Yi810dabd2006-01-24 16:36:59 +080010836 if (bt_caps & EEPROM_SKU_CAP_BT_OOB)
10837 priv->sys_config.bt_coexistence
Zhu Yi2638bc32006-01-24 16:37:52 +080010838 |= CFG_BT_COEXISTENCE_OOB;
Zhu Yi810dabd2006-01-24 16:36:59 +080010839 }
10840
Zhu Yid685b8c2006-04-13 17:20:27 +080010841#ifdef CONFIG_IPW2200_PROMISCUOUS
10842 if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
10843 priv->sys_config.accept_all_data_frames = 1;
10844 priv->sys_config.accept_non_directed_frames = 1;
10845 priv->sys_config.accept_all_mgmt_bcpr = 1;
10846 priv->sys_config.accept_all_mgmt_frames = 1;
10847 }
10848#endif
10849
James Ketrenosc848d0a2005-08-24 21:56:24 -050010850 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
10851 priv->sys_config.answer_broadcast_ssid_probe = 1;
10852 else
10853 priv->sys_config.answer_broadcast_ssid_probe = 0;
10854
Zhu Yid685b8c2006-04-13 17:20:27 +080010855 if (ipw_send_system_config(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -060010856 goto error;
10857
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010858 init_supported_rates(priv, &priv->rates);
10859 if (ipw_send_supported_rates(priv, &priv->rates))
James Ketrenos43f66a62005-03-25 12:31:53 -060010860 goto error;
10861
10862 /* Set request-to-send threshold */
10863 if (priv->rts_threshold) {
10864 if (ipw_send_rts_threshold(priv, priv->rts_threshold))
10865 goto error;
10866 }
Zhu Yie43e3c12006-04-13 17:20:45 +080010867#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -050010868 IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
10869 ipw_qos_activate(priv, NULL);
Zhu Yie43e3c12006-04-13 17:20:45 +080010870#endif /* CONFIG_IPW2200_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010871
10872 if (ipw_set_random_seed(priv))
10873 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -040010874
James Ketrenos43f66a62005-03-25 12:31:53 -060010875 /* final state transition to the RUN state */
10876 if (ipw_send_host_complete(priv))
10877 goto error;
10878
James Ketrenose6666192005-08-12 09:17:04 -050010879 priv->status |= STATUS_INIT;
10880
10881 ipw_led_init(priv);
10882 ipw_led_radio_on(priv);
10883 priv->notif_missed_beacons = 0;
10884
10885 /* Set hardware WEP key if it is configured. */
10886 if ((priv->capability & CAP_PRIVACY_ON) &&
10887 (priv->ieee->sec.level == SEC_LEVEL_1) &&
10888 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
10889 ipw_set_hwcrypto_keys(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010890
10891 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -040010892
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010893 error:
James Ketrenos43f66a62005-03-25 12:31:53 -060010894 return -EIO;
10895}
10896
James Ketrenos4f36f802005-08-03 20:36:56 -050010897/*
10898 * NOTE:
10899 *
10900 * These tables have been tested in conjunction with the
10901 * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters.
10902 *
10903 * Altering this values, using it on other hardware, or in geographies
10904 * not intended for resale of the above mentioned Intel adapters has
10905 * not been tested.
10906 *
Henrik Brix Andersen48a84772006-02-14 09:09:52 +080010907 * Remember to update the table in README.ipw2200 when changing this
10908 * table.
10909 *
James Ketrenos4f36f802005-08-03 20:36:56 -050010910 */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010911static const struct libipw_geo ipw_geos[] = {
James Ketrenos4f36f802005-08-03 20:36:56 -050010912 { /* Restricted */
10913 "---",
10914 .bg_channels = 11,
10915 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10916 {2427, 4}, {2432, 5}, {2437, 6},
10917 {2442, 7}, {2447, 8}, {2452, 9},
10918 {2457, 10}, {2462, 11}},
10919 },
10920
10921 { /* Custom US/Canada */
10922 "ZZF",
10923 .bg_channels = 11,
10924 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10925 {2427, 4}, {2432, 5}, {2437, 6},
10926 {2442, 7}, {2447, 8}, {2452, 9},
10927 {2457, 10}, {2462, 11}},
10928 .a_channels = 8,
10929 .a = {{5180, 36},
10930 {5200, 40},
10931 {5220, 44},
10932 {5240, 48},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010933 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
10934 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
10935 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
10936 {5320, 64, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050010937 },
10938
10939 { /* Rest of World */
10940 "ZZD",
10941 .bg_channels = 13,
10942 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10943 {2427, 4}, {2432, 5}, {2437, 6},
10944 {2442, 7}, {2447, 8}, {2452, 9},
10945 {2457, 10}, {2462, 11}, {2467, 12},
10946 {2472, 13}},
10947 },
10948
10949 { /* Custom USA & Europe & High */
10950 "ZZA",
10951 .bg_channels = 11,
10952 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10953 {2427, 4}, {2432, 5}, {2437, 6},
10954 {2442, 7}, {2447, 8}, {2452, 9},
10955 {2457, 10}, {2462, 11}},
10956 .a_channels = 13,
10957 .a = {{5180, 36},
10958 {5200, 40},
10959 {5220, 44},
10960 {5240, 48},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010961 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
10962 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
10963 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
10964 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
James Ketrenos4f36f802005-08-03 20:36:56 -050010965 {5745, 149},
10966 {5765, 153},
10967 {5785, 157},
10968 {5805, 161},
10969 {5825, 165}},
10970 },
10971
10972 { /* Custom NA & Europe */
10973 "ZZB",
10974 .bg_channels = 11,
10975 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10976 {2427, 4}, {2432, 5}, {2437, 6},
10977 {2442, 7}, {2447, 8}, {2452, 9},
10978 {2457, 10}, {2462, 11}},
10979 .a_channels = 13,
10980 .a = {{5180, 36},
10981 {5200, 40},
10982 {5220, 44},
10983 {5240, 48},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010984 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
10985 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
10986 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
10987 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
10988 {5745, 149, LIBIPW_CH_PASSIVE_ONLY},
10989 {5765, 153, LIBIPW_CH_PASSIVE_ONLY},
10990 {5785, 157, LIBIPW_CH_PASSIVE_ONLY},
10991 {5805, 161, LIBIPW_CH_PASSIVE_ONLY},
10992 {5825, 165, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050010993 },
10994
10995 { /* Custom Japan */
10996 "ZZC",
10997 .bg_channels = 11,
10998 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10999 {2427, 4}, {2432, 5}, {2437, 6},
11000 {2442, 7}, {2447, 8}, {2452, 9},
11001 {2457, 10}, {2462, 11}},
11002 .a_channels = 4,
11003 .a = {{5170, 34}, {5190, 38},
11004 {5210, 42}, {5230, 46}},
11005 },
11006
11007 { /* Custom */
11008 "ZZM",
11009 .bg_channels = 11,
11010 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11011 {2427, 4}, {2432, 5}, {2437, 6},
11012 {2442, 7}, {2447, 8}, {2452, 9},
11013 {2457, 10}, {2462, 11}},
11014 },
11015
11016 { /* Europe */
11017 "ZZE",
11018 .bg_channels = 13,
11019 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11020 {2427, 4}, {2432, 5}, {2437, 6},
11021 {2442, 7}, {2447, 8}, {2452, 9},
11022 {2457, 10}, {2462, 11}, {2467, 12},
11023 {2472, 13}},
11024 .a_channels = 19,
11025 .a = {{5180, 36},
11026 {5200, 40},
11027 {5220, 44},
11028 {5240, 48},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011029 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
11030 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
11031 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
11032 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
11033 {5500, 100, LIBIPW_CH_PASSIVE_ONLY},
11034 {5520, 104, LIBIPW_CH_PASSIVE_ONLY},
11035 {5540, 108, LIBIPW_CH_PASSIVE_ONLY},
11036 {5560, 112, LIBIPW_CH_PASSIVE_ONLY},
11037 {5580, 116, LIBIPW_CH_PASSIVE_ONLY},
11038 {5600, 120, LIBIPW_CH_PASSIVE_ONLY},
11039 {5620, 124, LIBIPW_CH_PASSIVE_ONLY},
11040 {5640, 128, LIBIPW_CH_PASSIVE_ONLY},
11041 {5660, 132, LIBIPW_CH_PASSIVE_ONLY},
11042 {5680, 136, LIBIPW_CH_PASSIVE_ONLY},
11043 {5700, 140, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011044 },
11045
11046 { /* Custom Japan */
11047 "ZZJ",
11048 .bg_channels = 14,
11049 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11050 {2427, 4}, {2432, 5}, {2437, 6},
11051 {2442, 7}, {2447, 8}, {2452, 9},
11052 {2457, 10}, {2462, 11}, {2467, 12},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011053 {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011054 .a_channels = 4,
11055 .a = {{5170, 34}, {5190, 38},
11056 {5210, 42}, {5230, 46}},
11057 },
11058
James Ketrenos03520572005-10-19 16:12:31 -050011059 { /* Rest of World */
11060 "ZZR",
11061 .bg_channels = 14,
11062 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11063 {2427, 4}, {2432, 5}, {2437, 6},
11064 {2442, 7}, {2447, 8}, {2452, 9},
11065 {2457, 10}, {2462, 11}, {2467, 12},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011066 {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY |
11067 LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos03520572005-10-19 16:12:31 -050011068 },
11069
James Ketrenos4f36f802005-08-03 20:36:56 -050011070 { /* High Band */
11071 "ZZH",
11072 .bg_channels = 13,
11073 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11074 {2427, 4}, {2432, 5}, {2437, 6},
11075 {2442, 7}, {2447, 8}, {2452, 9},
11076 {2457, 10}, {2462, 11},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011077 {2467, 12, LIBIPW_CH_PASSIVE_ONLY},
11078 {2472, 13, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011079 .a_channels = 4,
11080 .a = {{5745, 149}, {5765, 153},
11081 {5785, 157}, {5805, 161}},
11082 },
11083
11084 { /* Custom Europe */
11085 "ZZG",
11086 .bg_channels = 13,
11087 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11088 {2427, 4}, {2432, 5}, {2437, 6},
11089 {2442, 7}, {2447, 8}, {2452, 9},
11090 {2457, 10}, {2462, 11},
11091 {2467, 12}, {2472, 13}},
11092 .a_channels = 4,
11093 .a = {{5180, 36}, {5200, 40},
11094 {5220, 44}, {5240, 48}},
11095 },
11096
11097 { /* Europe */
11098 "ZZK",
11099 .bg_channels = 13,
11100 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11101 {2427, 4}, {2432, 5}, {2437, 6},
11102 {2442, 7}, {2447, 8}, {2452, 9},
11103 {2457, 10}, {2462, 11},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011104 {2467, 12, LIBIPW_CH_PASSIVE_ONLY},
11105 {2472, 13, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011106 .a_channels = 24,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011107 .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY},
11108 {5200, 40, LIBIPW_CH_PASSIVE_ONLY},
11109 {5220, 44, LIBIPW_CH_PASSIVE_ONLY},
11110 {5240, 48, LIBIPW_CH_PASSIVE_ONLY},
11111 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
11112 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
11113 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
11114 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
11115 {5500, 100, LIBIPW_CH_PASSIVE_ONLY},
11116 {5520, 104, LIBIPW_CH_PASSIVE_ONLY},
11117 {5540, 108, LIBIPW_CH_PASSIVE_ONLY},
11118 {5560, 112, LIBIPW_CH_PASSIVE_ONLY},
11119 {5580, 116, LIBIPW_CH_PASSIVE_ONLY},
11120 {5600, 120, LIBIPW_CH_PASSIVE_ONLY},
11121 {5620, 124, LIBIPW_CH_PASSIVE_ONLY},
11122 {5640, 128, LIBIPW_CH_PASSIVE_ONLY},
11123 {5660, 132, LIBIPW_CH_PASSIVE_ONLY},
11124 {5680, 136, LIBIPW_CH_PASSIVE_ONLY},
11125 {5700, 140, LIBIPW_CH_PASSIVE_ONLY},
11126 {5745, 149, LIBIPW_CH_PASSIVE_ONLY},
11127 {5765, 153, LIBIPW_CH_PASSIVE_ONLY},
11128 {5785, 157, LIBIPW_CH_PASSIVE_ONLY},
11129 {5805, 161, LIBIPW_CH_PASSIVE_ONLY},
11130 {5825, 165, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011131 },
11132
11133 { /* Europe */
11134 "ZZL",
11135 .bg_channels = 11,
11136 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11137 {2427, 4}, {2432, 5}, {2437, 6},
11138 {2442, 7}, {2447, 8}, {2452, 9},
11139 {2457, 10}, {2462, 11}},
11140 .a_channels = 13,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011141 .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY},
11142 {5200, 40, LIBIPW_CH_PASSIVE_ONLY},
11143 {5220, 44, LIBIPW_CH_PASSIVE_ONLY},
11144 {5240, 48, LIBIPW_CH_PASSIVE_ONLY},
11145 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
11146 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
11147 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
11148 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
11149 {5745, 149, LIBIPW_CH_PASSIVE_ONLY},
11150 {5765, 153, LIBIPW_CH_PASSIVE_ONLY},
11151 {5785, 157, LIBIPW_CH_PASSIVE_ONLY},
11152 {5805, 161, LIBIPW_CH_PASSIVE_ONLY},
11153 {5825, 165, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011154 }
James Ketrenosafbf30a2005-08-25 00:05:33 -050011155};
11156
Stanislav Yakovlev06729382012-11-20 23:54:20 +000011157static void ipw_set_geo(struct ipw_priv *priv)
11158{
11159 int j;
11160
11161 for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
11162 if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
11163 ipw_geos[j].name, 3))
11164 break;
11165 }
11166
11167 if (j == ARRAY_SIZE(ipw_geos)) {
11168 IPW_WARNING("SKU [%c%c%c] not recognized.\n",
11169 priv->eeprom[EEPROM_COUNTRY_CODE + 0],
11170 priv->eeprom[EEPROM_COUNTRY_CODE + 1],
11171 priv->eeprom[EEPROM_COUNTRY_CODE + 2]);
11172 j = 0;
11173 }
11174
11175 libipw_set_geo(priv->ieee, &ipw_geos[j]);
11176}
11177
James Ketrenos43f66a62005-03-25 12:31:53 -060011178#define MAX_HW_RESTARTS 5
11179static int ipw_up(struct ipw_priv *priv)
11180{
Stanislav Yakovlev06729382012-11-20 23:54:20 +000011181 int rc, i;
James Ketrenos43f66a62005-03-25 12:31:53 -060011182
Dan Williamsc3d72b92009-02-11 13:26:06 -050011183 /* Age scan list entries found before suspend */
11184 if (priv->suspend_time) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011185 libipw_networks_age(priv->ieee, priv->suspend_time);
Dan Williamsc3d72b92009-02-11 13:26:06 -050011186 priv->suspend_time = 0;
11187 }
11188
James Ketrenos43f66a62005-03-25 12:31:53 -060011189 if (priv->status & STATUS_EXIT_PENDING)
11190 return -EIO;
11191
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011192 if (cmdlog && !priv->cmdlog) {
Yan Burmane6e3f122006-12-02 13:38:14 +020011193 priv->cmdlog = kcalloc(cmdlog, sizeof(*priv->cmdlog),
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011194 GFP_KERNEL);
11195 if (priv->cmdlog == NULL) {
11196 IPW_ERROR("Error allocating %d command log entries.\n",
11197 cmdlog);
Zhu Yid0b526b2006-04-13 17:19:50 +080011198 return -ENOMEM;
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011199 } else {
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011200 priv->cmdlog_len = cmdlog;
11201 }
11202 }
11203
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011204 for (i = 0; i < MAX_HW_RESTARTS; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -040011205 /* Load the microcode, firmware, and eeprom.
James Ketrenos43f66a62005-03-25 12:31:53 -060011206 * Also start the clocks. */
11207 rc = ipw_load(priv);
11208 if (rc) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -050011209 IPW_ERROR("Unable to load firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060011210 return rc;
11211 }
11212
11213 ipw_init_ordinals(priv);
11214 if (!(priv->config & CFG_CUSTOM_MAC))
11215 eeprom_parse_mac(priv, priv->mac_addr);
11216 memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
11217
Stanislav Yakovlev06729382012-11-20 23:54:20 +000011218 ipw_set_geo(priv);
James Ketrenos4f36f802005-08-03 20:36:56 -050011219
James Ketrenosb095c382005-08-24 22:04:42 -050011220 if (priv->status & STATUS_RF_KILL_SW) {
11221 IPW_WARNING("Radio disabled by module parameter.\n");
11222 return 0;
11223 } else if (rf_kill_active(priv)) {
11224 IPW_WARNING("Radio Frequency Kill Switch is On:\n"
11225 "Kill switch must be turned off for "
11226 "wireless networking to work.\n");
Tejun Heobcb6d912011-01-26 12:12:50 +010011227 schedule_delayed_work(&priv->rf_kill, 2 * HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -060011228 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -050011229 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011230
11231 rc = ipw_config(priv);
11232 if (!rc) {
11233 IPW_DEBUG_INFO("Configured device on count %i\n", i);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011234
James Ketrenose6666192005-08-12 09:17:04 -050011235 /* If configure to try and auto-associate, kick
11236 * off a scan. */
Tejun Heobcb6d912011-01-26 12:12:50 +010011237 schedule_delayed_work(&priv->request_scan, 0);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011238
James Ketrenos43f66a62005-03-25 12:31:53 -060011239 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060011240 }
Jeff Garzikbf794512005-07-31 13:07:26 -040011241
James Ketrenosc848d0a2005-08-24 21:56:24 -050011242 IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060011243 IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
11244 i, MAX_HW_RESTARTS);
11245
11246 /* We had an error bringing up the hardware, so take it
11247 * all the way back down so we can try again */
11248 ipw_down(priv);
11249 }
11250
Jeff Garzikbf794512005-07-31 13:07:26 -040011251 /* tried to restart and config the device for as long as our
James Ketrenos43f66a62005-03-25 12:31:53 -060011252 * patience could withstand */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011253 IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011254
James Ketrenos43f66a62005-03-25 12:31:53 -060011255 return -EIO;
11256}
11257
David Howellsc4028952006-11-22 14:57:56 +000011258static void ipw_bg_up(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050011259{
David Howellsc4028952006-11-22 14:57:56 +000011260 struct ipw_priv *priv =
11261 container_of(work, struct ipw_priv, up);
Zhu Yi46441512006-01-24 16:37:59 +080011262 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000011263 ipw_up(priv);
Zhu Yi46441512006-01-24 16:37:59 +080011264 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011265}
11266
James Ketrenosb095c382005-08-24 22:04:42 -050011267static void ipw_deinit(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060011268{
James Ketrenosb095c382005-08-24 22:04:42 -050011269 int i;
11270
11271 if (priv->status & STATUS_SCANNING) {
11272 IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
11273 ipw_abort_scan(priv);
11274 }
11275
11276 if (priv->status & STATUS_ASSOCIATED) {
11277 IPW_DEBUG_INFO("Disassociating during shutdown.\n");
11278 ipw_disassociate(priv);
11279 }
11280
11281 ipw_led_shutdown(priv);
11282
11283 /* Wait up to 1s for status to change to not scanning and not
11284 * associated (disassociation can take a while for a ful 802.11
11285 * exchange */
11286 for (i = 1000; i && (priv->status &
11287 (STATUS_DISASSOCIATING |
11288 STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
11289 udelay(10);
11290
11291 if (priv->status & (STATUS_DISASSOCIATING |
11292 STATUS_ASSOCIATED | STATUS_SCANNING))
11293 IPW_DEBUG_INFO("Still associated or scanning...\n");
11294 else
11295 IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
11296
James Ketrenosc848d0a2005-08-24 21:56:24 -050011297 /* Attempt to disable the card */
James Ketrenos43f66a62005-03-25 12:31:53 -060011298 ipw_send_card_disable(priv, 0);
James Ketrenosb095c382005-08-24 22:04:42 -050011299
11300 priv->status &= ~STATUS_INIT;
11301}
11302
James Ketrenos43f66a62005-03-25 12:31:53 -060011303static void ipw_down(struct ipw_priv *priv)
11304{
James Ketrenosb095c382005-08-24 22:04:42 -050011305 int exit_pending = priv->status & STATUS_EXIT_PENDING;
11306
11307 priv->status |= STATUS_EXIT_PENDING;
11308
11309 if (ipw_is_init(priv))
11310 ipw_deinit(priv);
11311
11312 /* Wipe out the EXIT_PENDING status bit if we are not actually
11313 * exiting the module */
11314 if (!exit_pending)
11315 priv->status &= ~STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011316
11317 /* tell the device to stop sending interrupts */
11318 ipw_disable_interrupts(priv);
11319
11320 /* Clear all bits but the RF Kill */
James Ketrenosb095c382005-08-24 22:04:42 -050011321 priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011322 netif_carrier_off(priv->net_dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011323
11324 ipw_stop_nic(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -050011325
11326 ipw_led_radio_off(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011327}
11328
David Howellsc4028952006-11-22 14:57:56 +000011329static void ipw_bg_down(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050011330{
David Howellsc4028952006-11-22 14:57:56 +000011331 struct ipw_priv *priv =
11332 container_of(work, struct ipw_priv, down);
Zhu Yi46441512006-01-24 16:37:59 +080011333 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000011334 ipw_down(priv);
Zhu Yi46441512006-01-24 16:37:59 +080011335 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011336}
11337
Stanislaw Gruszka7cabafc2011-09-14 16:47:50 +020011338static int ipw_wdev_init(struct net_device *dev)
11339{
John W. Linvillea3caa992009-08-25 14:12:25 -040011340 int i, rc = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011341 struct ipw_priv *priv = libipw_priv(dev);
John W. Linvillea3caa992009-08-25 14:12:25 -040011342 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
11343 struct wireless_dev *wdev = &priv->ieee->wdev;
James Ketrenos43f66a62005-03-25 12:31:53 -060011344
John W. Linvillea3caa992009-08-25 14:12:25 -040011345 memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
11346
11347 /* fill-out priv->ieee->bg_band */
11348 if (geo->bg_channels) {
11349 struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band;
11350
Johannes Berg57fbcce2016-04-12 15:56:15 +020011351 bg_band->band = NL80211_BAND_2GHZ;
John W. Linvillea3caa992009-08-25 14:12:25 -040011352 bg_band->n_channels = geo->bg_channels;
Joe Perchesbaeb2ff2010-08-11 07:02:48 +000011353 bg_band->channels = kcalloc(geo->bg_channels,
11354 sizeof(struct ieee80211_channel),
11355 GFP_KERNEL);
Dan Carpenter2ee4e272010-10-04 14:31:46 +020011356 if (!bg_band->channels) {
11357 rc = -ENOMEM;
11358 goto out;
11359 }
John W. Linvillea3caa992009-08-25 14:12:25 -040011360 /* translate geo->bg to bg_band.channels */
11361 for (i = 0; i < geo->bg_channels; i++) {
Johannes Berg57fbcce2016-04-12 15:56:15 +020011362 bg_band->channels[i].band = NL80211_BAND_2GHZ;
John W. Linvillea3caa992009-08-25 14:12:25 -040011363 bg_band->channels[i].center_freq = geo->bg[i].freq;
11364 bg_band->channels[i].hw_value = geo->bg[i].channel;
11365 bg_band->channels[i].max_power = geo->bg[i].max_power;
11366 if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY)
11367 bg_band->channels[i].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +020011368 IEEE80211_CHAN_NO_IR;
John W. Linvillea3caa992009-08-25 14:12:25 -040011369 if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS)
11370 bg_band->channels[i].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +020011371 IEEE80211_CHAN_NO_IR;
John W. Linvillea3caa992009-08-25 14:12:25 -040011372 if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)
11373 bg_band->channels[i].flags |=
11374 IEEE80211_CHAN_RADAR;
11375 /* No equivalent for LIBIPW_CH_80211H_RULES,
11376 LIBIPW_CH_UNIFORM_SPREADING, or
11377 LIBIPW_CH_B_ONLY... */
11378 }
11379 /* point at bitrate info */
11380 bg_band->bitrates = ipw2200_bg_rates;
11381 bg_band->n_bitrates = ipw2200_num_bg_rates;
11382
Johannes Berg57fbcce2016-04-12 15:56:15 +020011383 wdev->wiphy->bands[NL80211_BAND_2GHZ] = bg_band;
John W. Linvillea3caa992009-08-25 14:12:25 -040011384 }
11385
11386 /* fill-out priv->ieee->a_band */
11387 if (geo->a_channels) {
11388 struct ieee80211_supported_band *a_band = &priv->ieee->a_band;
11389
Johannes Berg57fbcce2016-04-12 15:56:15 +020011390 a_band->band = NL80211_BAND_5GHZ;
John W. Linvillea3caa992009-08-25 14:12:25 -040011391 a_band->n_channels = geo->a_channels;
Joe Perchesbaeb2ff2010-08-11 07:02:48 +000011392 a_band->channels = kcalloc(geo->a_channels,
11393 sizeof(struct ieee80211_channel),
11394 GFP_KERNEL);
Dan Carpenter2ee4e272010-10-04 14:31:46 +020011395 if (!a_band->channels) {
11396 rc = -ENOMEM;
11397 goto out;
11398 }
Stanislav Yakovlev75836b82012-03-20 17:52:57 -040011399 /* translate geo->a to a_band.channels */
John W. Linvillea3caa992009-08-25 14:12:25 -040011400 for (i = 0; i < geo->a_channels; i++) {
Johannes Berg57fbcce2016-04-12 15:56:15 +020011401 a_band->channels[i].band = NL80211_BAND_5GHZ;
John W. Linvillea3caa992009-08-25 14:12:25 -040011402 a_band->channels[i].center_freq = geo->a[i].freq;
11403 a_band->channels[i].hw_value = geo->a[i].channel;
11404 a_band->channels[i].max_power = geo->a[i].max_power;
11405 if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY)
11406 a_band->channels[i].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +020011407 IEEE80211_CHAN_NO_IR;
John W. Linvillea3caa992009-08-25 14:12:25 -040011408 if (geo->a[i].flags & LIBIPW_CH_NO_IBSS)
11409 a_band->channels[i].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +020011410 IEEE80211_CHAN_NO_IR;
John W. Linvillea3caa992009-08-25 14:12:25 -040011411 if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT)
11412 a_band->channels[i].flags |=
11413 IEEE80211_CHAN_RADAR;
11414 /* No equivalent for LIBIPW_CH_80211H_RULES,
11415 LIBIPW_CH_UNIFORM_SPREADING, or
11416 LIBIPW_CH_B_ONLY... */
11417 }
11418 /* point at bitrate info */
11419 a_band->bitrates = ipw2200_a_rates;
11420 a_band->n_bitrates = ipw2200_num_a_rates;
11421
Johannes Berg57fbcce2016-04-12 15:56:15 +020011422 wdev->wiphy->bands[NL80211_BAND_5GHZ] = a_band;
John W. Linvillea3caa992009-08-25 14:12:25 -040011423 }
11424
Stanislav Yakovleva141e6a2012-04-10 21:44:47 -040011425 wdev->wiphy->cipher_suites = ipw_cipher_suites;
11426 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites);
11427
John W. Linvillea3caa992009-08-25 14:12:25 -040011428 set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
11429
11430 /* With that information in place, we can now register the wiphy... */
Stanislaw Gruszka7cabafc2011-09-14 16:47:50 +020011431 if (wiphy_register(wdev->wiphy))
John W. Linvillea3caa992009-08-25 14:12:25 -040011432 rc = -EIO;
John W. Linvillea3caa992009-08-25 14:12:25 -040011433out:
John W. Linvillea3caa992009-08-25 14:12:25 -040011434 return rc;
James Ketrenos43f66a62005-03-25 12:31:53 -060011435}
11436
11437/* PCI driver stuff */
Benoit Taine9baa3c32014-08-08 15:56:03 +020011438static const struct pci_device_id card_ids[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -060011439 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
11440 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
11441 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
11442 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
11443 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
11444 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
11445 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
11446 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
11447 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
11448 {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
11449 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
11450 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
11451 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
11452 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
11453 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
11454 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
11455 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
Joe Perches8ab0ea72009-06-24 22:13:27 -070011456 {PCI_VDEVICE(INTEL, 0x104f), 0},
11457 {PCI_VDEVICE(INTEL, 0x4220), 0}, /* BG */
11458 {PCI_VDEVICE(INTEL, 0x4221), 0}, /* BG */
11459 {PCI_VDEVICE(INTEL, 0x4223), 0}, /* ABG */
11460 {PCI_VDEVICE(INTEL, 0x4224), 0}, /* ABG */
Jeff Garzikbf794512005-07-31 13:07:26 -040011461
James Ketrenos43f66a62005-03-25 12:31:53 -060011462 /* required last entry */
11463 {0,}
11464};
11465
11466MODULE_DEVICE_TABLE(pci, card_ids);
11467
11468static struct attribute *ipw_sysfs_entries[] = {
11469 &dev_attr_rf_kill.attr,
11470 &dev_attr_direct_dword.attr,
11471 &dev_attr_indirect_byte.attr,
11472 &dev_attr_indirect_dword.attr,
11473 &dev_attr_mem_gpio_reg.attr,
11474 &dev_attr_command_event_reg.attr,
11475 &dev_attr_nic_type.attr,
11476 &dev_attr_status.attr,
11477 &dev_attr_cfg.attr,
James Ketrenosb39860c2005-08-12 09:36:32 -050011478 &dev_attr_error.attr,
11479 &dev_attr_event_log.attr,
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011480 &dev_attr_cmd_log.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -060011481 &dev_attr_eeprom_delay.attr,
11482 &dev_attr_ucode_version.attr,
11483 &dev_attr_rtc.attr,
James Ketrenosa613bff2005-08-24 21:43:11 -050011484 &dev_attr_scan_age.attr,
11485 &dev_attr_led.attr,
James Ketrenosb095c382005-08-24 22:04:42 -050011486 &dev_attr_speed_scan.attr,
11487 &dev_attr_net_stats.attr,
Zhu Yi375dd242007-02-14 16:04:24 +080011488 &dev_attr_channels.attr,
Zhu Yid685b8c2006-04-13 17:20:27 +080011489#ifdef CONFIG_IPW2200_PROMISCUOUS
11490 &dev_attr_rtap_iface.attr,
11491 &dev_attr_rtap_filter.attr,
11492#endif
James Ketrenos43f66a62005-03-25 12:31:53 -060011493 NULL
11494};
11495
Arvind Yadavd7979552017-07-18 15:14:59 +053011496static const struct attribute_group ipw_attribute_group = {
James Ketrenos43f66a62005-03-25 12:31:53 -060011497 .name = NULL, /* put in device directory */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011498 .attrs = ipw_sysfs_entries,
James Ketrenos43f66a62005-03-25 12:31:53 -060011499};
11500
Zhu Yid685b8c2006-04-13 17:20:27 +080011501#ifdef CONFIG_IPW2200_PROMISCUOUS
11502static int ipw_prom_open(struct net_device *dev)
11503{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011504 struct ipw_prom_priv *prom_priv = libipw_priv(dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011505 struct ipw_priv *priv = prom_priv->priv;
11506
11507 IPW_DEBUG_INFO("prom dev->open\n");
11508 netif_carrier_off(dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011509
11510 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
11511 priv->sys_config.accept_all_data_frames = 1;
11512 priv->sys_config.accept_non_directed_frames = 1;
11513 priv->sys_config.accept_all_mgmt_bcpr = 1;
11514 priv->sys_config.accept_all_mgmt_frames = 1;
11515
11516 ipw_send_system_config(priv);
11517 }
11518
11519 return 0;
11520}
11521
11522static int ipw_prom_stop(struct net_device *dev)
11523{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011524 struct ipw_prom_priv *prom_priv = libipw_priv(dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011525 struct ipw_priv *priv = prom_priv->priv;
11526
11527 IPW_DEBUG_INFO("prom dev->stop\n");
11528
11529 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
11530 priv->sys_config.accept_all_data_frames = 0;
11531 priv->sys_config.accept_non_directed_frames = 0;
11532 priv->sys_config.accept_all_mgmt_bcpr = 0;
11533 priv->sys_config.accept_all_mgmt_frames = 0;
11534
11535 ipw_send_system_config(priv);
11536 }
11537
11538 return 0;
11539}
11540
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +000011541static netdev_tx_t ipw_prom_hard_start_xmit(struct sk_buff *skb,
11542 struct net_device *dev)
Zhu Yid685b8c2006-04-13 17:20:27 +080011543{
11544 IPW_DEBUG_INFO("prom dev->xmit\n");
Patrick McHardy4153e772009-06-12 04:08:02 +000011545 dev_kfree_skb(skb);
11546 return NETDEV_TX_OK;
Zhu Yid685b8c2006-04-13 17:20:27 +080011547}
11548
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011549static const struct net_device_ops ipw_prom_netdev_ops = {
11550 .ndo_open = ipw_prom_open,
11551 .ndo_stop = ipw_prom_stop,
11552 .ndo_start_xmit = ipw_prom_hard_start_xmit,
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011553 .ndo_set_mac_address = eth_mac_addr,
11554 .ndo_validate_addr = eth_validate_addr,
11555};
11556
Zhu Yid685b8c2006-04-13 17:20:27 +080011557static int ipw_prom_alloc(struct ipw_priv *priv)
11558{
11559 int rc = 0;
11560
11561 if (priv->prom_net_dev)
11562 return -EPERM;
11563
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011564 priv->prom_net_dev = alloc_libipw(sizeof(struct ipw_prom_priv), 1);
Zhu Yid685b8c2006-04-13 17:20:27 +080011565 if (priv->prom_net_dev == NULL)
11566 return -ENOMEM;
11567
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011568 priv->prom_priv = libipw_priv(priv->prom_net_dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011569 priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev);
11570 priv->prom_priv->priv = priv;
11571
11572 strcpy(priv->prom_net_dev->name, "rtap%d");
Daniel Drake3f2eeac2008-04-02 20:33:54 +010011573 memcpy(priv->prom_net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
Zhu Yid685b8c2006-04-13 17:20:27 +080011574
11575 priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011576 priv->prom_net_dev->netdev_ops = &ipw_prom_netdev_ops;
Zhu Yid685b8c2006-04-13 17:20:27 +080011577
Jarod Wilson9c22b4a2016-10-20 13:55:18 -040011578 priv->prom_net_dev->min_mtu = 68;
11579 priv->prom_net_dev->max_mtu = LIBIPW_DATA_LEN;
11580
Zhu Yid685b8c2006-04-13 17:20:27 +080011581 priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR;
Masakazu Mokuno229ce3a2008-05-14 14:16:50 +090011582 SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011583
11584 rc = register_netdev(priv->prom_net_dev);
11585 if (rc) {
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011586 free_libipw(priv->prom_net_dev, 1);
Zhu Yid685b8c2006-04-13 17:20:27 +080011587 priv->prom_net_dev = NULL;
11588 return rc;
11589 }
11590
11591 return 0;
11592}
11593
11594static void ipw_prom_free(struct ipw_priv *priv)
11595{
11596 if (!priv->prom_net_dev)
11597 return;
11598
11599 unregister_netdev(priv->prom_net_dev);
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011600 free_libipw(priv->prom_net_dev, 1);
Zhu Yid685b8c2006-04-13 17:20:27 +080011601
11602 priv->prom_net_dev = NULL;
11603}
11604
11605#endif
11606
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011607static const struct net_device_ops ipw_netdev_ops = {
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011608 .ndo_open = ipw_net_open,
11609 .ndo_stop = ipw_net_stop,
Jiri Pirkoafc4b132011-08-16 06:29:01 +000011610 .ndo_set_rx_mode = ipw_net_set_multicast_list,
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011611 .ndo_set_mac_address = ipw_net_set_mac_address,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011612 .ndo_start_xmit = libipw_xmit,
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011613 .ndo_validate_addr = eth_validate_addr,
11614};
Zhu Yid685b8c2006-04-13 17:20:27 +080011615
Bill Pembertoneb9248e2012-12-03 09:56:32 -050011616static int ipw_pci_probe(struct pci_dev *pdev,
Adrian Bunk2ef19e62007-12-11 23:20:22 +010011617 const struct pci_device_id *ent)
James Ketrenos43f66a62005-03-25 12:31:53 -060011618{
11619 int err = 0;
11620 struct net_device *net_dev;
11621 void __iomem *base;
11622 u32 length, val;
11623 struct ipw_priv *priv;
James Ketrenosafbf30a2005-08-25 00:05:33 -050011624 int i;
James Ketrenos43f66a62005-03-25 12:31:53 -060011625
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011626 net_dev = alloc_libipw(sizeof(struct ipw_priv), 0);
James Ketrenos43f66a62005-03-25 12:31:53 -060011627 if (net_dev == NULL) {
11628 err = -ENOMEM;
11629 goto out;
11630 }
11631
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011632 priv = libipw_priv(net_dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011633 priv->ieee = netdev_priv(net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050011634
James Ketrenos43f66a62005-03-25 12:31:53 -060011635 priv->net_dev = net_dev;
11636 priv->pci_dev = pdev;
James Ketrenos43f66a62005-03-25 12:31:53 -060011637 ipw_debug_level = debug;
Zhu Yi89c318e2006-06-08 22:19:49 -070011638 spin_lock_init(&priv->irq_lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060011639 spin_lock_init(&priv->lock);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011640 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
11641 INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -060011642
Zhu Yi46441512006-01-24 16:37:59 +080011643 mutex_init(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011644 if (pci_enable_device(pdev)) {
11645 err = -ENODEV;
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011646 goto out_free_libipw;
James Ketrenos43f66a62005-03-25 12:31:53 -060011647 }
11648
11649 pci_set_master(pdev);
11650
Yang Hongyang284901a2009-04-06 19:01:15 -070011651 err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
Jeff Garzikbf794512005-07-31 13:07:26 -040011652 if (!err)
Yang Hongyang284901a2009-04-06 19:01:15 -070011653 err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
James Ketrenos43f66a62005-03-25 12:31:53 -060011654 if (err) {
11655 printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
11656 goto out_pci_disable_device;
11657 }
11658
11659 pci_set_drvdata(pdev, priv);
11660
11661 err = pci_request_regions(pdev, DRV_NAME);
Jeff Garzikbf794512005-07-31 13:07:26 -040011662 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -060011663 goto out_pci_disable_device;
11664
Jeff Garzikbf794512005-07-31 13:07:26 -040011665 /* We disable the RETRY_TIMEOUT register (0x41) to keep
James Ketrenos43f66a62005-03-25 12:31:53 -060011666 * PCI Tx retries from interfering with C3 CPU state */
Jeff Garzikbf794512005-07-31 13:07:26 -040011667 pci_read_config_dword(pdev, 0x40, &val);
11668 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011669 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
Jeff Garzikbf794512005-07-31 13:07:26 -040011670
James Ketrenos43f66a62005-03-25 12:31:53 -060011671 length = pci_resource_len(pdev, 0);
11672 priv->hw_len = length;
Jeff Garzikbf794512005-07-31 13:07:26 -040011673
Arjan van de Ven275f1652008-10-20 21:42:39 -070011674 base = pci_ioremap_bar(pdev, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -060011675 if (!base) {
11676 err = -ENODEV;
11677 goto out_pci_release_regions;
11678 }
11679
11680 priv->hw_base = base;
11681 IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
11682 IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
11683
11684 err = ipw_setup_deferred_work(priv);
11685 if (err) {
11686 IPW_ERROR("Unable to setup deferred work\n");
11687 goto out_iounmap;
11688 }
11689
James Ketrenosb095c382005-08-24 22:04:42 -050011690 ipw_sw_reset(priv, 1);
James Ketrenos43f66a62005-03-25 12:31:53 -060011691
Thomas Gleixner1fb9df52006-07-01 19:29:39 -070011692 err = request_irq(pdev->irq, ipw_isr, IRQF_SHARED, DRV_NAME, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011693 if (err) {
11694 IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
Tejun Heobcb6d912011-01-26 12:12:50 +010011695 goto out_iounmap;
James Ketrenos43f66a62005-03-25 12:31:53 -060011696 }
11697
James Ketrenos43f66a62005-03-25 12:31:53 -060011698 SET_NETDEV_DEV(net_dev, &pdev->dev);
11699
Zhu Yi46441512006-01-24 16:37:59 +080011700 mutex_lock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011701
James Ketrenos43f66a62005-03-25 12:31:53 -060011702 priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
11703 priv->ieee->set_security = shim__set_security;
James Ketrenos227d2dc2005-07-28 16:25:55 -050011704 priv->ieee->is_queue_full = ipw_net_is_queue_full;
James Ketrenos43f66a62005-03-25 12:31:53 -060011705
Zhu Yie43e3c12006-04-13 17:20:45 +080011706#ifdef CONFIG_IPW2200_QOS
Zhu Yia5cf4fe2006-04-13 17:19:11 +080011707 priv->ieee->is_qos_active = ipw_is_qos_active;
James Ketrenos3b9990c2005-08-19 13:18:55 -050011708 priv->ieee->handle_probe_response = ipw_handle_beacon;
11709 priv->ieee->handle_beacon = ipw_handle_probe_response;
11710 priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
Zhu Yie43e3c12006-04-13 17:20:45 +080011711#endif /* CONFIG_IPW2200_QOS */
James Ketrenosb095c382005-08-24 22:04:42 -050011712
James Ketrenosc848d0a2005-08-24 21:56:24 -050011713 priv->ieee->perfect_rssi = -20;
11714 priv->ieee->worst_rssi = -85;
James Ketrenos43f66a62005-03-25 12:31:53 -060011715
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011716 net_dev->netdev_ops = &ipw_netdev_ops;
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000011717 priv->wireless_data.spy_data = &priv->ieee->spy_data;
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000011718 net_dev->wireless_data = &priv->wireless_data;
James Ketrenos43f66a62005-03-25 12:31:53 -060011719 net_dev->wireless_handlers = &ipw_wx_handler_def;
11720 net_dev->ethtool_ops = &ipw_ethtool_ops;
James Ketrenos43f66a62005-03-25 12:31:53 -060011721
Jarod Wilson9c22b4a2016-10-20 13:55:18 -040011722 net_dev->min_mtu = 68;
11723 net_dev->max_mtu = LIBIPW_DATA_LEN;
11724
James Ketrenos43f66a62005-03-25 12:31:53 -060011725 err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
11726 if (err) {
11727 IPW_ERROR("failed to create sysfs device attributes\n");
Zhu Yi46441512006-01-24 16:37:59 +080011728 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011729 goto out_release_irq;
11730 }
11731
Stanislav Yakovlev7ed85b62012-04-16 06:38:30 -040011732 if (ipw_up(priv)) {
11733 mutex_unlock(&priv->mutex);
11734 err = -EIO;
11735 goto out_remove_sysfs;
11736 }
11737
Zhu Yi46441512006-01-24 16:37:59 +080011738 mutex_unlock(&priv->mutex);
Henrik Brix Andersen48a84772006-02-14 09:09:52 +080011739
Stanislaw Gruszka7cabafc2011-09-14 16:47:50 +020011740 err = ipw_wdev_init(net_dev);
11741 if (err) {
11742 IPW_ERROR("failed to register wireless device\n");
Ben Hutchingsb4050792012-01-22 03:09:35 +000011743 goto out_remove_sysfs;
11744 }
11745
11746 err = register_netdev(net_dev);
11747 if (err) {
11748 IPW_ERROR("failed to register network device\n");
11749 goto out_unregister_wiphy;
Stanislaw Gruszka7cabafc2011-09-14 16:47:50 +020011750 }
11751
Zhu Yid685b8c2006-04-13 17:20:27 +080011752#ifdef CONFIG_IPW2200_PROMISCUOUS
11753 if (rtap_iface) {
11754 err = ipw_prom_alloc(priv);
11755 if (err) {
11756 IPW_ERROR("Failed to register promiscuous network "
11757 "device (error %d).\n", err);
Ben Hutchingsb4050792012-01-22 03:09:35 +000011758 unregister_netdev(priv->net_dev);
11759 goto out_unregister_wiphy;
Zhu Yid685b8c2006-04-13 17:20:27 +080011760 }
11761 }
11762#endif
11763
Henrik Brix Andersen48a84772006-02-14 09:09:52 +080011764 printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg "
11765 "channels, %d 802.11a channels)\n",
11766 priv->ieee->geo.name, priv->ieee->geo.bg_channels,
11767 priv->ieee->geo.a_channels);
11768
James Ketrenos43f66a62005-03-25 12:31:53 -060011769 return 0;
11770
Ben Hutchingsb4050792012-01-22 03:09:35 +000011771 out_unregister_wiphy:
11772 wiphy_unregister(priv->ieee->wdev.wiphy);
11773 kfree(priv->ieee->a_band.channels);
11774 kfree(priv->ieee->bg_band.channels);
James Ketrenosa613bff2005-08-24 21:43:11 -050011775 out_remove_sysfs:
James Ketrenos43f66a62005-03-25 12:31:53 -060011776 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011777 out_release_irq:
James Ketrenos43f66a62005-03-25 12:31:53 -060011778 free_irq(pdev->irq, priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011779 out_iounmap:
James Ketrenos43f66a62005-03-25 12:31:53 -060011780 iounmap(priv->hw_base);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011781 out_pci_release_regions:
James Ketrenos43f66a62005-03-25 12:31:53 -060011782 pci_release_regions(pdev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011783 out_pci_disable_device:
James Ketrenos43f66a62005-03-25 12:31:53 -060011784 pci_disable_device(pdev);
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011785 out_free_libipw:
11786 free_libipw(priv->net_dev, 0);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011787 out:
James Ketrenos43f66a62005-03-25 12:31:53 -060011788 return err;
11789}
11790
Bill Pembertoneb9248e2012-12-03 09:56:32 -050011791static void ipw_pci_remove(struct pci_dev *pdev)
James Ketrenos43f66a62005-03-25 12:31:53 -060011792{
11793 struct ipw_priv *priv = pci_get_drvdata(pdev);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011794 struct list_head *p, *q;
11795 int i;
James Ketrenosb095c382005-08-24 22:04:42 -050011796
James Ketrenos43f66a62005-03-25 12:31:53 -060011797 if (!priv)
11798 return;
11799
Zhu Yi46441512006-01-24 16:37:59 +080011800 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011801
James Ketrenosafbf30a2005-08-25 00:05:33 -050011802 priv->status |= STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011803 ipw_down(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011804 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
11805
Zhu Yi46441512006-01-24 16:37:59 +080011806 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011807
11808 unregister_netdev(priv->net_dev);
11809
11810 if (priv->rxq) {
11811 ipw_rx_queue_free(priv, priv->rxq);
11812 priv->rxq = NULL;
11813 }
11814 ipw_tx_queue_free(priv);
11815
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011816 if (priv->cmdlog) {
11817 kfree(priv->cmdlog);
11818 priv->cmdlog = NULL;
11819 }
Tejun Heobcb6d912011-01-26 12:12:50 +010011820
11821 /* make sure all works are inactive */
11822 cancel_delayed_work_sync(&priv->adhoc_check);
11823 cancel_work_sync(&priv->associate);
11824 cancel_work_sync(&priv->disassociate);
11825 cancel_work_sync(&priv->system_config);
11826 cancel_work_sync(&priv->rx_replenish);
11827 cancel_work_sync(&priv->adapter_restart);
11828 cancel_delayed_work_sync(&priv->rf_kill);
11829 cancel_work_sync(&priv->up);
11830 cancel_work_sync(&priv->down);
11831 cancel_delayed_work_sync(&priv->request_scan);
11832 cancel_delayed_work_sync(&priv->request_direct_scan);
11833 cancel_delayed_work_sync(&priv->request_passive_scan);
11834 cancel_delayed_work_sync(&priv->scan_event);
11835 cancel_delayed_work_sync(&priv->gather_stats);
11836 cancel_work_sync(&priv->abort_scan);
11837 cancel_work_sync(&priv->roam);
11838 cancel_delayed_work_sync(&priv->scan_check);
11839 cancel_work_sync(&priv->link_up);
11840 cancel_work_sync(&priv->link_down);
11841 cancel_delayed_work_sync(&priv->led_link_on);
11842 cancel_delayed_work_sync(&priv->led_link_off);
11843 cancel_delayed_work_sync(&priv->led_act_off);
11844 cancel_work_sync(&priv->merge_networks);
James Ketrenos43f66a62005-03-25 12:31:53 -060011845
James Ketrenosafbf30a2005-08-25 00:05:33 -050011846 /* Free MAC hash list for ADHOC */
11847 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) {
11848 list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050011849 list_del(p);
Zhu Yi489f4452006-01-24 16:37:41 +080011850 kfree(list_entry(p, struct ipw_ibss_seq, list));
James Ketrenosafbf30a2005-08-25 00:05:33 -050011851 }
11852 }
11853
Jesper Juhl8f760782006-06-27 02:55:06 -070011854 kfree(priv->error);
11855 priv->error = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -060011856
Zhu Yid685b8c2006-04-13 17:20:27 +080011857#ifdef CONFIG_IPW2200_PROMISCUOUS
11858 ipw_prom_free(priv);
11859#endif
11860
James Ketrenos43f66a62005-03-25 12:31:53 -060011861 free_irq(pdev->irq, priv);
11862 iounmap(priv->hw_base);
11863 pci_release_regions(pdev);
11864 pci_disable_device(pdev);
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011865 /* wiphy_unregister needs to be here, before free_libipw */
John W. Linvillea3caa992009-08-25 14:12:25 -040011866 wiphy_unregister(priv->ieee->wdev.wiphy);
11867 kfree(priv->ieee->a_band.channels);
11868 kfree(priv->ieee->bg_band.channels);
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011869 free_libipw(priv->net_dev, 0);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011870 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -060011871}
11872
James Ketrenos43f66a62005-03-25 12:31:53 -060011873#ifdef CONFIG_PM
Pavel Machek583a4e82005-09-03 15:56:58 -070011874static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
James Ketrenos43f66a62005-03-25 12:31:53 -060011875{
11876 struct ipw_priv *priv = pci_get_drvdata(pdev);
11877 struct net_device *dev = priv->net_dev;
11878
11879 printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
11880
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011881 /* Take down the device; powers it off, etc. */
James Ketrenos43f66a62005-03-25 12:31:53 -060011882 ipw_down(priv);
11883
11884 /* Remove the PRESENT state of the device */
11885 netif_device_detach(dev);
11886
James Ketrenos43f66a62005-03-25 12:31:53 -060011887 pci_save_state(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011888 pci_disable_device(pdev);
Pavel Machek583a4e82005-09-03 15:56:58 -070011889 pci_set_power_state(pdev, pci_choose_state(pdev, state));
Jeff Garzikbf794512005-07-31 13:07:26 -040011890
Dan Williamsc3d72b92009-02-11 13:26:06 -050011891 priv->suspend_at = get_seconds();
11892
James Ketrenos43f66a62005-03-25 12:31:53 -060011893 return 0;
11894}
11895
11896static int ipw_pci_resume(struct pci_dev *pdev)
11897{
11898 struct ipw_priv *priv = pci_get_drvdata(pdev);
11899 struct net_device *dev = priv->net_dev;
John W. Linville02e0e5e2006-11-07 20:53:48 -050011900 int err;
James Ketrenos43f66a62005-03-25 12:31:53 -060011901 u32 val;
Jeff Garzikbf794512005-07-31 13:07:26 -040011902
James Ketrenos43f66a62005-03-25 12:31:53 -060011903 printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
11904
James Ketrenosea2b26e2005-08-24 21:25:16 -050011905 pci_set_power_state(pdev, PCI_D0);
John W. Linville02e0e5e2006-11-07 20:53:48 -050011906 err = pci_enable_device(pdev);
11907 if (err) {
11908 printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
11909 dev->name);
11910 return err;
11911 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011912 pci_restore_state(pdev);
James Ketrenosea2b26e2005-08-24 21:25:16 -050011913
James Ketrenos43f66a62005-03-25 12:31:53 -060011914 /*
11915 * Suspend/Resume resets the PCI configuration space, so we have to
11916 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
11917 * from interfering with C3 CPU state. pci_restore_state won't help
11918 * here since it only restores the first 64 bytes pci config header.
11919 */
Jeff Garzikbf794512005-07-31 13:07:26 -040011920 pci_read_config_dword(pdev, 0x40, &val);
11921 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011922 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
11923
11924 /* Set the device back into the PRESENT state; this will also wake
11925 * the queue of needed */
11926 netif_device_attach(dev);
11927
Dan Williamsc3d72b92009-02-11 13:26:06 -050011928 priv->suspend_time = get_seconds() - priv->suspend_at;
11929
James Ketrenos43f66a62005-03-25 12:31:53 -060011930 /* Bring the device back up */
Tejun Heobcb6d912011-01-26 12:12:50 +010011931 schedule_work(&priv->up);
Jeff Garzikbf794512005-07-31 13:07:26 -040011932
James Ketrenos43f66a62005-03-25 12:31:53 -060011933 return 0;
11934}
11935#endif
11936
Zhu Yic8c22c92006-08-21 11:38:39 +080011937static void ipw_pci_shutdown(struct pci_dev *pdev)
11938{
11939 struct ipw_priv *priv = pci_get_drvdata(pdev);
11940
11941 /* Take down the device; powers it off, etc. */
11942 ipw_down(priv);
11943
11944 pci_disable_device(pdev);
11945}
11946
James Ketrenos43f66a62005-03-25 12:31:53 -060011947/* driver initialization stuff */
11948static struct pci_driver ipw_driver = {
11949 .name = DRV_NAME,
11950 .id_table = card_ids,
11951 .probe = ipw_pci_probe,
Bill Pembertoneb9248e2012-12-03 09:56:32 -050011952 .remove = ipw_pci_remove,
James Ketrenos43f66a62005-03-25 12:31:53 -060011953#ifdef CONFIG_PM
11954 .suspend = ipw_pci_suspend,
11955 .resume = ipw_pci_resume,
11956#endif
Zhu Yic8c22c92006-08-21 11:38:39 +080011957 .shutdown = ipw_pci_shutdown,
James Ketrenos43f66a62005-03-25 12:31:53 -060011958};
11959
11960static int __init ipw_init(void)
11961{
11962 int ret;
11963
11964 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
11965 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
11966
Jeff Garzik29917622006-08-19 17:48:59 -040011967 ret = pci_register_driver(&ipw_driver);
James Ketrenos43f66a62005-03-25 12:31:53 -060011968 if (ret) {
11969 IPW_ERROR("Unable to initialize PCI module\n");
11970 return ret;
11971 }
11972
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011973 ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -060011974 if (ret) {
11975 IPW_ERROR("Unable to create driver sysfs file\n");
11976 pci_unregister_driver(&ipw_driver);
11977 return ret;
11978 }
11979
11980 return ret;
11981}
11982
11983static void __exit ipw_exit(void)
11984{
11985 driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
11986 pci_unregister_driver(&ipw_driver);
11987}
11988
11989module_param(disable, int, 0444);
11990MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
11991
11992module_param(associate, int, 0444);
Tim Gardner5c7f9b72008-10-14 10:38:03 -060011993MODULE_PARM_DESC(associate, "auto associate when scanning (default off)");
James Ketrenos43f66a62005-03-25 12:31:53 -060011994
11995module_param(auto_create, int, 0444);
11996MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
11997
Reinette Chatre21f8a732009-08-18 10:25:05 -070011998module_param_named(led, led_support, int, 0444);
ubuntu@tjworld.netc086aba2009-03-23 20:29:28 +000011999MODULE_PARM_DESC(led, "enable led control on some systems (default 1 on)");
James Ketrenosa613bff2005-08-24 21:43:11 -050012000
James Ketrenos43f66a62005-03-25 12:31:53 -060012001module_param(debug, int, 0444);
12002MODULE_PARM_DESC(debug, "debug output mask");
12003
Reinette Chatre21f8a732009-08-18 10:25:05 -070012004module_param_named(channel, default_channel, int, 0444);
Jeff Garzikbf794512005-07-31 13:07:26 -040012005MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
James Ketrenos43f66a62005-03-25 12:31:53 -060012006
Zhu Yid685b8c2006-04-13 17:20:27 +080012007#ifdef CONFIG_IPW2200_PROMISCUOUS
12008module_param(rtap_iface, int, 0444);
12009MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)");
12010#endif
12011
Zhu Yie43e3c12006-04-13 17:20:45 +080012012#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -050012013module_param(qos_enable, int, 0444);
Colin Ian King7e7939e2018-05-01 10:36:43 +010012014MODULE_PARM_DESC(qos_enable, "enable all QoS functionalities");
James Ketrenos43f66a62005-03-25 12:31:53 -060012015
James Ketrenosb095c382005-08-24 22:04:42 -050012016module_param(qos_burst_enable, int, 0444);
12017MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
12018
12019module_param(qos_no_ack_mask, int, 0444);
12020MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack");
12021
12022module_param(burst_duration_CCK, int, 0444);
12023MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value");
12024
12025module_param(burst_duration_OFDM, int, 0444);
12026MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
Zhu Yie43e3c12006-04-13 17:20:45 +080012027#endif /* CONFIG_IPW2200_QOS */
James Ketrenosb095c382005-08-24 22:04:42 -050012028
12029#ifdef CONFIG_IPW2200_MONITOR
Reinette Chatre21f8a732009-08-18 10:25:05 -070012030module_param_named(mode, network_mode, int, 0444);
James Ketrenos43f66a62005-03-25 12:31:53 -060012031MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
12032#else
Reinette Chatre21f8a732009-08-18 10:25:05 -070012033module_param_named(mode, network_mode, int, 0444);
James Ketrenos43f66a62005-03-25 12:31:53 -060012034MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
12035#endif
12036
Zhu Yi810dabd2006-01-24 16:36:59 +080012037module_param(bt_coexist, int, 0444);
12038MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)");
12039
James Ketrenosb095c382005-08-24 22:04:42 -050012040module_param(hwcrypto, int, 0444);
Zhu Yibde37d02006-01-24 16:38:08 +080012041MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default off)");
James Ketrenosb095c382005-08-24 22:04:42 -050012042
James Ketrenosf6c5cb72005-08-25 00:39:09 -050012043module_param(cmdlog, int, 0444);
12044MODULE_PARM_DESC(cmdlog,
12045 "allocate a ring buffer for logging firmware commands");
12046
Zhu Yi4bfdb912006-01-24 16:37:16 +080012047module_param(roaming, int, 0444);
12048MODULE_PARM_DESC(roaming, "enable roaming support (default on)");
12049
Zhu Yid2b83e12006-04-13 17:19:36 +080012050module_param(antenna, int, 0444);
12051MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)");
12052
James Ketrenos43f66a62005-03-25 12:31:53 -060012053module_exit(ipw_exit);
12054module_init(ipw_init);