blob: ba3fb1d2ddb43b003cf0829cced21d9500d36e83 [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
Zhu Yi41093162010-03-09 16:05:31 +08003211 virts = kmalloc(sizeof(void *) * CB_NUMBER_OF_ELEMENTS_SMALL,
3212 GFP_KERNEL);
3213 if (!virts)
3214 return -ENOMEM;
3215
3216 phys = kmalloc(sizeof(dma_addr_t) * CB_NUMBER_OF_ELEMENTS_SMALL,
3217 GFP_KERNEL);
3218 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
3785 q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
3786 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) {
7560 IPW_DEBUG_ASSOC("Not attempting association (in "
7561 "disassociating)\n ");
Tejun Heobcb6d912011-01-26 12:12:50 +01007562 schedule_work(&priv->associate);
Hong Liue6324722005-09-14 21:04:15 -05007563 return 0;
7564 }
7565
James Ketrenosc848d0a2005-08-24 21:56:24 -05007566 if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007567 IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
7568 "initialized)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007569 return 0;
7570 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007571
7572 if (!(priv->config & CFG_ASSOCIATE) &&
Alexey Fisher3e4127f2008-11-06 09:51:16 +01007573 !(priv->config & (CFG_STATIC_ESSID | CFG_STATIC_BSSID))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007574 IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007575 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007576 }
7577
James Ketrenosa613bff2005-08-24 21:43:11 -05007578 /* Protect our use of the network_list */
7579 spin_lock_irqsave(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007580 list_for_each_entry(network, &priv->ieee->network_list, list)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007581 ipw_best_network(priv, &match, network, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06007582
7583 network = match.network;
7584 rates = &match.rates;
7585
7586 if (network == NULL &&
7587 priv->ieee->iw_mode == IW_MODE_ADHOC &&
7588 priv->config & CFG_ADHOC_CREATE &&
7589 priv->config & CFG_STATIC_ESSID &&
Dan Williamsa6d4eae2008-05-29 14:38:28 -04007590 priv->config & CFG_STATIC_CHANNEL) {
7591 /* Use oldest network if the free list is empty */
7592 if (list_empty(&priv->ieee->network_free_list)) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007593 struct libipw_network *oldest = NULL;
7594 struct libipw_network *target;
Dan Williamsa6d4eae2008-05-29 14:38:28 -04007595
7596 list_for_each_entry(target, &priv->ieee->network_list, list) {
7597 if ((oldest == NULL) ||
7598 (target->last_scanned < oldest->last_scanned))
7599 oldest = target;
7600 }
7601
7602 /* If there are no more slots, expire the oldest */
7603 list_del(&oldest->list);
7604 target = oldest;
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07007605 IPW_DEBUG_ASSOC("Expired '%*pE' (%pM) from network list.\n",
7606 target->ssid_len, target->ssid,
Johannes Berge1749612008-10-27 15:59:26 -07007607 target->bssid);
Dan Williamsa6d4eae2008-05-29 14:38:28 -04007608 list_add_tail(&target->list,
7609 &priv->ieee->network_free_list);
7610 }
7611
James Ketrenos43f66a62005-03-25 12:31:53 -06007612 element = priv->ieee->network_free_list.next;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007613 network = list_entry(element, struct libipw_network, list);
James Ketrenos43f66a62005-03-25 12:31:53 -06007614 ipw_adhoc_create(priv, network);
7615 rates = &priv->rates;
7616 list_del(element);
7617 list_add_tail(&network->list, &priv->ieee->network_list);
7618 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007619 spin_unlock_irqrestore(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007620
James Ketrenos43f66a62005-03-25 12:31:53 -06007621 /* If we reached the end of the list, then we don't have any valid
7622 * matching APs */
7623 if (!network) {
7624 ipw_debug_config(priv);
7625
James Ketrenosb095c382005-08-24 22:04:42 -05007626 if (!(priv->status & STATUS_SCANNING)) {
7627 if (!(priv->config & CFG_SPEED_SCAN))
Tejun Heobcb6d912011-01-26 12:12:50 +01007628 schedule_delayed_work(&priv->request_scan,
7629 SCAN_INTERVAL);
James Ketrenosb095c382005-08-24 22:04:42 -05007630 else
Tejun Heobcb6d912011-01-26 12:12:50 +01007631 schedule_delayed_work(&priv->request_scan, 0);
James Ketrenosb095c382005-08-24 22:04:42 -05007632 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007633
James Ketrenosc848d0a2005-08-24 21:56:24 -05007634 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007635 }
7636
7637 ipw_associate_network(priv, network, rates, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007638
7639 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06007640}
Jeff Garzikbf794512005-07-31 13:07:26 -04007641
David Howellsc4028952006-11-22 14:57:56 +00007642static void ipw_bg_associate(struct work_struct *work)
James Ketrenos43f66a62005-03-25 12:31:53 -06007643{
David Howellsc4028952006-11-22 14:57:56 +00007644 struct ipw_priv *priv =
7645 container_of(work, struct ipw_priv, associate);
Zhu Yi46441512006-01-24 16:37:59 +08007646 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +00007647 ipw_associate(priv);
Zhu Yi46441512006-01-24 16:37:59 +08007648 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06007649}
7650
James Ketrenosb095c382005-08-24 22:04:42 -05007651static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
7652 struct sk_buff *skb)
7653{
7654 struct ieee80211_hdr *hdr;
7655 u16 fc;
7656
7657 hdr = (struct ieee80211_hdr *)skb->data;
John W. Linville72118012008-09-30 21:43:03 -04007658 fc = le16_to_cpu(hdr->frame_control);
James Ketrenosb095c382005-08-24 22:04:42 -05007659 if (!(fc & IEEE80211_FCTL_PROTECTED))
7660 return;
7661
7662 fc &= ~IEEE80211_FCTL_PROTECTED;
John W. Linville72118012008-09-30 21:43:03 -04007663 hdr->frame_control = cpu_to_le16(fc);
James Ketrenosb095c382005-08-24 22:04:42 -05007664 switch (priv->ieee->sec.level) {
7665 case SEC_LEVEL_3:
7666 /* Remove CCMP HDR */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007667 memmove(skb->data + LIBIPW_3ADDR_LEN,
7668 skb->data + LIBIPW_3ADDR_LEN + 8,
7669 skb->len - LIBIPW_3ADDR_LEN - 8);
Zhu Yif4ff4972005-09-12 10:48:48 -05007670 skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */
James Ketrenosb095c382005-08-24 22:04:42 -05007671 break;
7672 case SEC_LEVEL_2:
7673 break;
7674 case SEC_LEVEL_1:
7675 /* Remove IV */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007676 memmove(skb->data + LIBIPW_3ADDR_LEN,
7677 skb->data + LIBIPW_3ADDR_LEN + 4,
7678 skb->len - LIBIPW_3ADDR_LEN - 4);
Zhu Yif4ff4972005-09-12 10:48:48 -05007679 skb_trim(skb, skb->len - 8); /* IV + ICV */
James Ketrenosb095c382005-08-24 22:04:42 -05007680 break;
7681 case SEC_LEVEL_0:
7682 break;
7683 default:
André Goddard Rosaaf901ca2009-11-14 13:09:05 -02007684 printk(KERN_ERR "Unknown security level %d\n",
James Ketrenosb095c382005-08-24 22:04:42 -05007685 priv->ieee->sec.level);
7686 break;
7687 }
7688}
7689
7690static void ipw_handle_data_packet(struct ipw_priv *priv,
7691 struct ipw_rx_mem_buffer *rxb,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007692 struct libipw_rx_stats *stats)
James Ketrenos43f66a62005-03-25 12:31:53 -06007693{
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007694 struct net_device *dev = priv->net_dev;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007695 struct libipw_hdr_4addr *hdr;
James Ketrenos43f66a62005-03-25 12:31:53 -06007696 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7697
7698 /* We received data from the HW, so stop the watchdog */
Florian Westphal860e9532016-05-03 16:33:13 +02007699 netif_trans_update(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06007700
Jeff Garzikbf794512005-07-31 13:07:26 -04007701 /* We only process data packets if the
James Ketrenos43f66a62005-03-25 12:31:53 -06007702 * interface is open */
James Ketrenosa613bff2005-08-24 21:43:11 -05007703 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
James Ketrenos43f66a62005-03-25 12:31:53 -06007704 skb_tailroom(rxb->skb))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007705 dev->stats.rx_errors++;
James Ketrenos43f66a62005-03-25 12:31:53 -06007706 priv->wstats.discard.misc++;
7707 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7708 return;
7709 } else if (unlikely(!netif_running(priv->net_dev))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007710 dev->stats.rx_dropped++;
James Ketrenos43f66a62005-03-25 12:31:53 -06007711 priv->wstats.discard.misc++;
7712 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7713 return;
7714 }
7715
7716 /* Advance skb->data to the start of the actual payload */
Jiri Bencaaa4d302005-06-07 14:58:41 +02007717 skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
James Ketrenos43f66a62005-03-25 12:31:53 -06007718
7719 /* Set the size of the skb to the size of the frame */
James Ketrenosa613bff2005-08-24 21:43:11 -05007720 skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
James Ketrenos43f66a62005-03-25 12:31:53 -06007721
7722 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7723
James Ketrenosb095c382005-08-24 22:04:42 -05007724 /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007725 hdr = (struct libipw_hdr_4addr *)rxb->skb->data;
Hong Liu567deaf2005-08-31 18:07:22 +08007726 if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
Stephen Hemminger3c190652006-01-03 15:27:38 -08007727 (is_multicast_ether_addr(hdr->addr1) ?
Hong Liu567deaf2005-08-31 18:07:22 +08007728 !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
James Ketrenosb095c382005-08-24 22:04:42 -05007729 ipw_rebuild_decrypted_skb(priv, rxb->skb);
7730
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007731 if (!libipw_rx(priv->ieee, rxb->skb, stats))
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007732 dev->stats.rx_errors++;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007733 else { /* libipw_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007734 rxb->skb = NULL;
James Ketrenosb095c382005-08-24 22:04:42 -05007735 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05007736 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007737}
7738
Zhu Yi459d4082006-04-13 17:21:00 +08007739#ifdef CONFIG_IPW2200_RADIOTAP
Mike Kershaw24a47db2005-08-26 00:41:54 -05007740static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
7741 struct ipw_rx_mem_buffer *rxb,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007742 struct libipw_rx_stats *stats)
Mike Kershaw24a47db2005-08-26 00:41:54 -05007743{
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007744 struct net_device *dev = priv->net_dev;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007745 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7746 struct ipw_rx_frame *frame = &pkt->u.frame;
7747
7748 /* initial pull of some data */
7749 u16 received_channel = frame->received_channel;
7750 u8 antennaAndPhy = frame->antennaAndPhy;
7751 s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */
7752 u16 pktrate = frame->rate;
7753
7754 /* Magic struct that slots into the radiotap header -- no reason
7755 * to build this manually element by element, we can write it much
7756 * more efficiently than we can parse it. ORDER MATTERS HERE */
Zhu Yid685b8c2006-04-13 17:20:27 +08007757 struct ipw_rt_hdr *ipw_rt;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007758
Dan Carpenter92c1ff12012-01-12 09:34:50 +03007759 unsigned short len = le16_to_cpu(pkt->u.frame.length);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007760
7761 /* We received data from the HW, so stop the watchdog */
Florian Westphal860e9532016-05-03 16:33:13 +02007762 netif_trans_update(dev);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007763
7764 /* We only process data packets if the
7765 * interface is open */
7766 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
7767 skb_tailroom(rxb->skb))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007768 dev->stats.rx_errors++;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007769 priv->wstats.discard.misc++;
7770 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7771 return;
7772 } else if (unlikely(!netif_running(priv->net_dev))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007773 dev->stats.rx_dropped++;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007774 priv->wstats.discard.misc++;
7775 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7776 return;
7777 }
7778
7779 /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
7780 * that now */
7781 if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
7782 /* FIXME: Should alloc bigger skb instead */
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007783 dev->stats.rx_dropped++;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007784 priv->wstats.discard.misc++;
7785 IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
7786 return;
7787 }
7788
7789 /* copy the frame itself */
7790 memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr),
7791 rxb->skb->data + IPW_RX_FRAME_SIZE, len);
7792
Mike Kershaw24a47db2005-08-26 00:41:54 -05007793 ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data;
7794
7795 ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
7796 ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
Al Viro743b84d2007-12-27 01:43:16 -05007797 ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total header+data */
Mike Kershaw24a47db2005-08-26 00:41:54 -05007798
7799 /* Big bitfield of all the fields we provide in radiotap */
Al Viro743b84d2007-12-27 01:43:16 -05007800 ipw_rt->rt_hdr.it_present = cpu_to_le32(
7801 (1 << IEEE80211_RADIOTAP_TSFT) |
Zhu Yi4b1f8a92006-12-05 14:42:14 +08007802 (1 << IEEE80211_RADIOTAP_FLAGS) |
Mike Kershaw24a47db2005-08-26 00:41:54 -05007803 (1 << IEEE80211_RADIOTAP_RATE) |
7804 (1 << IEEE80211_RADIOTAP_CHANNEL) |
7805 (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
Zhu Yid685b8c2006-04-13 17:20:27 +08007806 (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
Mike Kershaw24a47db2005-08-26 00:41:54 -05007807 (1 << IEEE80211_RADIOTAP_ANTENNA));
7808
7809 /* Zero the flags, we'll add to them as we go */
7810 ipw_rt->rt_flags = 0;
Zhu Yi4b1f8a92006-12-05 14:42:14 +08007811 ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
7812 frame->parent_tsf[2] << 16 |
7813 frame->parent_tsf[1] << 8 |
7814 frame->parent_tsf[0]);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007815
7816 /* Convert signal to DBM */
7817 ipw_rt->rt_dbmsignal = antsignal;
Reinette Chatre21f8a732009-08-18 10:25:05 -07007818 ipw_rt->rt_dbmnoise = (s8) le16_to_cpu(frame->noise);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007819
7820 /* Convert the channel data and set the flags */
7821 ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
7822 if (received_channel > 14) { /* 802.11a */
7823 ipw_rt->rt_chbitmask =
7824 cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
7825 } else if (antennaAndPhy & 32) { /* 802.11b */
7826 ipw_rt->rt_chbitmask =
7827 cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
7828 } else { /* 802.11g */
7829 ipw_rt->rt_chbitmask =
Al Viro472caf82007-12-27 01:50:54 -05007830 cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007831 }
7832
7833 /* set the rate in multiples of 500k/s */
7834 switch (pktrate) {
7835 case IPW_TX_RATE_1MB:
7836 ipw_rt->rt_rate = 2;
7837 break;
7838 case IPW_TX_RATE_2MB:
7839 ipw_rt->rt_rate = 4;
7840 break;
7841 case IPW_TX_RATE_5MB:
7842 ipw_rt->rt_rate = 10;
7843 break;
7844 case IPW_TX_RATE_6MB:
7845 ipw_rt->rt_rate = 12;
7846 break;
7847 case IPW_TX_RATE_9MB:
7848 ipw_rt->rt_rate = 18;
7849 break;
7850 case IPW_TX_RATE_11MB:
7851 ipw_rt->rt_rate = 22;
7852 break;
7853 case IPW_TX_RATE_12MB:
7854 ipw_rt->rt_rate = 24;
7855 break;
7856 case IPW_TX_RATE_18MB:
7857 ipw_rt->rt_rate = 36;
7858 break;
7859 case IPW_TX_RATE_24MB:
7860 ipw_rt->rt_rate = 48;
7861 break;
7862 case IPW_TX_RATE_36MB:
7863 ipw_rt->rt_rate = 72;
7864 break;
7865 case IPW_TX_RATE_48MB:
7866 ipw_rt->rt_rate = 96;
7867 break;
7868 case IPW_TX_RATE_54MB:
7869 ipw_rt->rt_rate = 108;
7870 break;
7871 default:
7872 ipw_rt->rt_rate = 0;
7873 break;
7874 }
7875
7876 /* antenna number */
7877 ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */
7878
7879 /* set the preamble flag if we have it */
7880 if ((antennaAndPhy & 64))
7881 ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
7882
7883 /* Set the size of the skb to the size of the frame */
7884 skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr));
James Ketrenos43f66a62005-03-25 12:31:53 -06007885
7886 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7887
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007888 if (!libipw_rx(priv->ieee, rxb->skb, stats))
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007889 dev->stats.rx_errors++;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007890 else { /* libipw_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007891 rxb->skb = NULL;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007892 /* no LED during capture */
7893 }
7894}
7895#endif
7896
Zhu Yid685b8c2006-04-13 17:20:27 +08007897#ifdef CONFIG_IPW2200_PROMISCUOUS
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007898#define libipw_is_probe_response(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007899 ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \
7900 (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP )
7901
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007902#define libipw_is_management(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007903 ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
7904
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007905#define libipw_is_control(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007906 ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
7907
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007908#define libipw_is_data(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007909 ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
7910
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007911#define libipw_is_assoc_request(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007912 ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ)
7913
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007914#define libipw_is_reassoc_request(fc) \
Zhu Yid685b8c2006-04-13 17:20:27 +08007915 ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ)
7916
7917static void ipw_handle_promiscuous_rx(struct ipw_priv *priv,
7918 struct ipw_rx_mem_buffer *rxb,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007919 struct libipw_rx_stats *stats)
Zhu Yid685b8c2006-04-13 17:20:27 +08007920{
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007921 struct net_device *dev = priv->prom_net_dev;
Zhu Yid685b8c2006-04-13 17:20:27 +08007922 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7923 struct ipw_rx_frame *frame = &pkt->u.frame;
7924 struct ipw_rt_hdr *ipw_rt;
7925
7926 /* First cache any information we need before we overwrite
7927 * the information provided in the skb from the hardware */
7928 struct ieee80211_hdr *hdr;
7929 u16 channel = frame->received_channel;
7930 u8 phy_flags = frame->antennaAndPhy;
7931 s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM;
Reinette Chatre21f8a732009-08-18 10:25:05 -07007932 s8 noise = (s8) le16_to_cpu(frame->noise);
Zhu Yid685b8c2006-04-13 17:20:27 +08007933 u8 rate = frame->rate;
Dan Carpenter92c1ff12012-01-12 09:34:50 +03007934 unsigned short len = le16_to_cpu(pkt->u.frame.length);
Zhu Yid685b8c2006-04-13 17:20:27 +08007935 struct sk_buff *skb;
7936 int hdr_only = 0;
7937 u16 filter = priv->prom_priv->filter;
7938
7939 /* If the filter is set to not include Rx frames then return */
7940 if (filter & IPW_PROM_NO_RX)
7941 return;
7942
Zhu Yid685b8c2006-04-13 17:20:27 +08007943 /* We received data from the HW, so stop the watchdog */
Florian Westphal860e9532016-05-03 16:33:13 +02007944 netif_trans_update(dev);
Zhu Yid685b8c2006-04-13 17:20:27 +08007945
7946 if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007947 dev->stats.rx_errors++;
Zhu Yid685b8c2006-04-13 17:20:27 +08007948 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7949 return;
7950 }
7951
7952 /* We only process data packets if the interface is open */
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007953 if (unlikely(!netif_running(dev))) {
7954 dev->stats.rx_dropped++;
Zhu Yid685b8c2006-04-13 17:20:27 +08007955 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7956 return;
7957 }
7958
7959 /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
7960 * that now */
7961 if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
7962 /* FIXME: Should alloc bigger skb instead */
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00007963 dev->stats.rx_dropped++;
Zhu Yid685b8c2006-04-13 17:20:27 +08007964 IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
7965 return;
7966 }
7967
7968 hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007969 if (libipw_is_management(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +08007970 if (filter & IPW_PROM_NO_MGMT)
7971 return;
7972 if (filter & IPW_PROM_MGMT_HEADER_ONLY)
7973 hdr_only = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007974 } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +08007975 if (filter & IPW_PROM_NO_CTL)
7976 return;
7977 if (filter & IPW_PROM_CTL_HEADER_ONLY)
7978 hdr_only = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007979 } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +08007980 if (filter & IPW_PROM_NO_DATA)
7981 return;
7982 if (filter & IPW_PROM_DATA_HEADER_ONLY)
7983 hdr_only = 1;
7984 }
7985
7986 /* Copy the SKB since this is for the promiscuous side */
7987 skb = skb_copy(rxb->skb, GFP_ATOMIC);
7988 if (skb == NULL) {
7989 IPW_ERROR("skb_clone failed for promiscuous copy.\n");
7990 return;
7991 }
7992
7993 /* copy the frame data to write after where the radiotap header goes */
7994 ipw_rt = (void *)skb->data;
7995
7996 if (hdr_only)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04007997 len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control));
Zhu Yid685b8c2006-04-13 17:20:27 +08007998
7999 memcpy(ipw_rt->payload, hdr, len);
8000
Zhu Yid685b8c2006-04-13 17:20:27 +08008001 ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
8002 ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
Al Viro743b84d2007-12-27 01:43:16 -05008003 ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*ipw_rt)); /* total header+data */
Zhu Yid685b8c2006-04-13 17:20:27 +08008004
8005 /* Set the size of the skb to the size of the frame */
Al Viro743b84d2007-12-27 01:43:16 -05008006 skb_put(skb, sizeof(*ipw_rt) + len);
Zhu Yid685b8c2006-04-13 17:20:27 +08008007
8008 /* Big bitfield of all the fields we provide in radiotap */
Al Viro743b84d2007-12-27 01:43:16 -05008009 ipw_rt->rt_hdr.it_present = cpu_to_le32(
8010 (1 << IEEE80211_RADIOTAP_TSFT) |
Zhu Yi4b1f8a92006-12-05 14:42:14 +08008011 (1 << IEEE80211_RADIOTAP_FLAGS) |
Zhu Yid685b8c2006-04-13 17:20:27 +08008012 (1 << IEEE80211_RADIOTAP_RATE) |
8013 (1 << IEEE80211_RADIOTAP_CHANNEL) |
8014 (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
8015 (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
8016 (1 << IEEE80211_RADIOTAP_ANTENNA));
8017
8018 /* Zero the flags, we'll add to them as we go */
8019 ipw_rt->rt_flags = 0;
Zhu Yi4b1f8a92006-12-05 14:42:14 +08008020 ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
8021 frame->parent_tsf[2] << 16 |
8022 frame->parent_tsf[1] << 8 |
8023 frame->parent_tsf[0]);
Zhu Yid685b8c2006-04-13 17:20:27 +08008024
8025 /* Convert to DBM */
8026 ipw_rt->rt_dbmsignal = signal;
8027 ipw_rt->rt_dbmnoise = noise;
8028
8029 /* Convert the channel data and set the flags */
8030 ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel));
8031 if (channel > 14) { /* 802.11a */
8032 ipw_rt->rt_chbitmask =
8033 cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
8034 } else if (phy_flags & (1 << 5)) { /* 802.11b */
8035 ipw_rt->rt_chbitmask =
8036 cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
8037 } else { /* 802.11g */
8038 ipw_rt->rt_chbitmask =
Al Viro472caf82007-12-27 01:50:54 -05008039 cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
Zhu Yid685b8c2006-04-13 17:20:27 +08008040 }
8041
8042 /* set the rate in multiples of 500k/s */
8043 switch (rate) {
8044 case IPW_TX_RATE_1MB:
8045 ipw_rt->rt_rate = 2;
8046 break;
8047 case IPW_TX_RATE_2MB:
8048 ipw_rt->rt_rate = 4;
8049 break;
8050 case IPW_TX_RATE_5MB:
8051 ipw_rt->rt_rate = 10;
8052 break;
8053 case IPW_TX_RATE_6MB:
8054 ipw_rt->rt_rate = 12;
8055 break;
8056 case IPW_TX_RATE_9MB:
8057 ipw_rt->rt_rate = 18;
8058 break;
8059 case IPW_TX_RATE_11MB:
8060 ipw_rt->rt_rate = 22;
8061 break;
8062 case IPW_TX_RATE_12MB:
8063 ipw_rt->rt_rate = 24;
8064 break;
8065 case IPW_TX_RATE_18MB:
8066 ipw_rt->rt_rate = 36;
8067 break;
8068 case IPW_TX_RATE_24MB:
8069 ipw_rt->rt_rate = 48;
8070 break;
8071 case IPW_TX_RATE_36MB:
8072 ipw_rt->rt_rate = 72;
8073 break;
8074 case IPW_TX_RATE_48MB:
8075 ipw_rt->rt_rate = 96;
8076 break;
8077 case IPW_TX_RATE_54MB:
8078 ipw_rt->rt_rate = 108;
8079 break;
8080 default:
8081 ipw_rt->rt_rate = 0;
8082 break;
8083 }
8084
8085 /* antenna number */
8086 ipw_rt->rt_antenna = (phy_flags & 3);
8087
8088 /* set the preamble flag if we have it */
8089 if (phy_flags & (1 << 6))
8090 ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
8091
8092 IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len);
8093
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008094 if (!libipw_rx(priv->prom_priv->ieee, skb, stats)) {
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00008095 dev->stats.rx_errors++;
Zhu Yid685b8c2006-04-13 17:20:27 +08008096 dev_kfree_skb_any(skb);
8097 }
8098}
8099#endif
8100
Arjan van de Ven858119e2006-01-14 13:20:43 -08008101static int is_network_packet(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008102 struct libipw_hdr_4addr *header)
James Ketrenosea2b26e2005-08-24 21:25:16 -05008103{
Lucas De Marchi25985ed2011-03-30 22:57:33 -03008104 /* Filter incoming packets to determine if they are targeted toward
James Ketrenosea2b26e2005-08-24 21:25:16 -05008105 * this network, discarding packets coming from ourselves */
8106 switch (priv->ieee->iw_mode) {
James Ketrenosa613bff2005-08-24 21:43:11 -05008107 case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008108 /* packets from our adapter are dropped (echo) */
dingtianhong36325f32013-12-26 19:41:23 +08008109 if (ether_addr_equal(header->addr2, priv->net_dev->dev_addr))
James Ketrenosc848d0a2005-08-24 21:56:24 -05008110 return 0;
8111
Peter Jones90700fd2005-08-26 16:51:06 -05008112 /* {broad,multi}cast packets to our BSSID go through */
Stephen Hemminger3c190652006-01-03 15:27:38 -08008113 if (is_multicast_ether_addr(header->addr1))
dingtianhong36325f32013-12-26 19:41:23 +08008114 return ether_addr_equal(header->addr3, priv->bssid);
James Ketrenosa613bff2005-08-24 21:43:11 -05008115
8116 /* packets to our adapter go through */
dingtianhong36325f32013-12-26 19:41:23 +08008117 return ether_addr_equal(header->addr1,
8118 priv->net_dev->dev_addr);
James Ketrenosa613bff2005-08-24 21:43:11 -05008119
Peter Jones90700fd2005-08-26 16:51:06 -05008120 case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008121 /* packets from our adapter are dropped (echo) */
dingtianhong36325f32013-12-26 19:41:23 +08008122 if (ether_addr_equal(header->addr3, priv->net_dev->dev_addr))
James Ketrenosc848d0a2005-08-24 21:56:24 -05008123 return 0;
8124
Peter Jones90700fd2005-08-26 16:51:06 -05008125 /* {broad,multi}cast packets to our BSS go through */
Stephen Hemminger3c190652006-01-03 15:27:38 -08008126 if (is_multicast_ether_addr(header->addr1))
dingtianhong36325f32013-12-26 19:41:23 +08008127 return ether_addr_equal(header->addr2, priv->bssid);
James Ketrenosa613bff2005-08-24 21:43:11 -05008128
8129 /* packets to our adapter go through */
dingtianhong36325f32013-12-26 19:41:23 +08008130 return ether_addr_equal(header->addr1,
8131 priv->net_dev->dev_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008132 }
James Ketrenosa613bff2005-08-24 21:43:11 -05008133
James Ketrenosea2b26e2005-08-24 21:25:16 -05008134 return 1;
8135}
8136
James Ketrenosafbf30a2005-08-25 00:05:33 -05008137#define IPW_PACKET_RETRY_TIME HZ
8138
Arjan van de Ven858119e2006-01-14 13:20:43 -08008139static int is_duplicate_packet(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008140 struct libipw_hdr_4addr *header)
James Ketrenosafbf30a2005-08-25 00:05:33 -05008141{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008142 u16 sc = le16_to_cpu(header->seq_ctl);
8143 u16 seq = WLAN_GET_SEQ_SEQ(sc);
8144 u16 frag = WLAN_GET_SEQ_FRAG(sc);
8145 u16 *last_seq, *last_frag;
8146 unsigned long *last_time;
8147
8148 switch (priv->ieee->iw_mode) {
8149 case IW_MODE_ADHOC:
8150 {
8151 struct list_head *p;
8152 struct ipw_ibss_seq *entry = NULL;
8153 u8 *mac = header->addr2;
8154 int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE;
8155
Dave Jones5cb06562013-07-03 15:05:18 -07008156 list_for_each(p, &priv->ibss_mac_hash[index]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008157 entry =
8158 list_entry(p, struct ipw_ibss_seq, list);
dingtianhong36325f32013-12-26 19:41:23 +08008159 if (ether_addr_equal(entry->mac, mac))
James Ketrenosafbf30a2005-08-25 00:05:33 -05008160 break;
8161 }
8162 if (p == &priv->ibss_mac_hash[index]) {
8163 entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
8164 if (!entry) {
8165 IPW_ERROR
8166 ("Cannot malloc new mac entry\n");
8167 return 0;
8168 }
8169 memcpy(entry->mac, mac, ETH_ALEN);
8170 entry->seq_num = seq;
8171 entry->frag_num = frag;
8172 entry->packet_time = jiffies;
8173 list_add(&entry->list,
8174 &priv->ibss_mac_hash[index]);
8175 return 0;
8176 }
8177 last_seq = &entry->seq_num;
8178 last_frag = &entry->frag_num;
8179 last_time = &entry->packet_time;
8180 break;
8181 }
8182 case IW_MODE_INFRA:
8183 last_seq = &priv->last_seq_num;
8184 last_frag = &priv->last_frag_num;
8185 last_time = &priv->last_packet_time;
8186 break;
8187 default:
8188 return 0;
8189 }
8190 if ((*last_seq == seq) &&
8191 time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) {
8192 if (*last_frag == frag)
8193 goto drop;
8194 if (*last_frag + 1 != frag)
8195 /* out-of-order fragment */
8196 goto drop;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008197 } else
8198 *last_seq = seq;
8199
Zhu Yif57ce7c2005-07-13 12:22:15 -05008200 *last_frag = frag;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008201 *last_time = jiffies;
8202 return 0;
8203
8204 drop:
Zhu Yi87b016c2005-08-05 17:17:35 +08008205 /* Comment this line now since we observed the card receives
8206 * duplicate packets but the FCTL_RETRY bit is not set in the
8207 * IBSS mode with fragmentation enabled.
John W. Linville72118012008-09-30 21:43:03 -04008208 BUG_ON(!(le16_to_cpu(header->frame_control) & IEEE80211_FCTL_RETRY)); */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008209 return 1;
8210}
8211
James Ketrenosb095c382005-08-24 22:04:42 -05008212static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
8213 struct ipw_rx_mem_buffer *rxb,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008214 struct libipw_rx_stats *stats)
James Ketrenosb095c382005-08-24 22:04:42 -05008215{
8216 struct sk_buff *skb = rxb->skb;
8217 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008218 struct libipw_hdr_4addr *header = (struct libipw_hdr_4addr *)
James Ketrenosb095c382005-08-24 22:04:42 -05008219 (skb->data + IPW_RX_FRAME_SIZE);
8220
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008221 libipw_rx_mgt(priv->ieee, header, stats);
James Ketrenosb095c382005-08-24 22:04:42 -05008222
8223 if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
8224 ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
8225 IEEE80211_STYPE_PROBE_RESP) ||
8226 (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
8227 IEEE80211_STYPE_BEACON))) {
dingtianhong36325f32013-12-26 19:41:23 +08008228 if (ether_addr_equal(header->addr3, priv->bssid))
James Ketrenosb095c382005-08-24 22:04:42 -05008229 ipw_add_station(priv, header->addr2);
8230 }
8231
8232 if (priv->config & CFG_NET_STATS) {
8233 IPW_DEBUG_HC("sending stat packet\n");
8234
8235 /* Set the size of the skb to the size of the full
8236 * ipw header and 802.11 frame */
8237 skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
8238 IPW_RX_FRAME_SIZE);
8239
8240 /* Advance past the ipw packet header to the 802.11 frame */
8241 skb_pull(skb, IPW_RX_FRAME_SIZE);
8242
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008243 /* Push the libipw_rx_stats before the 802.11 frame */
James Ketrenosb095c382005-08-24 22:04:42 -05008244 memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
8245
8246 skb->dev = priv->ieee->dev;
8247
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008248 /* Point raw at the libipw_stats */
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -07008249 skb_reset_mac_header(skb);
James Ketrenosb095c382005-08-24 22:04:42 -05008250
8251 skb->pkt_type = PACKET_OTHERHOST;
Harvey Harrisonc1b4aa32009-01-29 13:26:44 -08008252 skb->protocol = cpu_to_be16(ETH_P_80211_STATS);
James Ketrenosb095c382005-08-24 22:04:42 -05008253 memset(skb->cb, 0, sizeof(rxb->skb->cb));
8254 netif_rx(skb);
8255 rxb->skb = NULL;
8256 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008257}
8258
James Ketrenos43f66a62005-03-25 12:31:53 -06008259/*
Lucas De Marchi25985ed2011-03-30 22:57:33 -03008260 * Main entry function for receiving a packet with 80211 headers. This
James Ketrenos43f66a62005-03-25 12:31:53 -06008261 * should be called when ever the FW has notified us that there is a new
Lucas De Marchi25985ed2011-03-30 22:57:33 -03008262 * skb in the receive queue.
James Ketrenos43f66a62005-03-25 12:31:53 -06008263 */
8264static void ipw_rx(struct ipw_priv *priv)
8265{
8266 struct ipw_rx_mem_buffer *rxb;
8267 struct ipw_rx_packet *pkt;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008268 struct libipw_hdr_4addr *header;
James Ketrenos43f66a62005-03-25 12:31:53 -06008269 u32 r, w, i;
8270 u8 network_packet;
Dan Williams943dbef2008-02-14 17:49:41 -05008271 u8 fill_rx = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008272
James Ketrenosb095c382005-08-24 22:04:42 -05008273 r = ipw_read32(priv, IPW_RX_READ_INDEX);
8274 w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
Dan Williams943dbef2008-02-14 17:49:41 -05008275 i = priv->rxq->read;
8276
8277 if (ipw_rx_queue_space (priv->rxq) > (RX_QUEUE_SIZE / 2))
8278 fill_rx = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06008279
8280 while (i != r) {
8281 rxb = priv->rxq->queue[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06008282 if (unlikely(rxb == NULL)) {
8283 printk(KERN_CRIT "Queue not allocated!\n");
8284 break;
8285 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008286 priv->rxq->queue[i] = NULL;
8287
8288 pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05008289 IPW_RX_BUF_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06008290 PCI_DMA_FROMDEVICE);
8291
8292 pkt = (struct ipw_rx_packet *)rxb->skb->data;
8293 IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
8294 pkt->header.message_type,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008295 pkt->header.rx_seq_num, pkt->header.control_bits);
James Ketrenos43f66a62005-03-25 12:31:53 -06008296
8297 switch (pkt->header.message_type) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008298 case RX_FRAME_TYPE: /* 802.11 frame */ {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008299 struct libipw_rx_stats stats = {
Zhu Yi851ca262006-08-21 11:37:58 +08008300 .rssi = pkt->u.frame.rssi_dbm -
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008301 IPW_RSSI_TO_DBM,
James Ketrenosc848d0a2005-08-24 21:56:24 -05008302 .signal =
Reinette Chatre21f8a732009-08-18 10:25:05 -07008303 pkt->u.frame.rssi_dbm -
Bill Mossb1916082006-02-15 08:50:18 +08008304 IPW_RSSI_TO_DBM + 0x100,
James Ketrenosc848d0a2005-08-24 21:56:24 -05008305 .noise =
8306 le16_to_cpu(pkt->u.frame.noise),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008307 .rate = pkt->u.frame.rate,
8308 .mac_time = jiffies,
8309 .received_channel =
8310 pkt->u.frame.received_channel,
8311 .freq =
8312 (pkt->u.frame.
8313 control & (1 << 0)) ?
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008314 LIBIPW_24GHZ_BAND :
8315 LIBIPW_52GHZ_BAND,
James Ketrenosa613bff2005-08-24 21:43:11 -05008316 .len = le16_to_cpu(pkt->u.frame.length),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008317 };
James Ketrenos43f66a62005-03-25 12:31:53 -06008318
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008319 if (stats.rssi != 0)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008320 stats.mask |= LIBIPW_STATMASK_RSSI;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008321 if (stats.signal != 0)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008322 stats.mask |= LIBIPW_STATMASK_SIGNAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008323 if (stats.noise != 0)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008324 stats.mask |= LIBIPW_STATMASK_NOISE;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008325 if (stats.rate != 0)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008326 stats.mask |= LIBIPW_STATMASK_RATE;
James Ketrenos43f66a62005-03-25 12:31:53 -06008327
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008328 priv->rx_packets++;
James Ketrenos43f66a62005-03-25 12:31:53 -06008329
Zhu Yid685b8c2006-04-13 17:20:27 +08008330#ifdef CONFIG_IPW2200_PROMISCUOUS
8331 if (priv->prom_net_dev && netif_running(priv->prom_net_dev))
8332 ipw_handle_promiscuous_rx(priv, rxb, &stats);
8333#endif
8334
James Ketrenosb095c382005-08-24 22:04:42 -05008335#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008336 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
Zhu Yi459d4082006-04-13 17:21:00 +08008337#ifdef CONFIG_IPW2200_RADIOTAP
Zhu Yid685b8c2006-04-13 17:20:27 +08008338
8339 ipw_handle_data_packet_monitor(priv,
8340 rxb,
8341 &stats);
Mike Kershaw24a47db2005-08-26 00:41:54 -05008342#else
Zhu Yid685b8c2006-04-13 17:20:27 +08008343 ipw_handle_data_packet(priv, rxb,
8344 &stats);
Mike Kershaw24a47db2005-08-26 00:41:54 -05008345#endif
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008346 break;
8347 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008348#endif
Jeff Garzikbf794512005-07-31 13:07:26 -04008349
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008350 header =
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008351 (struct libipw_hdr_4addr *)(rxb->skb->
James Ketrenos0dacca12005-09-21 12:23:41 -05008352 data +
8353 IPW_RX_FRAME_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06008354 /* TODO: Check Ad-Hoc dest/source and make sure
8355 * that we are actually parsing these packets
Jeff Garzikbf794512005-07-31 13:07:26 -04008356 * correctly -- we should probably use the
James Ketrenos43f66a62005-03-25 12:31:53 -06008357 * frame control of the packet and disregard
8358 * the current iw_mode */
James Ketrenos43f66a62005-03-25 12:31:53 -06008359
James Ketrenosea2b26e2005-08-24 21:25:16 -05008360 network_packet =
8361 is_network_packet(priv, header);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008362 if (network_packet && priv->assoc_network) {
8363 priv->assoc_network->stats.rssi =
8364 stats.rssi;
Zhu Yi00d21de2006-04-13 17:19:02 +08008365 priv->exp_avg_rssi =
8366 exponential_average(priv->exp_avg_rssi,
8367 stats.rssi, DEPTH_RSSI);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008368 }
8369
8370 IPW_DEBUG_RX("Frame: len=%u\n",
James Ketrenosa613bff2005-08-24 21:43:11 -05008371 le16_to_cpu(pkt->u.frame.length));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008372
James Ketrenosa613bff2005-08-24 21:43:11 -05008373 if (le16_to_cpu(pkt->u.frame.length) <
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008374 libipw_get_hdrlen(le16_to_cpu(
Zhu Yi9d0be032006-02-15 06:18:19 +08008375 header->frame_ctl))) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008376 IPW_DEBUG_DROP
8377 ("Received packet is too small. "
8378 "Dropping.\n");
Stephen Hemmingerce55cba2009-03-20 19:36:38 +00008379 priv->net_dev->stats.rx_errors++;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008380 priv->wstats.discard.misc++;
8381 break;
8382 }
8383
James Ketrenosa613bff2005-08-24 21:43:11 -05008384 switch (WLAN_FC_GET_TYPE
8385 (le16_to_cpu(header->frame_ctl))) {
James Ketrenosb095c382005-08-24 22:04:42 -05008386
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008387 case IEEE80211_FTYPE_MGMT:
James Ketrenosb095c382005-08-24 22:04:42 -05008388 ipw_handle_mgmt_packet(priv, rxb,
8389 &stats);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008390 break;
8391
8392 case IEEE80211_FTYPE_CTL:
8393 break;
8394
8395 case IEEE80211_FTYPE_DATA:
James Ketrenosafbf30a2005-08-25 00:05:33 -05008396 if (unlikely(!network_packet ||
8397 is_duplicate_packet(priv,
8398 header)))
8399 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008400 IPW_DEBUG_DROP("Dropping: "
Johannes Berge1749612008-10-27 15:59:26 -07008401 "%pM, "
8402 "%pM, "
8403 "%pM\n",
8404 header->addr1,
8405 header->addr2,
8406 header->addr3);
James Ketrenosb095c382005-08-24 22:04:42 -05008407 break;
8408 }
8409
8410 ipw_handle_data_packet(priv, rxb,
8411 &stats);
8412
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008413 break;
8414 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008415 break;
8416 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008417
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008418 case RX_HOST_NOTIFICATION_TYPE:{
8419 IPW_DEBUG_RX
8420 ("Notification: subtype=%02X flags=%02X size=%d\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008421 pkt->u.notification.subtype,
8422 pkt->u.notification.flags,
Zhu Yi720eeb42006-12-05 14:41:40 +08008423 le16_to_cpu(pkt->u.notification.size));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008424 ipw_rx_notification(priv, &pkt->u.notification);
8425 break;
8426 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008427
8428 default:
8429 IPW_DEBUG_RX("Bad Rx packet of type %d\n",
8430 pkt->header.message_type);
8431 break;
8432 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008433
8434 /* For now we just don't re-use anything. We can tweak this
8435 * later to try and re-use notification packets and SKBs that
James Ketrenos43f66a62005-03-25 12:31:53 -06008436 * fail to Rx correctly */
8437 if (rxb->skb != NULL) {
8438 dev_kfree_skb_any(rxb->skb);
8439 rxb->skb = NULL;
8440 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008441
James Ketrenos43f66a62005-03-25 12:31:53 -06008442 pci_unmap_single(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05008443 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06008444 list_add_tail(&rxb->list, &priv->rxq->rx_used);
Jeff Garzikbf794512005-07-31 13:07:26 -04008445
James Ketrenos43f66a62005-03-25 12:31:53 -06008446 i = (i + 1) % RX_QUEUE_SIZE;
Dan Williams943dbef2008-02-14 17:49:41 -05008447
8448 /* If there are a lot of unsued frames, restock the Rx queue
8449 * so the ucode won't assert */
8450 if (fill_rx) {
8451 priv->rxq->read = i;
8452 ipw_rx_queue_replenish(priv);
8453 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008454 }
8455
8456 /* Backtrack one entry */
Dan Williams943dbef2008-02-14 17:49:41 -05008457 priv->rxq->read = i;
James Ketrenos43f66a62005-03-25 12:31:53 -06008458 ipw_rx_queue_restock(priv);
8459}
8460
James Ketrenosafbf30a2005-08-25 00:05:33 -05008461#define DEFAULT_RTS_THRESHOLD 2304U
8462#define MIN_RTS_THRESHOLD 1U
8463#define MAX_RTS_THRESHOLD 2304U
8464#define DEFAULT_BEACON_INTERVAL 100U
8465#define DEFAULT_SHORT_RETRY_LIMIT 7U
8466#define DEFAULT_LONG_RETRY_LIMIT 4U
8467
Zhu Yid6d5b5c2006-02-16 16:21:09 +08008468/**
8469 * ipw_sw_reset
8470 * @option: options to control different reset behaviour
8471 * 0 = reset everything except the 'disable' module_param
8472 * 1 = reset everything and print out driver info (for probe only)
8473 * 2 = reset everything
8474 */
8475static int ipw_sw_reset(struct ipw_priv *priv, int option)
James Ketrenos43f66a62005-03-25 12:31:53 -06008476{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008477 int band, modulation;
8478 int old_mode = priv->ieee->iw_mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06008479
James Ketrenosafbf30a2005-08-25 00:05:33 -05008480 /* Initialize module parameter values here */
8481 priv->config = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008482
James Ketrenosafbf30a2005-08-25 00:05:33 -05008483 /* We default to disabling the LED code as right now it causes
8484 * too many systems to lock up... */
Reinette Chatre21f8a732009-08-18 10:25:05 -07008485 if (!led_support)
James Ketrenosafbf30a2005-08-25 00:05:33 -05008486 priv->config |= CFG_NO_LED;
James Ketrenos43f66a62005-03-25 12:31:53 -06008487
James Ketrenosafbf30a2005-08-25 00:05:33 -05008488 if (associate)
8489 priv->config |= CFG_ASSOCIATE;
8490 else
8491 IPW_DEBUG_INFO("Auto associate disabled.\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04008492
James Ketrenosafbf30a2005-08-25 00:05:33 -05008493 if (auto_create)
8494 priv->config |= CFG_ADHOC_CREATE;
8495 else
8496 IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
8497
Zhu Yi17ed0812006-01-24 16:36:31 +08008498 priv->config &= ~CFG_STATIC_ESSID;
8499 priv->essid_len = 0;
8500 memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
8501
Zhu Yid6d5b5c2006-02-16 16:21:09 +08008502 if (disable && option) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008503 priv->status |= STATUS_RF_KILL_SW;
8504 IPW_DEBUG_INFO("Radio disabled.\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06008505 }
8506
Reinette Chatre21f8a732009-08-18 10:25:05 -07008507 if (default_channel != 0) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008508 priv->config |= CFG_STATIC_CHANNEL;
Reinette Chatre21f8a732009-08-18 10:25:05 -07008509 priv->channel = default_channel;
8510 IPW_DEBUG_INFO("Bind to static channel %d\n", default_channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008511 /* TODO: Validate that provided channel is in range */
8512 }
Zhu Yie43e3c12006-04-13 17:20:45 +08008513#ifdef CONFIG_IPW2200_QOS
James Ketrenosafbf30a2005-08-25 00:05:33 -05008514 ipw_qos_init(priv, qos_enable, qos_burst_enable,
8515 burst_duration_CCK, burst_duration_OFDM);
Zhu Yie43e3c12006-04-13 17:20:45 +08008516#endif /* CONFIG_IPW2200_QOS */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008517
Reinette Chatre21f8a732009-08-18 10:25:05 -07008518 switch (network_mode) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008519 case 1:
8520 priv->ieee->iw_mode = IW_MODE_ADHOC;
8521 priv->net_dev->type = ARPHRD_ETHER;
8522
8523 break;
8524#ifdef CONFIG_IPW2200_MONITOR
8525 case 2:
8526 priv->ieee->iw_mode = IW_MODE_MONITOR;
Zhu Yi459d4082006-04-13 17:21:00 +08008527#ifdef CONFIG_IPW2200_RADIOTAP
Mike Kershaw24a47db2005-08-26 00:41:54 -05008528 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
8529#else
James Ketrenosafbf30a2005-08-25 00:05:33 -05008530 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008531#endif
James Ketrenosafbf30a2005-08-25 00:05:33 -05008532 break;
8533#endif
8534 default:
8535 case 0:
8536 priv->net_dev->type = ARPHRD_ETHER;
8537 priv->ieee->iw_mode = IW_MODE_INFRA;
8538 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06008539 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008540
James Ketrenosafbf30a2005-08-25 00:05:33 -05008541 if (hwcrypto) {
8542 priv->ieee->host_encrypt = 0;
8543 priv->ieee->host_encrypt_msdu = 0;
8544 priv->ieee->host_decrypt = 0;
Hong Liu567deaf2005-08-31 18:07:22 +08008545 priv->ieee->host_mc_decrypt = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008546 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05008547 IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06008548
Zhu Yie402c932005-08-05 17:20:40 +08008549 /* IPW2200/2915 is abled to do hardware fragmentation. */
8550 priv->ieee->host_open_frag = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008551
James Ketrenosafbf30a2005-08-25 00:05:33 -05008552 if ((priv->pci_dev->device == 0x4223) ||
8553 (priv->pci_dev->device == 0x4224)) {
Zhu Yie8c69e22006-02-17 08:25:12 +08008554 if (option == 1)
James Ketrenosafbf30a2005-08-25 00:05:33 -05008555 printk(KERN_INFO DRV_NAME
8556 ": Detected Intel PRO/Wireless 2915ABG Network "
8557 "Connection\n");
8558 priv->ieee->abg_true = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008559 band = LIBIPW_52GHZ_BAND | LIBIPW_24GHZ_BAND;
8560 modulation = LIBIPW_OFDM_MODULATION |
8561 LIBIPW_CCK_MODULATION;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008562 priv->adapter = IPW_2915ABG;
8563 priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008564 } else {
Zhu Yie8c69e22006-02-17 08:25:12 +08008565 if (option == 1)
James Ketrenosafbf30a2005-08-25 00:05:33 -05008566 printk(KERN_INFO DRV_NAME
8567 ": Detected Intel PRO/Wireless 2200BG Network "
8568 "Connection\n");
8569
8570 priv->ieee->abg_true = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008571 band = LIBIPW_24GHZ_BAND;
8572 modulation = LIBIPW_OFDM_MODULATION |
8573 LIBIPW_CCK_MODULATION;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008574 priv->adapter = IPW_2200BG;
8575 priv->ieee->mode = IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008576 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008577
James Ketrenosafbf30a2005-08-25 00:05:33 -05008578 priv->ieee->freq_band = band;
8579 priv->ieee->modulation = modulation;
Jeff Garzikbf794512005-07-31 13:07:26 -04008580
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008581 priv->rates_mask = LIBIPW_DEFAULT_RATES_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06008582
James Ketrenosafbf30a2005-08-25 00:05:33 -05008583 priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
8584 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008585
James Ketrenosafbf30a2005-08-25 00:05:33 -05008586 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8587 priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
8588 priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
Jeff Garzikbf794512005-07-31 13:07:26 -04008589
James Ketrenosafbf30a2005-08-25 00:05:33 -05008590 /* If power management is turned on, default to AC mode */
8591 priv->power_mode = IPW_POWER_AC;
8592 priv->tx_power = IPW_TX_POWER_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008593
Zhu Yi0ece35b2005-08-05 17:26:51 +08008594 return old_mode == priv->ieee->iw_mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06008595}
8596
8597/*
8598 * This file defines the Wireless Extension handlers. It does not
8599 * define any methods of hardware manipulation and relies on the
8600 * functions defined in ipw_main to provide the HW interaction.
Jeff Garzikbf794512005-07-31 13:07:26 -04008601 *
8602 * The exception to this is the use of the ipw_get_ordinal()
Lucas De Marchi25985ed2011-03-30 22:57:33 -03008603 * function used to poll the hardware vs. making unnecessary calls.
James Ketrenos43f66a62005-03-25 12:31:53 -06008604 *
8605 */
8606
James Ketrenos43f66a62005-03-25 12:31:53 -06008607static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
8608{
8609 if (channel == 0) {
8610 IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
8611 priv->config &= ~CFG_STATIC_CHANNEL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008612 IPW_DEBUG_ASSOC("Attempting to associate with new "
8613 "parameters.\n");
8614 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008615 return 0;
8616 }
8617
8618 priv->config |= CFG_STATIC_CHANNEL;
8619
8620 if (priv->channel == channel) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008621 IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
8622 channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06008623 return 0;
8624 }
8625
8626 IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
8627 priv->channel = channel;
8628
James Ketrenosb095c382005-08-24 22:04:42 -05008629#ifdef CONFIG_IPW2200_MONITOR
8630 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008631 int i;
James Ketrenosb095c382005-08-24 22:04:42 -05008632 if (priv->status & STATUS_SCANNING) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008633 IPW_DEBUG_SCAN("Scan abort triggered due to "
James Ketrenosb095c382005-08-24 22:04:42 -05008634 "channel change.\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008635 ipw_abort_scan(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05008636 }
8637
8638 for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
8639 udelay(10);
8640
8641 if (priv->status & STATUS_SCANNING)
8642 IPW_DEBUG_SCAN("Still scanning...\n");
8643 else
8644 IPW_DEBUG_SCAN("Took %dms to abort current scan\n",
8645 1000 - i);
8646
8647 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008648 }
James Ketrenosb095c382005-08-24 22:04:42 -05008649#endif /* CONFIG_IPW2200_MONITOR */
8650
James Ketrenosc848d0a2005-08-24 21:56:24 -05008651 /* Network configuration changed -- force [re]association */
8652 IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
8653 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008654 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008655
8656 return 0;
8657}
8658
Jeff Garzikbf794512005-07-31 13:07:26 -04008659static int ipw_wx_set_freq(struct net_device *dev,
8660 struct iw_request_info *info,
8661 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008662{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008663 struct ipw_priv *priv = libipw_priv(dev);
8664 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
James Ketrenos43f66a62005-03-25 12:31:53 -06008665 struct iw_freq *fwrq = &wrqu->freq;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008666 int ret = 0, i;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008667 u8 channel, flags;
8668 int band;
Jeff Garzikbf794512005-07-31 13:07:26 -04008669
James Ketrenosb095c382005-08-24 22:04:42 -05008670 if (fwrq->m == 0) {
8671 IPW_DEBUG_WX("SET Freq/Channel -> any\n");
Zhu Yi46441512006-01-24 16:37:59 +08008672 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05008673 ret = ipw_set_channel(priv, 0);
Zhu Yi46441512006-01-24 16:37:59 +08008674 mutex_unlock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05008675 return ret;
8676 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008677 /* if setting by freq convert to channel */
8678 if (fwrq->e == 1) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008679 channel = libipw_freq_to_channel(priv->ieee, fwrq->m);
James Ketrenosb095c382005-08-24 22:04:42 -05008680 if (channel == 0)
8681 return -EINVAL;
8682 } else
8683 channel = fwrq->m;
Jeff Garzikbf794512005-07-31 13:07:26 -04008684
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008685 if (!(band = libipw_is_valid_channel(priv->ieee, channel)))
James Ketrenosb095c382005-08-24 22:04:42 -05008686 return -EINVAL;
Jeff Garzikbf794512005-07-31 13:07:26 -04008687
Liu Hong1fe0adb2005-08-19 09:33:10 -05008688 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008689 i = libipw_channel_to_index(priv->ieee, channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008690 if (i == -1)
8691 return -EINVAL;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008692
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008693 flags = (band == LIBIPW_24GHZ_BAND) ?
Liu Hong1fe0adb2005-08-19 09:33:10 -05008694 geo->bg[i].flags : geo->a[i].flags;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008695 if (flags & LIBIPW_CH_PASSIVE_ONLY) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008696 IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
8697 return -EINVAL;
James Ketrenos43f66a62005-03-25 12:31:53 -06008698 }
8699 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008700
Frans Pop9fd1ea42010-03-24 19:46:31 +01008701 IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m);
Zhu Yi46441512006-01-24 16:37:59 +08008702 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05008703 ret = ipw_set_channel(priv, channel);
Zhu Yi46441512006-01-24 16:37:59 +08008704 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008705 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06008706}
8707
Jeff Garzikbf794512005-07-31 13:07:26 -04008708static int ipw_wx_get_freq(struct net_device *dev,
8709 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008710 union iwreq_data *wrqu, char *extra)
8711{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008712 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008713
8714 wrqu->freq.e = 0;
8715
8716 /* If we are associated, trying to associate, or have a statically
8717 * configured CHANNEL then return that; otherwise return ANY */
Zhu Yi46441512006-01-24 16:37:59 +08008718 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008719 if (priv->config & CFG_STATIC_CHANNEL ||
Zhu Yic580f672006-08-21 11:37:01 +08008720 priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) {
8721 int i;
8722
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008723 i = libipw_channel_to_index(priv->ieee, priv->channel);
Zhu Yic580f672006-08-21 11:37:01 +08008724 BUG_ON(i == -1);
8725 wrqu->freq.e = 1;
8726
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008727 switch (libipw_is_valid_channel(priv->ieee, priv->channel)) {
8728 case LIBIPW_52GHZ_BAND:
Zhu Yic580f672006-08-21 11:37:01 +08008729 wrqu->freq.m = priv->ieee->geo.a[i].freq * 100000;
8730 break;
8731
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008732 case LIBIPW_24GHZ_BAND:
Zhu Yic580f672006-08-21 11:37:01 +08008733 wrqu->freq.m = priv->ieee->geo.bg[i].freq * 100000;
8734 break;
8735
8736 default:
8737 BUG();
8738 }
8739 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06008740 wrqu->freq.m = 0;
8741
Zhu Yi46441512006-01-24 16:37:59 +08008742 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01008743 IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06008744 return 0;
8745}
8746
Jeff Garzikbf794512005-07-31 13:07:26 -04008747static int ipw_wx_set_mode(struct net_device *dev,
8748 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008749 union iwreq_data *wrqu, char *extra)
8750{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008751 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008752 int err = 0;
8753
8754 IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
8755
James Ketrenos43f66a62005-03-25 12:31:53 -06008756 switch (wrqu->mode) {
James Ketrenosb095c382005-08-24 22:04:42 -05008757#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06008758 case IW_MODE_MONITOR:
8759#endif
8760 case IW_MODE_ADHOC:
8761 case IW_MODE_INFRA:
8762 break;
8763 case IW_MODE_AUTO:
8764 wrqu->mode = IW_MODE_INFRA;
8765 break;
8766 default:
8767 return -EINVAL;
8768 }
James Ketrenosb095c382005-08-24 22:04:42 -05008769 if (wrqu->mode == priv->ieee->iw_mode)
8770 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008771
Zhu Yi46441512006-01-24 16:37:59 +08008772 mutex_lock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008773
8774 ipw_sw_reset(priv, 0);
8775
James Ketrenosb095c382005-08-24 22:04:42 -05008776#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzikbf794512005-07-31 13:07:26 -04008777 if (priv->ieee->iw_mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06008778 priv->net_dev->type = ARPHRD_ETHER;
Jeff Garzikbf794512005-07-31 13:07:26 -04008779
8780 if (wrqu->mode == IW_MODE_MONITOR)
Zhu Yi459d4082006-04-13 17:21:00 +08008781#ifdef CONFIG_IPW2200_RADIOTAP
Mike Kershaw24a47db2005-08-26 00:41:54 -05008782 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
8783#else
James Ketrenos43f66a62005-03-25 12:31:53 -06008784 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008785#endif
James Ketrenosb095c382005-08-24 22:04:42 -05008786#endif /* CONFIG_IPW2200_MONITOR */
Jeff Garzikbf794512005-07-31 13:07:26 -04008787
Jeff Garzikbf794512005-07-31 13:07:26 -04008788 /* Free the existing firmware and reset the fw_loaded
Nick Andrew877d0312009-01-26 11:06:57 +01008789 * flag so ipw_load() will bring in the new firmware */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008790 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -06008791
8792 priv->ieee->iw_mode = wrqu->mode;
Jeff Garzikbf794512005-07-31 13:07:26 -04008793
Tejun Heobcb6d912011-01-26 12:12:50 +01008794 schedule_work(&priv->adapter_restart);
Zhu Yi46441512006-01-24 16:37:59 +08008795 mutex_unlock(&priv->mutex);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008796 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06008797}
8798
Jeff Garzikbf794512005-07-31 13:07:26 -04008799static int ipw_wx_get_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008800 struct iw_request_info *info,
8801 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008802{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008803 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08008804 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008805 wrqu->mode = priv->ieee->iw_mode;
8806 IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
Zhu Yi46441512006-01-24 16:37:59 +08008807 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008808 return 0;
8809}
8810
James Ketrenos43f66a62005-03-25 12:31:53 -06008811/* Values are in microsecond */
8812static const s32 timeout_duration[] = {
8813 350000,
8814 250000,
8815 75000,
8816 37000,
8817 25000,
8818};
8819
8820static const s32 period_duration[] = {
8821 400000,
8822 700000,
8823 1000000,
8824 1000000,
8825 1000000
8826};
8827
Jeff Garzikbf794512005-07-31 13:07:26 -04008828static int ipw_wx_get_range(struct net_device *dev,
8829 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008830 union iwreq_data *wrqu, char *extra)
8831{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008832 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008833 struct iw_range *range = (struct iw_range *)extra;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008834 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
James Ketrenosb095c382005-08-24 22:04:42 -05008835 int i = 0, j;
James Ketrenos43f66a62005-03-25 12:31:53 -06008836
8837 wrqu->data.length = sizeof(*range);
8838 memset(range, 0, sizeof(*range));
8839
8840 /* 54Mbs == ~27 Mb/s real (802.11g) */
Jeff Garzikbf794512005-07-31 13:07:26 -04008841 range->throughput = 27 * 1000 * 1000;
James Ketrenos43f66a62005-03-25 12:31:53 -06008842
8843 range->max_qual.qual = 100;
8844 /* TODO: Find real max RSSI and stick here */
8845 range->max_qual.level = 0;
Bill Mossb1916082006-02-15 08:50:18 +08008846 range->max_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008847 range->max_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06008848
8849 range->avg_qual.qual = 70;
André Goddard Rosaaf901ca2009-11-14 13:09:05 -02008850 /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008851 range->avg_qual.level = 0; /* FIXME to real average level */
James Ketrenos43f66a62005-03-25 12:31:53 -06008852 range->avg_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008853 range->avg_qual.updated = 7; /* Updated all three */
Zhu Yi46441512006-01-24 16:37:59 +08008854 mutex_lock(&priv->mutex);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008855 range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06008856
Jeff Garzikbf794512005-07-31 13:07:26 -04008857 for (i = 0; i < range->num_bitrates; i++)
8858 range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008859 500000;
Jeff Garzikbf794512005-07-31 13:07:26 -04008860
James Ketrenos43f66a62005-03-25 12:31:53 -06008861 range->max_rts = DEFAULT_RTS_THRESHOLD;
8862 range->min_frag = MIN_FRAG_THRESHOLD;
8863 range->max_frag = MAX_FRAG_THRESHOLD;
8864
8865 range->encoding_size[0] = 5;
Jeff Garzikbf794512005-07-31 13:07:26 -04008866 range->encoding_size[1] = 13;
James Ketrenos43f66a62005-03-25 12:31:53 -06008867 range->num_encoding_sizes = 2;
8868 range->max_encoding_tokens = WEP_KEYS;
8869
8870 /* Set the Wireless Extension versions */
8871 range->we_version_compiled = WIRELESS_EXT;
Dan Williamsf1b50862006-01-30 13:58:56 -05008872 range->we_version_source = 18;
James Ketrenos43f66a62005-03-25 12:31:53 -06008873
James Ketrenosb095c382005-08-24 22:04:42 -05008874 i = 0;
8875 if (priv->ieee->mode & (IEEE_B | IEEE_G)) {
Zhu Yie815de42006-03-02 05:55:51 +08008876 for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES; j++) {
8877 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008878 (geo->bg[j].flags & LIBIPW_CH_PASSIVE_ONLY))
Zhu Yie815de42006-03-02 05:55:51 +08008879 continue;
8880
James Ketrenosb095c382005-08-24 22:04:42 -05008881 range->freq[i].i = geo->bg[j].channel;
8882 range->freq[i].m = geo->bg[j].freq * 100000;
8883 range->freq[i].e = 1;
Zhu Yie815de42006-03-02 05:55:51 +08008884 i++;
James Ketrenosb095c382005-08-24 22:04:42 -05008885 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008886 }
James Ketrenosb095c382005-08-24 22:04:42 -05008887
8888 if (priv->ieee->mode & IEEE_A) {
Zhu Yie815de42006-03-02 05:55:51 +08008889 for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES; j++) {
8890 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008891 (geo->a[j].flags & LIBIPW_CH_PASSIVE_ONLY))
Zhu Yie815de42006-03-02 05:55:51 +08008892 continue;
8893
James Ketrenosb095c382005-08-24 22:04:42 -05008894 range->freq[i].i = geo->a[j].channel;
8895 range->freq[i].m = geo->a[j].freq * 100000;
8896 range->freq[i].e = 1;
Zhu Yie815de42006-03-02 05:55:51 +08008897 i++;
James Ketrenosb095c382005-08-24 22:04:42 -05008898 }
8899 }
8900
8901 range->num_channels = i;
8902 range->num_frequency = i;
8903
Zhu Yi46441512006-01-24 16:37:59 +08008904 mutex_unlock(&priv->mutex);
Benoit Boissinot97a78ca2005-09-15 17:30:28 +00008905
8906 /* Event capability (kernel + driver) */
8907 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
8908 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
Zhu Yi07f02e42006-04-13 17:19:25 +08008909 IW_EVENT_CAPA_MASK(SIOCGIWAP) |
8910 IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
Benoit Boissinot97a78ca2005-09-15 17:30:28 +00008911 range->event_capa[1] = IW_EVENT_CAPA_K_1;
James Ketrenos43f66a62005-03-25 12:31:53 -06008912
Dan Williamsf1b50862006-01-30 13:58:56 -05008913 range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
8914 IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
8915
Dan Williams374fdfb2007-12-12 10:25:07 -05008916 range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE;
8917
James Ketrenos43f66a62005-03-25 12:31:53 -06008918 IPW_DEBUG_WX("GET Range\n");
8919 return 0;
8920}
8921
Jeff Garzikbf794512005-07-31 13:07:26 -04008922static int ipw_wx_set_wap(struct net_device *dev,
8923 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008924 union iwreq_data *wrqu, char *extra)
8925{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008926 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06008927
Jeff Garzikbf794512005-07-31 13:07:26 -04008928 if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
James Ketrenos43f66a62005-03-25 12:31:53 -06008929 return -EINVAL;
Zhu Yi46441512006-01-24 16:37:59 +08008930 mutex_lock(&priv->mutex);
Wei Yongjun7949d202012-08-23 14:54:05 +08008931 if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) ||
8932 is_zero_ether_addr(wrqu->ap_addr.sa_data)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008933 /* we disable mandatory BSSID association */
8934 IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
8935 priv->config &= ~CFG_STATIC_BSSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008936 IPW_DEBUG_ASSOC("Attempting to associate with new "
8937 "parameters.\n");
8938 ipw_associate(priv);
Zhu Yi46441512006-01-24 16:37:59 +08008939 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008940 return 0;
8941 }
8942
8943 priv->config |= CFG_STATIC_BSSID;
dingtianhong36325f32013-12-26 19:41:23 +08008944 if (ether_addr_equal(priv->bssid, wrqu->ap_addr.sa_data)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06008945 IPW_DEBUG_WX("BSSID set to current BSSID.\n");
Zhu Yi46441512006-01-24 16:37:59 +08008946 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008947 return 0;
8948 }
8949
Johannes Berge1749612008-10-27 15:59:26 -07008950 IPW_DEBUG_WX("Setting mandatory BSSID to %pM\n",
8951 wrqu->ap_addr.sa_data);
James Ketrenos43f66a62005-03-25 12:31:53 -06008952
8953 memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
8954
James Ketrenosc848d0a2005-08-24 21:56:24 -05008955 /* Network configuration changed -- force [re]association */
8956 IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
8957 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008958 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008959
Zhu Yi46441512006-01-24 16:37:59 +08008960 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008961 return 0;
8962}
8963
Jeff Garzikbf794512005-07-31 13:07:26 -04008964static int ipw_wx_get_wap(struct net_device *dev,
8965 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008966 union iwreq_data *wrqu, char *extra)
8967{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008968 struct ipw_priv *priv = libipw_priv(dev);
Joe Perches0795af52007-10-03 17:59:30 -07008969
James Ketrenos43f66a62005-03-25 12:31:53 -06008970 /* If we are associated, trying to associate, or have a statically
8971 * configured BSSID then return that; otherwise return ANY */
Zhu Yi46441512006-01-24 16:37:59 +08008972 mutex_lock(&priv->mutex);
Jeff Garzikbf794512005-07-31 13:07:26 -04008973 if (priv->config & CFG_STATIC_BSSID ||
James Ketrenos43f66a62005-03-25 12:31:53 -06008974 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
8975 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008976 memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06008977 } else
Joe Perches93803b32015-03-02 19:54:49 -08008978 eth_zero_addr(wrqu->ap_addr.sa_data);
James Ketrenos43f66a62005-03-25 12:31:53 -06008979
Johannes Berge1749612008-10-27 15:59:26 -07008980 IPW_DEBUG_WX("Getting WAP BSSID: %pM\n",
8981 wrqu->ap_addr.sa_data);
Zhu Yi46441512006-01-24 16:37:59 +08008982 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06008983 return 0;
8984}
8985
Jeff Garzikbf794512005-07-31 13:07:26 -04008986static int ipw_wx_set_essid(struct net_device *dev,
8987 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008988 union iwreq_data *wrqu, char *extra)
8989{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04008990 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yiab644b02006-08-21 11:37:13 +08008991 int length;
James Ketrenos43f66a62005-03-25 12:31:53 -06008992
Zhu Yiab644b02006-08-21 11:37:13 +08008993 mutex_lock(&priv->mutex);
8994
8995 if (!wrqu->essid.flags)
8996 {
8997 IPW_DEBUG_WX("Setting ESSID to ANY\n");
8998 ipw_disassociate(priv);
8999 priv->config &= ~CFG_STATIC_ESSID;
9000 ipw_associate(priv);
9001 mutex_unlock(&priv->mutex);
9002 return 0;
9003 }
9004
Zhu Yia9f0d422006-08-21 11:37:26 +08009005 length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06009006
9007 priv->config |= CFG_STATIC_ESSID;
9008
Zhu Yia9f0d422006-08-21 11:37:26 +08009009 if (priv->essid_len == length && !memcmp(priv->essid, extra, length)
9010 && (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009011 IPW_DEBUG_WX("ESSID set to current ESSID.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009012 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009013 return 0;
9014 }
9015
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07009016 IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, extra, length);
James Ketrenos43f66a62005-03-25 12:31:53 -06009017
9018 priv->essid_len = length;
Zhu Yia9f0d422006-08-21 11:37:26 +08009019 memcpy(priv->essid, extra, priv->essid_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009020
James Ketrenosc848d0a2005-08-24 21:56:24 -05009021 /* Network configuration changed -- force [re]association */
9022 IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
9023 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06009024 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009025
Zhu Yi46441512006-01-24 16:37:59 +08009026 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009027 return 0;
9028}
9029
Jeff Garzikbf794512005-07-31 13:07:26 -04009030static int ipw_wx_get_essid(struct net_device *dev,
9031 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009032 union iwreq_data *wrqu, char *extra)
9033{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009034 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009035
9036 /* If we are associated, trying to associate, or have a statically
9037 * configured ESSID then return that; otherwise return ANY */
Zhu Yi46441512006-01-24 16:37:59 +08009038 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009039 if (priv->config & CFG_STATIC_ESSID ||
Jeff Garzikbf794512005-07-31 13:07:26 -04009040 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
Andy Shevchenko4b4890c2014-10-13 15:55:22 -07009041 IPW_DEBUG_WX("Getting essid: '%*pE'\n",
9042 priv->essid_len, priv->essid);
Jeff Garzikbf794512005-07-31 13:07:26 -04009043 memcpy(extra, priv->essid, priv->essid_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06009044 wrqu->essid.length = priv->essid_len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009045 wrqu->essid.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06009046 } else {
9047 IPW_DEBUG_WX("Getting essid: ANY\n");
9048 wrqu->essid.length = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009049 wrqu->essid.flags = 0; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06009050 }
Zhu Yi46441512006-01-24 16:37:59 +08009051 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009052 return 0;
9053}
9054
Jeff Garzikbf794512005-07-31 13:07:26 -04009055static int ipw_wx_set_nick(struct net_device *dev,
9056 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009057 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009058{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009059 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009060
9061 IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
9062 if (wrqu->data.length > IW_ESSID_MAX_SIZE)
9063 return -E2BIG;
Zhu Yi46441512006-01-24 16:37:59 +08009064 mutex_lock(&priv->mutex);
Silvan Jegenc8e49552014-02-25 18:12:52 +01009065 wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick));
James Ketrenos43f66a62005-03-25 12:31:53 -06009066 memset(priv->nick, 0, sizeof(priv->nick));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009067 memcpy(priv->nick, extra, wrqu->data.length);
James Ketrenos43f66a62005-03-25 12:31:53 -06009068 IPW_DEBUG_TRACE("<<\n");
Zhu Yi46441512006-01-24 16:37:59 +08009069 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009070 return 0;
9071
9072}
9073
Jeff Garzikbf794512005-07-31 13:07:26 -04009074static int ipw_wx_get_nick(struct net_device *dev,
9075 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009076 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009077{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009078 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009079 IPW_DEBUG_WX("Getting nick\n");
Zhu Yi46441512006-01-24 16:37:59 +08009080 mutex_lock(&priv->mutex);
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009081 wrqu->data.length = strlen(priv->nick);
James Ketrenos43f66a62005-03-25 12:31:53 -06009082 memcpy(extra, priv->nick, wrqu->data.length);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009083 wrqu->data.flags = 1; /* active */
Zhu Yi46441512006-01-24 16:37:59 +08009084 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009085 return 0;
9086}
9087
Olivier Hochreutiner651be262006-03-08 03:13:55 +08009088static int ipw_wx_set_sens(struct net_device *dev,
9089 struct iw_request_info *info,
9090 union iwreq_data *wrqu, char *extra)
9091{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009092 struct ipw_priv *priv = libipw_priv(dev);
Olivier Hochreutiner651be262006-03-08 03:13:55 +08009093 int err = 0;
9094
9095 IPW_DEBUG_WX("Setting roaming threshold to %d\n", wrqu->sens.value);
9096 IPW_DEBUG_WX("Setting disassociate threshold to %d\n", 3*wrqu->sens.value);
9097 mutex_lock(&priv->mutex);
9098
9099 if (wrqu->sens.fixed == 0)
9100 {
9101 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
9102 priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
9103 goto out;
9104 }
9105 if ((wrqu->sens.value > IPW_MB_ROAMING_THRESHOLD_MAX) ||
9106 (wrqu->sens.value < IPW_MB_ROAMING_THRESHOLD_MIN)) {
9107 err = -EINVAL;
9108 goto out;
9109 }
9110
9111 priv->roaming_threshold = wrqu->sens.value;
9112 priv->disassociate_threshold = 3*wrqu->sens.value;
9113 out:
9114 mutex_unlock(&priv->mutex);
9115 return err;
9116}
9117
9118static int ipw_wx_get_sens(struct net_device *dev,
9119 struct iw_request_info *info,
9120 union iwreq_data *wrqu, char *extra)
9121{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009122 struct ipw_priv *priv = libipw_priv(dev);
Olivier Hochreutiner651be262006-03-08 03:13:55 +08009123 mutex_lock(&priv->mutex);
9124 wrqu->sens.fixed = 1;
9125 wrqu->sens.value = priv->roaming_threshold;
9126 mutex_unlock(&priv->mutex);
9127
Frans Pop9fd1ea42010-03-24 19:46:31 +01009128 IPW_DEBUG_WX("GET roaming threshold -> %s %d\n",
Olivier Hochreutiner651be262006-03-08 03:13:55 +08009129 wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
9130
9131 return 0;
9132}
9133
James Ketrenos43f66a62005-03-25 12:31:53 -06009134static int ipw_wx_set_rate(struct net_device *dev,
9135 struct iw_request_info *info,
9136 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009137{
James Ketrenosea2b26e2005-08-24 21:25:16 -05009138 /* TODO: We should use semaphores or locks for access to priv */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009139 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009140 u32 target_rate = wrqu->bitrate.value;
9141 u32 fixed, mask;
9142
9143 /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
9144 /* value = X, fixed = 1 means only rate X */
9145 /* value = X, fixed = 0 means all rates lower equal X */
9146
9147 if (target_rate == -1) {
9148 fixed = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009149 mask = LIBIPW_DEFAULT_RATES_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009150 /* Now we should reassociate */
9151 goto apply;
9152 }
9153
9154 mask = 0;
9155 fixed = wrqu->bitrate.fixed;
9156
9157 if (target_rate == 1000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009158 mask |= LIBIPW_CCK_RATE_1MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009159 if (target_rate == 1000000)
9160 goto apply;
9161
9162 if (target_rate == 2000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009163 mask |= LIBIPW_CCK_RATE_2MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009164 if (target_rate == 2000000)
9165 goto apply;
9166
9167 if (target_rate == 5500000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009168 mask |= LIBIPW_CCK_RATE_5MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009169 if (target_rate == 5500000)
9170 goto apply;
9171
9172 if (target_rate == 6000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009173 mask |= LIBIPW_OFDM_RATE_6MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009174 if (target_rate == 6000000)
9175 goto apply;
9176
9177 if (target_rate == 9000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009178 mask |= LIBIPW_OFDM_RATE_9MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009179 if (target_rate == 9000000)
9180 goto apply;
9181
9182 if (target_rate == 11000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009183 mask |= LIBIPW_CCK_RATE_11MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009184 if (target_rate == 11000000)
9185 goto apply;
9186
9187 if (target_rate == 12000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009188 mask |= LIBIPW_OFDM_RATE_12MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009189 if (target_rate == 12000000)
9190 goto apply;
9191
9192 if (target_rate == 18000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009193 mask |= LIBIPW_OFDM_RATE_18MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009194 if (target_rate == 18000000)
9195 goto apply;
9196
9197 if (target_rate == 24000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009198 mask |= LIBIPW_OFDM_RATE_24MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009199 if (target_rate == 24000000)
9200 goto apply;
9201
9202 if (target_rate == 36000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009203 mask |= LIBIPW_OFDM_RATE_36MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009204 if (target_rate == 36000000)
9205 goto apply;
9206
9207 if (target_rate == 48000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009208 mask |= LIBIPW_OFDM_RATE_48MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009209 if (target_rate == 48000000)
9210 goto apply;
9211
9212 if (target_rate == 54000000 || !fixed)
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009213 mask |= LIBIPW_OFDM_RATE_54MB_MASK;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009214 if (target_rate == 54000000)
9215 goto apply;
9216
9217 IPW_DEBUG_WX("invalid rate specified, returning error\n");
9218 return -EINVAL;
9219
9220 apply:
9221 IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
9222 mask, fixed ? "fixed" : "sub-rates");
Zhu Yi46441512006-01-24 16:37:59 +08009223 mutex_lock(&priv->mutex);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009224 if (mask == LIBIPW_DEFAULT_RATES_MASK) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009225 priv->config &= ~CFG_FIXED_RATE;
James Ketrenosb095c382005-08-24 22:04:42 -05009226 ipw_set_fixed_rate(priv, priv->ieee->mode);
9227 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05009228 priv->config |= CFG_FIXED_RATE;
9229
James Ketrenosc848d0a2005-08-24 21:56:24 -05009230 if (priv->rates_mask == mask) {
9231 IPW_DEBUG_WX("Mask set to current mask.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009232 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009233 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009234 }
9235
James Ketrenosc848d0a2005-08-24 21:56:24 -05009236 priv->rates_mask = mask;
9237
9238 /* Network configuration changed -- force [re]association */
9239 IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
9240 if (!ipw_disassociate(priv))
9241 ipw_associate(priv);
9242
Zhu Yi46441512006-01-24 16:37:59 +08009243 mutex_unlock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009244 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009245}
9246
Jeff Garzikbf794512005-07-31 13:07:26 -04009247static int ipw_wx_get_rate(struct net_device *dev,
9248 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009249 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009250{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009251 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009252 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009253 wrqu->bitrate.value = priv->last_rate;
Zhu Yi455936c2006-04-13 17:20:05 +08009254 wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0;
Zhu Yi46441512006-01-24 16:37:59 +08009255 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009256 IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009257 return 0;
9258}
9259
Jeff Garzikbf794512005-07-31 13:07:26 -04009260static int ipw_wx_set_rts(struct net_device *dev,
9261 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009262 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009263{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009264 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009265 mutex_lock(&priv->mutex);
Zhu Yiea8862d2007-01-11 17:32:54 +08009266 if (wrqu->rts.disabled || !wrqu->rts.fixed)
James Ketrenos43f66a62005-03-25 12:31:53 -06009267 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
9268 else {
9269 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
James Ketrenosc848d0a2005-08-24 21:56:24 -05009270 wrqu->rts.value > MAX_RTS_THRESHOLD) {
Zhu Yi46441512006-01-24 16:37:59 +08009271 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009272 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009273 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009274 priv->rts_threshold = wrqu->rts.value;
9275 }
9276
9277 ipw_send_rts_threshold(priv, priv->rts_threshold);
Zhu Yi46441512006-01-24 16:37:59 +08009278 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009279 IPW_DEBUG_WX("SET RTS Threshold -> %d\n", priv->rts_threshold);
James Ketrenos43f66a62005-03-25 12:31:53 -06009280 return 0;
9281}
9282
Jeff Garzikbf794512005-07-31 13:07:26 -04009283static int ipw_wx_get_rts(struct net_device *dev,
9284 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009285 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009286{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009287 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009288 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009289 wrqu->rts.value = priv->rts_threshold;
9290 wrqu->rts.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009291 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
Zhu Yi46441512006-01-24 16:37:59 +08009292 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009293 IPW_DEBUG_WX("GET RTS Threshold -> %d\n", wrqu->rts.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009294 return 0;
9295}
9296
Jeff Garzikbf794512005-07-31 13:07:26 -04009297static int ipw_wx_set_txpow(struct net_device *dev,
9298 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009299 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009300{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009301 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009302 int err = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009303
Zhu Yi46441512006-01-24 16:37:59 +08009304 mutex_lock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009305 if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009306 err = -EINPROGRESS;
9307 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009308 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009309
James Ketrenosb095c382005-08-24 22:04:42 -05009310 if (!wrqu->power.fixed)
9311 wrqu->power.value = IPW_TX_POWER_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06009312
James Ketrenosc848d0a2005-08-24 21:56:24 -05009313 if (wrqu->power.flags != IW_TXPOW_DBM) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009314 err = -EINVAL;
9315 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009316 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009317
James Ketrenosb095c382005-08-24 22:04:42 -05009318 if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
James Ketrenosafbf30a2005-08-25 00:05:33 -05009319 (wrqu->power.value < IPW_TX_POWER_MIN)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009320 err = -EINVAL;
9321 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009322 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009323
9324 priv->tx_power = wrqu->power.value;
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009325 err = ipw_set_tx_power(priv);
9326 out:
Zhu Yi46441512006-01-24 16:37:59 +08009327 mutex_unlock(&priv->mutex);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08009328 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06009329}
9330
Jeff Garzikbf794512005-07-31 13:07:26 -04009331static int ipw_wx_get_txpow(struct net_device *dev,
9332 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009333 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009334{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009335 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009336 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009337 wrqu->power.value = priv->tx_power;
9338 wrqu->power.fixed = 1;
9339 wrqu->power.flags = IW_TXPOW_DBM;
9340 wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
Zhu Yi46441512006-01-24 16:37:59 +08009341 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009342
Frans Pop9fd1ea42010-03-24 19:46:31 +01009343 IPW_DEBUG_WX("GET TX Power -> %s %d\n",
Zhu Yi22501c82005-08-11 10:49:17 +08009344 wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009345
9346 return 0;
9347}
9348
Jeff Garzikbf794512005-07-31 13:07:26 -04009349static int ipw_wx_set_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009350 struct iw_request_info *info,
9351 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009352{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009353 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009354 mutex_lock(&priv->mutex);
Zhu Yiea8862d2007-01-11 17:32:54 +08009355 if (wrqu->frag.disabled || !wrqu->frag.fixed)
James Ketrenos43f66a62005-03-25 12:31:53 -06009356 priv->ieee->fts = DEFAULT_FTS;
9357 else {
9358 if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
James Ketrenosb095c382005-08-24 22:04:42 -05009359 wrqu->frag.value > MAX_FRAG_THRESHOLD) {
Zhu Yi46441512006-01-24 16:37:59 +08009360 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009361 return -EINVAL;
James Ketrenosb095c382005-08-24 22:04:42 -05009362 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009363
James Ketrenos43f66a62005-03-25 12:31:53 -06009364 priv->ieee->fts = wrqu->frag.value & ~0x1;
9365 }
9366
9367 ipw_send_frag_threshold(priv, wrqu->frag.value);
Zhu Yi46441512006-01-24 16:37:59 +08009368 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009369 IPW_DEBUG_WX("SET Frag Threshold -> %d\n", wrqu->frag.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009370 return 0;
9371}
9372
Jeff Garzikbf794512005-07-31 13:07:26 -04009373static int ipw_wx_get_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009374 struct iw_request_info *info,
9375 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009376{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009377 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009378 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009379 wrqu->frag.value = priv->ieee->fts;
9380 wrqu->frag.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009381 wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
Zhu Yi46441512006-01-24 16:37:59 +08009382 mutex_unlock(&priv->mutex);
Frans Pop9fd1ea42010-03-24 19:46:31 +01009383 IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06009384
9385 return 0;
9386}
9387
Jeff Garzikbf794512005-07-31 13:07:26 -04009388static int ipw_wx_set_retry(struct net_device *dev,
9389 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009390 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009391{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009392 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009393
9394 if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
9395 return -EINVAL;
9396
9397 if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
9398 return 0;
9399
Zhu Yid5f7ac22006-08-21 11:38:17 +08009400 if (wrqu->retry.value < 0 || wrqu->retry.value >= 255)
James Ketrenosafbf30a2005-08-25 00:05:33 -05009401 return -EINVAL;
9402
Zhu Yi46441512006-01-24 16:37:59 +08009403 mutex_lock(&priv->mutex);
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009404 if (wrqu->retry.flags & IW_RETRY_SHORT)
James Ketrenosafbf30a2005-08-25 00:05:33 -05009405 priv->short_retry_limit = (u8) wrqu->retry.value;
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009406 else if (wrqu->retry.flags & IW_RETRY_LONG)
James Ketrenosafbf30a2005-08-25 00:05:33 -05009407 priv->long_retry_limit = (u8) wrqu->retry.value;
9408 else {
9409 priv->short_retry_limit = (u8) wrqu->retry.value;
9410 priv->long_retry_limit = (u8) wrqu->retry.value;
9411 }
9412
9413 ipw_send_retry_limit(priv, priv->short_retry_limit,
9414 priv->long_retry_limit);
Zhu Yi46441512006-01-24 16:37:59 +08009415 mutex_unlock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009416 IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n",
9417 priv->short_retry_limit, priv->long_retry_limit);
9418 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009419}
9420
Jeff Garzikbf794512005-07-31 13:07:26 -04009421static int ipw_wx_get_retry(struct net_device *dev,
9422 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009423 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009424{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009425 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009426
Zhu Yi46441512006-01-24 16:37:59 +08009427 mutex_lock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009428 wrqu->retry.disabled = 0;
9429
9430 if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
Zhu Yi46441512006-01-24 16:37:59 +08009431 mutex_unlock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009432 return -EINVAL;
9433 }
9434
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009435 if (wrqu->retry.flags & IW_RETRY_LONG) {
9436 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009437 wrqu->retry.value = priv->long_retry_limit;
Jean Tourrilhes919ee6d2006-08-29 18:01:40 -07009438 } else if (wrqu->retry.flags & IW_RETRY_SHORT) {
9439 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009440 wrqu->retry.value = priv->short_retry_limit;
9441 } else {
9442 wrqu->retry.flags = IW_RETRY_LIMIT;
9443 wrqu->retry.value = priv->short_retry_limit;
9444 }
Zhu Yi46441512006-01-24 16:37:59 +08009445 mutex_unlock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009446
Frans Pop9fd1ea42010-03-24 19:46:31 +01009447 IPW_DEBUG_WX("GET retry -> %d\n", wrqu->retry.value);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009448
9449 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009450}
9451
Jeff Garzikbf794512005-07-31 13:07:26 -04009452static int ipw_wx_set_scan(struct net_device *dev,
9453 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009454 union iwreq_data *wrqu, char *extra)
9455{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009456 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi094c4d22006-08-21 11:39:03 +08009457 struct iw_scan_req *req = (struct iw_scan_req *)extra;
Dan Williamsea177302008-06-02 17:51:23 -04009458 struct delayed_work *work = NULL;
Zhu Yi094c4d22006-08-21 11:39:03 +08009459
Dan Williams0b531672007-10-09 13:55:24 -04009460 mutex_lock(&priv->mutex);
Dan Williamsea177302008-06-02 17:51:23 -04009461
Dan Williams0b531672007-10-09 13:55:24 -04009462 priv->user_requested_scan = 1;
Dan Williams0b531672007-10-09 13:55:24 -04009463
Zhu Yi094c4d22006-08-21 11:39:03 +08009464 if (wrqu->data.length == sizeof(struct iw_scan_req)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05009465 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
Dan Williamsea177302008-06-02 17:51:23 -04009466 int len = min((int)req->essid_len,
9467 (int)sizeof(priv->direct_scan_ssid));
9468 memcpy(priv->direct_scan_ssid, req->essid, len);
9469 priv->direct_scan_ssid_len = len;
9470 work = &priv->request_direct_scan;
9471 } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
9472 work = &priv->request_passive_scan;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009473 }
Dan Williamsea177302008-06-02 17:51:23 -04009474 } else {
9475 /* Normal active broadcast scan */
9476 work = &priv->request_scan;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009477 }
James Ketrenos8935f392005-10-05 15:59:08 -05009478
Dan Williamsea177302008-06-02 17:51:23 -04009479 mutex_unlock(&priv->mutex);
9480
James Ketrenos43f66a62005-03-25 12:31:53 -06009481 IPW_DEBUG_WX("Start scan\n");
James Ketrenosb095c382005-08-24 22:04:42 -05009482
Tejun Heobcb6d912011-01-26 12:12:50 +01009483 schedule_delayed_work(work, 0);
James Ketrenosb095c382005-08-24 22:04:42 -05009484
James Ketrenos43f66a62005-03-25 12:31:53 -06009485 return 0;
9486}
9487
Jeff Garzikbf794512005-07-31 13:07:26 -04009488static int ipw_wx_get_scan(struct net_device *dev,
9489 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009490 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009491{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009492 struct ipw_priv *priv = libipw_priv(dev);
9493 return libipw_wx_get_scan(priv->ieee, info, wrqu, extra);
James Ketrenos43f66a62005-03-25 12:31:53 -06009494}
9495
Jeff Garzikbf794512005-07-31 13:07:26 -04009496static int ipw_wx_set_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009497 struct iw_request_info *info,
9498 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009499{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009500 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009501 int ret;
Hong Liucaeff812005-08-05 17:25:50 +08009502 u32 cap = priv->capability;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009503
Zhu Yi46441512006-01-24 16:37:59 +08009504 mutex_lock(&priv->mutex);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009505 ret = libipw_wx_set_encode(priv->ieee, info, wrqu, key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009506
Hong Liucaeff812005-08-05 17:25:50 +08009507 /* In IBSS mode, we need to notify the firmware to update
9508 * the beacon info after we changed the capability. */
9509 if (cap != priv->capability &&
9510 priv->ieee->iw_mode == IW_MODE_ADHOC &&
9511 priv->status & STATUS_ASSOCIATED)
9512 ipw_disassociate(priv);
9513
Zhu Yi46441512006-01-24 16:37:59 +08009514 mutex_unlock(&priv->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009515 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009516}
9517
Jeff Garzikbf794512005-07-31 13:07:26 -04009518static int ipw_wx_get_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009519 struct iw_request_info *info,
9520 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009521{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009522 struct ipw_priv *priv = libipw_priv(dev);
9523 return libipw_wx_get_encode(priv->ieee, info, wrqu, key);
James Ketrenos43f66a62005-03-25 12:31:53 -06009524}
9525
Jeff Garzikbf794512005-07-31 13:07:26 -04009526static int ipw_wx_set_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009527 struct iw_request_info *info,
9528 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009529{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009530 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009531 int err;
Zhu Yi46441512006-01-24 16:37:59 +08009532 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009533 if (wrqu->power.disabled) {
9534 priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
9535 err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
9536 if (err) {
9537 IPW_DEBUG_WX("failed setting power mode.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009538 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009539 return err;
9540 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009541 IPW_DEBUG_WX("SET Power Management Mode -> off\n");
Zhu Yi46441512006-01-24 16:37:59 +08009542 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009543 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009544 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009545
9546 switch (wrqu->power.flags & IW_POWER_MODE) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009547 case IW_POWER_ON: /* If not specified */
9548 case IW_POWER_MODE: /* If set all mask */
Jean Delvarec03983a2007-10-19 23:22:55 +02009549 case IW_POWER_ALL_R: /* If explicitly state all */
James Ketrenos43f66a62005-03-25 12:31:53 -06009550 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009551 default: /* Otherwise we don't support it */
James Ketrenos43f66a62005-03-25 12:31:53 -06009552 IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
9553 wrqu->power.flags);
Zhu Yi46441512006-01-24 16:37:59 +08009554 mutex_unlock(&priv->mutex);
Jeff Garzikbf794512005-07-31 13:07:26 -04009555 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06009556 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009557
James Ketrenos43f66a62005-03-25 12:31:53 -06009558 /* If the user hasn't specified a power management mode yet, default
9559 * to BATTERY */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009560 if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
James Ketrenos43f66a62005-03-25 12:31:53 -06009561 priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
Jeff Garzikbf794512005-07-31 13:07:26 -04009562 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009563 priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
Zhu Yi4e157f02007-07-12 16:09:32 +08009564
James Ketrenos43f66a62005-03-25 12:31:53 -06009565 err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
9566 if (err) {
9567 IPW_DEBUG_WX("failed setting power mode.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009568 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009569 return err;
9570 }
9571
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009572 IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
Zhu Yi46441512006-01-24 16:37:59 +08009573 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009574 return 0;
9575}
9576
Jeff Garzikbf794512005-07-31 13:07:26 -04009577static int ipw_wx_get_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009578 struct iw_request_info *info,
9579 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009580{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009581 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009582 mutex_lock(&priv->mutex);
James Ketrenosa613bff2005-08-24 21:43:11 -05009583 if (!(priv->power_mode & IPW_POWER_ENABLED))
James Ketrenos43f66a62005-03-25 12:31:53 -06009584 wrqu->power.disabled = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -05009585 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009586 wrqu->power.disabled = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009587
Zhu Yi46441512006-01-24 16:37:59 +08009588 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009589 IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04009590
James Ketrenos43f66a62005-03-25 12:31:53 -06009591 return 0;
9592}
9593
Jeff Garzikbf794512005-07-31 13:07:26 -04009594static int ipw_wx_set_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009595 struct iw_request_info *info,
9596 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009597{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009598 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009599 int mode = *(int *)extra;
9600 int err;
Zhu Yi4e157f02007-07-12 16:09:32 +08009601
Zhu Yi46441512006-01-24 16:37:59 +08009602 mutex_lock(&priv->mutex);
Zhu Yi4e157f02007-07-12 16:09:32 +08009603 if ((mode < 1) || (mode > IPW_POWER_LIMIT))
James Ketrenos43f66a62005-03-25 12:31:53 -06009604 mode = IPW_POWER_AC;
Jeff Garzikbf794512005-07-31 13:07:26 -04009605
Zhu Yi4e157f02007-07-12 16:09:32 +08009606 if (IPW_POWER_LEVEL(priv->power_mode) != mode) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009607 err = ipw_send_power_mode(priv, mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06009608 if (err) {
9609 IPW_DEBUG_WX("failed setting power mode.\n");
Zhu Yi46441512006-01-24 16:37:59 +08009610 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009611 return err;
9612 }
Zhu Yi4e157f02007-07-12 16:09:32 +08009613 priv->power_mode = IPW_POWER_ENABLED | mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06009614 }
Zhu Yi46441512006-01-24 16:37:59 +08009615 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009616 return 0;
9617}
9618
9619#define MAX_WX_STRING 80
Jeff Garzikbf794512005-07-31 13:07:26 -04009620static int ipw_wx_get_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009621 struct iw_request_info *info,
9622 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009623{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009624 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009625 int level = IPW_POWER_LEVEL(priv->power_mode);
9626 char *p = extra;
9627
9628 p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
9629
9630 switch (level) {
9631 case IPW_POWER_AC:
9632 p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
9633 break;
9634 case IPW_POWER_BATTERY:
9635 p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
9636 break;
9637 default:
9638 p += snprintf(p, MAX_WX_STRING - (p - extra),
Jeff Garzikbf794512005-07-31 13:07:26 -04009639 "(Timeout %dms, Period %dms)",
James Ketrenos43f66a62005-03-25 12:31:53 -06009640 timeout_duration[level - 1] / 1000,
9641 period_duration[level - 1] / 1000);
9642 }
9643
9644 if (!(priv->power_mode & IPW_POWER_ENABLED))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009645 p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
James Ketrenos43f66a62005-03-25 12:31:53 -06009646
9647 wrqu->data.length = p - extra + 1;
9648
9649 return 0;
9650}
9651
9652static int ipw_wx_set_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009653 struct iw_request_info *info,
9654 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009655{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009656 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009657 int mode = *(int *)extra;
9658 u8 band = 0, modulation = 0;
9659
9660 if (mode == 0 || mode & ~IEEE_MODE_MASK) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009661 IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06009662 return -EINVAL;
9663 }
Zhu Yi46441512006-01-24 16:37:59 +08009664 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009665 if (priv->adapter == IPW_2915ABG) {
James Ketrenosa33a1982005-09-14 14:28:59 -05009666 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06009667 if (mode & IEEE_A) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009668 band |= LIBIPW_52GHZ_BAND;
9669 modulation |= LIBIPW_OFDM_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06009670 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009671 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009672 } else {
9673 if (mode & IEEE_A) {
9674 IPW_WARNING("Attempt to set 2200BG into "
9675 "802.11a mode\n");
Zhu Yi46441512006-01-24 16:37:59 +08009676 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009677 return -EINVAL;
9678 }
9679
James Ketrenosa33a1982005-09-14 14:28:59 -05009680 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009681 }
9682
9683 if (mode & IEEE_B) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009684 band |= LIBIPW_24GHZ_BAND;
9685 modulation |= LIBIPW_CCK_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06009686 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009687 priv->ieee->abg_true = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009688
James Ketrenos43f66a62005-03-25 12:31:53 -06009689 if (mode & IEEE_G) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009690 band |= LIBIPW_24GHZ_BAND;
9691 modulation |= LIBIPW_OFDM_MODULATION;
James Ketrenos43f66a62005-03-25 12:31:53 -06009692 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009693 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009694
9695 priv->ieee->mode = mode;
9696 priv->ieee->freq_band = band;
9697 priv->ieee->modulation = modulation;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009698 init_supported_rates(priv, &priv->rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06009699
James Ketrenosc848d0a2005-08-24 21:56:24 -05009700 /* Network configuration changed -- force [re]association */
9701 IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
9702 if (!ipw_disassociate(priv)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009703 ipw_send_supported_rates(priv, &priv->rates);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009704 ipw_associate(priv);
9705 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009706
James Ketrenosa613bff2005-08-24 21:43:11 -05009707 /* Update the band LEDs */
9708 ipw_led_band_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009709
Jeff Garzikbf794512005-07-31 13:07:26 -04009710 IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009711 mode & IEEE_A ? 'a' : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009712 mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
Zhu Yi46441512006-01-24 16:37:59 +08009713 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009714 return 0;
9715}
9716
9717static int ipw_wx_get_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009718 struct iw_request_info *info,
9719 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009720{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009721 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009722 mutex_lock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009723 switch (priv->ieee->mode) {
9724 case IEEE_A:
James Ketrenos43f66a62005-03-25 12:31:53 -06009725 strncpy(extra, "802.11a (1)", MAX_WX_STRING);
9726 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009727 case IEEE_B:
9728 strncpy(extra, "802.11b (2)", MAX_WX_STRING);
9729 break;
9730 case IEEE_A | IEEE_B:
9731 strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
9732 break;
9733 case IEEE_G:
9734 strncpy(extra, "802.11g (4)", MAX_WX_STRING);
9735 break;
9736 case IEEE_A | IEEE_G:
9737 strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
9738 break;
9739 case IEEE_B | IEEE_G:
9740 strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
9741 break;
9742 case IEEE_A | IEEE_B | IEEE_G:
9743 strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
9744 break;
9745 default:
9746 strncpy(extra, "unknown", MAX_WX_STRING);
James Ketrenos43f66a62005-03-25 12:31:53 -06009747 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04009748 }
Rickard Strandqvist2da2c582014-08-10 01:39:28 +02009749 extra[MAX_WX_STRING - 1] = '\0';
Jeff Garzikbf794512005-07-31 13:07:26 -04009750
James Ketrenos43f66a62005-03-25 12:31:53 -06009751 IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
9752
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009753 wrqu->data.length = strlen(extra) + 1;
Zhu Yi46441512006-01-24 16:37:59 +08009754 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009755
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009756 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009757}
9758
James Ketrenosea2b26e2005-08-24 21:25:16 -05009759static int ipw_wx_set_preamble(struct net_device *dev,
9760 struct iw_request_info *info,
9761 union iwreq_data *wrqu, char *extra)
9762{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009763 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009764 int mode = *(int *)extra;
Zhu Yi46441512006-01-24 16:37:59 +08009765 mutex_lock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009766 /* Switching from SHORT -> LONG requires a disassociation */
9767 if (mode == 1) {
9768 if (!(priv->config & CFG_PREAMBLE_LONG)) {
9769 priv->config |= CFG_PREAMBLE_LONG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009770
9771 /* Network configuration changed -- force [re]association */
9772 IPW_DEBUG_ASSOC
9773 ("[re]association triggered due to preamble change.\n");
9774 if (!ipw_disassociate(priv))
9775 ipw_associate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009776 }
9777 goto done;
9778 }
9779
9780 if (mode == 0) {
9781 priv->config &= ~CFG_PREAMBLE_LONG;
9782 goto done;
9783 }
Zhu Yi46441512006-01-24 16:37:59 +08009784 mutex_unlock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009785 return -EINVAL;
9786
9787 done:
Zhu Yi46441512006-01-24 16:37:59 +08009788 mutex_unlock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009789 return 0;
9790}
9791
9792static int ipw_wx_get_preamble(struct net_device *dev,
9793 struct iw_request_info *info,
9794 union iwreq_data *wrqu, char *extra)
9795{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009796 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yi46441512006-01-24 16:37:59 +08009797 mutex_lock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009798 if (priv->config & CFG_PREAMBLE_LONG)
9799 snprintf(wrqu->name, IFNAMSIZ, "long (1)");
9800 else
9801 snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
Zhu Yi46441512006-01-24 16:37:59 +08009802 mutex_unlock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009803 return 0;
9804}
9805
James Ketrenosb095c382005-08-24 22:04:42 -05009806#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05009807static int ipw_wx_set_monitor(struct net_device *dev,
Jeff Garzikbf794512005-07-31 13:07:26 -04009808 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009809 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009810{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009811 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009812 int *parms = (int *)extra;
9813 int enable = (parms[0] > 0);
Zhu Yi46441512006-01-24 16:37:59 +08009814 mutex_lock(&priv->mutex);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009815 IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
James Ketrenos43f66a62005-03-25 12:31:53 -06009816 if (enable) {
9817 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
Zhu Yi459d4082006-04-13 17:21:00 +08009818#ifdef CONFIG_IPW2200_RADIOTAP
Mike Kershaw24a47db2005-08-26 00:41:54 -05009819 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
9820#else
James Ketrenos43f66a62005-03-25 12:31:53 -06009821 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05009822#endif
Tejun Heobcb6d912011-01-26 12:12:50 +01009823 schedule_work(&priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009824 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009825
James Ketrenos43f66a62005-03-25 12:31:53 -06009826 ipw_set_channel(priv, parms[1]);
9827 } else {
James Ketrenosc848d0a2005-08-24 21:56:24 -05009828 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
Zhu Yi46441512006-01-24 16:37:59 +08009829 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009830 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009831 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009832 priv->net_dev->type = ARPHRD_ETHER;
Tejun Heobcb6d912011-01-26 12:12:50 +01009833 schedule_work(&priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009834 }
Zhu Yi46441512006-01-24 16:37:59 +08009835 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -06009836 return 0;
9837}
9838
Pavel Machek67fd6b42006-07-11 15:34:05 +02009839#endif /* CONFIG_IPW2200_MONITOR */
James Ketrenosb095c382005-08-24 22:04:42 -05009840
Jeff Garzikbf794512005-07-31 13:07:26 -04009841static int ipw_wx_reset(struct net_device *dev,
9842 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009843 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009844{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009845 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009846 IPW_DEBUG_WX("RESET\n");
Tejun Heobcb6d912011-01-26 12:12:50 +01009847 schedule_work(&priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009848 return 0;
9849}
James Ketrenosb095c382005-08-24 22:04:42 -05009850
James Ketrenosb095c382005-08-24 22:04:42 -05009851static int ipw_wx_sw_reset(struct net_device *dev,
9852 struct iw_request_info *info,
9853 union iwreq_data *wrqu, char *extra)
9854{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009855 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenosb095c382005-08-24 22:04:42 -05009856 union iwreq_data wrqu_sec = {
9857 .encoding = {
9858 .flags = IW_ENCODE_DISABLED,
9859 },
9860 };
James Ketrenosafbf30a2005-08-25 00:05:33 -05009861 int ret;
James Ketrenosb095c382005-08-24 22:04:42 -05009862
9863 IPW_DEBUG_WX("SW_RESET\n");
9864
Zhu Yi46441512006-01-24 16:37:59 +08009865 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05009866
Zhu Yid6d5b5c2006-02-16 16:21:09 +08009867 ret = ipw_sw_reset(priv, 2);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009868 if (!ret) {
9869 free_firmware();
9870 ipw_adapter_restart(priv);
9871 }
James Ketrenosb095c382005-08-24 22:04:42 -05009872
9873 /* The SW reset bit might have been toggled on by the 'disable'
9874 * module parameter, so take appropriate action */
9875 ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
9876
Zhu Yi46441512006-01-24 16:37:59 +08009877 mutex_unlock(&priv->mutex);
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -04009878 libipw_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL);
Zhu Yi46441512006-01-24 16:37:59 +08009879 mutex_lock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05009880
9881 if (!(priv->status & STATUS_RF_KILL_MASK)) {
9882 /* Configuration likely changed -- force [re]association */
9883 IPW_DEBUG_ASSOC("[re]association triggered due to sw "
9884 "reset.\n");
9885 if (!ipw_disassociate(priv))
9886 ipw_associate(priv);
9887 }
9888
Zhu Yi46441512006-01-24 16:37:59 +08009889 mutex_unlock(&priv->mutex);
James Ketrenosb095c382005-08-24 22:04:42 -05009890
9891 return 0;
9892}
James Ketrenos43f66a62005-03-25 12:31:53 -06009893
9894/* Rebase the WE IOCTLs to zero for the handler array */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009895static iw_handler ipw_wx_handlers[] = {
Joe Perches56b632e2010-03-18 18:29:38 -07009896 IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname),
9897 IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq),
9898 IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq),
9899 IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode),
9900 IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode),
9901 IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens),
9902 IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens),
9903 IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range),
9904 IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap),
9905 IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap),
9906 IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan),
9907 IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan),
9908 IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid),
9909 IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid),
9910 IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick),
9911 IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick),
9912 IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate),
9913 IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate),
9914 IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts),
9915 IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts),
9916 IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag),
9917 IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag),
9918 IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow),
9919 IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow),
9920 IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry),
9921 IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry),
9922 IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode),
9923 IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode),
9924 IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power),
9925 IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power),
9926 IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
9927 IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
9928 IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
9929 IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
9930 IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie),
9931 IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie),
9932 IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme),
9933 IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth),
9934 IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth),
9935 IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext),
9936 IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext),
James Ketrenos43f66a62005-03-25 12:31:53 -06009937};
9938
James Ketrenosb095c382005-08-24 22:04:42 -05009939enum {
9940 IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV,
9941 IPW_PRIV_GET_POWER,
9942 IPW_PRIV_SET_MODE,
9943 IPW_PRIV_GET_MODE,
9944 IPW_PRIV_SET_PREAMBLE,
9945 IPW_PRIV_GET_PREAMBLE,
9946 IPW_PRIV_RESET,
9947 IPW_PRIV_SW_RESET,
9948#ifdef CONFIG_IPW2200_MONITOR
9949 IPW_PRIV_SET_MONITOR,
9950#endif
9951};
James Ketrenos43f66a62005-03-25 12:31:53 -06009952
Jeff Garzikbf794512005-07-31 13:07:26 -04009953static struct iw_priv_args ipw_priv_args[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -06009954 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009955 .cmd = IPW_PRIV_SET_POWER,
9956 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9957 .name = "set_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009958 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009959 .cmd = IPW_PRIV_GET_POWER,
9960 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9961 .name = "get_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009962 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009963 .cmd = IPW_PRIV_SET_MODE,
9964 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9965 .name = "set_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009966 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009967 .cmd = IPW_PRIV_GET_MODE,
9968 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9969 .name = "get_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009970 {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009971 .cmd = IPW_PRIV_SET_PREAMBLE,
9972 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9973 .name = "set_preamble"},
9974 {
9975 .cmd = IPW_PRIV_GET_PREAMBLE,
9976 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
9977 .name = "get_preamble"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009978 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009979 IPW_PRIV_RESET,
9980 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
James Ketrenosb095c382005-08-24 22:04:42 -05009981 {
9982 IPW_PRIV_SW_RESET,
9983 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"},
9984#ifdef CONFIG_IPW2200_MONITOR
9985 {
9986 IPW_PRIV_SET_MONITOR,
9987 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
9988#endif /* CONFIG_IPW2200_MONITOR */
James Ketrenos43f66a62005-03-25 12:31:53 -06009989};
9990
9991static iw_handler ipw_priv_handler[] = {
9992 ipw_wx_set_powermode,
9993 ipw_wx_get_powermode,
9994 ipw_wx_set_wireless_mode,
9995 ipw_wx_get_wireless_mode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05009996 ipw_wx_set_preamble,
9997 ipw_wx_get_preamble,
Jeff Garzikbf794512005-07-31 13:07:26 -04009998 ipw_wx_reset,
James Ketrenosb095c382005-08-24 22:04:42 -05009999 ipw_wx_sw_reset,
10000#ifdef CONFIG_IPW2200_MONITOR
10001 ipw_wx_set_monitor,
James Ketrenos43f66a62005-03-25 12:31:53 -060010002#endif
10003};
10004
Bhumika Goyal2c1dca32017-08-23 18:06:42 +053010005static const struct iw_handler_def ipw_wx_handler_def = {
James Ketrenosea2b26e2005-08-24 21:25:16 -050010006 .standard = ipw_wx_handlers,
10007 .num_standard = ARRAY_SIZE(ipw_wx_handlers),
10008 .num_private = ARRAY_SIZE(ipw_priv_handler),
10009 .num_private_args = ARRAY_SIZE(ipw_priv_args),
10010 .private = ipw_priv_handler,
10011 .private_args = ipw_priv_args,
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000010012 .get_wireless_stats = ipw_get_wireless_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -060010013};
10014
James Ketrenos43f66a62005-03-25 12:31:53 -060010015/*
10016 * Get wireless statistics.
10017 * Called by /proc/net/wireless
10018 * Also called by SIOCGIWSTATS
10019 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010020static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
James Ketrenos43f66a62005-03-25 12:31:53 -060010021{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010022 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010023 struct iw_statistics *wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -040010024
James Ketrenos43f66a62005-03-25 12:31:53 -060010025 wstats = &priv->wstats;
10026
James Ketrenosea2b26e2005-08-24 21:25:16 -050010027 /* if hw is disabled, then ipw_get_ordinal() can't be called.
James Ketrenosafbf30a2005-08-25 00:05:33 -050010028 * netdev->get_wireless_stats seems to be called before fw is
James Ketrenos43f66a62005-03-25 12:31:53 -060010029 * initialized. STATUS_ASSOCIATED will only be set if the hw is up
10030 * and associated; if not associcated, the values are all meaningless
10031 * anyway, so set them all to NULL and INVALID */
10032 if (!(priv->status & STATUS_ASSOCIATED)) {
10033 wstats->miss.beacon = 0;
10034 wstats->discard.retries = 0;
10035 wstats->qual.qual = 0;
10036 wstats->qual.level = 0;
10037 wstats->qual.noise = 0;
10038 wstats->qual.updated = 7;
10039 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010040 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
James Ketrenos43f66a62005-03-25 12:31:53 -060010041 return wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -040010042 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010043
10044 wstats->qual.qual = priv->quality;
Zhu Yi00d21de2006-04-13 17:19:02 +080010045 wstats->qual.level = priv->exp_avg_rssi;
10046 wstats->qual.noise = priv->exp_avg_noise;
James Ketrenos43f66a62005-03-25 12:31:53 -060010047 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
Bill Mossb1916082006-02-15 08:50:18 +080010048 IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM;
James Ketrenos43f66a62005-03-25 12:31:53 -060010049
10050 wstats->miss.beacon = average_value(&priv->average_missed_beacons);
10051 wstats->discard.retries = priv->last_tx_failures;
10052 wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
Jeff Garzikbf794512005-07-31 13:07:26 -040010053
James Ketrenos43f66a62005-03-25 12:31:53 -060010054/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
10055 goto fail_get_ordinal;
10056 wstats->discard.retries += tx_retry; */
Jeff Garzikbf794512005-07-31 13:07:26 -040010057
James Ketrenos43f66a62005-03-25 12:31:53 -060010058 return wstats;
10059}
10060
James Ketrenos43f66a62005-03-25 12:31:53 -060010061/* net device stuff */
10062
Arjan van de Ven858119e2006-01-14 13:20:43 -080010063static void init_sys_config(struct ipw_sys_config *sys_config)
James Ketrenos43f66a62005-03-25 12:31:53 -060010064{
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010065 memset(sys_config, 0, sizeof(struct ipw_sys_config));
Zhu Yi810dabd2006-01-24 16:36:59 +080010066 sys_config->bt_coexistence = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010067 sys_config->answer_broadcast_ssid_probe = 0;
10068 sys_config->accept_all_data_frames = 0;
10069 sys_config->accept_non_directed_frames = 1;
10070 sys_config->exclude_unicast_unencrypted = 0;
10071 sys_config->disable_unicast_decryption = 1;
10072 sys_config->exclude_multicast_unencrypted = 0;
10073 sys_config->disable_multicast_decryption = 1;
Zhu Yid2b83e12006-04-13 17:19:36 +080010074 if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B)
10075 antenna = CFG_SYS_ANTENNA_BOTH;
10076 sys_config->antenna_diversity = antenna;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010077 sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010078 sys_config->dot11g_auto_detection = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -040010079 sys_config->enable_cts_to_self = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010080 sys_config->bt_coexist_collision_thr = 0;
Pavel Machek67fd6b42006-07-11 15:34:05 +020010081 sys_config->pass_noise_stats_to_host = 1; /* 1 -- fix for 256 */
Cahill, Ben M12977152006-03-08 02:58:02 +080010082 sys_config->silence_threshold = 0x1e;
James Ketrenos43f66a62005-03-25 12:31:53 -060010083}
10084
10085static int ipw_net_open(struct net_device *dev)
10086{
James Ketrenos43f66a62005-03-25 12:31:53 -060010087 IPW_DEBUG_INFO("dev->open\n");
David S. Miller521c4d92008-07-22 18:32:47 -070010088 netif_start_queue(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010089 return 0;
10090}
10091
10092static int ipw_net_stop(struct net_device *dev)
10093{
10094 IPW_DEBUG_INFO("dev->close\n");
10095 netif_stop_queue(dev);
10096 return 0;
10097}
10098
10099/*
10100todo:
10101
10102modify to send one tfd per fragment instead of using chunking. otherwise
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010103we need to heavily modify the libipw_skb_to_txb.
James Ketrenos43f66a62005-03-25 12:31:53 -060010104*/
10105
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010106static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb,
James Ketrenos227d2dc2005-07-28 16:25:55 -050010107 int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -060010108{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010109 struct libipw_hdr_3addrqos *hdr = (struct libipw_hdr_3addrqos *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010110 txb->fragments[0]->data;
James Ketrenos43f66a62005-03-25 12:31:53 -060010111 int i = 0;
10112 struct tfd_frame *tfd;
Zhu Yie43e3c12006-04-13 17:20:45 +080010113#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -050010114 int tx_id = ipw_get_tx_queue_number(priv, pri);
10115 struct clx2_tx_queue *txq = &priv->txq[tx_id];
10116#else
James Ketrenos43f66a62005-03-25 12:31:53 -060010117 struct clx2_tx_queue *txq = &priv->txq[0];
James Ketrenosb095c382005-08-24 22:04:42 -050010118#endif
James Ketrenos43f66a62005-03-25 12:31:53 -060010119 struct clx2_queue *q = &txq->q;
10120 u8 id, hdr_len, unicast;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010121 int fc;
James Ketrenos43f66a62005-03-25 12:31:53 -060010122
Zhu Yib8ddafd2008-11-27 13:42:20 +080010123 if (!(priv->status & STATUS_ASSOCIATED))
10124 goto drop;
10125
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010126 hdr_len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
James Ketrenos43f66a62005-03-25 12:31:53 -060010127 switch (priv->ieee->iw_mode) {
10128 case IW_MODE_ADHOC:
Stephen Hemminger3c190652006-01-03 15:27:38 -080010129 unicast = !is_multicast_ether_addr(hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -060010130 id = ipw_find_station(priv, hdr->addr1);
10131 if (id == IPW_INVALID_STATION) {
10132 id = ipw_add_station(priv, hdr->addr1);
10133 if (id == IPW_INVALID_STATION) {
10134 IPW_WARNING("Attempt to send data to "
Johannes Berge1749612008-10-27 15:59:26 -070010135 "invalid cell: %pM\n",
10136 hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -060010137 goto drop;
10138 }
10139 }
10140 break;
10141
10142 case IW_MODE_INFRA:
10143 default:
Stephen Hemminger3c190652006-01-03 15:27:38 -080010144 unicast = !is_multicast_ether_addr(hdr->addr3);
James Ketrenos43f66a62005-03-25 12:31:53 -060010145 id = 0;
10146 break;
10147 }
10148
10149 tfd = &txq->bd[q->first_empty];
10150 txq->txb[q->first_empty] = txb;
10151 memset(tfd, 0, sizeof(*tfd));
10152 tfd->u.data.station_number = id;
10153
10154 tfd->control_flags.message_type = TX_FRAME_TYPE;
10155 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
10156
10157 tfd->u.data.cmd_id = DINO_CMD_TX;
James Ketrenosa613bff2005-08-24 21:43:11 -050010158 tfd->u.data.len = cpu_to_le16(txb->payload_size);
Jeff Garzikbf794512005-07-31 13:07:26 -040010159
James Ketrenos43f66a62005-03-25 12:31:53 -060010160 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
James Ketrenosb095c382005-08-24 22:04:42 -050010161 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK;
James Ketrenos43f66a62005-03-25 12:31:53 -060010162 else
James Ketrenosb095c382005-08-24 22:04:42 -050010163 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM;
James Ketrenos43f66a62005-03-25 12:31:53 -060010164
James Ketrenosea2b26e2005-08-24 21:25:16 -050010165 if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
10166 tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010167
James Ketrenosc848d0a2005-08-24 21:56:24 -050010168 fc = le16_to_cpu(hdr->frame_ctl);
10169 hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
James Ketrenos43f66a62005-03-25 12:31:53 -060010170
10171 memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
10172
James Ketrenosb095c382005-08-24 22:04:42 -050010173 if (likely(unicast))
10174 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
10175
10176 if (txb->encrypted && !priv->ieee->host_encrypt) {
10177 switch (priv->ieee->sec.level) {
10178 case SEC_LEVEL_3:
10179 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
Zhu Yi851ca262006-08-21 11:37:58 +080010180 cpu_to_le16(IEEE80211_FCTL_PROTECTED);
James Ketrenosb095c382005-08-24 22:04:42 -050010181 /* XXX: ACK flag must be set for CCMP even if it
10182 * is a multicast/broadcast packet, because CCMP
10183 * group communication encrypted by GTK is
10184 * actually done by the AP. */
10185 if (!unicast)
10186 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
10187
10188 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
10189 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM;
10190 tfd->u.data.key_index = 0;
10191 tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE;
10192 break;
10193 case SEC_LEVEL_2:
10194 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
Zhu Yi851ca262006-08-21 11:37:58 +080010195 cpu_to_le16(IEEE80211_FCTL_PROTECTED);
James Ketrenosb095c382005-08-24 22:04:42 -050010196 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
10197 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP;
10198 tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE;
10199 break;
10200 case SEC_LEVEL_1:
10201 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
Zhu Yi851ca262006-08-21 11:37:58 +080010202 cpu_to_le16(IEEE80211_FCTL_PROTECTED);
John W. Linville274bfb82008-10-29 11:35:05 -040010203 tfd->u.data.key_index = priv->ieee->crypt_info.tx_keyidx;
10204 if (priv->ieee->sec.key_sizes[priv->ieee->crypt_info.tx_keyidx] <=
James Ketrenosb095c382005-08-24 22:04:42 -050010205 40)
10206 tfd->u.data.key_index |= DCT_WEP_KEY_64Bit;
10207 else
10208 tfd->u.data.key_index |= DCT_WEP_KEY_128Bit;
10209 break;
10210 case SEC_LEVEL_0:
10211 break;
10212 default:
André Goddard Rosaaf901ca2009-11-14 13:09:05 -020010213 printk(KERN_ERR "Unknown security level %d\n",
James Ketrenosb095c382005-08-24 22:04:42 -050010214 priv->ieee->sec.level);
10215 break;
10216 }
10217 } else
10218 /* No hardware encryption */
10219 tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
10220
Zhu Yie43e3c12006-04-13 17:20:45 +080010221#ifdef CONFIG_IPW2200_QOS
Zhu Yia5cf4fe2006-04-13 17:19:11 +080010222 if (fc & IEEE80211_STYPE_QOS_DATA)
10223 ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data));
Zhu Yie43e3c12006-04-13 17:20:45 +080010224#endif /* CONFIG_IPW2200_QOS */
James Ketrenosb095c382005-08-24 22:04:42 -050010225
James Ketrenos43f66a62005-03-25 12:31:53 -060010226 /* payload */
James Ketrenosa613bff2005-08-24 21:43:11 -050010227 tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
10228 txb->nr_frags));
10229 IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
10230 txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
10231 for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
10232 IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
10233 i, le32_to_cpu(tfd->u.data.num_chunks),
10234 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -040010235 IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
James Ketrenos43f66a62005-03-25 12:31:53 -060010236 i, tfd->u.data.num_chunks,
10237 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -040010238 printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
James Ketrenos43f66a62005-03-25 12:31:53 -060010239 txb->fragments[i]->len - hdr_len);
10240
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010241 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -050010242 cpu_to_le32(pci_map_single
10243 (priv->pci_dev,
10244 txb->fragments[i]->data + hdr_len,
10245 txb->fragments[i]->len - hdr_len,
10246 PCI_DMA_TODEVICE));
10247 tfd->u.data.chunk_len[i] =
10248 cpu_to_le16(txb->fragments[i]->len - hdr_len);
James Ketrenos43f66a62005-03-25 12:31:53 -060010249 }
10250
10251 if (i != txb->nr_frags) {
10252 struct sk_buff *skb;
10253 u16 remaining_bytes = 0;
10254 int j;
10255
10256 for (j = i; j < txb->nr_frags; j++)
10257 remaining_bytes += txb->fragments[j]->len - hdr_len;
10258
10259 printk(KERN_INFO "Trying to reallocate for %d bytes\n",
10260 remaining_bytes);
10261 skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
10262 if (skb != NULL) {
James Ketrenosa613bff2005-08-24 21:43:11 -050010263 tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
James Ketrenos43f66a62005-03-25 12:31:53 -060010264 for (j = i; j < txb->nr_frags; j++) {
10265 int size = txb->fragments[j]->len - hdr_len;
James Ketrenosafbf30a2005-08-25 00:05:33 -050010266
James Ketrenos43f66a62005-03-25 12:31:53 -060010267 printk(KERN_INFO "Adding frag %d %d...\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010268 j, size);
Johannes Berg59ae1d12017-06-16 14:29:20 +020010269 skb_put_data(skb,
10270 txb->fragments[j]->data + hdr_len,
10271 size);
James Ketrenos43f66a62005-03-25 12:31:53 -060010272 }
10273 dev_kfree_skb_any(txb->fragments[i]);
10274 txb->fragments[i] = skb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010275 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -050010276 cpu_to_le32(pci_map_single
10277 (priv->pci_dev, skb->data,
Al Viro49587302007-12-27 01:57:47 -050010278 remaining_bytes,
James Ketrenosa613bff2005-08-24 21:43:11 -050010279 PCI_DMA_TODEVICE));
10280
Marcin Slusarz5c058632008-02-13 00:06:12 +010010281 le32_add_cpu(&tfd->u.data.num_chunks, 1);
Jeff Garzikbf794512005-07-31 13:07:26 -040010282 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010283 }
10284
10285 /* kick DMA */
10286 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
10287 ipw_write32(priv, q->reg_w, q->first_empty);
10288
Dan Williams943dbef2008-02-14 17:49:41 -050010289 if (ipw_tx_queue_space(q) < q->high_mark)
James Ketrenosf6970142006-02-14 09:10:51 +080010290 netif_stop_queue(priv->net_dev);
10291
James Ketrenos227d2dc2005-07-28 16:25:55 -050010292 return NETDEV_TX_OK;
James Ketrenos43f66a62005-03-25 12:31:53 -060010293
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010294 drop:
James Ketrenos43f66a62005-03-25 12:31:53 -060010295 IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010296 libipw_txb_free(txb);
James Ketrenos227d2dc2005-07-28 16:25:55 -050010297 return NETDEV_TX_OK;
10298}
10299
10300static int ipw_net_is_queue_full(struct net_device *dev, int pri)
10301{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010302 struct ipw_priv *priv = libipw_priv(dev);
Zhu Yie43e3c12006-04-13 17:20:45 +080010303#ifdef CONFIG_IPW2200_QOS
James Ketrenos227d2dc2005-07-28 16:25:55 -050010304 int tx_id = ipw_get_tx_queue_number(priv, pri);
10305 struct clx2_tx_queue *txq = &priv->txq[tx_id];
10306#else
10307 struct clx2_tx_queue *txq = &priv->txq[0];
Zhu Yie43e3c12006-04-13 17:20:45 +080010308#endif /* CONFIG_IPW2200_QOS */
James Ketrenos227d2dc2005-07-28 16:25:55 -050010309
Dan Williams943dbef2008-02-14 17:49:41 -050010310 if (ipw_tx_queue_space(&txq->q) < txq->q.high_mark)
James Ketrenos227d2dc2005-07-28 16:25:55 -050010311 return 1;
10312
10313 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010314}
10315
Zhu Yid685b8c2006-04-13 17:20:27 +080010316#ifdef CONFIG_IPW2200_PROMISCUOUS
10317static void ipw_handle_promiscuous_tx(struct ipw_priv *priv,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010318 struct libipw_txb *txb)
Zhu Yid685b8c2006-04-13 17:20:27 +080010319{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010320 struct libipw_rx_stats dummystats;
Zhu Yid685b8c2006-04-13 17:20:27 +080010321 struct ieee80211_hdr *hdr;
10322 u8 n;
10323 u16 filter = priv->prom_priv->filter;
10324 int hdr_only = 0;
10325
10326 if (filter & IPW_PROM_NO_TX)
10327 return;
10328
10329 memset(&dummystats, 0, sizeof(dummystats));
10330
Lucas De Marchi25985ed2011-03-30 22:57:33 -030010331 /* Filtering of fragment chains is done against the first fragment */
Zhu Yid685b8c2006-04-13 17:20:27 +080010332 hdr = (void *)txb->fragments[0]->data;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010333 if (libipw_is_management(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +080010334 if (filter & IPW_PROM_NO_MGMT)
10335 return;
10336 if (filter & IPW_PROM_MGMT_HEADER_ONLY)
10337 hdr_only = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010338 } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +080010339 if (filter & IPW_PROM_NO_CTL)
10340 return;
10341 if (filter & IPW_PROM_CTL_HEADER_ONLY)
10342 hdr_only = 1;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010343 } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) {
Zhu Yid685b8c2006-04-13 17:20:27 +080010344 if (filter & IPW_PROM_NO_DATA)
10345 return;
10346 if (filter & IPW_PROM_DATA_HEADER_ONLY)
10347 hdr_only = 1;
10348 }
10349
10350 for(n=0; n<txb->nr_frags; ++n) {
10351 struct sk_buff *src = txb->fragments[n];
10352 struct sk_buff *dst;
10353 struct ieee80211_radiotap_header *rt_hdr;
10354 int len;
10355
10356 if (hdr_only) {
10357 hdr = (void *)src->data;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010358 len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control));
Zhu Yid685b8c2006-04-13 17:20:27 +080010359 } else
10360 len = src->len;
10361
Stanislav Yakovlevbf113152012-10-15 14:14:32 +000010362 dst = alloc_skb(len + sizeof(*rt_hdr) + sizeof(u16)*2, GFP_ATOMIC);
Johannes Berg007e5dd2008-11-27 23:13:38 +010010363 if (!dst)
10364 continue;
Zhu Yid685b8c2006-04-13 17:20:27 +080010365
Johannes Berg4df864c2017-06-16 14:29:21 +020010366 rt_hdr = skb_put(dst, sizeof(*rt_hdr));
Zhu Yid685b8c2006-04-13 17:20:27 +080010367
10368 rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION;
10369 rt_hdr->it_pad = 0;
10370 rt_hdr->it_present = 0; /* after all, it's just an idea */
Al Viro743b84d2007-12-27 01:43:16 -050010371 rt_hdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
Zhu Yid685b8c2006-04-13 17:20:27 +080010372
Al Viroe62e1ee2007-12-27 01:36:46 -050010373 *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(
Zhu Yid685b8c2006-04-13 17:20:27 +080010374 ieee80211chan2mhz(priv->channel));
10375 if (priv->channel > 14) /* 802.11a */
Al Viroe62e1ee2007-12-27 01:36:46 -050010376 *(__le16*)skb_put(dst, sizeof(u16)) =
Zhu Yid685b8c2006-04-13 17:20:27 +080010377 cpu_to_le16(IEEE80211_CHAN_OFDM |
10378 IEEE80211_CHAN_5GHZ);
10379 else if (priv->ieee->mode == IEEE_B) /* 802.11b */
Al Viroe62e1ee2007-12-27 01:36:46 -050010380 *(__le16*)skb_put(dst, sizeof(u16)) =
Zhu Yid685b8c2006-04-13 17:20:27 +080010381 cpu_to_le16(IEEE80211_CHAN_CCK |
10382 IEEE80211_CHAN_2GHZ);
10383 else /* 802.11g */
Al Viroe62e1ee2007-12-27 01:36:46 -050010384 *(__le16*)skb_put(dst, sizeof(u16)) =
Zhu Yid685b8c2006-04-13 17:20:27 +080010385 cpu_to_le16(IEEE80211_CHAN_OFDM |
10386 IEEE80211_CHAN_2GHZ);
10387
Al Viro743b84d2007-12-27 01:43:16 -050010388 rt_hdr->it_len = cpu_to_le16(dst->len);
Zhu Yid685b8c2006-04-13 17:20:27 +080010389
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -030010390 skb_copy_from_linear_data(src, skb_put(dst, len), len);
Zhu Yid685b8c2006-04-13 17:20:27 +080010391
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010392 if (!libipw_rx(priv->prom_priv->ieee, dst, &dummystats))
Zhu Yid685b8c2006-04-13 17:20:27 +080010393 dev_kfree_skb_any(dst);
10394 }
10395}
10396#endif
10397
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +000010398static netdev_tx_t ipw_net_hard_start_xmit(struct libipw_txb *txb,
10399 struct net_device *dev, int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -060010400{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010401 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010402 unsigned long flags;
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +000010403 netdev_tx_t ret;
James Ketrenos43f66a62005-03-25 12:31:53 -060010404
10405 IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -060010406 spin_lock_irqsave(&priv->lock, flags);
10407
Zhu Yid685b8c2006-04-13 17:20:27 +080010408#ifdef CONFIG_IPW2200_PROMISCUOUS
10409 if (rtap_iface && netif_running(priv->prom_net_dev))
10410 ipw_handle_promiscuous_tx(priv, txb);
10411#endif
10412
James Ketrenos227d2dc2005-07-28 16:25:55 -050010413 ret = ipw_tx_skb(priv, txb, pri);
10414 if (ret == NETDEV_TX_OK)
10415 __ipw_led_activity_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010416 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -060010417
James Ketrenos227d2dc2005-07-28 16:25:55 -050010418 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -060010419}
10420
James Ketrenos43f66a62005-03-25 12:31:53 -060010421static void ipw_net_set_multicast_list(struct net_device *dev)
10422{
10423
10424}
10425
10426static int ipw_net_set_mac_address(struct net_device *dev, void *p)
10427{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010428 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010429 struct sockaddr *addr = p;
Joe Perches0795af52007-10-03 17:59:30 -070010430
James Ketrenos43f66a62005-03-25 12:31:53 -060010431 if (!is_valid_ether_addr(addr->sa_data))
10432 return -EADDRNOTAVAIL;
Zhu Yi46441512006-01-24 16:37:59 +080010433 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060010434 priv->config |= CFG_CUSTOM_MAC;
10435 memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
Johannes Berge1749612008-10-27 15:59:26 -070010436 printk(KERN_INFO "%s: Setting MAC to %pM\n",
10437 priv->net_dev->name, priv->mac_addr);
Tejun Heobcb6d912011-01-26 12:12:50 +010010438 schedule_work(&priv->adapter_restart);
Zhu Yi46441512006-01-24 16:37:59 +080010439 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060010440 return 0;
10441}
10442
Jeff Garzikbf794512005-07-31 13:07:26 -040010443static void ipw_ethtool_get_drvinfo(struct net_device *dev,
James Ketrenos43f66a62005-03-25 12:31:53 -060010444 struct ethtool_drvinfo *info)
10445{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010446 struct ipw_priv *p = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010447 char vers[64];
10448 char date[32];
10449 u32 len;
10450
Rick Jones1f80c232011-11-15 10:40:49 -080010451 strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
10452 strlcpy(info->version, DRV_VERSION, sizeof(info->version));
James Ketrenos43f66a62005-03-25 12:31:53 -060010453
10454 len = sizeof(vers);
10455 ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
10456 len = sizeof(date);
10457 ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
10458
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010459 snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
James Ketrenos43f66a62005-03-25 12:31:53 -060010460 vers, date);
Rick Jones1f80c232011-11-15 10:40:49 -080010461 strlcpy(info->bus_info, pci_name(p->pci_dev),
10462 sizeof(info->bus_info));
James Ketrenos43f66a62005-03-25 12:31:53 -060010463}
10464
10465static u32 ipw_ethtool_get_link(struct net_device *dev)
10466{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010467 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010468 return (priv->status & STATUS_ASSOCIATED) != 0;
10469}
10470
10471static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
10472{
James Ketrenosb095c382005-08-24 22:04:42 -050010473 return IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010474}
10475
10476static int ipw_ethtool_get_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010477 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010478{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010479 struct ipw_priv *p = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010480
James Ketrenosb095c382005-08-24 22:04:42 -050010481 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010482 return -EINVAL;
Zhu Yi46441512006-01-24 16:37:59 +080010483 mutex_lock(&p->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010484 memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len);
Zhu Yi46441512006-01-24 16:37:59 +080010485 mutex_unlock(&p->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060010486 return 0;
10487}
10488
10489static int ipw_ethtool_set_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010490 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010491{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010492 struct ipw_priv *p = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010493 int i;
10494
James Ketrenosb095c382005-08-24 22:04:42 -050010495 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010496 return -EINVAL;
Zhu Yi46441512006-01-24 16:37:59 +080010497 mutex_lock(&p->mutex);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010498 memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len);
Adrian Bunk71e585f2006-03-11 04:42:58 +010010499 for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
10500 ipw_write8(p, i + IPW_EEPROM_DATA, p->eeprom[i]);
Zhu Yi46441512006-01-24 16:37:59 +080010501 mutex_unlock(&p->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060010502 return 0;
10503}
10504
Jeff Garzik7282d492006-09-13 14:30:00 -040010505static const struct ethtool_ops ipw_ethtool_ops = {
James Ketrenosea2b26e2005-08-24 21:25:16 -050010506 .get_link = ipw_ethtool_get_link,
10507 .get_drvinfo = ipw_ethtool_get_drvinfo,
10508 .get_eeprom_len = ipw_ethtool_get_eeprom_len,
10509 .get_eeprom = ipw_ethtool_get_eeprom,
10510 .set_eeprom = ipw_ethtool_set_eeprom,
James Ketrenos43f66a62005-03-25 12:31:53 -060010511};
10512
David Howells7d12e782006-10-05 14:55:46 +010010513static irqreturn_t ipw_isr(int irq, void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -060010514{
10515 struct ipw_priv *priv = data;
10516 u32 inta, inta_mask;
Jeff Garzikbf794512005-07-31 13:07:26 -040010517
James Ketrenos43f66a62005-03-25 12:31:53 -060010518 if (!priv)
10519 return IRQ_NONE;
10520
Zhu Yi89c318e2006-06-08 22:19:49 -070010521 spin_lock(&priv->irq_lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010522
10523 if (!(priv->status & STATUS_INT_ENABLED)) {
Zhu Yid00d0122007-07-12 16:09:40 +080010524 /* IRQ is disabled */
James Ketrenos43f66a62005-03-25 12:31:53 -060010525 goto none;
10526 }
10527
James Ketrenosb095c382005-08-24 22:04:42 -050010528 inta = ipw_read32(priv, IPW_INTA_RW);
10529 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
Jeff Garzikbf794512005-07-31 13:07:26 -040010530
James Ketrenos43f66a62005-03-25 12:31:53 -060010531 if (inta == 0xFFFFFFFF) {
10532 /* Hardware disappeared */
10533 IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
10534 goto none;
10535 }
10536
James Ketrenosb095c382005-08-24 22:04:42 -050010537 if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010538 /* Shared interrupt */
10539 goto none;
10540 }
10541
10542 /* tell the device to stop sending interrupts */
Zhu Yi89c318e2006-06-08 22:19:49 -070010543 __ipw_disable_interrupts(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010544
James Ketrenos43f66a62005-03-25 12:31:53 -060010545 /* ack current interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -050010546 inta &= (IPW_INTA_MASK_ALL & inta_mask);
10547 ipw_write32(priv, IPW_INTA_RW, inta);
Jeff Garzikbf794512005-07-31 13:07:26 -040010548
James Ketrenos43f66a62005-03-25 12:31:53 -060010549 /* Cache INTA value for our tasklet */
10550 priv->isr_inta = inta;
10551
10552 tasklet_schedule(&priv->irq_tasklet);
10553
Zhu Yi89c318e2006-06-08 22:19:49 -070010554 spin_unlock(&priv->irq_lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010555
10556 return IRQ_HANDLED;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010557 none:
Zhu Yi89c318e2006-06-08 22:19:49 -070010558 spin_unlock(&priv->irq_lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010559 return IRQ_NONE;
10560}
10561
10562static void ipw_rf_kill(void *adapter)
10563{
10564 struct ipw_priv *priv = adapter;
10565 unsigned long flags;
Jeff Garzikbf794512005-07-31 13:07:26 -040010566
James Ketrenos43f66a62005-03-25 12:31:53 -060010567 spin_lock_irqsave(&priv->lock, flags);
10568
10569 if (rf_kill_active(priv)) {
10570 IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
Tejun Heobcb6d912011-01-26 12:12:50 +010010571 schedule_delayed_work(&priv->rf_kill, 2 * HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -060010572 goto exit_unlock;
10573 }
10574
10575 /* RF Kill is now disabled, so bring the device back up */
10576
10577 if (!(priv->status & STATUS_RF_KILL_MASK)) {
10578 IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
10579 "device\n");
10580
10581 /* we can not do an adapter restart while inside an irq lock */
Tejun Heobcb6d912011-01-26 12:12:50 +010010582 schedule_work(&priv->adapter_restart);
Jeff Garzikbf794512005-07-31 13:07:26 -040010583 } else
James Ketrenos43f66a62005-03-25 12:31:53 -060010584 IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
10585 "enabled\n");
10586
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010587 exit_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -060010588 spin_unlock_irqrestore(&priv->lock, flags);
10589}
10590
David Howellsc4028952006-11-22 14:57:56 +000010591static void ipw_bg_rf_kill(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050010592{
David Howellsc4028952006-11-22 14:57:56 +000010593 struct ipw_priv *priv =
10594 container_of(work, struct ipw_priv, rf_kill.work);
Zhu Yi46441512006-01-24 16:37:59 +080010595 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000010596 ipw_rf_kill(priv);
Zhu Yi46441512006-01-24 16:37:59 +080010597 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010598}
10599
Adrian Bunka73e22b2006-01-21 01:39:42 +010010600static void ipw_link_up(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -050010601{
James Ketrenosafbf30a2005-08-25 00:05:33 -050010602 priv->last_seq_num = -1;
10603 priv->last_frag_num = -1;
10604 priv->last_packet_time = 0;
10605
James Ketrenosa613bff2005-08-24 21:43:11 -050010606 netif_carrier_on(priv->net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050010607
James Ketrenosc848d0a2005-08-24 21:56:24 -050010608 cancel_delayed_work(&priv->request_scan);
Dan Williamsea177302008-06-02 17:51:23 -040010609 cancel_delayed_work(&priv->request_direct_scan);
10610 cancel_delayed_work(&priv->request_passive_scan);
Dan Williams0b531672007-10-09 13:55:24 -040010611 cancel_delayed_work(&priv->scan_event);
James Ketrenosa613bff2005-08-24 21:43:11 -050010612 ipw_reset_stats(priv);
10613 /* Ensure the rate is updated immediately */
10614 priv->last_rate = ipw_get_current_rate(priv);
10615 ipw_gather_stats(priv);
10616 ipw_led_link_up(priv);
10617 notify_wx_assoc_event(priv);
10618
10619 if (priv->config & CFG_BACKGROUND_SCAN)
Tejun Heobcb6d912011-01-26 12:12:50 +010010620 schedule_delayed_work(&priv->request_scan, HZ);
James Ketrenosa613bff2005-08-24 21:43:11 -050010621}
10622
David Howellsc4028952006-11-22 14:57:56 +000010623static void ipw_bg_link_up(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050010624{
David Howellsc4028952006-11-22 14:57:56 +000010625 struct ipw_priv *priv =
10626 container_of(work, struct ipw_priv, link_up);
Zhu Yi46441512006-01-24 16:37:59 +080010627 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000010628 ipw_link_up(priv);
Zhu Yi46441512006-01-24 16:37:59 +080010629 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010630}
10631
Adrian Bunka73e22b2006-01-21 01:39:42 +010010632static void ipw_link_down(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -050010633{
10634 ipw_led_link_down(priv);
10635 netif_carrier_off(priv->net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050010636 notify_wx_assoc_event(priv);
10637
10638 /* Cancel any queued work ... */
10639 cancel_delayed_work(&priv->request_scan);
Dan Williamsea177302008-06-02 17:51:23 -040010640 cancel_delayed_work(&priv->request_direct_scan);
10641 cancel_delayed_work(&priv->request_passive_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -050010642 cancel_delayed_work(&priv->adhoc_check);
10643 cancel_delayed_work(&priv->gather_stats);
10644
10645 ipw_reset_stats(priv);
10646
James Ketrenosafbf30a2005-08-25 00:05:33 -050010647 if (!(priv->status & STATUS_EXIT_PENDING)) {
10648 /* Queue up another scan... */
Tejun Heobcb6d912011-01-26 12:12:50 +010010649 schedule_delayed_work(&priv->request_scan, 0);
Dan Williams0b531672007-10-09 13:55:24 -040010650 } else
10651 cancel_delayed_work(&priv->scan_event);
James Ketrenosa613bff2005-08-24 21:43:11 -050010652}
10653
David Howellsc4028952006-11-22 14:57:56 +000010654static void ipw_bg_link_down(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050010655{
David Howellsc4028952006-11-22 14:57:56 +000010656 struct ipw_priv *priv =
10657 container_of(work, struct ipw_priv, link_down);
Zhu Yi46441512006-01-24 16:37:59 +080010658 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000010659 ipw_link_down(priv);
Zhu Yi46441512006-01-24 16:37:59 +080010660 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010661}
10662
Bill Pembertoneb9248e2012-12-03 09:56:32 -050010663static int ipw_setup_deferred_work(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010664{
10665 int ret = 0;
10666
James Ketrenos43f66a62005-03-25 12:31:53 -060010667 init_waitqueue_head(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010668 init_waitqueue_head(&priv->wait_state);
James Ketrenos43f66a62005-03-25 12:31:53 -060010669
David Howellsc4028952006-11-22 14:57:56 +000010670 INIT_DELAYED_WORK(&priv->adhoc_check, ipw_bg_adhoc_check);
10671 INIT_WORK(&priv->associate, ipw_bg_associate);
10672 INIT_WORK(&priv->disassociate, ipw_bg_disassociate);
10673 INIT_WORK(&priv->system_config, ipw_system_config);
10674 INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish);
10675 INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart);
10676 INIT_DELAYED_WORK(&priv->rf_kill, ipw_bg_rf_kill);
10677 INIT_WORK(&priv->up, ipw_bg_up);
10678 INIT_WORK(&priv->down, ipw_bg_down);
10679 INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
Dan Williamsea177302008-06-02 17:51:23 -040010680 INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan);
10681 INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
Dan Williams0b531672007-10-09 13:55:24 -040010682 INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event);
David Howellsc4028952006-11-22 14:57:56 +000010683 INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
10684 INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
10685 INIT_WORK(&priv->roam, ipw_bg_roam);
10686 INIT_DELAYED_WORK(&priv->scan_check, ipw_bg_scan_check);
10687 INIT_WORK(&priv->link_up, ipw_bg_link_up);
10688 INIT_WORK(&priv->link_down, ipw_bg_link_down);
10689 INIT_DELAYED_WORK(&priv->led_link_on, ipw_bg_led_link_on);
10690 INIT_DELAYED_WORK(&priv->led_link_off, ipw_bg_led_link_off);
10691 INIT_DELAYED_WORK(&priv->led_act_off, ipw_bg_led_activity_off);
10692 INIT_WORK(&priv->merge_networks, ipw_merge_adhoc_network);
James Ketrenos43f66a62005-03-25 12:31:53 -060010693
Zhu Yie43e3c12006-04-13 17:20:45 +080010694#ifdef CONFIG_IPW2200_QOS
David Howellsc4028952006-11-22 14:57:56 +000010695 INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate);
Zhu Yie43e3c12006-04-13 17:20:45 +080010696#endif /* CONFIG_IPW2200_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010697
10698 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
10699 ipw_irq_tasklet, (unsigned long)priv);
10700
10701 return ret;
10702}
10703
James Ketrenos43f66a62005-03-25 12:31:53 -060010704static void shim__set_security(struct net_device *dev,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010705 struct libipw_security *sec)
James Ketrenos43f66a62005-03-25 12:31:53 -060010706{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010707 struct ipw_priv *priv = libipw_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060010708 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -040010709 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010710 if (sec->flags & (1 << i)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050010711 priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
James Ketrenosb095c382005-08-24 22:04:42 -050010712 priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
James Ketrenos43f66a62005-03-25 12:31:53 -060010713 if (sec->key_sizes[i] == 0)
James Ketrenosb095c382005-08-24 22:04:42 -050010714 priv->ieee->sec.flags &= ~(1 << i);
10715 else {
10716 memcpy(priv->ieee->sec.keys[i], sec->keys[i],
James Ketrenos43f66a62005-03-25 12:31:53 -060010717 sec->key_sizes[i]);
James Ketrenosb095c382005-08-24 22:04:42 -050010718 priv->ieee->sec.flags |= (1 << i);
10719 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010720 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010721 } else if (sec->level != SEC_LEVEL_1)
10722 priv->ieee->sec.flags &= ~(1 << i);
James Ketrenos43f66a62005-03-25 12:31:53 -060010723 }
10724
James Ketrenosb095c382005-08-24 22:04:42 -050010725 if (sec->flags & SEC_ACTIVE_KEY) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010726 if (sec->active_key <= 3) {
James Ketrenosb095c382005-08-24 22:04:42 -050010727 priv->ieee->sec.active_key = sec->active_key;
10728 priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikbf794512005-07-31 13:07:26 -040010729 } else
James Ketrenosb095c382005-08-24 22:04:42 -050010730 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010731 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010732 } else
10733 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010734
10735 if ((sec->flags & SEC_AUTH_MODE) &&
James Ketrenosb095c382005-08-24 22:04:42 -050010736 (priv->ieee->sec.auth_mode != sec->auth_mode)) {
10737 priv->ieee->sec.auth_mode = sec->auth_mode;
10738 priv->ieee->sec.flags |= SEC_AUTH_MODE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010739 if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
10740 priv->capability |= CAP_SHARED_KEY;
10741 else
10742 priv->capability &= ~CAP_SHARED_KEY;
10743 priv->status |= STATUS_SECURITY_UPDATED;
10744 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010745
James Ketrenosb095c382005-08-24 22:04:42 -050010746 if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
10747 priv->ieee->sec.flags |= SEC_ENABLED;
10748 priv->ieee->sec.enabled = sec->enabled;
James Ketrenos43f66a62005-03-25 12:31:53 -060010749 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -040010750 if (sec->enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -060010751 priv->capability |= CAP_PRIVACY_ON;
10752 else
10753 priv->capability &= ~CAP_PRIVACY_ON;
10754 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010755
James Ketrenosafbf30a2005-08-25 00:05:33 -050010756 if (sec->flags & SEC_ENCRYPT)
10757 priv->ieee->sec.encrypt = sec->encrypt;
James Ketrenos43f66a62005-03-25 12:31:53 -060010758
James Ketrenosb095c382005-08-24 22:04:42 -050010759 if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
10760 priv->ieee->sec.level = sec->level;
10761 priv->ieee->sec.flags |= SEC_LEVEL;
James Ketrenos43f66a62005-03-25 12:31:53 -060010762 priv->status |= STATUS_SECURITY_UPDATED;
10763 }
10764
Zhu Yi1fbfea52005-08-05 17:22:56 +080010765 if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
10766 ipw_set_hwcrypto_keys(priv);
10767
Jeff Garzikbf794512005-07-31 13:07:26 -040010768 /* To match current functionality of ipw2100 (which works well w/
10769 * various supplicants, we don't force a disassociate if the
James Ketrenos43f66a62005-03-25 12:31:53 -060010770 * privacy capability changes ... */
10771#if 0
10772 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
Jeff Garzikbf794512005-07-31 13:07:26 -040010773 (((priv->assoc_request.capability &
Al Viro5b5e8072007-12-27 01:54:06 -050010774 cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && !sec->enabled) ||
Jeff Garzikbf794512005-07-31 13:07:26 -040010775 (!(priv->assoc_request.capability &
Al Viro5b5e8072007-12-27 01:54:06 -050010776 cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && sec->enabled))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010777 IPW_DEBUG_ASSOC("Disassociating due to capability "
10778 "change.\n");
10779 ipw_disassociate(priv);
10780 }
10781#endif
10782}
10783
Jeff Garzikbf794512005-07-31 13:07:26 -040010784static int init_supported_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -060010785 struct ipw_supported_rates *rates)
10786{
10787 /* TODO: Mask out rates based on priv->rates_mask */
10788
10789 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010790 /* configure supported rates */
James Ketrenos43f66a62005-03-25 12:31:53 -060010791 switch (priv->ieee->freq_band) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010792 case LIBIPW_52GHZ_BAND:
James Ketrenos43f66a62005-03-25 12:31:53 -060010793 rates->ieee_mode = IPW_A_MODE;
10794 rates->purpose = IPW_RATE_CAPABILITIES;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010795 ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION,
10796 LIBIPW_OFDM_DEFAULT_RATES_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -060010797 break;
10798
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010799 default: /* Mixed or 2.4Ghz */
James Ketrenos43f66a62005-03-25 12:31:53 -060010800 rates->ieee_mode = IPW_G_MODE;
10801 rates->purpose = IPW_RATE_CAPABILITIES;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010802 ipw_add_cck_scan_rates(rates, LIBIPW_CCK_MODULATION,
10803 LIBIPW_CCK_DEFAULT_RATES_MASK);
10804 if (priv->ieee->modulation & LIBIPW_OFDM_MODULATION) {
10805 ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION,
10806 LIBIPW_OFDM_DEFAULT_RATES_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -060010807 }
10808 break;
10809 }
10810
10811 return 0;
10812}
10813
Jeff Garzikbf794512005-07-31 13:07:26 -040010814static int ipw_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010815{
James Ketrenos43f66a62005-03-25 12:31:53 -060010816 /* This is only called from ipw_up, which resets/reloads the firmware
10817 so, we don't need to first disable the card before we configure
10818 it */
Zhu Yi6de9f7f2005-08-11 14:39:33 +080010819 if (ipw_set_tx_power(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -060010820 goto error;
10821
10822 /* initialize adapter address */
10823 if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
10824 goto error;
10825
10826 /* set basic system config settings */
10827 init_sys_config(&priv->sys_config);
Zhu Yi810dabd2006-01-24 16:36:59 +080010828
10829 /* Support Bluetooth if we have BT h/w on board, and user wants to.
10830 * Does not support BT priority yet (don't abort or defer our Tx) */
10831 if (bt_coexist) {
Zhu Yi2638bc32006-01-24 16:37:52 +080010832 unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY];
Zhu Yi810dabd2006-01-24 16:36:59 +080010833
10834 if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG)
10835 priv->sys_config.bt_coexistence
Zhu Yi2638bc32006-01-24 16:37:52 +080010836 |= CFG_BT_COEXISTENCE_SIGNAL_CHNL;
Zhu Yi810dabd2006-01-24 16:36:59 +080010837 if (bt_caps & EEPROM_SKU_CAP_BT_OOB)
10838 priv->sys_config.bt_coexistence
Zhu Yi2638bc32006-01-24 16:37:52 +080010839 |= CFG_BT_COEXISTENCE_OOB;
Zhu Yi810dabd2006-01-24 16:36:59 +080010840 }
10841
Zhu Yid685b8c2006-04-13 17:20:27 +080010842#ifdef CONFIG_IPW2200_PROMISCUOUS
10843 if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
10844 priv->sys_config.accept_all_data_frames = 1;
10845 priv->sys_config.accept_non_directed_frames = 1;
10846 priv->sys_config.accept_all_mgmt_bcpr = 1;
10847 priv->sys_config.accept_all_mgmt_frames = 1;
10848 }
10849#endif
10850
James Ketrenosc848d0a2005-08-24 21:56:24 -050010851 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
10852 priv->sys_config.answer_broadcast_ssid_probe = 1;
10853 else
10854 priv->sys_config.answer_broadcast_ssid_probe = 0;
10855
Zhu Yid685b8c2006-04-13 17:20:27 +080010856 if (ipw_send_system_config(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -060010857 goto error;
10858
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010859 init_supported_rates(priv, &priv->rates);
10860 if (ipw_send_supported_rates(priv, &priv->rates))
James Ketrenos43f66a62005-03-25 12:31:53 -060010861 goto error;
10862
10863 /* Set request-to-send threshold */
10864 if (priv->rts_threshold) {
10865 if (ipw_send_rts_threshold(priv, priv->rts_threshold))
10866 goto error;
10867 }
Zhu Yie43e3c12006-04-13 17:20:45 +080010868#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -050010869 IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
10870 ipw_qos_activate(priv, NULL);
Zhu Yie43e3c12006-04-13 17:20:45 +080010871#endif /* CONFIG_IPW2200_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010872
10873 if (ipw_set_random_seed(priv))
10874 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -040010875
James Ketrenos43f66a62005-03-25 12:31:53 -060010876 /* final state transition to the RUN state */
10877 if (ipw_send_host_complete(priv))
10878 goto error;
10879
James Ketrenose6666192005-08-12 09:17:04 -050010880 priv->status |= STATUS_INIT;
10881
10882 ipw_led_init(priv);
10883 ipw_led_radio_on(priv);
10884 priv->notif_missed_beacons = 0;
10885
10886 /* Set hardware WEP key if it is configured. */
10887 if ((priv->capability & CAP_PRIVACY_ON) &&
10888 (priv->ieee->sec.level == SEC_LEVEL_1) &&
10889 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
10890 ipw_set_hwcrypto_keys(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010891
10892 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -040010893
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010894 error:
James Ketrenos43f66a62005-03-25 12:31:53 -060010895 return -EIO;
10896}
10897
James Ketrenos4f36f802005-08-03 20:36:56 -050010898/*
10899 * NOTE:
10900 *
10901 * These tables have been tested in conjunction with the
10902 * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters.
10903 *
10904 * Altering this values, using it on other hardware, or in geographies
10905 * not intended for resale of the above mentioned Intel adapters has
10906 * not been tested.
10907 *
Henrik Brix Andersen48a84772006-02-14 09:09:52 +080010908 * Remember to update the table in README.ipw2200 when changing this
10909 * table.
10910 *
James Ketrenos4f36f802005-08-03 20:36:56 -050010911 */
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010912static const struct libipw_geo ipw_geos[] = {
James Ketrenos4f36f802005-08-03 20:36:56 -050010913 { /* Restricted */
10914 "---",
10915 .bg_channels = 11,
10916 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10917 {2427, 4}, {2432, 5}, {2437, 6},
10918 {2442, 7}, {2447, 8}, {2452, 9},
10919 {2457, 10}, {2462, 11}},
10920 },
10921
10922 { /* Custom US/Canada */
10923 "ZZF",
10924 .bg_channels = 11,
10925 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10926 {2427, 4}, {2432, 5}, {2437, 6},
10927 {2442, 7}, {2447, 8}, {2452, 9},
10928 {2457, 10}, {2462, 11}},
10929 .a_channels = 8,
10930 .a = {{5180, 36},
10931 {5200, 40},
10932 {5220, 44},
10933 {5240, 48},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010934 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
10935 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
10936 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
10937 {5320, 64, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050010938 },
10939
10940 { /* Rest of World */
10941 "ZZD",
10942 .bg_channels = 13,
10943 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10944 {2427, 4}, {2432, 5}, {2437, 6},
10945 {2442, 7}, {2447, 8}, {2452, 9},
10946 {2457, 10}, {2462, 11}, {2467, 12},
10947 {2472, 13}},
10948 },
10949
10950 { /* Custom USA & Europe & High */
10951 "ZZA",
10952 .bg_channels = 11,
10953 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10954 {2427, 4}, {2432, 5}, {2437, 6},
10955 {2442, 7}, {2447, 8}, {2452, 9},
10956 {2457, 10}, {2462, 11}},
10957 .a_channels = 13,
10958 .a = {{5180, 36},
10959 {5200, 40},
10960 {5220, 44},
10961 {5240, 48},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010962 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
10963 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
10964 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
10965 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
James Ketrenos4f36f802005-08-03 20:36:56 -050010966 {5745, 149},
10967 {5765, 153},
10968 {5785, 157},
10969 {5805, 161},
10970 {5825, 165}},
10971 },
10972
10973 { /* Custom NA & Europe */
10974 "ZZB",
10975 .bg_channels = 11,
10976 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10977 {2427, 4}, {2432, 5}, {2437, 6},
10978 {2442, 7}, {2447, 8}, {2452, 9},
10979 {2457, 10}, {2462, 11}},
10980 .a_channels = 13,
10981 .a = {{5180, 36},
10982 {5200, 40},
10983 {5220, 44},
10984 {5240, 48},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040010985 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
10986 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
10987 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
10988 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
10989 {5745, 149, LIBIPW_CH_PASSIVE_ONLY},
10990 {5765, 153, LIBIPW_CH_PASSIVE_ONLY},
10991 {5785, 157, LIBIPW_CH_PASSIVE_ONLY},
10992 {5805, 161, LIBIPW_CH_PASSIVE_ONLY},
10993 {5825, 165, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050010994 },
10995
10996 { /* Custom Japan */
10997 "ZZC",
10998 .bg_channels = 11,
10999 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11000 {2427, 4}, {2432, 5}, {2437, 6},
11001 {2442, 7}, {2447, 8}, {2452, 9},
11002 {2457, 10}, {2462, 11}},
11003 .a_channels = 4,
11004 .a = {{5170, 34}, {5190, 38},
11005 {5210, 42}, {5230, 46}},
11006 },
11007
11008 { /* Custom */
11009 "ZZM",
11010 .bg_channels = 11,
11011 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11012 {2427, 4}, {2432, 5}, {2437, 6},
11013 {2442, 7}, {2447, 8}, {2452, 9},
11014 {2457, 10}, {2462, 11}},
11015 },
11016
11017 { /* Europe */
11018 "ZZE",
11019 .bg_channels = 13,
11020 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11021 {2427, 4}, {2432, 5}, {2437, 6},
11022 {2442, 7}, {2447, 8}, {2452, 9},
11023 {2457, 10}, {2462, 11}, {2467, 12},
11024 {2472, 13}},
11025 .a_channels = 19,
11026 .a = {{5180, 36},
11027 {5200, 40},
11028 {5220, 44},
11029 {5240, 48},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011030 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
11031 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
11032 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
11033 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
11034 {5500, 100, LIBIPW_CH_PASSIVE_ONLY},
11035 {5520, 104, LIBIPW_CH_PASSIVE_ONLY},
11036 {5540, 108, LIBIPW_CH_PASSIVE_ONLY},
11037 {5560, 112, LIBIPW_CH_PASSIVE_ONLY},
11038 {5580, 116, LIBIPW_CH_PASSIVE_ONLY},
11039 {5600, 120, LIBIPW_CH_PASSIVE_ONLY},
11040 {5620, 124, LIBIPW_CH_PASSIVE_ONLY},
11041 {5640, 128, LIBIPW_CH_PASSIVE_ONLY},
11042 {5660, 132, LIBIPW_CH_PASSIVE_ONLY},
11043 {5680, 136, LIBIPW_CH_PASSIVE_ONLY},
11044 {5700, 140, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011045 },
11046
11047 { /* Custom Japan */
11048 "ZZJ",
11049 .bg_channels = 14,
11050 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11051 {2427, 4}, {2432, 5}, {2437, 6},
11052 {2442, 7}, {2447, 8}, {2452, 9},
11053 {2457, 10}, {2462, 11}, {2467, 12},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011054 {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011055 .a_channels = 4,
11056 .a = {{5170, 34}, {5190, 38},
11057 {5210, 42}, {5230, 46}},
11058 },
11059
James Ketrenos03520572005-10-19 16:12:31 -050011060 { /* Rest of World */
11061 "ZZR",
11062 .bg_channels = 14,
11063 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11064 {2427, 4}, {2432, 5}, {2437, 6},
11065 {2442, 7}, {2447, 8}, {2452, 9},
11066 {2457, 10}, {2462, 11}, {2467, 12},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011067 {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY |
11068 LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos03520572005-10-19 16:12:31 -050011069 },
11070
James Ketrenos4f36f802005-08-03 20:36:56 -050011071 { /* High Band */
11072 "ZZH",
11073 .bg_channels = 13,
11074 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11075 {2427, 4}, {2432, 5}, {2437, 6},
11076 {2442, 7}, {2447, 8}, {2452, 9},
11077 {2457, 10}, {2462, 11},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011078 {2467, 12, LIBIPW_CH_PASSIVE_ONLY},
11079 {2472, 13, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011080 .a_channels = 4,
11081 .a = {{5745, 149}, {5765, 153},
11082 {5785, 157}, {5805, 161}},
11083 },
11084
11085 { /* Custom Europe */
11086 "ZZG",
11087 .bg_channels = 13,
11088 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11089 {2427, 4}, {2432, 5}, {2437, 6},
11090 {2442, 7}, {2447, 8}, {2452, 9},
11091 {2457, 10}, {2462, 11},
11092 {2467, 12}, {2472, 13}},
11093 .a_channels = 4,
11094 .a = {{5180, 36}, {5200, 40},
11095 {5220, 44}, {5240, 48}},
11096 },
11097
11098 { /* Europe */
11099 "ZZK",
11100 .bg_channels = 13,
11101 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11102 {2427, 4}, {2432, 5}, {2437, 6},
11103 {2442, 7}, {2447, 8}, {2452, 9},
11104 {2457, 10}, {2462, 11},
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011105 {2467, 12, LIBIPW_CH_PASSIVE_ONLY},
11106 {2472, 13, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011107 .a_channels = 24,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011108 .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY},
11109 {5200, 40, LIBIPW_CH_PASSIVE_ONLY},
11110 {5220, 44, LIBIPW_CH_PASSIVE_ONLY},
11111 {5240, 48, LIBIPW_CH_PASSIVE_ONLY},
11112 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
11113 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
11114 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
11115 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
11116 {5500, 100, LIBIPW_CH_PASSIVE_ONLY},
11117 {5520, 104, LIBIPW_CH_PASSIVE_ONLY},
11118 {5540, 108, LIBIPW_CH_PASSIVE_ONLY},
11119 {5560, 112, LIBIPW_CH_PASSIVE_ONLY},
11120 {5580, 116, LIBIPW_CH_PASSIVE_ONLY},
11121 {5600, 120, LIBIPW_CH_PASSIVE_ONLY},
11122 {5620, 124, LIBIPW_CH_PASSIVE_ONLY},
11123 {5640, 128, LIBIPW_CH_PASSIVE_ONLY},
11124 {5660, 132, LIBIPW_CH_PASSIVE_ONLY},
11125 {5680, 136, LIBIPW_CH_PASSIVE_ONLY},
11126 {5700, 140, LIBIPW_CH_PASSIVE_ONLY},
11127 {5745, 149, LIBIPW_CH_PASSIVE_ONLY},
11128 {5765, 153, LIBIPW_CH_PASSIVE_ONLY},
11129 {5785, 157, LIBIPW_CH_PASSIVE_ONLY},
11130 {5805, 161, LIBIPW_CH_PASSIVE_ONLY},
11131 {5825, 165, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011132 },
11133
11134 { /* Europe */
11135 "ZZL",
11136 .bg_channels = 11,
11137 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
11138 {2427, 4}, {2432, 5}, {2437, 6},
11139 {2442, 7}, {2447, 8}, {2452, 9},
11140 {2457, 10}, {2462, 11}},
11141 .a_channels = 13,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011142 .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY},
11143 {5200, 40, LIBIPW_CH_PASSIVE_ONLY},
11144 {5220, 44, LIBIPW_CH_PASSIVE_ONLY},
11145 {5240, 48, LIBIPW_CH_PASSIVE_ONLY},
11146 {5260, 52, LIBIPW_CH_PASSIVE_ONLY},
11147 {5280, 56, LIBIPW_CH_PASSIVE_ONLY},
11148 {5300, 60, LIBIPW_CH_PASSIVE_ONLY},
11149 {5320, 64, LIBIPW_CH_PASSIVE_ONLY},
11150 {5745, 149, LIBIPW_CH_PASSIVE_ONLY},
11151 {5765, 153, LIBIPW_CH_PASSIVE_ONLY},
11152 {5785, 157, LIBIPW_CH_PASSIVE_ONLY},
11153 {5805, 161, LIBIPW_CH_PASSIVE_ONLY},
11154 {5825, 165, LIBIPW_CH_PASSIVE_ONLY}},
James Ketrenos4f36f802005-08-03 20:36:56 -050011155 }
James Ketrenosafbf30a2005-08-25 00:05:33 -050011156};
11157
Stanislav Yakovlev06729382012-11-20 23:54:20 +000011158static void ipw_set_geo(struct ipw_priv *priv)
11159{
11160 int j;
11161
11162 for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
11163 if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
11164 ipw_geos[j].name, 3))
11165 break;
11166 }
11167
11168 if (j == ARRAY_SIZE(ipw_geos)) {
11169 IPW_WARNING("SKU [%c%c%c] not recognized.\n",
11170 priv->eeprom[EEPROM_COUNTRY_CODE + 0],
11171 priv->eeprom[EEPROM_COUNTRY_CODE + 1],
11172 priv->eeprom[EEPROM_COUNTRY_CODE + 2]);
11173 j = 0;
11174 }
11175
11176 libipw_set_geo(priv->ieee, &ipw_geos[j]);
11177}
11178
James Ketrenos43f66a62005-03-25 12:31:53 -060011179#define MAX_HW_RESTARTS 5
11180static int ipw_up(struct ipw_priv *priv)
11181{
Stanislav Yakovlev06729382012-11-20 23:54:20 +000011182 int rc, i;
James Ketrenos43f66a62005-03-25 12:31:53 -060011183
Dan Williamsc3d72b92009-02-11 13:26:06 -050011184 /* Age scan list entries found before suspend */
11185 if (priv->suspend_time) {
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011186 libipw_networks_age(priv->ieee, priv->suspend_time);
Dan Williamsc3d72b92009-02-11 13:26:06 -050011187 priv->suspend_time = 0;
11188 }
11189
James Ketrenos43f66a62005-03-25 12:31:53 -060011190 if (priv->status & STATUS_EXIT_PENDING)
11191 return -EIO;
11192
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011193 if (cmdlog && !priv->cmdlog) {
Yan Burmane6e3f122006-12-02 13:38:14 +020011194 priv->cmdlog = kcalloc(cmdlog, sizeof(*priv->cmdlog),
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011195 GFP_KERNEL);
11196 if (priv->cmdlog == NULL) {
11197 IPW_ERROR("Error allocating %d command log entries.\n",
11198 cmdlog);
Zhu Yid0b526b2006-04-13 17:19:50 +080011199 return -ENOMEM;
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011200 } else {
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011201 priv->cmdlog_len = cmdlog;
11202 }
11203 }
11204
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011205 for (i = 0; i < MAX_HW_RESTARTS; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -040011206 /* Load the microcode, firmware, and eeprom.
James Ketrenos43f66a62005-03-25 12:31:53 -060011207 * Also start the clocks. */
11208 rc = ipw_load(priv);
11209 if (rc) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -050011210 IPW_ERROR("Unable to load firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060011211 return rc;
11212 }
11213
11214 ipw_init_ordinals(priv);
11215 if (!(priv->config & CFG_CUSTOM_MAC))
11216 eeprom_parse_mac(priv, priv->mac_addr);
11217 memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
11218
Stanislav Yakovlev06729382012-11-20 23:54:20 +000011219 ipw_set_geo(priv);
James Ketrenos4f36f802005-08-03 20:36:56 -050011220
James Ketrenosb095c382005-08-24 22:04:42 -050011221 if (priv->status & STATUS_RF_KILL_SW) {
11222 IPW_WARNING("Radio disabled by module parameter.\n");
11223 return 0;
11224 } else if (rf_kill_active(priv)) {
11225 IPW_WARNING("Radio Frequency Kill Switch is On:\n"
11226 "Kill switch must be turned off for "
11227 "wireless networking to work.\n");
Tejun Heobcb6d912011-01-26 12:12:50 +010011228 schedule_delayed_work(&priv->rf_kill, 2 * HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -060011229 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -050011230 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011231
11232 rc = ipw_config(priv);
11233 if (!rc) {
11234 IPW_DEBUG_INFO("Configured device on count %i\n", i);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011235
James Ketrenose6666192005-08-12 09:17:04 -050011236 /* If configure to try and auto-associate, kick
11237 * off a scan. */
Tejun Heobcb6d912011-01-26 12:12:50 +010011238 schedule_delayed_work(&priv->request_scan, 0);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011239
James Ketrenos43f66a62005-03-25 12:31:53 -060011240 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060011241 }
Jeff Garzikbf794512005-07-31 13:07:26 -040011242
James Ketrenosc848d0a2005-08-24 21:56:24 -050011243 IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060011244 IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
11245 i, MAX_HW_RESTARTS);
11246
11247 /* We had an error bringing up the hardware, so take it
11248 * all the way back down so we can try again */
11249 ipw_down(priv);
11250 }
11251
Jeff Garzikbf794512005-07-31 13:07:26 -040011252 /* tried to restart and config the device for as long as our
James Ketrenos43f66a62005-03-25 12:31:53 -060011253 * patience could withstand */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011254 IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011255
James Ketrenos43f66a62005-03-25 12:31:53 -060011256 return -EIO;
11257}
11258
David Howellsc4028952006-11-22 14:57:56 +000011259static void ipw_bg_up(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050011260{
David Howellsc4028952006-11-22 14:57:56 +000011261 struct ipw_priv *priv =
11262 container_of(work, struct ipw_priv, up);
Zhu Yi46441512006-01-24 16:37:59 +080011263 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000011264 ipw_up(priv);
Zhu Yi46441512006-01-24 16:37:59 +080011265 mutex_unlock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011266}
11267
James Ketrenosb095c382005-08-24 22:04:42 -050011268static void ipw_deinit(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060011269{
James Ketrenosb095c382005-08-24 22:04:42 -050011270 int i;
11271
11272 if (priv->status & STATUS_SCANNING) {
11273 IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
11274 ipw_abort_scan(priv);
11275 }
11276
11277 if (priv->status & STATUS_ASSOCIATED) {
11278 IPW_DEBUG_INFO("Disassociating during shutdown.\n");
11279 ipw_disassociate(priv);
11280 }
11281
11282 ipw_led_shutdown(priv);
11283
11284 /* Wait up to 1s for status to change to not scanning and not
11285 * associated (disassociation can take a while for a ful 802.11
11286 * exchange */
11287 for (i = 1000; i && (priv->status &
11288 (STATUS_DISASSOCIATING |
11289 STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
11290 udelay(10);
11291
11292 if (priv->status & (STATUS_DISASSOCIATING |
11293 STATUS_ASSOCIATED | STATUS_SCANNING))
11294 IPW_DEBUG_INFO("Still associated or scanning...\n");
11295 else
11296 IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
11297
James Ketrenosc848d0a2005-08-24 21:56:24 -050011298 /* Attempt to disable the card */
James Ketrenos43f66a62005-03-25 12:31:53 -060011299 ipw_send_card_disable(priv, 0);
James Ketrenosb095c382005-08-24 22:04:42 -050011300
11301 priv->status &= ~STATUS_INIT;
11302}
11303
James Ketrenos43f66a62005-03-25 12:31:53 -060011304static void ipw_down(struct ipw_priv *priv)
11305{
James Ketrenosb095c382005-08-24 22:04:42 -050011306 int exit_pending = priv->status & STATUS_EXIT_PENDING;
11307
11308 priv->status |= STATUS_EXIT_PENDING;
11309
11310 if (ipw_is_init(priv))
11311 ipw_deinit(priv);
11312
11313 /* Wipe out the EXIT_PENDING status bit if we are not actually
11314 * exiting the module */
11315 if (!exit_pending)
11316 priv->status &= ~STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011317
11318 /* tell the device to stop sending interrupts */
11319 ipw_disable_interrupts(priv);
11320
11321 /* Clear all bits but the RF Kill */
James Ketrenosb095c382005-08-24 22:04:42 -050011322 priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011323 netif_carrier_off(priv->net_dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011324
11325 ipw_stop_nic(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -050011326
11327 ipw_led_radio_off(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011328}
11329
David Howellsc4028952006-11-22 14:57:56 +000011330static void ipw_bg_down(struct work_struct *work)
James Ketrenosc848d0a2005-08-24 21:56:24 -050011331{
David Howellsc4028952006-11-22 14:57:56 +000011332 struct ipw_priv *priv =
11333 container_of(work, struct ipw_priv, down);
Zhu Yi46441512006-01-24 16:37:59 +080011334 mutex_lock(&priv->mutex);
David Howellsc4028952006-11-22 14:57:56 +000011335 ipw_down(priv);
Zhu Yi46441512006-01-24 16:37:59 +080011336 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011337}
11338
Stanislaw Gruszka7cabafc2011-09-14 16:47:50 +020011339static int ipw_wdev_init(struct net_device *dev)
11340{
John W. Linvillea3caa992009-08-25 14:12:25 -040011341 int i, rc = 0;
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011342 struct ipw_priv *priv = libipw_priv(dev);
John W. Linvillea3caa992009-08-25 14:12:25 -040011343 const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
11344 struct wireless_dev *wdev = &priv->ieee->wdev;
James Ketrenos43f66a62005-03-25 12:31:53 -060011345
John W. Linvillea3caa992009-08-25 14:12:25 -040011346 memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
11347
11348 /* fill-out priv->ieee->bg_band */
11349 if (geo->bg_channels) {
11350 struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band;
11351
Johannes Berg57fbcce2016-04-12 15:56:15 +020011352 bg_band->band = NL80211_BAND_2GHZ;
John W. Linvillea3caa992009-08-25 14:12:25 -040011353 bg_band->n_channels = geo->bg_channels;
Joe Perchesbaeb2ff2010-08-11 07:02:48 +000011354 bg_band->channels = kcalloc(geo->bg_channels,
11355 sizeof(struct ieee80211_channel),
11356 GFP_KERNEL);
Dan Carpenter2ee4e272010-10-04 14:31:46 +020011357 if (!bg_band->channels) {
11358 rc = -ENOMEM;
11359 goto out;
11360 }
John W. Linvillea3caa992009-08-25 14:12:25 -040011361 /* translate geo->bg to bg_band.channels */
11362 for (i = 0; i < geo->bg_channels; i++) {
Johannes Berg57fbcce2016-04-12 15:56:15 +020011363 bg_band->channels[i].band = NL80211_BAND_2GHZ;
John W. Linvillea3caa992009-08-25 14:12:25 -040011364 bg_band->channels[i].center_freq = geo->bg[i].freq;
11365 bg_band->channels[i].hw_value = geo->bg[i].channel;
11366 bg_band->channels[i].max_power = geo->bg[i].max_power;
11367 if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY)
11368 bg_band->channels[i].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +020011369 IEEE80211_CHAN_NO_IR;
John W. Linvillea3caa992009-08-25 14:12:25 -040011370 if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS)
11371 bg_band->channels[i].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +020011372 IEEE80211_CHAN_NO_IR;
John W. Linvillea3caa992009-08-25 14:12:25 -040011373 if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)
11374 bg_band->channels[i].flags |=
11375 IEEE80211_CHAN_RADAR;
11376 /* No equivalent for LIBIPW_CH_80211H_RULES,
11377 LIBIPW_CH_UNIFORM_SPREADING, or
11378 LIBIPW_CH_B_ONLY... */
11379 }
11380 /* point at bitrate info */
11381 bg_band->bitrates = ipw2200_bg_rates;
11382 bg_band->n_bitrates = ipw2200_num_bg_rates;
11383
Johannes Berg57fbcce2016-04-12 15:56:15 +020011384 wdev->wiphy->bands[NL80211_BAND_2GHZ] = bg_band;
John W. Linvillea3caa992009-08-25 14:12:25 -040011385 }
11386
11387 /* fill-out priv->ieee->a_band */
11388 if (geo->a_channels) {
11389 struct ieee80211_supported_band *a_band = &priv->ieee->a_band;
11390
Johannes Berg57fbcce2016-04-12 15:56:15 +020011391 a_band->band = NL80211_BAND_5GHZ;
John W. Linvillea3caa992009-08-25 14:12:25 -040011392 a_band->n_channels = geo->a_channels;
Joe Perchesbaeb2ff2010-08-11 07:02:48 +000011393 a_band->channels = kcalloc(geo->a_channels,
11394 sizeof(struct ieee80211_channel),
11395 GFP_KERNEL);
Dan Carpenter2ee4e272010-10-04 14:31:46 +020011396 if (!a_band->channels) {
11397 rc = -ENOMEM;
11398 goto out;
11399 }
Stanislav Yakovlev75836b82012-03-20 17:52:57 -040011400 /* translate geo->a to a_band.channels */
John W. Linvillea3caa992009-08-25 14:12:25 -040011401 for (i = 0; i < geo->a_channels; i++) {
Johannes Berg57fbcce2016-04-12 15:56:15 +020011402 a_band->channels[i].band = NL80211_BAND_5GHZ;
John W. Linvillea3caa992009-08-25 14:12:25 -040011403 a_band->channels[i].center_freq = geo->a[i].freq;
11404 a_band->channels[i].hw_value = geo->a[i].channel;
11405 a_band->channels[i].max_power = geo->a[i].max_power;
11406 if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY)
11407 a_band->channels[i].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +020011408 IEEE80211_CHAN_NO_IR;
John W. Linvillea3caa992009-08-25 14:12:25 -040011409 if (geo->a[i].flags & LIBIPW_CH_NO_IBSS)
11410 a_band->channels[i].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +020011411 IEEE80211_CHAN_NO_IR;
John W. Linvillea3caa992009-08-25 14:12:25 -040011412 if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT)
11413 a_band->channels[i].flags |=
11414 IEEE80211_CHAN_RADAR;
11415 /* No equivalent for LIBIPW_CH_80211H_RULES,
11416 LIBIPW_CH_UNIFORM_SPREADING, or
11417 LIBIPW_CH_B_ONLY... */
11418 }
11419 /* point at bitrate info */
11420 a_band->bitrates = ipw2200_a_rates;
11421 a_band->n_bitrates = ipw2200_num_a_rates;
11422
Johannes Berg57fbcce2016-04-12 15:56:15 +020011423 wdev->wiphy->bands[NL80211_BAND_5GHZ] = a_band;
John W. Linvillea3caa992009-08-25 14:12:25 -040011424 }
11425
Stanislav Yakovleva141e6a2012-04-10 21:44:47 -040011426 wdev->wiphy->cipher_suites = ipw_cipher_suites;
11427 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites);
11428
John W. Linvillea3caa992009-08-25 14:12:25 -040011429 set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
11430
11431 /* With that information in place, we can now register the wiphy... */
Stanislaw Gruszka7cabafc2011-09-14 16:47:50 +020011432 if (wiphy_register(wdev->wiphy))
John W. Linvillea3caa992009-08-25 14:12:25 -040011433 rc = -EIO;
John W. Linvillea3caa992009-08-25 14:12:25 -040011434out:
John W. Linvillea3caa992009-08-25 14:12:25 -040011435 return rc;
James Ketrenos43f66a62005-03-25 12:31:53 -060011436}
11437
11438/* PCI driver stuff */
Benoit Taine9baa3c32014-08-08 15:56:03 +020011439static const struct pci_device_id card_ids[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -060011440 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
11441 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
11442 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
11443 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
11444 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
11445 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
11446 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
11447 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
11448 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
11449 {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
11450 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
11451 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
11452 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
11453 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
11454 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
11455 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
11456 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
Joe Perches8ab0ea72009-06-24 22:13:27 -070011457 {PCI_VDEVICE(INTEL, 0x104f), 0},
11458 {PCI_VDEVICE(INTEL, 0x4220), 0}, /* BG */
11459 {PCI_VDEVICE(INTEL, 0x4221), 0}, /* BG */
11460 {PCI_VDEVICE(INTEL, 0x4223), 0}, /* ABG */
11461 {PCI_VDEVICE(INTEL, 0x4224), 0}, /* ABG */
Jeff Garzikbf794512005-07-31 13:07:26 -040011462
James Ketrenos43f66a62005-03-25 12:31:53 -060011463 /* required last entry */
11464 {0,}
11465};
11466
11467MODULE_DEVICE_TABLE(pci, card_ids);
11468
11469static struct attribute *ipw_sysfs_entries[] = {
11470 &dev_attr_rf_kill.attr,
11471 &dev_attr_direct_dword.attr,
11472 &dev_attr_indirect_byte.attr,
11473 &dev_attr_indirect_dword.attr,
11474 &dev_attr_mem_gpio_reg.attr,
11475 &dev_attr_command_event_reg.attr,
11476 &dev_attr_nic_type.attr,
11477 &dev_attr_status.attr,
11478 &dev_attr_cfg.attr,
James Ketrenosb39860c2005-08-12 09:36:32 -050011479 &dev_attr_error.attr,
11480 &dev_attr_event_log.attr,
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011481 &dev_attr_cmd_log.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -060011482 &dev_attr_eeprom_delay.attr,
11483 &dev_attr_ucode_version.attr,
11484 &dev_attr_rtc.attr,
James Ketrenosa613bff2005-08-24 21:43:11 -050011485 &dev_attr_scan_age.attr,
11486 &dev_attr_led.attr,
James Ketrenosb095c382005-08-24 22:04:42 -050011487 &dev_attr_speed_scan.attr,
11488 &dev_attr_net_stats.attr,
Zhu Yi375dd242007-02-14 16:04:24 +080011489 &dev_attr_channels.attr,
Zhu Yid685b8c2006-04-13 17:20:27 +080011490#ifdef CONFIG_IPW2200_PROMISCUOUS
11491 &dev_attr_rtap_iface.attr,
11492 &dev_attr_rtap_filter.attr,
11493#endif
James Ketrenos43f66a62005-03-25 12:31:53 -060011494 NULL
11495};
11496
Arvind Yadavd7979552017-07-18 15:14:59 +053011497static const struct attribute_group ipw_attribute_group = {
James Ketrenos43f66a62005-03-25 12:31:53 -060011498 .name = NULL, /* put in device directory */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011499 .attrs = ipw_sysfs_entries,
James Ketrenos43f66a62005-03-25 12:31:53 -060011500};
11501
Zhu Yid685b8c2006-04-13 17:20:27 +080011502#ifdef CONFIG_IPW2200_PROMISCUOUS
11503static int ipw_prom_open(struct net_device *dev)
11504{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011505 struct ipw_prom_priv *prom_priv = libipw_priv(dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011506 struct ipw_priv *priv = prom_priv->priv;
11507
11508 IPW_DEBUG_INFO("prom dev->open\n");
11509 netif_carrier_off(dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011510
11511 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
11512 priv->sys_config.accept_all_data_frames = 1;
11513 priv->sys_config.accept_non_directed_frames = 1;
11514 priv->sys_config.accept_all_mgmt_bcpr = 1;
11515 priv->sys_config.accept_all_mgmt_frames = 1;
11516
11517 ipw_send_system_config(priv);
11518 }
11519
11520 return 0;
11521}
11522
11523static int ipw_prom_stop(struct net_device *dev)
11524{
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011525 struct ipw_prom_priv *prom_priv = libipw_priv(dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011526 struct ipw_priv *priv = prom_priv->priv;
11527
11528 IPW_DEBUG_INFO("prom dev->stop\n");
11529
11530 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
11531 priv->sys_config.accept_all_data_frames = 0;
11532 priv->sys_config.accept_non_directed_frames = 0;
11533 priv->sys_config.accept_all_mgmt_bcpr = 0;
11534 priv->sys_config.accept_all_mgmt_frames = 0;
11535
11536 ipw_send_system_config(priv);
11537 }
11538
11539 return 0;
11540}
11541
Stephen Hemmingerd0cf9c02009-08-31 19:50:57 +000011542static netdev_tx_t ipw_prom_hard_start_xmit(struct sk_buff *skb,
11543 struct net_device *dev)
Zhu Yid685b8c2006-04-13 17:20:27 +080011544{
11545 IPW_DEBUG_INFO("prom dev->xmit\n");
Patrick McHardy4153e772009-06-12 04:08:02 +000011546 dev_kfree_skb(skb);
11547 return NETDEV_TX_OK;
Zhu Yid685b8c2006-04-13 17:20:27 +080011548}
11549
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011550static const struct net_device_ops ipw_prom_netdev_ops = {
11551 .ndo_open = ipw_prom_open,
11552 .ndo_stop = ipw_prom_stop,
11553 .ndo_start_xmit = ipw_prom_hard_start_xmit,
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011554 .ndo_set_mac_address = eth_mac_addr,
11555 .ndo_validate_addr = eth_validate_addr,
11556};
11557
Zhu Yid685b8c2006-04-13 17:20:27 +080011558static int ipw_prom_alloc(struct ipw_priv *priv)
11559{
11560 int rc = 0;
11561
11562 if (priv->prom_net_dev)
11563 return -EPERM;
11564
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011565 priv->prom_net_dev = alloc_libipw(sizeof(struct ipw_prom_priv), 1);
Zhu Yid685b8c2006-04-13 17:20:27 +080011566 if (priv->prom_net_dev == NULL)
11567 return -ENOMEM;
11568
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011569 priv->prom_priv = libipw_priv(priv->prom_net_dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011570 priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev);
11571 priv->prom_priv->priv = priv;
11572
11573 strcpy(priv->prom_net_dev->name, "rtap%d");
Daniel Drake3f2eeac2008-04-02 20:33:54 +010011574 memcpy(priv->prom_net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
Zhu Yid685b8c2006-04-13 17:20:27 +080011575
11576 priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011577 priv->prom_net_dev->netdev_ops = &ipw_prom_netdev_ops;
Zhu Yid685b8c2006-04-13 17:20:27 +080011578
Jarod Wilson9c22b4a2016-10-20 13:55:18 -040011579 priv->prom_net_dev->min_mtu = 68;
11580 priv->prom_net_dev->max_mtu = LIBIPW_DATA_LEN;
11581
Zhu Yid685b8c2006-04-13 17:20:27 +080011582 priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR;
Masakazu Mokuno229ce3a2008-05-14 14:16:50 +090011583 SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev);
Zhu Yid685b8c2006-04-13 17:20:27 +080011584
11585 rc = register_netdev(priv->prom_net_dev);
11586 if (rc) {
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011587 free_libipw(priv->prom_net_dev, 1);
Zhu Yid685b8c2006-04-13 17:20:27 +080011588 priv->prom_net_dev = NULL;
11589 return rc;
11590 }
11591
11592 return 0;
11593}
11594
11595static void ipw_prom_free(struct ipw_priv *priv)
11596{
11597 if (!priv->prom_net_dev)
11598 return;
11599
11600 unregister_netdev(priv->prom_net_dev);
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011601 free_libipw(priv->prom_net_dev, 1);
Zhu Yid685b8c2006-04-13 17:20:27 +080011602
11603 priv->prom_net_dev = NULL;
11604}
11605
11606#endif
11607
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011608static const struct net_device_ops ipw_netdev_ops = {
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011609 .ndo_open = ipw_net_open,
11610 .ndo_stop = ipw_net_stop,
Jiri Pirkoafc4b132011-08-16 06:29:01 +000011611 .ndo_set_rx_mode = ipw_net_set_multicast_list,
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011612 .ndo_set_mac_address = ipw_net_set_mac_address,
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011613 .ndo_start_xmit = libipw_xmit,
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011614 .ndo_validate_addr = eth_validate_addr,
11615};
Zhu Yid685b8c2006-04-13 17:20:27 +080011616
Bill Pembertoneb9248e2012-12-03 09:56:32 -050011617static int ipw_pci_probe(struct pci_dev *pdev,
Adrian Bunk2ef19e62007-12-11 23:20:22 +010011618 const struct pci_device_id *ent)
James Ketrenos43f66a62005-03-25 12:31:53 -060011619{
11620 int err = 0;
11621 struct net_device *net_dev;
11622 void __iomem *base;
11623 u32 length, val;
11624 struct ipw_priv *priv;
James Ketrenosafbf30a2005-08-25 00:05:33 -050011625 int i;
James Ketrenos43f66a62005-03-25 12:31:53 -060011626
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011627 net_dev = alloc_libipw(sizeof(struct ipw_priv), 0);
James Ketrenos43f66a62005-03-25 12:31:53 -060011628 if (net_dev == NULL) {
11629 err = -ENOMEM;
11630 goto out;
11631 }
11632
John W. Linvilleb0a4e7d2009-08-20 14:48:03 -040011633 priv = libipw_priv(net_dev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011634 priv->ieee = netdev_priv(net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050011635
James Ketrenos43f66a62005-03-25 12:31:53 -060011636 priv->net_dev = net_dev;
11637 priv->pci_dev = pdev;
James Ketrenos43f66a62005-03-25 12:31:53 -060011638 ipw_debug_level = debug;
Zhu Yi89c318e2006-06-08 22:19:49 -070011639 spin_lock_init(&priv->irq_lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060011640 spin_lock_init(&priv->lock);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011641 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
11642 INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -060011643
Zhu Yi46441512006-01-24 16:37:59 +080011644 mutex_init(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011645 if (pci_enable_device(pdev)) {
11646 err = -ENODEV;
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011647 goto out_free_libipw;
James Ketrenos43f66a62005-03-25 12:31:53 -060011648 }
11649
11650 pci_set_master(pdev);
11651
Yang Hongyang284901a2009-04-06 19:01:15 -070011652 err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
Jeff Garzikbf794512005-07-31 13:07:26 -040011653 if (!err)
Yang Hongyang284901a2009-04-06 19:01:15 -070011654 err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
James Ketrenos43f66a62005-03-25 12:31:53 -060011655 if (err) {
11656 printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
11657 goto out_pci_disable_device;
11658 }
11659
11660 pci_set_drvdata(pdev, priv);
11661
11662 err = pci_request_regions(pdev, DRV_NAME);
Jeff Garzikbf794512005-07-31 13:07:26 -040011663 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -060011664 goto out_pci_disable_device;
11665
Jeff Garzikbf794512005-07-31 13:07:26 -040011666 /* We disable the RETRY_TIMEOUT register (0x41) to keep
James Ketrenos43f66a62005-03-25 12:31:53 -060011667 * PCI Tx retries from interfering with C3 CPU state */
Jeff Garzikbf794512005-07-31 13:07:26 -040011668 pci_read_config_dword(pdev, 0x40, &val);
11669 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011670 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
Jeff Garzikbf794512005-07-31 13:07:26 -040011671
James Ketrenos43f66a62005-03-25 12:31:53 -060011672 length = pci_resource_len(pdev, 0);
11673 priv->hw_len = length;
Jeff Garzikbf794512005-07-31 13:07:26 -040011674
Arjan van de Ven275f1652008-10-20 21:42:39 -070011675 base = pci_ioremap_bar(pdev, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -060011676 if (!base) {
11677 err = -ENODEV;
11678 goto out_pci_release_regions;
11679 }
11680
11681 priv->hw_base = base;
11682 IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
11683 IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
11684
11685 err = ipw_setup_deferred_work(priv);
11686 if (err) {
11687 IPW_ERROR("Unable to setup deferred work\n");
11688 goto out_iounmap;
11689 }
11690
James Ketrenosb095c382005-08-24 22:04:42 -050011691 ipw_sw_reset(priv, 1);
James Ketrenos43f66a62005-03-25 12:31:53 -060011692
Thomas Gleixner1fb9df52006-07-01 19:29:39 -070011693 err = request_irq(pdev->irq, ipw_isr, IRQF_SHARED, DRV_NAME, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011694 if (err) {
11695 IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
Tejun Heobcb6d912011-01-26 12:12:50 +010011696 goto out_iounmap;
James Ketrenos43f66a62005-03-25 12:31:53 -060011697 }
11698
James Ketrenos43f66a62005-03-25 12:31:53 -060011699 SET_NETDEV_DEV(net_dev, &pdev->dev);
11700
Zhu Yi46441512006-01-24 16:37:59 +080011701 mutex_lock(&priv->mutex);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011702
James Ketrenos43f66a62005-03-25 12:31:53 -060011703 priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
11704 priv->ieee->set_security = shim__set_security;
James Ketrenos227d2dc2005-07-28 16:25:55 -050011705 priv->ieee->is_queue_full = ipw_net_is_queue_full;
James Ketrenos43f66a62005-03-25 12:31:53 -060011706
Zhu Yie43e3c12006-04-13 17:20:45 +080011707#ifdef CONFIG_IPW2200_QOS
Zhu Yia5cf4fe2006-04-13 17:19:11 +080011708 priv->ieee->is_qos_active = ipw_is_qos_active;
James Ketrenos3b9990c2005-08-19 13:18:55 -050011709 priv->ieee->handle_probe_response = ipw_handle_beacon;
11710 priv->ieee->handle_beacon = ipw_handle_probe_response;
11711 priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
Zhu Yie43e3c12006-04-13 17:20:45 +080011712#endif /* CONFIG_IPW2200_QOS */
James Ketrenosb095c382005-08-24 22:04:42 -050011713
James Ketrenosc848d0a2005-08-24 21:56:24 -050011714 priv->ieee->perfect_rssi = -20;
11715 priv->ieee->worst_rssi = -85;
James Ketrenos43f66a62005-03-25 12:31:53 -060011716
Stephen Hemminger44e9ad02009-03-20 19:36:41 +000011717 net_dev->netdev_ops = &ipw_netdev_ops;
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000011718 priv->wireless_data.spy_data = &priv->ieee->spy_data;
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000011719 net_dev->wireless_data = &priv->wireless_data;
James Ketrenos43f66a62005-03-25 12:31:53 -060011720 net_dev->wireless_handlers = &ipw_wx_handler_def;
11721 net_dev->ethtool_ops = &ipw_ethtool_ops;
James Ketrenos43f66a62005-03-25 12:31:53 -060011722
Jarod Wilson9c22b4a2016-10-20 13:55:18 -040011723 net_dev->min_mtu = 68;
11724 net_dev->max_mtu = LIBIPW_DATA_LEN;
11725
James Ketrenos43f66a62005-03-25 12:31:53 -060011726 err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
11727 if (err) {
11728 IPW_ERROR("failed to create sysfs device attributes\n");
Zhu Yi46441512006-01-24 16:37:59 +080011729 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011730 goto out_release_irq;
11731 }
11732
Stanislav Yakovlev7ed85b62012-04-16 06:38:30 -040011733 if (ipw_up(priv)) {
11734 mutex_unlock(&priv->mutex);
11735 err = -EIO;
11736 goto out_remove_sysfs;
11737 }
11738
Zhu Yi46441512006-01-24 16:37:59 +080011739 mutex_unlock(&priv->mutex);
Henrik Brix Andersen48a84772006-02-14 09:09:52 +080011740
Stanislaw Gruszka7cabafc2011-09-14 16:47:50 +020011741 err = ipw_wdev_init(net_dev);
11742 if (err) {
11743 IPW_ERROR("failed to register wireless device\n");
Ben Hutchingsb4050792012-01-22 03:09:35 +000011744 goto out_remove_sysfs;
11745 }
11746
11747 err = register_netdev(net_dev);
11748 if (err) {
11749 IPW_ERROR("failed to register network device\n");
11750 goto out_unregister_wiphy;
Stanislaw Gruszka7cabafc2011-09-14 16:47:50 +020011751 }
11752
Zhu Yid685b8c2006-04-13 17:20:27 +080011753#ifdef CONFIG_IPW2200_PROMISCUOUS
11754 if (rtap_iface) {
11755 err = ipw_prom_alloc(priv);
11756 if (err) {
11757 IPW_ERROR("Failed to register promiscuous network "
11758 "device (error %d).\n", err);
Ben Hutchingsb4050792012-01-22 03:09:35 +000011759 unregister_netdev(priv->net_dev);
11760 goto out_unregister_wiphy;
Zhu Yid685b8c2006-04-13 17:20:27 +080011761 }
11762 }
11763#endif
11764
Henrik Brix Andersen48a84772006-02-14 09:09:52 +080011765 printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg "
11766 "channels, %d 802.11a channels)\n",
11767 priv->ieee->geo.name, priv->ieee->geo.bg_channels,
11768 priv->ieee->geo.a_channels);
11769
James Ketrenos43f66a62005-03-25 12:31:53 -060011770 return 0;
11771
Ben Hutchingsb4050792012-01-22 03:09:35 +000011772 out_unregister_wiphy:
11773 wiphy_unregister(priv->ieee->wdev.wiphy);
11774 kfree(priv->ieee->a_band.channels);
11775 kfree(priv->ieee->bg_band.channels);
James Ketrenosa613bff2005-08-24 21:43:11 -050011776 out_remove_sysfs:
James Ketrenos43f66a62005-03-25 12:31:53 -060011777 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011778 out_release_irq:
James Ketrenos43f66a62005-03-25 12:31:53 -060011779 free_irq(pdev->irq, priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011780 out_iounmap:
James Ketrenos43f66a62005-03-25 12:31:53 -060011781 iounmap(priv->hw_base);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011782 out_pci_release_regions:
James Ketrenos43f66a62005-03-25 12:31:53 -060011783 pci_release_regions(pdev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011784 out_pci_disable_device:
James Ketrenos43f66a62005-03-25 12:31:53 -060011785 pci_disable_device(pdev);
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011786 out_free_libipw:
11787 free_libipw(priv->net_dev, 0);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011788 out:
James Ketrenos43f66a62005-03-25 12:31:53 -060011789 return err;
11790}
11791
Bill Pembertoneb9248e2012-12-03 09:56:32 -050011792static void ipw_pci_remove(struct pci_dev *pdev)
James Ketrenos43f66a62005-03-25 12:31:53 -060011793{
11794 struct ipw_priv *priv = pci_get_drvdata(pdev);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011795 struct list_head *p, *q;
11796 int i;
James Ketrenosb095c382005-08-24 22:04:42 -050011797
James Ketrenos43f66a62005-03-25 12:31:53 -060011798 if (!priv)
11799 return;
11800
Zhu Yi46441512006-01-24 16:37:59 +080011801 mutex_lock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011802
James Ketrenosafbf30a2005-08-25 00:05:33 -050011803 priv->status |= STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011804 ipw_down(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011805 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
11806
Zhu Yi46441512006-01-24 16:37:59 +080011807 mutex_unlock(&priv->mutex);
James Ketrenos43f66a62005-03-25 12:31:53 -060011808
11809 unregister_netdev(priv->net_dev);
11810
11811 if (priv->rxq) {
11812 ipw_rx_queue_free(priv, priv->rxq);
11813 priv->rxq = NULL;
11814 }
11815 ipw_tx_queue_free(priv);
11816
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011817 if (priv->cmdlog) {
11818 kfree(priv->cmdlog);
11819 priv->cmdlog = NULL;
11820 }
Tejun Heobcb6d912011-01-26 12:12:50 +010011821
11822 /* make sure all works are inactive */
11823 cancel_delayed_work_sync(&priv->adhoc_check);
11824 cancel_work_sync(&priv->associate);
11825 cancel_work_sync(&priv->disassociate);
11826 cancel_work_sync(&priv->system_config);
11827 cancel_work_sync(&priv->rx_replenish);
11828 cancel_work_sync(&priv->adapter_restart);
11829 cancel_delayed_work_sync(&priv->rf_kill);
11830 cancel_work_sync(&priv->up);
11831 cancel_work_sync(&priv->down);
11832 cancel_delayed_work_sync(&priv->request_scan);
11833 cancel_delayed_work_sync(&priv->request_direct_scan);
11834 cancel_delayed_work_sync(&priv->request_passive_scan);
11835 cancel_delayed_work_sync(&priv->scan_event);
11836 cancel_delayed_work_sync(&priv->gather_stats);
11837 cancel_work_sync(&priv->abort_scan);
11838 cancel_work_sync(&priv->roam);
11839 cancel_delayed_work_sync(&priv->scan_check);
11840 cancel_work_sync(&priv->link_up);
11841 cancel_work_sync(&priv->link_down);
11842 cancel_delayed_work_sync(&priv->led_link_on);
11843 cancel_delayed_work_sync(&priv->led_link_off);
11844 cancel_delayed_work_sync(&priv->led_act_off);
11845 cancel_work_sync(&priv->merge_networks);
James Ketrenos43f66a62005-03-25 12:31:53 -060011846
James Ketrenosafbf30a2005-08-25 00:05:33 -050011847 /* Free MAC hash list for ADHOC */
11848 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) {
11849 list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050011850 list_del(p);
Zhu Yi489f4452006-01-24 16:37:41 +080011851 kfree(list_entry(p, struct ipw_ibss_seq, list));
James Ketrenosafbf30a2005-08-25 00:05:33 -050011852 }
11853 }
11854
Jesper Juhl8f760782006-06-27 02:55:06 -070011855 kfree(priv->error);
11856 priv->error = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -060011857
Zhu Yid685b8c2006-04-13 17:20:27 +080011858#ifdef CONFIG_IPW2200_PROMISCUOUS
11859 ipw_prom_free(priv);
11860#endif
11861
James Ketrenos43f66a62005-03-25 12:31:53 -060011862 free_irq(pdev->irq, priv);
11863 iounmap(priv->hw_base);
11864 pci_release_regions(pdev);
11865 pci_disable_device(pdev);
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011866 /* wiphy_unregister needs to be here, before free_libipw */
John W. Linvillea3caa992009-08-25 14:12:25 -040011867 wiphy_unregister(priv->ieee->wdev.wiphy);
11868 kfree(priv->ieee->a_band.channels);
11869 kfree(priv->ieee->bg_band.channels);
Pavel Roskin27ae60f2010-03-12 00:01:22 -050011870 free_libipw(priv->net_dev, 0);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011871 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -060011872}
11873
James Ketrenos43f66a62005-03-25 12:31:53 -060011874#ifdef CONFIG_PM
Pavel Machek583a4e82005-09-03 15:56:58 -070011875static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
James Ketrenos43f66a62005-03-25 12:31:53 -060011876{
11877 struct ipw_priv *priv = pci_get_drvdata(pdev);
11878 struct net_device *dev = priv->net_dev;
11879
11880 printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
11881
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011882 /* Take down the device; powers it off, etc. */
James Ketrenos43f66a62005-03-25 12:31:53 -060011883 ipw_down(priv);
11884
11885 /* Remove the PRESENT state of the device */
11886 netif_device_detach(dev);
11887
James Ketrenos43f66a62005-03-25 12:31:53 -060011888 pci_save_state(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011889 pci_disable_device(pdev);
Pavel Machek583a4e82005-09-03 15:56:58 -070011890 pci_set_power_state(pdev, pci_choose_state(pdev, state));
Jeff Garzikbf794512005-07-31 13:07:26 -040011891
Dan Williamsc3d72b92009-02-11 13:26:06 -050011892 priv->suspend_at = get_seconds();
11893
James Ketrenos43f66a62005-03-25 12:31:53 -060011894 return 0;
11895}
11896
11897static int ipw_pci_resume(struct pci_dev *pdev)
11898{
11899 struct ipw_priv *priv = pci_get_drvdata(pdev);
11900 struct net_device *dev = priv->net_dev;
John W. Linville02e0e5e2006-11-07 20:53:48 -050011901 int err;
James Ketrenos43f66a62005-03-25 12:31:53 -060011902 u32 val;
Jeff Garzikbf794512005-07-31 13:07:26 -040011903
James Ketrenos43f66a62005-03-25 12:31:53 -060011904 printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
11905
James Ketrenosea2b26e2005-08-24 21:25:16 -050011906 pci_set_power_state(pdev, PCI_D0);
John W. Linville02e0e5e2006-11-07 20:53:48 -050011907 err = pci_enable_device(pdev);
11908 if (err) {
11909 printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
11910 dev->name);
11911 return err;
11912 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011913 pci_restore_state(pdev);
James Ketrenosea2b26e2005-08-24 21:25:16 -050011914
James Ketrenos43f66a62005-03-25 12:31:53 -060011915 /*
11916 * Suspend/Resume resets the PCI configuration space, so we have to
11917 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
11918 * from interfering with C3 CPU state. pci_restore_state won't help
11919 * here since it only restores the first 64 bytes pci config header.
11920 */
Jeff Garzikbf794512005-07-31 13:07:26 -040011921 pci_read_config_dword(pdev, 0x40, &val);
11922 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011923 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
11924
11925 /* Set the device back into the PRESENT state; this will also wake
11926 * the queue of needed */
11927 netif_device_attach(dev);
11928
Dan Williamsc3d72b92009-02-11 13:26:06 -050011929 priv->suspend_time = get_seconds() - priv->suspend_at;
11930
James Ketrenos43f66a62005-03-25 12:31:53 -060011931 /* Bring the device back up */
Tejun Heobcb6d912011-01-26 12:12:50 +010011932 schedule_work(&priv->up);
Jeff Garzikbf794512005-07-31 13:07:26 -040011933
James Ketrenos43f66a62005-03-25 12:31:53 -060011934 return 0;
11935}
11936#endif
11937
Zhu Yic8c22c92006-08-21 11:38:39 +080011938static void ipw_pci_shutdown(struct pci_dev *pdev)
11939{
11940 struct ipw_priv *priv = pci_get_drvdata(pdev);
11941
11942 /* Take down the device; powers it off, etc. */
11943 ipw_down(priv);
11944
11945 pci_disable_device(pdev);
11946}
11947
James Ketrenos43f66a62005-03-25 12:31:53 -060011948/* driver initialization stuff */
11949static struct pci_driver ipw_driver = {
11950 .name = DRV_NAME,
11951 .id_table = card_ids,
11952 .probe = ipw_pci_probe,
Bill Pembertoneb9248e2012-12-03 09:56:32 -050011953 .remove = ipw_pci_remove,
James Ketrenos43f66a62005-03-25 12:31:53 -060011954#ifdef CONFIG_PM
11955 .suspend = ipw_pci_suspend,
11956 .resume = ipw_pci_resume,
11957#endif
Zhu Yic8c22c92006-08-21 11:38:39 +080011958 .shutdown = ipw_pci_shutdown,
James Ketrenos43f66a62005-03-25 12:31:53 -060011959};
11960
11961static int __init ipw_init(void)
11962{
11963 int ret;
11964
11965 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
11966 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
11967
Jeff Garzik29917622006-08-19 17:48:59 -040011968 ret = pci_register_driver(&ipw_driver);
James Ketrenos43f66a62005-03-25 12:31:53 -060011969 if (ret) {
11970 IPW_ERROR("Unable to initialize PCI module\n");
11971 return ret;
11972 }
11973
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011974 ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -060011975 if (ret) {
11976 IPW_ERROR("Unable to create driver sysfs file\n");
11977 pci_unregister_driver(&ipw_driver);
11978 return ret;
11979 }
11980
11981 return ret;
11982}
11983
11984static void __exit ipw_exit(void)
11985{
11986 driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
11987 pci_unregister_driver(&ipw_driver);
11988}
11989
11990module_param(disable, int, 0444);
11991MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
11992
11993module_param(associate, int, 0444);
Tim Gardner5c7f9b72008-10-14 10:38:03 -060011994MODULE_PARM_DESC(associate, "auto associate when scanning (default off)");
James Ketrenos43f66a62005-03-25 12:31:53 -060011995
11996module_param(auto_create, int, 0444);
11997MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
11998
Reinette Chatre21f8a732009-08-18 10:25:05 -070011999module_param_named(led, led_support, int, 0444);
ubuntu@tjworld.netc086aba2009-03-23 20:29:28 +000012000MODULE_PARM_DESC(led, "enable led control on some systems (default 1 on)");
James Ketrenosa613bff2005-08-24 21:43:11 -050012001
James Ketrenos43f66a62005-03-25 12:31:53 -060012002module_param(debug, int, 0444);
12003MODULE_PARM_DESC(debug, "debug output mask");
12004
Reinette Chatre21f8a732009-08-18 10:25:05 -070012005module_param_named(channel, default_channel, int, 0444);
Jeff Garzikbf794512005-07-31 13:07:26 -040012006MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
James Ketrenos43f66a62005-03-25 12:31:53 -060012007
Zhu Yid685b8c2006-04-13 17:20:27 +080012008#ifdef CONFIG_IPW2200_PROMISCUOUS
12009module_param(rtap_iface, int, 0444);
12010MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)");
12011#endif
12012
Zhu Yie43e3c12006-04-13 17:20:45 +080012013#ifdef CONFIG_IPW2200_QOS
James Ketrenosb095c382005-08-24 22:04:42 -050012014module_param(qos_enable, int, 0444);
Colin Ian King7e7939e2018-05-01 10:36:43 +010012015MODULE_PARM_DESC(qos_enable, "enable all QoS functionalities");
James Ketrenos43f66a62005-03-25 12:31:53 -060012016
James Ketrenosb095c382005-08-24 22:04:42 -050012017module_param(qos_burst_enable, int, 0444);
12018MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
12019
12020module_param(qos_no_ack_mask, int, 0444);
12021MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack");
12022
12023module_param(burst_duration_CCK, int, 0444);
12024MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value");
12025
12026module_param(burst_duration_OFDM, int, 0444);
12027MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
Zhu Yie43e3c12006-04-13 17:20:45 +080012028#endif /* CONFIG_IPW2200_QOS */
James Ketrenosb095c382005-08-24 22:04:42 -050012029
12030#ifdef CONFIG_IPW2200_MONITOR
Reinette Chatre21f8a732009-08-18 10:25:05 -070012031module_param_named(mode, network_mode, int, 0444);
James Ketrenos43f66a62005-03-25 12:31:53 -060012032MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
12033#else
Reinette Chatre21f8a732009-08-18 10:25:05 -070012034module_param_named(mode, network_mode, int, 0444);
James Ketrenos43f66a62005-03-25 12:31:53 -060012035MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
12036#endif
12037
Zhu Yi810dabd2006-01-24 16:36:59 +080012038module_param(bt_coexist, int, 0444);
12039MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)");
12040
James Ketrenosb095c382005-08-24 22:04:42 -050012041module_param(hwcrypto, int, 0444);
Zhu Yibde37d02006-01-24 16:38:08 +080012042MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default off)");
James Ketrenosb095c382005-08-24 22:04:42 -050012043
James Ketrenosf6c5cb72005-08-25 00:39:09 -050012044module_param(cmdlog, int, 0444);
12045MODULE_PARM_DESC(cmdlog,
12046 "allocate a ring buffer for logging firmware commands");
12047
Zhu Yi4bfdb912006-01-24 16:37:16 +080012048module_param(roaming, int, 0444);
12049MODULE_PARM_DESC(roaming, "enable roaming support (default on)");
12050
Zhu Yid2b83e12006-04-13 17:19:36 +080012051module_param(antenna, int, 0444);
12052MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)");
12053
James Ketrenos43f66a62005-03-25 12:31:53 -060012054module_exit(ipw_exit);
12055module_init(ipw_init);