blob: 624f29500490fe4fcb69809025fc390ce2c19c27 [file] [log] [blame]
James Ketrenos43f66a62005-03-25 12:31:53 -06001/******************************************************************************
Jeff Garzikbf794512005-07-31 13:07:26 -04002
James Ketrenosafbf30a2005-08-25 00:05:33 -05003 Copyright(c) 2003 - 2005 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:
28 James P. Ketrenos <ipw2100-admin@linux.intel.com>
29 Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31******************************************************************************/
32
33#include "ipw2200.h"
Olaf Hering733482e2005-11-08 21:34:55 -080034#include <linux/version.h>
James Ketrenos43f66a62005-03-25 12:31:53 -060035
James Ketrenoscf1b4792005-10-20 16:35:24 -050036#define IPW2200_VERSION "git-1.0.8"
James Ketrenos43f66a62005-03-25 12:31:53 -060037#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
James Ketrenos2b184d52005-08-03 20:33:14 -050038#define DRV_COPYRIGHT "Copyright(c) 2003-2005 Intel Corporation"
James Ketrenos43f66a62005-03-25 12:31:53 -060039#define DRV_VERSION IPW2200_VERSION
40
James Ketrenosb095c382005-08-24 22:04:42 -050041#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
42
James Ketrenos43f66a62005-03-25 12:31:53 -060043MODULE_DESCRIPTION(DRV_DESCRIPTION);
44MODULE_VERSION(DRV_VERSION);
45MODULE_AUTHOR(DRV_COPYRIGHT);
46MODULE_LICENSE("GPL");
47
James Ketrenosf6c5cb72005-08-25 00:39:09 -050048static int cmdlog = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060049static int debug = 0;
50static int channel = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060051static int mode = 0;
52
53static u32 ipw_debug_level;
54static int associate = 1;
55static int auto_create = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -050056static int led = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060057static int disable = 0;
Zhu Yi810dabd2006-01-24 16:36:59 +080058static int bt_coexist = 0;
James Ketrenosb095c382005-08-24 22:04:42 -050059static int hwcrypto = 1;
Zhu Yi4bfdb912006-01-24 16:37:16 +080060static int roaming = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -060061static const char ipw_modes[] = {
62 'a', 'b', 'g', '?'
63};
64
James Ketrenosb095c382005-08-24 22:04:42 -050065#ifdef CONFIG_IPW_QOS
66static int qos_enable = 0;
67static int qos_burst_enable = 0;
68static int qos_no_ack_mask = 0;
69static int burst_duration_CCK = 0;
70static int burst_duration_OFDM = 0;
71
72static struct ieee80211_qos_parameters def_qos_parameters_OFDM = {
73 {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM,
74 QOS_TX3_CW_MIN_OFDM},
75 {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM,
76 QOS_TX3_CW_MAX_OFDM},
77 {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
78 {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
79 {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM,
80 QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM}
81};
82
83static struct ieee80211_qos_parameters def_qos_parameters_CCK = {
84 {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK,
85 QOS_TX3_CW_MIN_CCK},
86 {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK,
87 QOS_TX3_CW_MAX_CCK},
88 {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
89 {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
90 {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK,
91 QOS_TX3_TXOP_LIMIT_CCK}
92};
93
94static struct ieee80211_qos_parameters def_parameters_OFDM = {
95 {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM,
96 DEF_TX3_CW_MIN_OFDM},
97 {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM,
98 DEF_TX3_CW_MAX_OFDM},
99 {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
100 {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
101 {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM,
102 DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM}
103};
104
105static struct ieee80211_qos_parameters def_parameters_CCK = {
106 {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK,
107 DEF_TX3_CW_MIN_CCK},
108 {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK,
109 DEF_TX3_CW_MAX_CCK},
110 {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
111 {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
112 {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK,
113 DEF_TX3_TXOP_LIMIT_CCK}
114};
115
116static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
117
118static int from_priority_to_tx_queue[] = {
119 IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1,
120 IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
121};
122
123static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv);
124
125static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
126 *qos_param);
127static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
128 *qos_param);
129#endif /* CONFIG_IPW_QOS */
130
Benoit Boissinot97a78ca2005-09-15 17:30:28 +0000131static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
James Ketrenosb095c382005-08-24 22:04:42 -0500132static void ipw_remove_current_network(struct ipw_priv *priv);
James Ketrenos43f66a62005-03-25 12:31:53 -0600133static void ipw_rx(struct ipw_priv *priv);
Jeff Garzikbf794512005-07-31 13:07:26 -0400134static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -0600135 struct clx2_tx_queue *txq, int qindex);
136static int ipw_queue_reset(struct ipw_priv *priv);
137
138static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
139 int len, int sync);
140
141static void ipw_tx_queue_free(struct ipw_priv *);
142
143static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
144static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
145static void ipw_rx_queue_replenish(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600146static int ipw_up(struct ipw_priv *);
James Ketrenosc848d0a2005-08-24 21:56:24 -0500147static void ipw_bg_up(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600148static void ipw_down(struct ipw_priv *);
James Ketrenosc848d0a2005-08-24 21:56:24 -0500149static void ipw_bg_down(void *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600150static int ipw_config(struct ipw_priv *);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400151static int init_supported_rates(struct ipw_priv *priv,
152 struct ipw_supported_rates *prates);
James Ketrenosb095c382005-08-24 22:04:42 -0500153static void ipw_set_hwcrypto_keys(struct ipw_priv *);
154static void ipw_send_wep_keys(struct ipw_priv *, int);
James Ketrenos43f66a62005-03-25 12:31:53 -0600155
Liu Hong1fe0adb2005-08-19 09:33:10 -0500156static int ipw_is_valid_channel(struct ieee80211_device *, u8);
157static int ipw_channel_to_index(struct ieee80211_device *, u8);
158static u8 ipw_freq_to_channel(struct ieee80211_device *, u32);
159static int ipw_set_geo(struct ieee80211_device *, const struct ieee80211_geo *);
160static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *);
James Ketrenos43f66a62005-03-25 12:31:53 -0600161
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500162static int snprint_line(char *buf, size_t count,
163 const u8 * data, u32 len, u32 ofs)
James Ketrenos43f66a62005-03-25 12:31:53 -0600164{
165 int out, i, j, l;
166 char c;
Jeff Garzikbf794512005-07-31 13:07:26 -0400167
James Ketrenos43f66a62005-03-25 12:31:53 -0600168 out = snprintf(buf, count, "%08X", ofs);
169
170 for (l = 0, i = 0; i < 2; i++) {
171 out += snprintf(buf + out, count - out, " ");
Jeff Garzikbf794512005-07-31 13:07:26 -0400172 for (j = 0; j < 8 && l < len; j++, l++)
173 out += snprintf(buf + out, count - out, "%02X ",
James Ketrenos43f66a62005-03-25 12:31:53 -0600174 data[(i * 8 + j)]);
175 for (; j < 8; j++)
176 out += snprintf(buf + out, count - out, " ");
177 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400178
James Ketrenos43f66a62005-03-25 12:31:53 -0600179 out += snprintf(buf + out, count - out, " ");
180 for (l = 0, i = 0; i < 2; i++) {
181 out += snprintf(buf + out, count - out, " ");
182 for (j = 0; j < 8 && l < len; j++, l++) {
183 c = data[(i * 8 + j)];
184 if (!isascii(c) || !isprint(c))
185 c = '.';
Jeff Garzikbf794512005-07-31 13:07:26 -0400186
James Ketrenos43f66a62005-03-25 12:31:53 -0600187 out += snprintf(buf + out, count - out, "%c", c);
188 }
189
190 for (; j < 8; j++)
191 out += snprintf(buf + out, count - out, " ");
192 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400193
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500194 return out;
James Ketrenos43f66a62005-03-25 12:31:53 -0600195}
196
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400197static void printk_buf(int level, const u8 * data, u32 len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600198{
199 char line[81];
200 u32 ofs = 0;
201 if (!(ipw_debug_level & level))
202 return;
203
204 while (len) {
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500205 snprint_line(line, sizeof(line), &data[ofs],
206 min(len, 16U), ofs);
207 printk(KERN_DEBUG "%s\n", line);
James Ketrenos43f66a62005-03-25 12:31:53 -0600208 ofs += 16;
209 len -= min(len, 16U);
210 }
211}
212
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500213static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
214{
215 size_t out = size;
216 u32 ofs = 0;
217 int total = 0;
218
219 while (size && len) {
220 out = snprint_line(output, size, &data[ofs],
221 min_t(size_t, len, 16U), ofs);
222
223 ofs += 16;
224 output += out;
225 size -= out;
226 len -= min_t(size_t, len, 16U);
227 total += out;
228 }
229 return total;
230}
231
Zhu Yic8fe6672006-01-24 16:36:36 +0800232/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600233static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
234#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
235
Zhu Yic8fe6672006-01-24 16:36:36 +0800236/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600237static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
238#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
239
Zhu Yic8fe6672006-01-24 16:36:36 +0800240/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600241static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
242static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
243{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400244 IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
245 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600246 _ipw_write_reg8(a, b, c);
247}
248
Zhu Yic8fe6672006-01-24 16:36:36 +0800249/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600250static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
251static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
252{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400253 IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
254 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600255 _ipw_write_reg16(a, b, c);
256}
257
Zhu Yic8fe6672006-01-24 16:36:36 +0800258/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600259static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
260static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
261{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400262 IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
263 __LINE__, (u32) (b), (u32) (c));
James Ketrenos43f66a62005-03-25 12:31:53 -0600264 _ipw_write_reg32(a, b, c);
265}
266
Zhu Yic8fe6672006-01-24 16:36:36 +0800267/* 8-bit direct write (low 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600268#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
Zhu Yic8fe6672006-01-24 16:36:36 +0800269
270/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600271#define ipw_write8(ipw, ofs, val) \
272 IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
273 _ipw_write8(ipw, ofs, val)
274
Zhu Yic8fe6672006-01-24 16:36:36 +0800275
276/* 16-bit direct write (low 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600277#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
Zhu Yic8fe6672006-01-24 16:36:36 +0800278
279/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600280#define ipw_write16(ipw, ofs, val) \
281 IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
282 _ipw_write16(ipw, ofs, val)
283
Zhu Yic8fe6672006-01-24 16:36:36 +0800284
285/* 32-bit direct write (low 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600286#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
Zhu Yic8fe6672006-01-24 16:36:36 +0800287
288/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600289#define ipw_write32(ipw, ofs, val) \
290 IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
291 _ipw_write32(ipw, ofs, val)
292
Zhu Yic8fe6672006-01-24 16:36:36 +0800293
294/* 8-bit direct read (low 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600295#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
Zhu Yic8fe6672006-01-24 16:36:36 +0800296
297/* 8-bit direct read (low 4K), with debug wrapper */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400298static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
299{
300 IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600301 return _ipw_read8(ipw, ofs);
302}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400303
Zhu Yic8fe6672006-01-24 16:36:36 +0800304/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600305#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
306
Zhu Yic8fe6672006-01-24 16:36:36 +0800307
308/* 16-bit direct read (low 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600309#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
Zhu Yic8fe6672006-01-24 16:36:36 +0800310
311/* 16-bit direct read (low 4K), with debug wrapper */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400312static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
313{
314 IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600315 return _ipw_read16(ipw, ofs);
316}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400317
Zhu Yic8fe6672006-01-24 16:36:36 +0800318/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600319#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
320
Zhu Yic8fe6672006-01-24 16:36:36 +0800321
322/* 32-bit direct read (low 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600323#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
Zhu Yic8fe6672006-01-24 16:36:36 +0800324
325/* 32-bit direct read (low 4K), with debug wrapper */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400326static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
327{
328 IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs));
James Ketrenos43f66a62005-03-25 12:31:53 -0600329 return _ipw_read32(ipw, ofs);
330}
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400331
Zhu Yic8fe6672006-01-24 16:36:36 +0800332/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600333#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
334
Zhu Yic8fe6672006-01-24 16:36:36 +0800335
336/* multi-byte read (above 4K), with debug wrapper */
James Ketrenos43f66a62005-03-25 12:31:53 -0600337static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500338static inline void __ipw_read_indirect(const char *f, int l,
339 struct ipw_priv *a, u32 b, u8 * c, int d)
340{
341 IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b),
342 d);
343 _ipw_read_indirect(a, b, c, d);
344}
345
Zhu Yic8fe6672006-01-24 16:36:36 +0800346/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
James Ketrenosf6c5cb72005-08-25 00:39:09 -0500347#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
James Ketrenos43f66a62005-03-25 12:31:53 -0600348
Zhu Yic8fe6672006-01-24 16:36:36 +0800349/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400350static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
351 int num);
James Ketrenos43f66a62005-03-25 12:31:53 -0600352#define ipw_write_indirect(a, b, c, d) \
353 IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
James Ketrenosafbf30a2005-08-25 00:05:33 -0500354 _ipw_write_indirect(a, b, c, d)
James Ketrenos43f66a62005-03-25 12:31:53 -0600355
Zhu Yic8fe6672006-01-24 16:36:36 +0800356/* 32-bit indirect write (above 4K) */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400357static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600358{
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400359 IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
James Ketrenosb095c382005-08-24 22:04:42 -0500360 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
361 _ipw_write32(priv, IPW_INDIRECT_DATA, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600362}
363
Zhu Yic8fe6672006-01-24 16:36:36 +0800364/* 8-bit indirect write (above 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600365static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
366{
Zhu Yic8fe6672006-01-24 16:36:36 +0800367 u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
368 u32 dif_len = reg - aligned_addr;
369
James Ketrenos43f66a62005-03-25 12:31:53 -0600370 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
Zhu Yic8fe6672006-01-24 16:36:36 +0800371 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
372 _ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600373}
374
Zhu Yic8fe6672006-01-24 16:36:36 +0800375/* 16-bit indirect write (above 4K) */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400376static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
James Ketrenos43f66a62005-03-25 12:31:53 -0600377{
Zhu Yic8fe6672006-01-24 16:36:36 +0800378 u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
379 u32 dif_len = (reg - aligned_addr) & (~0x1ul);
380
James Ketrenos43f66a62005-03-25 12:31:53 -0600381 IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
Zhu Yic8fe6672006-01-24 16:36:36 +0800382 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
383 _ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
James Ketrenos43f66a62005-03-25 12:31:53 -0600384}
385
James Ketrenos43f66a62005-03-25 12:31:53 -0600386
Zhu Yic8fe6672006-01-24 16:36:36 +0800387/* 8-bit indirect read (above 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600388static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
389{
390 u32 word;
James Ketrenosb095c382005-08-24 22:04:42 -0500391 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -0600392 IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
James Ketrenosb095c382005-08-24 22:04:42 -0500393 word = _ipw_read32(priv, IPW_INDIRECT_DATA);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400394 return (word >> ((reg & 0x3) * 8)) & 0xff;
James Ketrenos43f66a62005-03-25 12:31:53 -0600395}
396
Zhu Yic8fe6672006-01-24 16:36:36 +0800397/* 32-bit indirect read (above 4K) */
James Ketrenos43f66a62005-03-25 12:31:53 -0600398static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
399{
400 u32 value;
401
402 IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
403
James Ketrenosb095c382005-08-24 22:04:42 -0500404 _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
405 value = _ipw_read32(priv, IPW_INDIRECT_DATA);
James Ketrenos43f66a62005-03-25 12:31:53 -0600406 IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
407 return value;
408}
409
Zhu Yic8fe6672006-01-24 16:36:36 +0800410/* General purpose, no alignment requirement, iterative (multi-byte) read, */
411/* for area above 1st 4K of SRAM/reg space */
James Ketrenos43f66a62005-03-25 12:31:53 -0600412static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
413 int num)
414{
Zhu Yic8fe6672006-01-24 16:36:36 +0800415 u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
James Ketrenos43f66a62005-03-25 12:31:53 -0600416 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600417 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400418
James Ketrenos43f66a62005-03-25 12:31:53 -0600419 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
420
James Ketrenosea2b26e2005-08-24 21:25:16 -0500421 if (num <= 0) {
422 return;
423 }
424
Zhu Yic8fe6672006-01-24 16:36:36 +0800425 /* Read the first dword (or portion) byte by byte */
James Ketrenos43f66a62005-03-25 12:31:53 -0600426 if (unlikely(dif_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500427 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -0600428 /* Start reading at aligned_addr + dif_len */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500429 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
James Ketrenosb095c382005-08-24 22:04:42 -0500430 *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenos43f66a62005-03-25 12:31:53 -0600431 aligned_addr += 4;
432 }
433
Zhu Yic8fe6672006-01-24 16:36:36 +0800434 /* Read all of the middle dwords as dwords, with auto-increment */
James Ketrenosb095c382005-08-24 22:04:42 -0500435 _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500436 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
James Ketrenosb095c382005-08-24 22:04:42 -0500437 *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
Jeff Garzikbf794512005-07-31 13:07:26 -0400438
Zhu Yic8fe6672006-01-24 16:36:36 +0800439 /* Read the last dword (or portion) byte by byte */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500440 if (unlikely(num)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500441 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500442 for (i = 0; num > 0; i++, num--)
James Ketrenosb095c382005-08-24 22:04:42 -0500443 *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500444 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600445}
446
Zhu Yic8fe6672006-01-24 16:36:36 +0800447/* General purpose, no alignment requirement, iterative (multi-byte) write, */
448/* for area above 1st 4K of SRAM/reg space */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400449static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600450 int num)
451{
Zhu Yic8fe6672006-01-24 16:36:36 +0800452 u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
James Ketrenos43f66a62005-03-25 12:31:53 -0600453 u32 dif_len = addr - aligned_addr;
James Ketrenos43f66a62005-03-25 12:31:53 -0600454 u32 i;
Jeff Garzikbf794512005-07-31 13:07:26 -0400455
James Ketrenos43f66a62005-03-25 12:31:53 -0600456 IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
Jeff Garzikbf794512005-07-31 13:07:26 -0400457
James Ketrenosea2b26e2005-08-24 21:25:16 -0500458 if (num <= 0) {
459 return;
460 }
461
Zhu Yic8fe6672006-01-24 16:36:36 +0800462 /* Write the first dword (or portion) byte by byte */
James Ketrenos43f66a62005-03-25 12:31:53 -0600463 if (unlikely(dif_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500464 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
Zhu Yic8fe6672006-01-24 16:36:36 +0800465 /* Start writing at aligned_addr + dif_len */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500466 for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
James Ketrenosb095c382005-08-24 22:04:42 -0500467 _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenos43f66a62005-03-25 12:31:53 -0600468 aligned_addr += 4;
469 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400470
Zhu Yic8fe6672006-01-24 16:36:36 +0800471 /* Write all of the middle dwords as dwords, with auto-increment */
James Ketrenosb095c382005-08-24 22:04:42 -0500472 _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500473 for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
James Ketrenosb095c382005-08-24 22:04:42 -0500474 _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
Jeff Garzikbf794512005-07-31 13:07:26 -0400475
Zhu Yic8fe6672006-01-24 16:36:36 +0800476 /* Write the last dword (or portion) byte by byte */
James Ketrenosea2b26e2005-08-24 21:25:16 -0500477 if (unlikely(num)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500478 _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500479 for (i = 0; num > 0; i++, num--, buf++)
James Ketrenosb095c382005-08-24 22:04:42 -0500480 _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
James Ketrenosea2b26e2005-08-24 21:25:16 -0500481 }
James Ketrenos43f66a62005-03-25 12:31:53 -0600482}
483
Zhu Yic8fe6672006-01-24 16:36:36 +0800484/* General purpose, no alignment requirement, iterative (multi-byte) write, */
485/* for 1st 4K of SRAM/regs space */
Jeff Garzikbf794512005-07-31 13:07:26 -0400486static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
James Ketrenos43f66a62005-03-25 12:31:53 -0600487 int num)
488{
489 memcpy_toio((priv->hw_base + addr), buf, num);
490}
491
Zhu Yic8fe6672006-01-24 16:36:36 +0800492/* Set bit(s) in low 4K of SRAM/regs */
James Ketrenos43f66a62005-03-25 12:31:53 -0600493static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
494{
495 ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
496}
497
Zhu Yic8fe6672006-01-24 16:36:36 +0800498/* Clear bit(s) in low 4K of SRAM/regs */
James Ketrenos43f66a62005-03-25 12:31:53 -0600499static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
500{
501 ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
502}
503
504static inline void ipw_enable_interrupts(struct ipw_priv *priv)
505{
506 if (priv->status & STATUS_INT_ENABLED)
507 return;
508 priv->status |= STATUS_INT_ENABLED;
James Ketrenosb095c382005-08-24 22:04:42 -0500509 ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600510}
511
512static inline void ipw_disable_interrupts(struct ipw_priv *priv)
513{
514 if (!(priv->status & STATUS_INT_ENABLED))
515 return;
516 priv->status &= ~STATUS_INT_ENABLED;
James Ketrenosb095c382005-08-24 22:04:42 -0500517 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -0600518}
519
Brice Goglin0f52bf92005-12-01 01:41:46 -0800520#ifdef CONFIG_IPW2200_DEBUG
James Ketrenos43f66a62005-03-25 12:31:53 -0600521static char *ipw_error_desc(u32 val)
522{
523 switch (val) {
Jeff Garzikbf794512005-07-31 13:07:26 -0400524 case IPW_FW_ERROR_OK:
James Ketrenos43f66a62005-03-25 12:31:53 -0600525 return "ERROR_OK";
Jeff Garzikbf794512005-07-31 13:07:26 -0400526 case IPW_FW_ERROR_FAIL:
James Ketrenos43f66a62005-03-25 12:31:53 -0600527 return "ERROR_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400528 case IPW_FW_ERROR_MEMORY_UNDERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600529 return "MEMORY_UNDERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400530 case IPW_FW_ERROR_MEMORY_OVERFLOW:
James Ketrenos43f66a62005-03-25 12:31:53 -0600531 return "MEMORY_OVERFLOW";
Jeff Garzikbf794512005-07-31 13:07:26 -0400532 case IPW_FW_ERROR_BAD_PARAM:
James Ketrenosb095c382005-08-24 22:04:42 -0500533 return "BAD_PARAM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400534 case IPW_FW_ERROR_BAD_CHECKSUM:
James Ketrenosb095c382005-08-24 22:04:42 -0500535 return "BAD_CHECKSUM";
Jeff Garzikbf794512005-07-31 13:07:26 -0400536 case IPW_FW_ERROR_NMI_INTERRUPT:
James Ketrenosb095c382005-08-24 22:04:42 -0500537 return "NMI_INTERRUPT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400538 case IPW_FW_ERROR_BAD_DATABASE:
James Ketrenosb095c382005-08-24 22:04:42 -0500539 return "BAD_DATABASE";
Jeff Garzikbf794512005-07-31 13:07:26 -0400540 case IPW_FW_ERROR_ALLOC_FAIL:
James Ketrenosb095c382005-08-24 22:04:42 -0500541 return "ALLOC_FAIL";
Jeff Garzikbf794512005-07-31 13:07:26 -0400542 case IPW_FW_ERROR_DMA_UNDERRUN:
James Ketrenosb095c382005-08-24 22:04:42 -0500543 return "DMA_UNDERRUN";
Jeff Garzikbf794512005-07-31 13:07:26 -0400544 case IPW_FW_ERROR_DMA_STATUS:
James Ketrenosb095c382005-08-24 22:04:42 -0500545 return "DMA_STATUS";
546 case IPW_FW_ERROR_DINO_ERROR:
547 return "DINO_ERROR";
548 case IPW_FW_ERROR_EEPROM_ERROR:
549 return "EEPROM_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400550 case IPW_FW_ERROR_SYSASSERT:
James Ketrenosb095c382005-08-24 22:04:42 -0500551 return "SYSASSERT";
Jeff Garzikbf794512005-07-31 13:07:26 -0400552 case IPW_FW_ERROR_FATAL_ERROR:
James Ketrenosb095c382005-08-24 22:04:42 -0500553 return "FATAL_ERROR";
Jeff Garzikbf794512005-07-31 13:07:26 -0400554 default:
James Ketrenosb095c382005-08-24 22:04:42 -0500555 return "UNKNOWN_ERROR";
James Ketrenos43f66a62005-03-25 12:31:53 -0600556 }
557}
558
James Ketrenosb39860c2005-08-12 09:36:32 -0500559static void ipw_dump_error_log(struct ipw_priv *priv,
560 struct ipw_fw_error *error)
James Ketrenos43f66a62005-03-25 12:31:53 -0600561{
James Ketrenosb39860c2005-08-12 09:36:32 -0500562 u32 i;
James Ketrenos43f66a62005-03-25 12:31:53 -0600563
James Ketrenosb39860c2005-08-12 09:36:32 -0500564 if (!error) {
565 IPW_ERROR("Error allocating and capturing error log. "
566 "Nothing to dump.\n");
567 return;
James Ketrenos43f66a62005-03-25 12:31:53 -0600568 }
569
James Ketrenosb39860c2005-08-12 09:36:32 -0500570 IPW_ERROR("Start IPW Error Log Dump:\n");
571 IPW_ERROR("Status: 0x%08X, Config: %08X\n",
572 error->status, error->config);
James Ketrenos43f66a62005-03-25 12:31:53 -0600573
James Ketrenosb39860c2005-08-12 09:36:32 -0500574 for (i = 0; i < error->elem_len; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400575 IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
James Ketrenosb39860c2005-08-12 09:36:32 -0500576 ipw_error_desc(error->elem[i].desc),
577 error->elem[i].time,
578 error->elem[i].blink1,
579 error->elem[i].blink2,
580 error->elem[i].link1,
581 error->elem[i].link2, error->elem[i].data);
582 for (i = 0; i < error->log_len; i++)
583 IPW_ERROR("%i\t0x%08x\t%i\n",
584 error->log[i].time,
James Ketrenos286568a2005-08-30 10:34:25 -0500585 error->log[i].data, error->log[i].event);
James Ketrenos43f66a62005-03-25 12:31:53 -0600586}
James Ketrenos43f66a62005-03-25 12:31:53 -0600587#endif
James Ketrenos43f66a62005-03-25 12:31:53 -0600588
James Ketrenosc848d0a2005-08-24 21:56:24 -0500589static inline int ipw_is_init(struct ipw_priv *priv)
590{
591 return (priv->status & STATUS_INIT) ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -0600592}
593
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400594static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
James Ketrenos43f66a62005-03-25 12:31:53 -0600595{
596 u32 addr, field_info, field_len, field_count, total_len;
597
598 IPW_DEBUG_ORD("ordinal = %i\n", ord);
599
600 if (!priv || !val || !len) {
601 IPW_DEBUG_ORD("Invalid argument\n");
602 return -EINVAL;
603 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400604
James Ketrenos43f66a62005-03-25 12:31:53 -0600605 /* verify device ordinal tables have been initialized */
606 if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
607 IPW_DEBUG_ORD("Access ordinals before initialization\n");
608 return -EINVAL;
609 }
610
611 switch (IPW_ORD_TABLE_ID_MASK & ord) {
612 case IPW_ORD_TABLE_0_MASK:
613 /*
614 * TABLE 0: Direct access to a table of 32 bit values
615 *
Jeff Garzikbf794512005-07-31 13:07:26 -0400616 * This is a very simple table with the data directly
James Ketrenos43f66a62005-03-25 12:31:53 -0600617 * read from the table
618 */
619
620 /* remove the table id from the ordinal */
621 ord &= IPW_ORD_TABLE_VALUE_MASK;
622
623 /* boundary check */
624 if (ord > priv->table0_len) {
625 IPW_DEBUG_ORD("ordinal value (%i) longer then "
626 "max (%i)\n", ord, priv->table0_len);
627 return -EINVAL;
628 }
629
630 /* verify we have enough room to store the value */
631 if (*len < sizeof(u32)) {
632 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200633 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600634 return -EINVAL;
635 }
636
637 IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400638 ord, priv->table0_addr + (ord << 2));
James Ketrenos43f66a62005-03-25 12:31:53 -0600639
640 *len = sizeof(u32);
641 ord <<= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400642 *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
James Ketrenos43f66a62005-03-25 12:31:53 -0600643 break;
644
645 case IPW_ORD_TABLE_1_MASK:
646 /*
647 * TABLE 1: Indirect access to a table of 32 bit values
Jeff Garzikbf794512005-07-31 13:07:26 -0400648 *
649 * This is a fairly large table of u32 values each
James Ketrenos43f66a62005-03-25 12:31:53 -0600650 * representing starting addr for the data (which is
651 * also a u32)
652 */
653
654 /* remove the table id from the ordinal */
655 ord &= IPW_ORD_TABLE_VALUE_MASK;
Jeff Garzikbf794512005-07-31 13:07:26 -0400656
James Ketrenos43f66a62005-03-25 12:31:53 -0600657 /* boundary check */
658 if (ord > priv->table1_len) {
659 IPW_DEBUG_ORD("ordinal value too long\n");
660 return -EINVAL;
661 }
662
663 /* verify we have enough room to store the value */
664 if (*len < sizeof(u32)) {
665 IPW_DEBUG_ORD("ordinal buffer length too small, "
Jiri Bencaaa4d302005-06-07 14:58:41 +0200666 "need %zd\n", sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -0600667 return -EINVAL;
668 }
669
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400670 *((u32 *) val) =
671 ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
James Ketrenos43f66a62005-03-25 12:31:53 -0600672 *len = sizeof(u32);
673 break;
674
675 case IPW_ORD_TABLE_2_MASK:
676 /*
677 * TABLE 2: Indirect access to a table of variable sized values
678 *
679 * This table consist of six values, each containing
680 * - dword containing the starting offset of the data
681 * - dword containing the lengh in the first 16bits
682 * and the count in the second 16bits
683 */
684
685 /* remove the table id from the ordinal */
686 ord &= IPW_ORD_TABLE_VALUE_MASK;
687
688 /* boundary check */
689 if (ord > priv->table2_len) {
690 IPW_DEBUG_ORD("ordinal value too long\n");
691 return -EINVAL;
692 }
693
694 /* get the address of statistic */
695 addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
Jeff Garzikbf794512005-07-31 13:07:26 -0400696
697 /* get the second DW of statistics ;
James Ketrenos43f66a62005-03-25 12:31:53 -0600698 * two 16-bit words - first is length, second is count */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400699 field_info =
700 ipw_read_reg32(priv,
701 priv->table2_addr + (ord << 3) +
702 sizeof(u32));
Jeff Garzikbf794512005-07-31 13:07:26 -0400703
James Ketrenos43f66a62005-03-25 12:31:53 -0600704 /* get each entry length */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400705 field_len = *((u16 *) & field_info);
Jeff Garzikbf794512005-07-31 13:07:26 -0400706
James Ketrenos43f66a62005-03-25 12:31:53 -0600707 /* get number of entries */
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400708 field_count = *(((u16 *) & field_info) + 1);
Jeff Garzikbf794512005-07-31 13:07:26 -0400709
James Ketrenos43f66a62005-03-25 12:31:53 -0600710 /* abort if not enought memory */
711 total_len = field_len * field_count;
712 if (total_len > *len) {
713 *len = total_len;
714 return -EINVAL;
715 }
Jeff Garzikbf794512005-07-31 13:07:26 -0400716
James Ketrenos43f66a62005-03-25 12:31:53 -0600717 *len = total_len;
718 if (!total_len)
719 return 0;
720
721 IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
Jeff Garzikbf794512005-07-31 13:07:26 -0400722 "field_info = 0x%08x\n",
James Ketrenos43f66a62005-03-25 12:31:53 -0600723 addr, total_len, field_info);
724 ipw_read_indirect(priv, addr, val, total_len);
725 break;
726
727 default:
728 IPW_DEBUG_ORD("Invalid ordinal!\n");
729 return -EINVAL;
730
731 }
732
James Ketrenos43f66a62005-03-25 12:31:53 -0600733 return 0;
734}
735
736static void ipw_init_ordinals(struct ipw_priv *priv)
737{
738 priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
Jeff Garzikbf794512005-07-31 13:07:26 -0400739 priv->table0_len = ipw_read32(priv, priv->table0_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -0600740
741 IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
742 priv->table0_addr, priv->table0_len);
743
744 priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
745 priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
746
747 IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
748 priv->table1_addr, priv->table1_len);
749
750 priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
751 priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
Jeff Garzik0edd5b42005-09-07 00:48:31 -0400752 priv->table2_len &= 0x0000ffff; /* use first two bytes */
James Ketrenos43f66a62005-03-25 12:31:53 -0600753
754 IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
755 priv->table2_addr, priv->table2_len);
756
757}
758
Adrian Bunka73e22b2006-01-21 01:39:42 +0100759static u32 ipw_register_toggle(u32 reg)
James Ketrenosa613bff2005-08-24 21:43:11 -0500760{
James Ketrenosb095c382005-08-24 22:04:42 -0500761 reg &= ~IPW_START_STANDBY;
762 if (reg & IPW_GATE_ODMA)
763 reg &= ~IPW_GATE_ODMA;
764 if (reg & IPW_GATE_IDMA)
765 reg &= ~IPW_GATE_IDMA;
766 if (reg & IPW_GATE_ADMA)
767 reg &= ~IPW_GATE_ADMA;
James Ketrenosa613bff2005-08-24 21:43:11 -0500768 return reg;
769}
770
771/*
772 * LED behavior:
773 * - On radio ON, turn on any LEDs that require to be on during start
774 * - On initialization, start unassociated blink
775 * - On association, disable unassociated blink
776 * - On disassociation, start unassociated blink
777 * - On radio OFF, turn off any LEDs started during radio on
778 *
779 */
Zhu Yiede61112006-01-24 16:37:10 +0800780#define LD_TIME_LINK_ON msecs_to_jiffies(300)
781#define LD_TIME_LINK_OFF msecs_to_jiffies(2700)
782#define LD_TIME_ACT_ON msecs_to_jiffies(250)
James Ketrenosa613bff2005-08-24 21:43:11 -0500783
Adrian Bunka73e22b2006-01-21 01:39:42 +0100784static void ipw_led_link_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500785{
786 unsigned long flags;
787 u32 led;
788
789 /* If configured to not use LEDs, or nic_type is 1,
790 * then we don't toggle a LINK led */
791 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
792 return;
793
794 spin_lock_irqsave(&priv->lock, flags);
795
796 if (!(priv->status & STATUS_RF_KILL_MASK) &&
797 !(priv->status & STATUS_LED_LINK_ON)) {
798 IPW_DEBUG_LED("Link LED On\n");
James Ketrenosb095c382005-08-24 22:04:42 -0500799 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500800 led |= priv->led_association_on;
801
802 led = ipw_register_toggle(led);
803
804 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500805 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500806
807 priv->status |= STATUS_LED_LINK_ON;
808
809 /* If we aren't associated, schedule turning the LED off */
810 if (!(priv->status & STATUS_ASSOCIATED))
811 queue_delayed_work(priv->workqueue,
812 &priv->led_link_off,
813 LD_TIME_LINK_ON);
814 }
815
816 spin_unlock_irqrestore(&priv->lock, flags);
817}
818
James Ketrenosc848d0a2005-08-24 21:56:24 -0500819static void ipw_bg_led_link_on(void *data)
820{
821 struct ipw_priv *priv = data;
822 down(&priv->sem);
823 ipw_led_link_on(data);
824 up(&priv->sem);
825}
826
Adrian Bunka73e22b2006-01-21 01:39:42 +0100827static void ipw_led_link_off(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500828{
829 unsigned long flags;
830 u32 led;
831
832 /* If configured not to use LEDs, or nic type is 1,
833 * then we don't goggle the LINK led. */
834 if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
835 return;
836
837 spin_lock_irqsave(&priv->lock, flags);
838
839 if (priv->status & STATUS_LED_LINK_ON) {
James Ketrenosb095c382005-08-24 22:04:42 -0500840 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500841 led &= priv->led_association_off;
842 led = ipw_register_toggle(led);
843
844 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500845 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500846
847 IPW_DEBUG_LED("Link LED Off\n");
848
849 priv->status &= ~STATUS_LED_LINK_ON;
850
851 /* If we aren't associated and the radio is on, schedule
852 * turning the LED on (blink while unassociated) */
853 if (!(priv->status & STATUS_RF_KILL_MASK) &&
854 !(priv->status & STATUS_ASSOCIATED))
855 queue_delayed_work(priv->workqueue, &priv->led_link_on,
856 LD_TIME_LINK_OFF);
857
858 }
859
860 spin_unlock_irqrestore(&priv->lock, flags);
861}
862
James Ketrenosc848d0a2005-08-24 21:56:24 -0500863static void ipw_bg_led_link_off(void *data)
864{
865 struct ipw_priv *priv = data;
866 down(&priv->sem);
867 ipw_led_link_off(data);
868 up(&priv->sem);
869}
870
Arjan van de Ven858119e2006-01-14 13:20:43 -0800871static void __ipw_led_activity_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500872{
James Ketrenosa613bff2005-08-24 21:43:11 -0500873 u32 led;
874
875 if (priv->config & CFG_NO_LED)
876 return;
877
James Ketrenosb095c382005-08-24 22:04:42 -0500878 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -0500879 return;
James Ketrenosa613bff2005-08-24 21:43:11 -0500880
881 if (!(priv->status & STATUS_LED_ACT_ON)) {
James Ketrenosb095c382005-08-24 22:04:42 -0500882 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500883 led |= priv->led_activity_on;
884
885 led = ipw_register_toggle(led);
886
887 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500888 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500889
890 IPW_DEBUG_LED("Activity LED On\n");
891
892 priv->status |= STATUS_LED_ACT_ON;
893
James Ketrenosc848d0a2005-08-24 21:56:24 -0500894 cancel_delayed_work(&priv->led_act_off);
James Ketrenosa613bff2005-08-24 21:43:11 -0500895 queue_delayed_work(priv->workqueue, &priv->led_act_off,
896 LD_TIME_ACT_ON);
897 } else {
898 /* Reschedule LED off for full time period */
899 cancel_delayed_work(&priv->led_act_off);
900 queue_delayed_work(priv->workqueue, &priv->led_act_off,
901 LD_TIME_ACT_ON);
902 }
James Ketrenosb095c382005-08-24 22:04:42 -0500903}
James Ketrenosa613bff2005-08-24 21:43:11 -0500904
Adrian Bunka73e22b2006-01-21 01:39:42 +0100905#if 0
James Ketrenosb095c382005-08-24 22:04:42 -0500906void ipw_led_activity_on(struct ipw_priv *priv)
907{
908 unsigned long flags;
909 spin_lock_irqsave(&priv->lock, flags);
910 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -0500911 spin_unlock_irqrestore(&priv->lock, flags);
912}
Adrian Bunka73e22b2006-01-21 01:39:42 +0100913#endif /* 0 */
James Ketrenosa613bff2005-08-24 21:43:11 -0500914
Adrian Bunka73e22b2006-01-21 01:39:42 +0100915static void ipw_led_activity_off(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500916{
917 unsigned long flags;
918 u32 led;
919
920 if (priv->config & CFG_NO_LED)
921 return;
922
923 spin_lock_irqsave(&priv->lock, flags);
924
925 if (priv->status & STATUS_LED_ACT_ON) {
James Ketrenosb095c382005-08-24 22:04:42 -0500926 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500927 led &= priv->led_activity_off;
928
929 led = ipw_register_toggle(led);
930
931 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500932 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500933
934 IPW_DEBUG_LED("Activity LED Off\n");
935
936 priv->status &= ~STATUS_LED_ACT_ON;
937 }
938
939 spin_unlock_irqrestore(&priv->lock, flags);
940}
941
James Ketrenosc848d0a2005-08-24 21:56:24 -0500942static void ipw_bg_led_activity_off(void *data)
943{
944 struct ipw_priv *priv = data;
945 down(&priv->sem);
946 ipw_led_activity_off(data);
947 up(&priv->sem);
948}
949
Adrian Bunka73e22b2006-01-21 01:39:42 +0100950static void ipw_led_band_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500951{
952 unsigned long flags;
953 u32 led;
954
955 /* Only nic type 1 supports mode LEDs */
James Ketrenosc848d0a2005-08-24 21:56:24 -0500956 if (priv->config & CFG_NO_LED ||
957 priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
James Ketrenosa613bff2005-08-24 21:43:11 -0500958 return;
959
960 spin_lock_irqsave(&priv->lock, flags);
961
James Ketrenosb095c382005-08-24 22:04:42 -0500962 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500963 if (priv->assoc_network->mode == IEEE_A) {
964 led |= priv->led_ofdm_on;
965 led &= priv->led_association_off;
966 IPW_DEBUG_LED("Mode LED On: 802.11a\n");
967 } else if (priv->assoc_network->mode == IEEE_G) {
968 led |= priv->led_ofdm_on;
969 led |= priv->led_association_on;
970 IPW_DEBUG_LED("Mode LED On: 802.11g\n");
971 } else {
972 led &= priv->led_ofdm_off;
973 led |= priv->led_association_on;
974 IPW_DEBUG_LED("Mode LED On: 802.11b\n");
975 }
976
977 led = ipw_register_toggle(led);
978
979 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -0500980 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -0500981
982 spin_unlock_irqrestore(&priv->lock, flags);
983}
984
Adrian Bunka73e22b2006-01-21 01:39:42 +0100985static void ipw_led_band_off(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -0500986{
987 unsigned long flags;
988 u32 led;
989
990 /* Only nic type 1 supports mode LEDs */
991 if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
992 return;
993
994 spin_lock_irqsave(&priv->lock, flags);
995
James Ketrenosb095c382005-08-24 22:04:42 -0500996 led = ipw_read_reg32(priv, IPW_EVENT_REG);
James Ketrenosa613bff2005-08-24 21:43:11 -0500997 led &= priv->led_ofdm_off;
998 led &= priv->led_association_off;
999
1000 led = ipw_register_toggle(led);
1001
1002 IPW_DEBUG_LED("Reg: 0x%08X\n", led);
James Ketrenosb095c382005-08-24 22:04:42 -05001003 ipw_write_reg32(priv, IPW_EVENT_REG, led);
James Ketrenosa613bff2005-08-24 21:43:11 -05001004
1005 spin_unlock_irqrestore(&priv->lock, flags);
1006}
1007
Adrian Bunka73e22b2006-01-21 01:39:42 +01001008static void ipw_led_radio_on(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001009{
1010 ipw_led_link_on(priv);
1011}
1012
Adrian Bunka73e22b2006-01-21 01:39:42 +01001013static void ipw_led_radio_off(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001014{
1015 ipw_led_activity_off(priv);
1016 ipw_led_link_off(priv);
1017}
1018
Adrian Bunka73e22b2006-01-21 01:39:42 +01001019static void ipw_led_link_up(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001020{
1021 /* Set the Link Led on for all nic types */
1022 ipw_led_link_on(priv);
1023}
1024
Adrian Bunka73e22b2006-01-21 01:39:42 +01001025static void ipw_led_link_down(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001026{
1027 ipw_led_activity_off(priv);
1028 ipw_led_link_off(priv);
1029
1030 if (priv->status & STATUS_RF_KILL_MASK)
1031 ipw_led_radio_off(priv);
1032}
1033
Adrian Bunka73e22b2006-01-21 01:39:42 +01001034static void ipw_led_init(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001035{
1036 priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE];
1037
1038 /* Set the default PINs for the link and activity leds */
James Ketrenosb095c382005-08-24 22:04:42 -05001039 priv->led_activity_on = IPW_ACTIVITY_LED;
1040 priv->led_activity_off = ~(IPW_ACTIVITY_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -05001041
James Ketrenosb095c382005-08-24 22:04:42 -05001042 priv->led_association_on = IPW_ASSOCIATED_LED;
1043 priv->led_association_off = ~(IPW_ASSOCIATED_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -05001044
1045 /* Set the default PINs for the OFDM leds */
James Ketrenosb095c382005-08-24 22:04:42 -05001046 priv->led_ofdm_on = IPW_OFDM_LED;
1047 priv->led_ofdm_off = ~(IPW_OFDM_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -05001048
1049 switch (priv->nic_type) {
1050 case EEPROM_NIC_TYPE_1:
1051 /* In this NIC type, the LEDs are reversed.... */
James Ketrenosb095c382005-08-24 22:04:42 -05001052 priv->led_activity_on = IPW_ASSOCIATED_LED;
1053 priv->led_activity_off = ~(IPW_ASSOCIATED_LED);
1054 priv->led_association_on = IPW_ACTIVITY_LED;
1055 priv->led_association_off = ~(IPW_ACTIVITY_LED);
James Ketrenosa613bff2005-08-24 21:43:11 -05001056
1057 if (!(priv->config & CFG_NO_LED))
1058 ipw_led_band_on(priv);
1059
1060 /* And we don't blink link LEDs for this nic, so
1061 * just return here */
1062 return;
1063
1064 case EEPROM_NIC_TYPE_3:
1065 case EEPROM_NIC_TYPE_2:
1066 case EEPROM_NIC_TYPE_4:
1067 case EEPROM_NIC_TYPE_0:
1068 break;
1069
1070 default:
1071 IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
1072 priv->nic_type);
1073 priv->nic_type = EEPROM_NIC_TYPE_0;
1074 break;
1075 }
1076
1077 if (!(priv->config & CFG_NO_LED)) {
1078 if (priv->status & STATUS_ASSOCIATED)
1079 ipw_led_link_on(priv);
1080 else
1081 ipw_led_link_off(priv);
1082 }
1083}
1084
Adrian Bunka73e22b2006-01-21 01:39:42 +01001085static void ipw_led_shutdown(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -05001086{
James Ketrenosa613bff2005-08-24 21:43:11 -05001087 ipw_led_activity_off(priv);
1088 ipw_led_link_off(priv);
1089 ipw_led_band_off(priv);
James Ketrenosafbf30a2005-08-25 00:05:33 -05001090 cancel_delayed_work(&priv->led_link_on);
1091 cancel_delayed_work(&priv->led_link_off);
1092 cancel_delayed_work(&priv->led_act_off);
James Ketrenosa613bff2005-08-24 21:43:11 -05001093}
1094
James Ketrenos43f66a62005-03-25 12:31:53 -06001095/*
1096 * The following adds a new attribute to the sysfs representation
1097 * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
1098 * used for controling the debug level.
Jeff Garzikbf794512005-07-31 13:07:26 -04001099 *
James Ketrenos43f66a62005-03-25 12:31:53 -06001100 * See the level definitions in ipw for details.
1101 */
1102static ssize_t show_debug_level(struct device_driver *d, char *buf)
1103{
1104 return sprintf(buf, "0x%08X\n", ipw_debug_level);
1105}
James Ketrenosa613bff2005-08-24 21:43:11 -05001106
1107static ssize_t store_debug_level(struct device_driver *d, const char *buf,
1108 size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001109{
1110 char *p = (char *)buf;
1111 u32 val;
1112
1113 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1114 p++;
1115 if (p[0] == 'x' || p[0] == 'X')
1116 p++;
1117 val = simple_strtoul(p, &p, 16);
1118 } else
1119 val = simple_strtoul(p, &p, 10);
Jeff Garzikbf794512005-07-31 13:07:26 -04001120 if (p == buf)
1121 printk(KERN_INFO DRV_NAME
James Ketrenos43f66a62005-03-25 12:31:53 -06001122 ": %s is not in hex or decimal form.\n", buf);
1123 else
1124 ipw_debug_level = val;
1125
1126 return strnlen(buf, count);
1127}
1128
Jeff Garzikbf794512005-07-31 13:07:26 -04001129static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -06001130 show_debug_level, store_debug_level);
1131
James Ketrenosb39860c2005-08-12 09:36:32 -05001132static inline u32 ipw_get_event_log_len(struct ipw_priv *priv)
1133{
Zhu Yic8fe6672006-01-24 16:36:36 +08001134 /* length = 1st dword in log */
James Ketrenosb39860c2005-08-12 09:36:32 -05001135 return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG));
1136}
1137
1138static void ipw_capture_event_log(struct ipw_priv *priv,
1139 u32 log_len, struct ipw_event *log)
1140{
1141 u32 base;
1142
1143 if (log_len) {
1144 base = ipw_read32(priv, IPW_EVENT_LOG);
1145 ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32),
1146 (u8 *) log, sizeof(*log) * log_len);
1147 }
1148}
1149
1150static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
1151{
1152 struct ipw_fw_error *error;
1153 u32 log_len = ipw_get_event_log_len(priv);
1154 u32 base = ipw_read32(priv, IPW_ERROR_LOG);
1155 u32 elem_len = ipw_read_reg32(priv, base);
1156
1157 error = kmalloc(sizeof(*error) +
1158 sizeof(*error->elem) * elem_len +
1159 sizeof(*error->log) * log_len, GFP_ATOMIC);
1160 if (!error) {
1161 IPW_ERROR("Memory allocation for firmware error log "
1162 "failed.\n");
1163 return NULL;
1164 }
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001165 error->jiffies = jiffies;
James Ketrenosb39860c2005-08-12 09:36:32 -05001166 error->status = priv->status;
1167 error->config = priv->config;
1168 error->elem_len = elem_len;
1169 error->log_len = log_len;
1170 error->elem = (struct ipw_error_elem *)error->payload;
Zhu Yi3b26b112005-11-17 13:58:30 +08001171 error->log = (struct ipw_event *)(error->elem + elem_len);
James Ketrenosb39860c2005-08-12 09:36:32 -05001172
1173 ipw_capture_event_log(priv, log_len, error->log);
1174
1175 if (elem_len)
1176 ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem,
1177 sizeof(*error->elem) * elem_len);
1178
1179 return error;
1180}
1181
1182static void ipw_free_error_log(struct ipw_fw_error *error)
1183{
1184 if (error)
1185 kfree(error);
1186}
1187
1188static ssize_t show_event_log(struct device *d,
1189 struct device_attribute *attr, char *buf)
1190{
1191 struct ipw_priv *priv = dev_get_drvdata(d);
1192 u32 log_len = ipw_get_event_log_len(priv);
1193 struct ipw_event log[log_len];
1194 u32 len = 0, i;
1195
1196 ipw_capture_event_log(priv, log_len, log);
1197
1198 len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
1199 for (i = 0; i < log_len; i++)
1200 len += snprintf(buf + len, PAGE_SIZE - len,
1201 "\n%08X%08X%08X",
1202 log[i].time, log[i].event, log[i].data);
1203 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1204 return len;
1205}
1206
1207static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL);
1208
1209static ssize_t show_error(struct device *d,
1210 struct device_attribute *attr, char *buf)
1211{
1212 struct ipw_priv *priv = dev_get_drvdata(d);
1213 u32 len = 0, i;
1214 if (!priv->error)
1215 return 0;
1216 len += snprintf(buf + len, PAGE_SIZE - len,
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001217 "%08lX%08X%08X%08X",
1218 priv->error->jiffies,
James Ketrenosb39860c2005-08-12 09:36:32 -05001219 priv->error->status,
1220 priv->error->config, priv->error->elem_len);
1221 for (i = 0; i < priv->error->elem_len; i++)
1222 len += snprintf(buf + len, PAGE_SIZE - len,
1223 "\n%08X%08X%08X%08X%08X%08X%08X",
1224 priv->error->elem[i].time,
1225 priv->error->elem[i].desc,
1226 priv->error->elem[i].blink1,
1227 priv->error->elem[i].blink2,
1228 priv->error->elem[i].link1,
1229 priv->error->elem[i].link2,
1230 priv->error->elem[i].data);
1231
1232 len += snprintf(buf + len, PAGE_SIZE - len,
1233 "\n%08X", priv->error->log_len);
1234 for (i = 0; i < priv->error->log_len; i++)
1235 len += snprintf(buf + len, PAGE_SIZE - len,
1236 "\n%08X%08X%08X",
1237 priv->error->log[i].time,
1238 priv->error->log[i].event,
1239 priv->error->log[i].data);
1240 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1241 return len;
1242}
1243
1244static ssize_t clear_error(struct device *d,
1245 struct device_attribute *attr,
1246 const char *buf, size_t count)
1247{
1248 struct ipw_priv *priv = dev_get_drvdata(d);
1249 if (priv->error) {
1250 ipw_free_error_log(priv->error);
1251 priv->error = NULL;
1252 }
1253 return count;
1254}
1255
1256static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error);
1257
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001258static ssize_t show_cmd_log(struct device *d,
1259 struct device_attribute *attr, char *buf)
1260{
1261 struct ipw_priv *priv = dev_get_drvdata(d);
1262 u32 len = 0, i;
1263 if (!priv->cmdlog)
1264 return 0;
1265 for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
1266 (i != priv->cmdlog_pos) && (PAGE_SIZE - len);
1267 i = (i + 1) % priv->cmdlog_len) {
1268 len +=
1269 snprintf(buf + len, PAGE_SIZE - len,
1270 "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies,
1271 priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd,
1272 priv->cmdlog[i].cmd.len);
1273 len +=
1274 snprintk_buf(buf + len, PAGE_SIZE - len,
1275 (u8 *) priv->cmdlog[i].cmd.param,
1276 priv->cmdlog[i].cmd.len);
1277 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1278 }
1279 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
1280 return len;
1281}
1282
1283static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
1284
James Ketrenosa613bff2005-08-24 21:43:11 -05001285static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
1286 char *buf)
1287{
1288 struct ipw_priv *priv = dev_get_drvdata(d);
1289 return sprintf(buf, "%d\n", priv->ieee->scan_age);
1290}
1291
1292static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
1293 const char *buf, size_t count)
1294{
1295 struct ipw_priv *priv = dev_get_drvdata(d);
Brice Goglin0f52bf92005-12-01 01:41:46 -08001296#ifdef CONFIG_IPW2200_DEBUG
James Ketrenosa613bff2005-08-24 21:43:11 -05001297 struct net_device *dev = priv->net_dev;
James Ketrenosc848d0a2005-08-24 21:56:24 -05001298#endif
James Ketrenosa613bff2005-08-24 21:43:11 -05001299 char buffer[] = "00000000";
1300 unsigned long len =
1301 (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
1302 unsigned long val;
1303 char *p = buffer;
1304
1305 IPW_DEBUG_INFO("enter\n");
1306
1307 strncpy(buffer, buf, len);
1308 buffer[len] = 0;
1309
1310 if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
1311 p++;
1312 if (p[0] == 'x' || p[0] == 'X')
1313 p++;
1314 val = simple_strtoul(p, &p, 16);
1315 } else
1316 val = simple_strtoul(p, &p, 10);
1317 if (p == buffer) {
1318 IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
1319 } else {
1320 priv->ieee->scan_age = val;
1321 IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
1322 }
1323
1324 IPW_DEBUG_INFO("exit\n");
1325 return len;
1326}
1327
1328static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
1329
1330static ssize_t show_led(struct device *d, struct device_attribute *attr,
1331 char *buf)
1332{
1333 struct ipw_priv *priv = dev_get_drvdata(d);
1334 return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1);
1335}
1336
1337static ssize_t store_led(struct device *d, struct device_attribute *attr,
1338 const char *buf, size_t count)
1339{
1340 struct ipw_priv *priv = dev_get_drvdata(d);
1341
1342 IPW_DEBUG_INFO("enter\n");
1343
1344 if (count == 0)
1345 return 0;
1346
1347 if (*buf == 0) {
1348 IPW_DEBUG_LED("Disabling LED control.\n");
1349 priv->config |= CFG_NO_LED;
1350 ipw_led_shutdown(priv);
1351 } else {
1352 IPW_DEBUG_LED("Enabling LED control.\n");
1353 priv->config &= ~CFG_NO_LED;
1354 ipw_led_init(priv);
1355 }
1356
1357 IPW_DEBUG_INFO("exit\n");
1358 return count;
1359}
1360
1361static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led);
1362
Andrew Mortonad3fee52005-06-20 14:30:36 -07001363static ssize_t show_status(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001364 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001365{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001366 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001367 return sprintf(buf, "0x%08x\n", (int)p->status);
1368}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001369
James Ketrenos43f66a62005-03-25 12:31:53 -06001370static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
1371
Andrew Mortonad3fee52005-06-20 14:30:36 -07001372static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
1373 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001374{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001375 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001376 return sprintf(buf, "0x%08x\n", (int)p->config);
1377}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001378
James Ketrenos43f66a62005-03-25 12:31:53 -06001379static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
1380
Andrew Mortonad3fee52005-06-20 14:30:36 -07001381static ssize_t show_nic_type(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001382 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001383{
James Ketrenosa613bff2005-08-24 21:43:11 -05001384 struct ipw_priv *priv = d->driver_data;
1385 return sprintf(buf, "TYPE: %d\n", priv->nic_type);
James Ketrenos43f66a62005-03-25 12:31:53 -06001386}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001387
James Ketrenos43f66a62005-03-25 12:31:53 -06001388static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
1389
Andrew Mortonad3fee52005-06-20 14:30:36 -07001390static ssize_t show_ucode_version(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001391 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001392{
1393 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001394 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001395
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001396 if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001397 return 0;
1398
1399 return sprintf(buf, "0x%08x\n", tmp);
1400}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001401
1402static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001403
Andrew Mortonad3fee52005-06-20 14:30:36 -07001404static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
1405 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001406{
1407 u32 len = sizeof(u32), tmp = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001408 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001409
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001410 if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
James Ketrenos43f66a62005-03-25 12:31:53 -06001411 return 0;
1412
1413 return sprintf(buf, "0x%08x\n", tmp);
1414}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001415
1416static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL);
James Ketrenos43f66a62005-03-25 12:31:53 -06001417
1418/*
1419 * Add a device attribute to view/control the delay between eeprom
1420 * operations.
1421 */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001422static ssize_t show_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001423 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001424{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001425 int n = ((struct ipw_priv *)d->driver_data)->eeprom_delay;
James Ketrenos43f66a62005-03-25 12:31:53 -06001426 return sprintf(buf, "%i\n", n);
1427}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001428static ssize_t store_eeprom_delay(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001429 struct device_attribute *attr,
1430 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001431{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001432 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001433 sscanf(buf, "%i", &p->eeprom_delay);
1434 return strnlen(buf, count);
1435}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001436
1437static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO,
1438 show_eeprom_delay, store_eeprom_delay);
James Ketrenos43f66a62005-03-25 12:31:53 -06001439
Andrew Mortonad3fee52005-06-20 14:30:36 -07001440static ssize_t show_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001441 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001442{
1443 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001444 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001445
James Ketrenosb095c382005-08-24 22:04:42 -05001446 reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001447 return sprintf(buf, "0x%08x\n", reg);
1448}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001449static ssize_t store_command_event_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001450 struct device_attribute *attr,
1451 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001452{
1453 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001454 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001455
1456 sscanf(buf, "%x", &reg);
James Ketrenosb095c382005-08-24 22:04:42 -05001457 ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001458 return strnlen(buf, count);
1459}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001460
1461static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO,
1462 show_command_event_reg, store_command_event_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001463
Andrew Mortonad3fee52005-06-20 14:30:36 -07001464static ssize_t show_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001465 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001466{
1467 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001468 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001469
1470 reg = ipw_read_reg32(p, 0x301100);
1471 return sprintf(buf, "0x%08x\n", reg);
1472}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001473static ssize_t store_mem_gpio_reg(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001474 struct device_attribute *attr,
1475 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001476{
1477 u32 reg;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001478 struct ipw_priv *p = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001479
1480 sscanf(buf, "%x", &reg);
1481 ipw_write_reg32(p, 0x301100, reg);
1482 return strnlen(buf, count);
1483}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001484
1485static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO,
1486 show_mem_gpio_reg, store_mem_gpio_reg);
James Ketrenos43f66a62005-03-25 12:31:53 -06001487
Andrew Mortonad3fee52005-06-20 14:30:36 -07001488static ssize_t show_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001489 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001490{
1491 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001492 struct ipw_priv *priv = d->driver_data;
James Ketrenosafbf30a2005-08-25 00:05:33 -05001493
Jeff Garzikbf794512005-07-31 13:07:26 -04001494 if (priv->status & STATUS_INDIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001495 reg = ipw_read_reg32(priv, priv->indirect_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001496 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001497 reg = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04001498
James Ketrenos43f66a62005-03-25 12:31:53 -06001499 return sprintf(buf, "0x%08x\n", reg);
1500}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001501static ssize_t store_indirect_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001502 struct device_attribute *attr,
1503 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001504{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001505 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001506
1507 sscanf(buf, "%x", &priv->indirect_dword);
1508 priv->status |= STATUS_INDIRECT_DWORD;
1509 return strnlen(buf, count);
1510}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001511
1512static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO,
1513 show_indirect_dword, store_indirect_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001514
Andrew Mortonad3fee52005-06-20 14:30:36 -07001515static ssize_t show_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001516 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001517{
1518 u8 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001519 struct ipw_priv *priv = d->driver_data;
James Ketrenosafbf30a2005-08-25 00:05:33 -05001520
Jeff Garzikbf794512005-07-31 13:07:26 -04001521 if (priv->status & STATUS_INDIRECT_BYTE)
James Ketrenos43f66a62005-03-25 12:31:53 -06001522 reg = ipw_read_reg8(priv, priv->indirect_byte);
Jeff Garzikbf794512005-07-31 13:07:26 -04001523 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001524 reg = 0;
1525
1526 return sprintf(buf, "0x%02x\n", reg);
1527}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001528static ssize_t store_indirect_byte(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001529 struct device_attribute *attr,
1530 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001531{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001532 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001533
1534 sscanf(buf, "%x", &priv->indirect_byte);
1535 priv->status |= STATUS_INDIRECT_BYTE;
1536 return strnlen(buf, count);
1537}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001538
1539static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO,
James Ketrenos43f66a62005-03-25 12:31:53 -06001540 show_indirect_byte, store_indirect_byte);
1541
Andrew Mortonad3fee52005-06-20 14:30:36 -07001542static ssize_t show_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001543 struct device_attribute *attr, char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001544{
1545 u32 reg = 0;
Andrew Mortonad3fee52005-06-20 14:30:36 -07001546 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001547
Jeff Garzikbf794512005-07-31 13:07:26 -04001548 if (priv->status & STATUS_DIRECT_DWORD)
James Ketrenos43f66a62005-03-25 12:31:53 -06001549 reg = ipw_read32(priv, priv->direct_dword);
Jeff Garzikbf794512005-07-31 13:07:26 -04001550 else
James Ketrenos43f66a62005-03-25 12:31:53 -06001551 reg = 0;
1552
1553 return sprintf(buf, "0x%08x\n", reg);
1554}
Andrew Mortonad3fee52005-06-20 14:30:36 -07001555static ssize_t store_direct_dword(struct device *d,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001556 struct device_attribute *attr,
1557 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001558{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001559 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001560
1561 sscanf(buf, "%x", &priv->direct_dword);
1562 priv->status |= STATUS_DIRECT_DWORD;
1563 return strnlen(buf, count);
1564}
James Ketrenos43f66a62005-03-25 12:31:53 -06001565
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001566static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
1567 show_direct_dword, store_direct_dword);
James Ketrenos43f66a62005-03-25 12:31:53 -06001568
Arjan van de Ven858119e2006-01-14 13:20:43 -08001569static int rf_kill_active(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06001570{
1571 if (0 == (ipw_read32(priv, 0x30) & 0x10000))
1572 priv->status |= STATUS_RF_KILL_HW;
1573 else
1574 priv->status &= ~STATUS_RF_KILL_HW;
1575
1576 return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
1577}
1578
Andrew Mortonad3fee52005-06-20 14:30:36 -07001579static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001580 char *buf)
James Ketrenos43f66a62005-03-25 12:31:53 -06001581{
1582 /* 0 - RF kill not enabled
Jeff Garzikbf794512005-07-31 13:07:26 -04001583 1 - SW based RF kill active (sysfs)
James Ketrenos43f66a62005-03-25 12:31:53 -06001584 2 - HW based RF kill active
1585 3 - Both HW and SW baed RF kill active */
Andrew Mortonad3fee52005-06-20 14:30:36 -07001586 struct ipw_priv *priv = d->driver_data;
James Ketrenos43f66a62005-03-25 12:31:53 -06001587 int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001588 (rf_kill_active(priv) ? 0x2 : 0x0);
James Ketrenos43f66a62005-03-25 12:31:53 -06001589 return sprintf(buf, "%i\n", val);
1590}
1591
1592static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
1593{
Jeff Garzikbf794512005-07-31 13:07:26 -04001594 if ((disable_radio ? 1 : 0) ==
James Ketrenosea2b26e2005-08-24 21:25:16 -05001595 ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001596 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06001597
1598 IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
1599 disable_radio ? "OFF" : "ON");
1600
1601 if (disable_radio) {
1602 priv->status |= STATUS_RF_KILL_SW;
1603
James Ketrenosa613bff2005-08-24 21:43:11 -05001604 if (priv->workqueue)
James Ketrenos43f66a62005-03-25 12:31:53 -06001605 cancel_delayed_work(&priv->request_scan);
James Ketrenos43f66a62005-03-25 12:31:53 -06001606 queue_work(priv->workqueue, &priv->down);
1607 } else {
1608 priv->status &= ~STATUS_RF_KILL_SW;
1609 if (rf_kill_active(priv)) {
1610 IPW_DEBUG_RF_KILL("Can not turn radio back on - "
1611 "disabled by HW switch\n");
1612 /* Make sure the RF_KILL check timer is running */
1613 cancel_delayed_work(&priv->rf_kill);
Jeff Garzikbf794512005-07-31 13:07:26 -04001614 queue_delayed_work(priv->workqueue, &priv->rf_kill,
James Ketrenos43f66a62005-03-25 12:31:53 -06001615 2 * HZ);
Jeff Garzikbf794512005-07-31 13:07:26 -04001616 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06001617 queue_work(priv->workqueue, &priv->up);
1618 }
1619
1620 return 1;
1621}
1622
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001623static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
1624 const char *buf, size_t count)
James Ketrenos43f66a62005-03-25 12:31:53 -06001625{
Andrew Mortonad3fee52005-06-20 14:30:36 -07001626 struct ipw_priv *priv = d->driver_data;
Jeff Garzikbf794512005-07-31 13:07:26 -04001627
James Ketrenos43f66a62005-03-25 12:31:53 -06001628 ipw_radio_kill_sw(priv, buf[0] == '1');
1629
1630 return count;
1631}
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001632
1633static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
James Ketrenos43f66a62005-03-25 12:31:53 -06001634
James Ketrenosb095c382005-08-24 22:04:42 -05001635static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr,
1636 char *buf)
1637{
1638 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1639 int pos = 0, len = 0;
1640 if (priv->config & CFG_SPEED_SCAN) {
1641 while (priv->speed_scan[pos] != 0)
1642 len += sprintf(&buf[len], "%d ",
1643 priv->speed_scan[pos++]);
1644 return len + sprintf(&buf[len], "\n");
1645 }
1646
1647 return sprintf(buf, "0\n");
1648}
1649
1650static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr,
1651 const char *buf, size_t count)
1652{
1653 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1654 int channel, pos = 0;
1655 const char *p = buf;
1656
1657 /* list of space separated channels to scan, optionally ending with 0 */
1658 while ((channel = simple_strtol(p, NULL, 0))) {
1659 if (pos == MAX_SPEED_SCAN - 1) {
1660 priv->speed_scan[pos] = 0;
1661 break;
1662 }
1663
Liu Hong1fe0adb2005-08-19 09:33:10 -05001664 if (ipw_is_valid_channel(priv->ieee, channel))
James Ketrenosb095c382005-08-24 22:04:42 -05001665 priv->speed_scan[pos++] = channel;
1666 else
1667 IPW_WARNING("Skipping invalid channel request: %d\n",
1668 channel);
1669 p = strchr(p, ' ');
1670 if (!p)
1671 break;
1672 while (*p == ' ' || *p == '\t')
1673 p++;
1674 }
1675
1676 if (pos == 0)
1677 priv->config &= ~CFG_SPEED_SCAN;
1678 else {
1679 priv->speed_scan_pos = 0;
1680 priv->config |= CFG_SPEED_SCAN;
1681 }
1682
1683 return count;
1684}
1685
1686static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan,
1687 store_speed_scan);
1688
1689static ssize_t show_net_stats(struct device *d, struct device_attribute *attr,
1690 char *buf)
1691{
1692 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1693 return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0');
1694}
1695
1696static ssize_t store_net_stats(struct device *d, struct device_attribute *attr,
1697 const char *buf, size_t count)
1698{
1699 struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
1700 if (buf[0] == '1')
1701 priv->config |= CFG_NET_STATS;
1702 else
1703 priv->config &= ~CFG_NET_STATS;
1704
1705 return count;
1706}
1707
James Ketrenosafbf30a2005-08-25 00:05:33 -05001708static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO,
1709 show_net_stats, store_net_stats);
James Ketrenosb095c382005-08-24 22:04:42 -05001710
James Ketrenosea2b26e2005-08-24 21:25:16 -05001711static void notify_wx_assoc_event(struct ipw_priv *priv)
1712{
1713 union iwreq_data wrqu;
1714 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1715 if (priv->status & STATUS_ASSOCIATED)
1716 memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
1717 else
1718 memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
1719 wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
1720}
1721
James Ketrenos43f66a62005-03-25 12:31:53 -06001722static void ipw_irq_tasklet(struct ipw_priv *priv)
1723{
1724 u32 inta, inta_mask, handled = 0;
1725 unsigned long flags;
1726 int rc = 0;
1727
1728 spin_lock_irqsave(&priv->lock, flags);
1729
James Ketrenosb095c382005-08-24 22:04:42 -05001730 inta = ipw_read32(priv, IPW_INTA_RW);
1731 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
1732 inta &= (IPW_INTA_MASK_ALL & inta_mask);
James Ketrenos43f66a62005-03-25 12:31:53 -06001733
1734 /* Add any cached INTA values that need to be handled */
1735 inta |= priv->isr_inta;
1736
1737 /* handle all the justifications for the interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05001738 if (inta & IPW_INTA_BIT_RX_TRANSFER) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001739 ipw_rx(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05001740 handled |= IPW_INTA_BIT_RX_TRANSFER;
James Ketrenos43f66a62005-03-25 12:31:53 -06001741 }
1742
James Ketrenosb095c382005-08-24 22:04:42 -05001743 if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001744 IPW_DEBUG_HC("Command completed.\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001745 rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
James Ketrenos43f66a62005-03-25 12:31:53 -06001746 priv->status &= ~STATUS_HCMD_ACTIVE;
1747 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosb095c382005-08-24 22:04:42 -05001748 handled |= IPW_INTA_BIT_TX_CMD_QUEUE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001749 }
1750
James Ketrenosb095c382005-08-24 22:04:42 -05001751 if (inta & IPW_INTA_BIT_TX_QUEUE_1) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001752 IPW_DEBUG_TX("TX_QUEUE_1\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001753 rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
James Ketrenosb095c382005-08-24 22:04:42 -05001754 handled |= IPW_INTA_BIT_TX_QUEUE_1;
James Ketrenos43f66a62005-03-25 12:31:53 -06001755 }
1756
James Ketrenosb095c382005-08-24 22:04:42 -05001757 if (inta & IPW_INTA_BIT_TX_QUEUE_2) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001758 IPW_DEBUG_TX("TX_QUEUE_2\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001759 rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
James Ketrenosb095c382005-08-24 22:04:42 -05001760 handled |= IPW_INTA_BIT_TX_QUEUE_2;
James Ketrenos43f66a62005-03-25 12:31:53 -06001761 }
1762
James Ketrenosb095c382005-08-24 22:04:42 -05001763 if (inta & IPW_INTA_BIT_TX_QUEUE_3) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001764 IPW_DEBUG_TX("TX_QUEUE_3\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001765 rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
James Ketrenosb095c382005-08-24 22:04:42 -05001766 handled |= IPW_INTA_BIT_TX_QUEUE_3;
James Ketrenos43f66a62005-03-25 12:31:53 -06001767 }
1768
James Ketrenosb095c382005-08-24 22:04:42 -05001769 if (inta & IPW_INTA_BIT_TX_QUEUE_4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001770 IPW_DEBUG_TX("TX_QUEUE_4\n");
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001771 rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
James Ketrenosb095c382005-08-24 22:04:42 -05001772 handled |= IPW_INTA_BIT_TX_QUEUE_4;
James Ketrenos43f66a62005-03-25 12:31:53 -06001773 }
1774
James Ketrenosb095c382005-08-24 22:04:42 -05001775 if (inta & IPW_INTA_BIT_STATUS_CHANGE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001776 IPW_WARNING("STATUS_CHANGE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001777 handled |= IPW_INTA_BIT_STATUS_CHANGE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001778 }
1779
James Ketrenosb095c382005-08-24 22:04:42 -05001780 if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001781 IPW_WARNING("TX_PERIOD_EXPIRED\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001782 handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED;
James Ketrenos43f66a62005-03-25 12:31:53 -06001783 }
1784
James Ketrenosb095c382005-08-24 22:04:42 -05001785 if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001786 IPW_WARNING("HOST_CMD_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001787 handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001788 }
1789
James Ketrenosb095c382005-08-24 22:04:42 -05001790 if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001791 IPW_WARNING("FW_INITIALIZATION_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001792 handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001793 }
1794
James Ketrenosb095c382005-08-24 22:04:42 -05001795 if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001796 IPW_WARNING("PHY_OFF_DONE\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001797 handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001798 }
1799
James Ketrenosb095c382005-08-24 22:04:42 -05001800 if (inta & IPW_INTA_BIT_RF_KILL_DONE) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001801 IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
1802 priv->status |= STATUS_RF_KILL_HW;
1803 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosea2b26e2005-08-24 21:25:16 -05001804 priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
James Ketrenos43f66a62005-03-25 12:31:53 -06001805 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05001806 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06001807 queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
James Ketrenosb095c382005-08-24 22:04:42 -05001808 handled |= IPW_INTA_BIT_RF_KILL_DONE;
James Ketrenos43f66a62005-03-25 12:31:53 -06001809 }
Jeff Garzikbf794512005-07-31 13:07:26 -04001810
James Ketrenosb095c382005-08-24 22:04:42 -05001811 if (inta & IPW_INTA_BIT_FATAL_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001812 IPW_ERROR("Firmware error detected. Restarting.\n");
James Ketrenosb39860c2005-08-12 09:36:32 -05001813 if (priv->error) {
1814 IPW_ERROR("Sysfs 'error' log already exists.\n");
Brice Goglin0f52bf92005-12-01 01:41:46 -08001815#ifdef CONFIG_IPW2200_DEBUG
James Ketrenosb39860c2005-08-12 09:36:32 -05001816 if (ipw_debug_level & IPW_DL_FW_ERRORS) {
1817 struct ipw_fw_error *error =
1818 ipw_alloc_error_log(priv);
1819 ipw_dump_error_log(priv, error);
1820 if (error)
1821 ipw_free_error_log(error);
1822 }
James Ketrenos43f66a62005-03-25 12:31:53 -06001823#endif
James Ketrenosb39860c2005-08-12 09:36:32 -05001824 } else {
1825 priv->error = ipw_alloc_error_log(priv);
1826 if (priv->error)
1827 IPW_ERROR("Sysfs 'error' log captured.\n");
1828 else
1829 IPW_ERROR("Error allocating sysfs 'error' "
1830 "log.\n");
Brice Goglin0f52bf92005-12-01 01:41:46 -08001831#ifdef CONFIG_IPW2200_DEBUG
James Ketrenosb39860c2005-08-12 09:36:32 -05001832 if (ipw_debug_level & IPW_DL_FW_ERRORS)
1833 ipw_dump_error_log(priv, priv->error);
1834#endif
1835 }
1836
James Ketrenosb095c382005-08-24 22:04:42 -05001837 /* XXX: If hardware encryption is for WPA/WPA2,
1838 * we have to notify the supplicant. */
1839 if (priv->ieee->sec.encrypt) {
1840 priv->status &= ~STATUS_ASSOCIATED;
1841 notify_wx_assoc_event(priv);
1842 }
1843
1844 /* Keep the restart process from trying to send host
1845 * commands by clearing the INIT status bit */
1846 priv->status &= ~STATUS_INIT;
James Ketrenosafbf30a2005-08-25 00:05:33 -05001847
1848 /* Cancel currently queued command. */
1849 priv->status &= ~STATUS_HCMD_ACTIVE;
1850 wake_up_interruptible(&priv->wait_command_queue);
1851
James Ketrenos43f66a62005-03-25 12:31:53 -06001852 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenosb095c382005-08-24 22:04:42 -05001853 handled |= IPW_INTA_BIT_FATAL_ERROR;
James Ketrenos43f66a62005-03-25 12:31:53 -06001854 }
1855
James Ketrenosb095c382005-08-24 22:04:42 -05001856 if (inta & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06001857 IPW_ERROR("Parity error\n");
James Ketrenosb095c382005-08-24 22:04:42 -05001858 handled |= IPW_INTA_BIT_PARITY_ERROR;
James Ketrenos43f66a62005-03-25 12:31:53 -06001859 }
1860
1861 if (handled != inta) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001862 IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
James Ketrenos43f66a62005-03-25 12:31:53 -06001863 }
1864
1865 /* enable all interrupts */
1866 ipw_enable_interrupts(priv);
1867
1868 spin_unlock_irqrestore(&priv->lock, flags);
1869}
Jeff Garzikbf794512005-07-31 13:07:26 -04001870
James Ketrenos43f66a62005-03-25 12:31:53 -06001871#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
1872static char *get_cmd_string(u8 cmd)
1873{
1874 switch (cmd) {
1875 IPW_CMD(HOST_COMPLETE);
Jeff Garzikbf794512005-07-31 13:07:26 -04001876 IPW_CMD(POWER_DOWN);
1877 IPW_CMD(SYSTEM_CONFIG);
1878 IPW_CMD(MULTICAST_ADDRESS);
1879 IPW_CMD(SSID);
1880 IPW_CMD(ADAPTER_ADDRESS);
1881 IPW_CMD(PORT_TYPE);
1882 IPW_CMD(RTS_THRESHOLD);
1883 IPW_CMD(FRAG_THRESHOLD);
1884 IPW_CMD(POWER_MODE);
1885 IPW_CMD(WEP_KEY);
1886 IPW_CMD(TGI_TX_KEY);
1887 IPW_CMD(SCAN_REQUEST);
1888 IPW_CMD(SCAN_REQUEST_EXT);
1889 IPW_CMD(ASSOCIATE);
1890 IPW_CMD(SUPPORTED_RATES);
1891 IPW_CMD(SCAN_ABORT);
1892 IPW_CMD(TX_FLUSH);
1893 IPW_CMD(QOS_PARAMETERS);
1894 IPW_CMD(DINO_CONFIG);
1895 IPW_CMD(RSN_CAPABILITIES);
1896 IPW_CMD(RX_KEY);
1897 IPW_CMD(CARD_DISABLE);
1898 IPW_CMD(SEED_NUMBER);
1899 IPW_CMD(TX_POWER);
1900 IPW_CMD(COUNTRY_INFO);
1901 IPW_CMD(AIRONET_INFO);
1902 IPW_CMD(AP_TX_POWER);
1903 IPW_CMD(CCKM_INFO);
1904 IPW_CMD(CCX_VER_INFO);
1905 IPW_CMD(SET_CALIBRATION);
1906 IPW_CMD(SENSITIVITY_CALIB);
1907 IPW_CMD(RETRY_LIMIT);
1908 IPW_CMD(IPW_PRE_POWER_DOWN);
1909 IPW_CMD(VAP_BEACON_TEMPLATE);
1910 IPW_CMD(VAP_DTIM_PERIOD);
1911 IPW_CMD(EXT_SUPPORTED_RATES);
1912 IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
1913 IPW_CMD(VAP_QUIET_INTERVALS);
1914 IPW_CMD(VAP_CHANNEL_SWITCH);
1915 IPW_CMD(VAP_MANDATORY_CHANNELS);
1916 IPW_CMD(VAP_CELL_PWR_LIMIT);
1917 IPW_CMD(VAP_CF_PARAM_SET);
1918 IPW_CMD(VAP_SET_BEACONING_STATE);
1919 IPW_CMD(MEASUREMENT);
1920 IPW_CMD(POWER_CAPABILITY);
1921 IPW_CMD(SUPPORTED_CHANNELS);
1922 IPW_CMD(TPC_REPORT);
1923 IPW_CMD(WME_INFO);
1924 IPW_CMD(PRODUCTION_COMMAND);
1925 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06001926 return "UNKNOWN";
1927 }
1928}
James Ketrenos43f66a62005-03-25 12:31:53 -06001929
1930#define HOST_COMPLETE_TIMEOUT HZ
1931static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
1932{
1933 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05001934 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06001935
James Ketrenosa613bff2005-08-24 21:43:11 -05001936 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001937 if (priv->status & STATUS_HCMD_ACTIVE) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001938 IPW_ERROR("Failed to send %s: Already sending a command.\n",
1939 get_cmd_string(cmd->cmd));
James Ketrenosa613bff2005-08-24 21:43:11 -05001940 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001941 return -EAGAIN;
James Ketrenos43f66a62005-03-25 12:31:53 -06001942 }
1943
1944 priv->status |= STATUS_HCMD_ACTIVE;
Jeff Garzikbf794512005-07-31 13:07:26 -04001945
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001946 if (priv->cmdlog) {
1947 priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies;
1948 priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd;
1949 priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len;
1950 memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param,
1951 cmd->len);
1952 priv->cmdlog[priv->cmdlog_pos].retcode = -1;
1953 }
1954
James Ketrenosb095c382005-08-24 22:04:42 -05001955 IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
1956 get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
1957 priv->status);
Zhu Yif516dbc2006-01-24 16:36:44 +08001958
1959#ifndef DEBUG_CMD_WEP_KEY
1960 if (cmd->cmd == IPW_CMD_WEP_KEY)
1961 IPW_DEBUG_HC("WEP_KEY command masked out for secure.\n");
1962 else
1963#endif
1964 printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
1965
James Ketrenos43f66a62005-03-25 12:31:53 -06001966
1967 rc = ipw_queue_tx_hcmd(priv, cmd->cmd, &cmd->param, cmd->len, 0);
James Ketrenosa613bff2005-08-24 21:43:11 -05001968 if (rc) {
1969 priv->status &= ~STATUS_HCMD_ACTIVE;
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001970 IPW_ERROR("Failed to send %s: Reason %d\n",
1971 get_cmd_string(cmd->cmd), rc);
James Ketrenosa613bff2005-08-24 21:43:11 -05001972 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001973 goto exit;
James Ketrenosa613bff2005-08-24 21:43:11 -05001974 }
1975 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06001976
Jeff Garzik0edd5b42005-09-07 00:48:31 -04001977 rc = wait_event_interruptible_timeout(priv->wait_command_queue,
1978 !(priv->
1979 status & STATUS_HCMD_ACTIVE),
1980 HOST_COMPLETE_TIMEOUT);
James Ketrenos43f66a62005-03-25 12:31:53 -06001981 if (rc == 0) {
James Ketrenosa613bff2005-08-24 21:43:11 -05001982 spin_lock_irqsave(&priv->lock, flags);
1983 if (priv->status & STATUS_HCMD_ACTIVE) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001984 IPW_ERROR("Failed to send %s: Command timed out.\n",
1985 get_cmd_string(cmd->cmd));
James Ketrenosa613bff2005-08-24 21:43:11 -05001986 priv->status &= ~STATUS_HCMD_ACTIVE;
1987 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001988 rc = -EIO;
1989 goto exit;
James Ketrenosa613bff2005-08-24 21:43:11 -05001990 }
1991 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos3b9990c2005-08-19 13:18:55 -05001992 } else
1993 rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05001994
James Ketrenosb095c382005-08-24 22:04:42 -05001995 if (priv->status & STATUS_RF_KILL_HW) {
James Ketrenos9ddf84f2005-08-16 17:07:11 -05001996 IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n",
1997 get_cmd_string(cmd->cmd));
James Ketrenosf6c5cb72005-08-25 00:39:09 -05001998 rc = -EIO;
1999 goto exit;
James Ketrenos43f66a62005-03-25 12:31:53 -06002000 }
2001
James Ketrenosf6c5cb72005-08-25 00:39:09 -05002002 exit:
2003 if (priv->cmdlog) {
2004 priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
2005 priv->cmdlog_pos %= priv->cmdlog_len;
2006 }
2007 return rc;
James Ketrenos43f66a62005-03-25 12:31:53 -06002008}
2009
2010static int ipw_send_host_complete(struct ipw_priv *priv)
2011{
2012 struct host_cmd cmd = {
2013 .cmd = IPW_CMD_HOST_COMPLETE,
2014 .len = 0
2015 };
2016
2017 if (!priv) {
2018 IPW_ERROR("Invalid args\n");
2019 return -1;
2020 }
2021
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002022 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002023}
2024
Jeff Garzikbf794512005-07-31 13:07:26 -04002025static int ipw_send_system_config(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06002026 struct ipw_sys_config *config)
2027{
2028 struct host_cmd cmd = {
2029 .cmd = IPW_CMD_SYSTEM_CONFIG,
2030 .len = sizeof(*config)
2031 };
2032
2033 if (!priv || !config) {
2034 IPW_ERROR("Invalid args\n");
2035 return -1;
2036 }
2037
James Ketrenosafbf30a2005-08-25 00:05:33 -05002038 memcpy(cmd.param, config, sizeof(*config));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002039 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002040}
2041
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002042static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002043{
2044 struct host_cmd cmd = {
2045 .cmd = IPW_CMD_SSID,
2046 .len = min(len, IW_ESSID_MAX_SIZE)
2047 };
2048
2049 if (!priv || !ssid) {
2050 IPW_ERROR("Invalid args\n");
2051 return -1;
2052 }
2053
James Ketrenosafbf30a2005-08-25 00:05:33 -05002054 memcpy(cmd.param, ssid, cmd.len);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002055 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002056}
2057
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002058static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06002059{
2060 struct host_cmd cmd = {
2061 .cmd = IPW_CMD_ADAPTER_ADDRESS,
2062 .len = ETH_ALEN
2063 };
2064
2065 if (!priv || !mac) {
2066 IPW_ERROR("Invalid args\n");
2067 return -1;
2068 }
2069
2070 IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n",
2071 priv->net_dev->name, MAC_ARG(mac));
2072
James Ketrenosafbf30a2005-08-25 00:05:33 -05002073 memcpy(cmd.param, mac, ETH_ALEN);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002074 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002075}
2076
James Ketrenosa613bff2005-08-24 21:43:11 -05002077/*
2078 * NOTE: This must be executed from our workqueue as it results in udelay
2079 * being called which may corrupt the keyboard if executed on default
2080 * workqueue
2081 */
James Ketrenos43f66a62005-03-25 12:31:53 -06002082static void ipw_adapter_restart(void *adapter)
2083{
2084 struct ipw_priv *priv = adapter;
2085
2086 if (priv->status & STATUS_RF_KILL_MASK)
2087 return;
2088
2089 ipw_down(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05002090
2091 if (priv->assoc_network &&
2092 (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS))
2093 ipw_remove_current_network(priv);
2094
James Ketrenos43f66a62005-03-25 12:31:53 -06002095 if (ipw_up(priv)) {
2096 IPW_ERROR("Failed to up device\n");
2097 return;
2098 }
2099}
2100
James Ketrenosc848d0a2005-08-24 21:56:24 -05002101static void ipw_bg_adapter_restart(void *data)
2102{
2103 struct ipw_priv *priv = data;
2104 down(&priv->sem);
2105 ipw_adapter_restart(data);
2106 up(&priv->sem);
2107}
2108
James Ketrenos43f66a62005-03-25 12:31:53 -06002109#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
2110
2111static void ipw_scan_check(void *data)
2112{
2113 struct ipw_priv *priv = data;
2114 if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
2115 IPW_DEBUG_SCAN("Scan completion watchdog resetting "
Zhu Yic7b6a672006-01-24 16:37:05 +08002116 "adapter after (%dms).\n",
2117 jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG));
James Ketrenosa613bff2005-08-24 21:43:11 -05002118 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06002119 }
2120}
2121
James Ketrenosc848d0a2005-08-24 21:56:24 -05002122static void ipw_bg_scan_check(void *data)
2123{
2124 struct ipw_priv *priv = data;
2125 down(&priv->sem);
2126 ipw_scan_check(data);
2127 up(&priv->sem);
2128}
2129
James Ketrenos43f66a62005-03-25 12:31:53 -06002130static int ipw_send_scan_request_ext(struct ipw_priv *priv,
2131 struct ipw_scan_request_ext *request)
2132{
2133 struct host_cmd cmd = {
2134 .cmd = IPW_CMD_SCAN_REQUEST_EXT,
2135 .len = sizeof(*request)
2136 };
2137
James Ketrenosafbf30a2005-08-25 00:05:33 -05002138 memcpy(cmd.param, request, sizeof(*request));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002139 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002140}
2141
2142static int ipw_send_scan_abort(struct ipw_priv *priv)
2143{
2144 struct host_cmd cmd = {
2145 .cmd = IPW_CMD_SCAN_ABORT,
2146 .len = 0
2147 };
2148
2149 if (!priv) {
2150 IPW_ERROR("Invalid args\n");
2151 return -1;
2152 }
2153
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002154 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002155}
2156
2157static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
2158{
2159 struct host_cmd cmd = {
2160 .cmd = IPW_CMD_SENSITIVITY_CALIB,
2161 .len = sizeof(struct ipw_sensitivity_calib)
2162 };
2163 struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002164 &cmd.param;
James Ketrenos43f66a62005-03-25 12:31:53 -06002165 calib->beacon_rssi_raw = sens;
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002166 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002167}
2168
2169static int ipw_send_associate(struct ipw_priv *priv,
2170 struct ipw_associate *associate)
2171{
2172 struct host_cmd cmd = {
2173 .cmd = IPW_CMD_ASSOCIATE,
2174 .len = sizeof(*associate)
2175 };
2176
James Ketrenosa613bff2005-08-24 21:43:11 -05002177 struct ipw_associate tmp_associate;
2178 memcpy(&tmp_associate, associate, sizeof(*associate));
2179 tmp_associate.policy_support =
2180 cpu_to_le16(tmp_associate.policy_support);
2181 tmp_associate.assoc_tsf_msw = cpu_to_le32(tmp_associate.assoc_tsf_msw);
2182 tmp_associate.assoc_tsf_lsw = cpu_to_le32(tmp_associate.assoc_tsf_lsw);
2183 tmp_associate.capability = cpu_to_le16(tmp_associate.capability);
2184 tmp_associate.listen_interval =
2185 cpu_to_le16(tmp_associate.listen_interval);
2186 tmp_associate.beacon_interval =
2187 cpu_to_le16(tmp_associate.beacon_interval);
2188 tmp_associate.atim_window = cpu_to_le16(tmp_associate.atim_window);
2189
James Ketrenos43f66a62005-03-25 12:31:53 -06002190 if (!priv || !associate) {
2191 IPW_ERROR("Invalid args\n");
2192 return -1;
2193 }
2194
James Ketrenosafbf30a2005-08-25 00:05:33 -05002195 memcpy(cmd.param, &tmp_associate, sizeof(*associate));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002196 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002197}
2198
2199static int ipw_send_supported_rates(struct ipw_priv *priv,
2200 struct ipw_supported_rates *rates)
2201{
2202 struct host_cmd cmd = {
2203 .cmd = IPW_CMD_SUPPORTED_RATES,
2204 .len = sizeof(*rates)
2205 };
2206
2207 if (!priv || !rates) {
2208 IPW_ERROR("Invalid args\n");
2209 return -1;
2210 }
2211
James Ketrenosafbf30a2005-08-25 00:05:33 -05002212 memcpy(cmd.param, rates, sizeof(*rates));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002213 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002214}
2215
2216static int ipw_set_random_seed(struct ipw_priv *priv)
2217{
2218 struct host_cmd cmd = {
2219 .cmd = IPW_CMD_SEED_NUMBER,
2220 .len = sizeof(u32)
2221 };
2222
2223 if (!priv) {
2224 IPW_ERROR("Invalid args\n");
2225 return -1;
2226 }
2227
2228 get_random_bytes(&cmd.param, sizeof(u32));
2229
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002230 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002231}
2232
James Ketrenos43f66a62005-03-25 12:31:53 -06002233static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
2234{
2235 struct host_cmd cmd = {
2236 .cmd = IPW_CMD_CARD_DISABLE,
2237 .len = sizeof(u32)
2238 };
2239
2240 if (!priv) {
2241 IPW_ERROR("Invalid args\n");
2242 return -1;
2243 }
2244
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002245 *((u32 *) & cmd.param) = phy_off;
James Ketrenos43f66a62005-03-25 12:31:53 -06002246
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002247 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002248}
James Ketrenos43f66a62005-03-25 12:31:53 -06002249
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002250static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
James Ketrenos43f66a62005-03-25 12:31:53 -06002251{
2252 struct host_cmd cmd = {
2253 .cmd = IPW_CMD_TX_POWER,
2254 .len = sizeof(*power)
2255 };
2256
2257 if (!priv || !power) {
2258 IPW_ERROR("Invalid args\n");
2259 return -1;
2260 }
2261
James Ketrenosafbf30a2005-08-25 00:05:33 -05002262 memcpy(cmd.param, power, sizeof(*power));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002263 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002264}
James Ketrenos43f66a62005-03-25 12:31:53 -06002265
Zhu Yi6de9f7f2005-08-11 14:39:33 +08002266static int ipw_set_tx_power(struct ipw_priv *priv)
2267{
Liu Hong1fe0adb2005-08-19 09:33:10 -05002268 const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08002269 struct ipw_tx_power tx_power;
2270 s8 max_power;
2271 int i;
2272
2273 memset(&tx_power, 0, sizeof(tx_power));
2274
2275 /* configure device for 'G' band */
2276 tx_power.ieee_mode = IPW_G_MODE;
2277 tx_power.num_channels = geo->bg_channels;
2278 for (i = 0; i < geo->bg_channels; i++) {
2279 max_power = geo->bg[i].max_power;
2280 tx_power.channels_tx_power[i].channel_number =
2281 geo->bg[i].channel;
2282 tx_power.channels_tx_power[i].tx_power = max_power ?
2283 min(max_power, priv->tx_power) : priv->tx_power;
2284 }
2285 if (ipw_send_tx_power(priv, &tx_power))
2286 return -EIO;
2287
2288 /* configure device to also handle 'B' band */
2289 tx_power.ieee_mode = IPW_B_MODE;
2290 if (ipw_send_tx_power(priv, &tx_power))
2291 return -EIO;
2292
2293 /* configure device to also handle 'A' band */
2294 if (priv->ieee->abg_true) {
2295 tx_power.ieee_mode = IPW_A_MODE;
2296 tx_power.num_channels = geo->a_channels;
2297 for (i = 0; i < tx_power.num_channels; i++) {
2298 max_power = geo->a[i].max_power;
2299 tx_power.channels_tx_power[i].channel_number =
2300 geo->a[i].channel;
2301 tx_power.channels_tx_power[i].tx_power = max_power ?
2302 min(max_power, priv->tx_power) : priv->tx_power;
2303 }
2304 if (ipw_send_tx_power(priv, &tx_power))
2305 return -EIO;
2306 }
James Ketrenos43f66a62005-03-25 12:31:53 -06002307 return 0;
2308}
2309
2310static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
2311{
2312 struct ipw_rts_threshold rts_threshold = {
2313 .rts_threshold = rts,
2314 };
2315 struct host_cmd cmd = {
2316 .cmd = IPW_CMD_RTS_THRESHOLD,
2317 .len = sizeof(rts_threshold)
2318 };
2319
2320 if (!priv) {
2321 IPW_ERROR("Invalid args\n");
2322 return -1;
2323 }
2324
James Ketrenosafbf30a2005-08-25 00:05:33 -05002325 memcpy(cmd.param, &rts_threshold, sizeof(rts_threshold));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002326 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002327}
2328
2329static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
2330{
2331 struct ipw_frag_threshold frag_threshold = {
2332 .frag_threshold = frag,
2333 };
2334 struct host_cmd cmd = {
2335 .cmd = IPW_CMD_FRAG_THRESHOLD,
2336 .len = sizeof(frag_threshold)
2337 };
2338
2339 if (!priv) {
2340 IPW_ERROR("Invalid args\n");
2341 return -1;
2342 }
2343
James Ketrenosafbf30a2005-08-25 00:05:33 -05002344 memcpy(cmd.param, &frag_threshold, sizeof(frag_threshold));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002345 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002346}
2347
2348static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
2349{
2350 struct host_cmd cmd = {
2351 .cmd = IPW_CMD_POWER_MODE,
2352 .len = sizeof(u32)
2353 };
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002354 u32 *param = (u32 *) (&cmd.param);
James Ketrenos43f66a62005-03-25 12:31:53 -06002355
2356 if (!priv) {
2357 IPW_ERROR("Invalid args\n");
2358 return -1;
2359 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002360
James Ketrenos43f66a62005-03-25 12:31:53 -06002361 /* If on battery, set to 3, if AC set to CAM, else user
2362 * level */
2363 switch (mode) {
2364 case IPW_POWER_BATTERY:
2365 *param = IPW_POWER_INDEX_3;
2366 break;
2367 case IPW_POWER_AC:
2368 *param = IPW_POWER_MODE_CAM;
2369 break;
2370 default:
2371 *param = mode;
2372 break;
2373 }
2374
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002375 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002376}
2377
James Ketrenosafbf30a2005-08-25 00:05:33 -05002378static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit)
2379{
2380 struct ipw_retry_limit retry_limit = {
2381 .short_retry_limit = slimit,
2382 .long_retry_limit = llimit
2383 };
2384 struct host_cmd cmd = {
2385 .cmd = IPW_CMD_RETRY_LIMIT,
2386 .len = sizeof(retry_limit)
2387 };
2388
2389 if (!priv) {
2390 IPW_ERROR("Invalid args\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002391 return -1;
2392 }
2393
James Ketrenosafbf30a2005-08-25 00:05:33 -05002394 memcpy(cmd.param, &retry_limit, sizeof(retry_limit));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05002395 return ipw_send_cmd(priv, &cmd);
James Ketrenos43f66a62005-03-25 12:31:53 -06002396}
2397
2398/*
2399 * The IPW device contains a Microwire compatible EEPROM that stores
2400 * various data like the MAC address. Usually the firmware has exclusive
2401 * access to the eeprom, but during device initialization (before the
2402 * device driver has sent the HostComplete command to the firmware) the
2403 * device driver has read access to the EEPROM by way of indirect addressing
2404 * through a couple of memory mapped registers.
2405 *
2406 * The following is a simplified implementation for pulling data out of the
2407 * the eeprom, along with some helper functions to find information in
2408 * the per device private data's copy of the eeprom.
2409 *
2410 * NOTE: To better understand how these functions work (i.e what is a chip
2411 * select and why do have to keep driving the eeprom clock?), read
2412 * just about any data sheet for a Microwire compatible EEPROM.
2413 */
2414
2415/* write a 32 bit value into the indirect accessor register */
2416static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
2417{
2418 ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
Jeff Garzikbf794512005-07-31 13:07:26 -04002419
James Ketrenos43f66a62005-03-25 12:31:53 -06002420 /* the eeprom requires some time to complete the operation */
2421 udelay(p->eeprom_delay);
2422
2423 return;
2424}
2425
2426/* perform a chip select operation */
Arjan van de Ven858119e2006-01-14 13:20:43 -08002427static void eeprom_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002428{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002429 eeprom_write_reg(priv, 0);
2430 eeprom_write_reg(priv, EEPROM_BIT_CS);
2431 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2432 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002433}
2434
2435/* perform a chip select operation */
Arjan van de Ven858119e2006-01-14 13:20:43 -08002436static void eeprom_disable_cs(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002437{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002438 eeprom_write_reg(priv, EEPROM_BIT_CS);
2439 eeprom_write_reg(priv, 0);
2440 eeprom_write_reg(priv, EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002441}
2442
2443/* push a single bit down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002444static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
James Ketrenos43f66a62005-03-25 12:31:53 -06002445{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002446 int d = (bit ? EEPROM_BIT_DI : 0);
2447 eeprom_write_reg(p, EEPROM_BIT_CS | d);
2448 eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
James Ketrenos43f66a62005-03-25 12:31:53 -06002449}
2450
2451/* push an opcode followed by an address down to the eeprom */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002452static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002453{
2454 int i;
2455
2456 eeprom_cs(priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002457 eeprom_write_bit(priv, 1);
2458 eeprom_write_bit(priv, op & 2);
2459 eeprom_write_bit(priv, op & 1);
2460 for (i = 7; i >= 0; i--) {
2461 eeprom_write_bit(priv, addr & (1 << i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002462 }
2463}
2464
2465/* pull 16 bits off the eeprom, one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002466static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
James Ketrenos43f66a62005-03-25 12:31:53 -06002467{
2468 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002469 u16 r = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002470
James Ketrenos43f66a62005-03-25 12:31:53 -06002471 /* Send READ Opcode */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002472 eeprom_op(priv, EEPROM_CMD_READ, addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06002473
2474 /* Send dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002475 eeprom_write_reg(priv, EEPROM_BIT_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002476
2477 /* Read the byte off the eeprom one bit at a time */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002478 for (i = 0; i < 16; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002479 u32 data = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002480 eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
2481 eeprom_write_reg(priv, EEPROM_BIT_CS);
2482 data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
2483 r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002484 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002485
James Ketrenos43f66a62005-03-25 12:31:53 -06002486 /* Send another dummy bit */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002487 eeprom_write_reg(priv, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002488 eeprom_disable_cs(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002489
James Ketrenos43f66a62005-03-25 12:31:53 -06002490 return r;
2491}
2492
2493/* helper function for pulling the mac address out of the private */
2494/* data's copy of the eeprom data */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002495static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
James Ketrenos43f66a62005-03-25 12:31:53 -06002496{
James Ketrenosafbf30a2005-08-25 00:05:33 -05002497 memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6);
James Ketrenos43f66a62005-03-25 12:31:53 -06002498}
2499
2500/*
2501 * Either the device driver (i.e. the host) or the firmware can
2502 * load eeprom data into the designated region in SRAM. If neither
2503 * happens then the FW will shutdown with a fatal error.
2504 *
2505 * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
2506 * bit needs region of shared SRAM needs to be non-zero.
2507 */
2508static void ipw_eeprom_init_sram(struct ipw_priv *priv)
2509{
2510 int i;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002511 u16 *eeprom = (u16 *) priv->eeprom;
Jeff Garzikbf794512005-07-31 13:07:26 -04002512
James Ketrenos43f66a62005-03-25 12:31:53 -06002513 IPW_DEBUG_TRACE(">>\n");
2514
2515 /* read entire contents of eeprom into private buffer */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002516 for (i = 0; i < 128; i++)
James Ketrenosa613bff2005-08-24 21:43:11 -05002517 eeprom[i] = le16_to_cpu(eeprom_read_u16(priv, (u8) i));
James Ketrenos43f66a62005-03-25 12:31:53 -06002518
Jeff Garzikbf794512005-07-31 13:07:26 -04002519 /*
2520 If the data looks correct, then copy it to our private
James Ketrenos43f66a62005-03-25 12:31:53 -06002521 copy. Otherwise let the firmware know to perform the operation
Zhu Yic7b6a672006-01-24 16:37:05 +08002522 on its own.
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002523 */
James Ketrenos43f66a62005-03-25 12:31:53 -06002524 if ((priv->eeprom + EEPROM_VERSION) != 0) {
2525 IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
2526
2527 /* write the eeprom data to sram */
James Ketrenosb095c382005-08-24 22:04:42 -05002528 for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002529 ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002530
2531 /* Do not load eeprom data on fatal error or suspend */
2532 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
2533 } else {
2534 IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
2535
2536 /* Load eeprom data on fatal error or suspend */
2537 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
2538 }
2539
2540 IPW_DEBUG_TRACE("<<\n");
2541}
2542
Arjan van de Ven858119e2006-01-14 13:20:43 -08002543static void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
James Ketrenos43f66a62005-03-25 12:31:53 -06002544{
2545 count >>= 2;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002546 if (!count)
2547 return;
James Ketrenosb095c382005-08-24 22:04:42 -05002548 _ipw_write32(priv, IPW_AUTOINC_ADDR, start);
Jeff Garzikbf794512005-07-31 13:07:26 -04002549 while (count--)
James Ketrenosb095c382005-08-24 22:04:42 -05002550 _ipw_write32(priv, IPW_AUTOINC_DATA, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002551}
2552
2553static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
2554{
James Ketrenosb095c382005-08-24 22:04:42 -05002555 ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL,
Jeff Garzikbf794512005-07-31 13:07:26 -04002556 CB_NUMBER_OF_ELEMENTS_SMALL *
James Ketrenos43f66a62005-03-25 12:31:53 -06002557 sizeof(struct command_block));
2558}
2559
2560static int ipw_fw_dma_enable(struct ipw_priv *priv)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002561{ /* start dma engine but no transfers yet */
James Ketrenos43f66a62005-03-25 12:31:53 -06002562
2563 IPW_DEBUG_FW(">> : \n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002564
James Ketrenos43f66a62005-03-25 12:31:53 -06002565 /* Start the dma */
2566 ipw_fw_dma_reset_command_blocks(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002567
James Ketrenos43f66a62005-03-25 12:31:53 -06002568 /* Write CB base address */
James Ketrenosb095c382005-08-24 22:04:42 -05002569 ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL);
James Ketrenos43f66a62005-03-25 12:31:53 -06002570
2571 IPW_DEBUG_FW("<< : \n");
2572 return 0;
2573}
2574
2575static void ipw_fw_dma_abort(struct ipw_priv *priv)
2576{
2577 u32 control = 0;
2578
2579 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002580
2581 //set the Stop and Abort bit
James Ketrenos43f66a62005-03-25 12:31:53 -06002582 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
James Ketrenosb095c382005-08-24 22:04:42 -05002583 ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
James Ketrenos43f66a62005-03-25 12:31:53 -06002584 priv->sram_desc.last_cb_index = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04002585
James Ketrenos43f66a62005-03-25 12:31:53 -06002586 IPW_DEBUG_FW("<< \n");
2587}
2588
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002589static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
2590 struct command_block *cb)
James Ketrenos43f66a62005-03-25 12:31:53 -06002591{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002592 u32 address =
James Ketrenosb095c382005-08-24 22:04:42 -05002593 IPW_SHARED_SRAM_DMA_CONTROL +
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002594 (sizeof(struct command_block) * index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002595 IPW_DEBUG_FW(">> :\n");
2596
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002597 ipw_write_indirect(priv, address, (u8 *) cb,
2598 (int)sizeof(struct command_block));
James Ketrenos43f66a62005-03-25 12:31:53 -06002599
2600 IPW_DEBUG_FW("<< :\n");
2601 return 0;
2602
2603}
2604
2605static int ipw_fw_dma_kick(struct ipw_priv *priv)
2606{
2607 u32 control = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002608 u32 index = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002609
2610 IPW_DEBUG_FW(">> :\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04002611
James Ketrenos43f66a62005-03-25 12:31:53 -06002612 for (index = 0; index < priv->sram_desc.last_cb_index; index++)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002613 ipw_fw_dma_write_command_block(priv, index,
2614 &priv->sram_desc.cb_list[index]);
James Ketrenos43f66a62005-03-25 12:31:53 -06002615
2616 /* Enable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002617 ipw_clear_bit(priv, IPW_RESET_REG,
2618 IPW_RESET_REG_MASTER_DISABLED |
2619 IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04002620
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002621 /* Set the Start bit. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002622 control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
James Ketrenosb095c382005-08-24 22:04:42 -05002623 ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
James Ketrenos43f66a62005-03-25 12:31:53 -06002624
2625 IPW_DEBUG_FW("<< :\n");
2626 return 0;
2627}
2628
2629static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
2630{
2631 u32 address;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002632 u32 register_value = 0;
2633 u32 cb_fields_address = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002634
2635 IPW_DEBUG_FW(">> :\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002636 address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002637 IPW_DEBUG_FW_INFO("Current CB is 0x%x \n", address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002638
2639 /* Read the DMA Controlor register */
James Ketrenosb095c382005-08-24 22:04:42 -05002640 register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL);
2641 IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002642
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002643 /* Print the CB values */
James Ketrenos43f66a62005-03-25 12:31:53 -06002644 cb_fields_address = address;
2645 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002646 IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002647
2648 cb_fields_address += sizeof(u32);
2649 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002650 IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002651
2652 cb_fields_address += sizeof(u32);
2653 register_value = ipw_read_reg32(priv, cb_fields_address);
2654 IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
2655 register_value);
2656
2657 cb_fields_address += sizeof(u32);
2658 register_value = ipw_read_reg32(priv, cb_fields_address);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002659 IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n", register_value);
James Ketrenos43f66a62005-03-25 12:31:53 -06002660
2661 IPW_DEBUG_FW(">> :\n");
2662}
2663
2664static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
2665{
2666 u32 current_cb_address = 0;
2667 u32 current_cb_index = 0;
2668
2669 IPW_DEBUG_FW("<< :\n");
James Ketrenosb095c382005-08-24 22:04:42 -05002670 current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
Jeff Garzikbf794512005-07-31 13:07:26 -04002671
James Ketrenosb095c382005-08-24 22:04:42 -05002672 current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002673 sizeof(struct command_block);
Jeff Garzikbf794512005-07-31 13:07:26 -04002674
James Ketrenos43f66a62005-03-25 12:31:53 -06002675 IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002676 current_cb_index, current_cb_address);
James Ketrenos43f66a62005-03-25 12:31:53 -06002677
2678 IPW_DEBUG_FW(">> :\n");
2679 return current_cb_index;
2680
2681}
2682
2683static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
2684 u32 src_address,
2685 u32 dest_address,
2686 u32 length,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002687 int interrupt_enabled, int is_last)
James Ketrenos43f66a62005-03-25 12:31:53 -06002688{
2689
Jeff Garzikbf794512005-07-31 13:07:26 -04002690 u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002691 CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
2692 CB_DEST_SIZE_LONG;
James Ketrenos43f66a62005-03-25 12:31:53 -06002693 struct command_block *cb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002694 u32 last_cb_element = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002695
2696 IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
2697 src_address, dest_address, length);
2698
2699 if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
2700 return -1;
2701
2702 last_cb_element = priv->sram_desc.last_cb_index;
2703 cb = &priv->sram_desc.cb_list[last_cb_element];
2704 priv->sram_desc.last_cb_index++;
2705
2706 /* Calculate the new CB control word */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002707 if (interrupt_enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -06002708 control |= CB_INT_ENABLED;
2709
2710 if (is_last)
2711 control |= CB_LAST_VALID;
Jeff Garzikbf794512005-07-31 13:07:26 -04002712
James Ketrenos43f66a62005-03-25 12:31:53 -06002713 control |= length;
2714
2715 /* Calculate the CB Element's checksum value */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002716 cb->status = control ^ src_address ^ dest_address;
James Ketrenos43f66a62005-03-25 12:31:53 -06002717
2718 /* Copy the Source and Destination addresses */
2719 cb->dest_addr = dest_address;
2720 cb->source_addr = src_address;
2721
2722 /* Copy the Control Word last */
2723 cb->control = control;
2724
2725 return 0;
2726}
2727
2728static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002729 u32 src_phys, u32 dest_address, u32 length)
James Ketrenos43f66a62005-03-25 12:31:53 -06002730{
2731 u32 bytes_left = length;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002732 u32 src_offset = 0;
2733 u32 dest_offset = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06002734 int status = 0;
2735 IPW_DEBUG_FW(">> \n");
2736 IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
2737 src_phys, dest_address, length);
2738 while (bytes_left > CB_MAX_LENGTH) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002739 status = ipw_fw_dma_add_command_block(priv,
2740 src_phys + src_offset,
2741 dest_address +
2742 dest_offset,
2743 CB_MAX_LENGTH, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002744 if (status) {
2745 IPW_DEBUG_FW_INFO(": Failed\n");
2746 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002747 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06002748 IPW_DEBUG_FW_INFO(": Added new cb\n");
2749
2750 src_offset += CB_MAX_LENGTH;
2751 dest_offset += CB_MAX_LENGTH;
2752 bytes_left -= CB_MAX_LENGTH;
2753 }
2754
2755 /* add the buffer tail */
2756 if (bytes_left > 0) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002757 status =
2758 ipw_fw_dma_add_command_block(priv, src_phys + src_offset,
2759 dest_address + dest_offset,
2760 bytes_left, 0, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002761 if (status) {
2762 IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
2763 return -1;
Jeff Garzikbf794512005-07-31 13:07:26 -04002764 } else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002765 IPW_DEBUG_FW_INFO
2766 (": Adding new cb - the buffer tail\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002767 }
Jeff Garzikbf794512005-07-31 13:07:26 -04002768
James Ketrenos43f66a62005-03-25 12:31:53 -06002769 IPW_DEBUG_FW("<< \n");
2770 return 0;
2771}
2772
2773static int ipw_fw_dma_wait(struct ipw_priv *priv)
2774{
2775 u32 current_index = 0;
2776 u32 watchdog = 0;
2777
2778 IPW_DEBUG_FW(">> : \n");
2779
2780 current_index = ipw_fw_dma_command_block_index(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04002781 IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%8X\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002782 (int)priv->sram_desc.last_cb_index);
James Ketrenos43f66a62005-03-25 12:31:53 -06002783
2784 while (current_index < priv->sram_desc.last_cb_index) {
2785 udelay(50);
2786 current_index = ipw_fw_dma_command_block_index(priv);
2787
2788 watchdog++;
2789
2790 if (watchdog > 400) {
2791 IPW_DEBUG_FW_INFO("Timeout\n");
2792 ipw_fw_dma_dump_command_block(priv);
2793 ipw_fw_dma_abort(priv);
2794 return -1;
2795 }
2796 }
2797
2798 ipw_fw_dma_abort(priv);
2799
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002800 /*Disable the DMA in the CSR register */
James Ketrenosb095c382005-08-24 22:04:42 -05002801 ipw_set_bit(priv, IPW_RESET_REG,
2802 IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002803
2804 IPW_DEBUG_FW("<< dmaWaitSync \n");
2805 return 0;
2806}
2807
Jeff Garzikbf794512005-07-31 13:07:26 -04002808static void ipw_remove_current_network(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002809{
2810 struct list_head *element, *safe;
Jeff Garzikbf794512005-07-31 13:07:26 -04002811 struct ieee80211_network *network = NULL;
James Ketrenosa613bff2005-08-24 21:43:11 -05002812 unsigned long flags;
2813
2814 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002815 list_for_each_safe(element, safe, &priv->ieee->network_list) {
2816 network = list_entry(element, struct ieee80211_network, list);
2817 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
2818 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04002819 list_add_tail(&network->list,
James Ketrenos43f66a62005-03-25 12:31:53 -06002820 &priv->ieee->network_free_list);
2821 }
2822 }
James Ketrenosa613bff2005-08-24 21:43:11 -05002823 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06002824}
2825
2826/**
Jeff Garzikbf794512005-07-31 13:07:26 -04002827 * Check that card is still alive.
James Ketrenos43f66a62005-03-25 12:31:53 -06002828 * Reads debug register from domain0.
2829 * If card is present, pre-defined value should
2830 * be found there.
Jeff Garzikbf794512005-07-31 13:07:26 -04002831 *
James Ketrenos43f66a62005-03-25 12:31:53 -06002832 * @param priv
2833 * @return 1 if card is present, 0 otherwise
2834 */
2835static inline int ipw_alive(struct ipw_priv *priv)
2836{
2837 return ipw_read32(priv, 0x90) == 0xd55555d5;
2838}
2839
Zhu Yic7b6a672006-01-24 16:37:05 +08002840/* timeout in msec, attempted in 10-msec quanta */
Arjan van de Ven858119e2006-01-14 13:20:43 -08002841static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
James Ketrenos43f66a62005-03-25 12:31:53 -06002842 int timeout)
2843{
2844 int i = 0;
2845
2846 do {
Jeff Garzikbf794512005-07-31 13:07:26 -04002847 if ((ipw_read32(priv, addr) & mask) == mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06002848 return i;
2849 mdelay(10);
2850 i += 10;
2851 } while (i < timeout);
Jeff Garzikbf794512005-07-31 13:07:26 -04002852
James Ketrenos43f66a62005-03-25 12:31:53 -06002853 return -ETIME;
2854}
2855
Jeff Garzikbf794512005-07-31 13:07:26 -04002856/* These functions load the firmware and micro code for the operation of
James Ketrenos43f66a62005-03-25 12:31:53 -06002857 * the ipw hardware. It assumes the buffer has all the bits for the
2858 * image and the caller is handling the memory allocation and clean up.
2859 */
2860
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002861static int ipw_stop_master(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06002862{
2863 int rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002864
James Ketrenos43f66a62005-03-25 12:31:53 -06002865 IPW_DEBUG_TRACE(">> \n");
2866 /* stop master. typical delay - 0 */
James Ketrenosb095c382005-08-24 22:04:42 -05002867 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06002868
Zhu Yic7b6a672006-01-24 16:37:05 +08002869 /* timeout is in msec, polled in 10-msec quanta */
James Ketrenosb095c382005-08-24 22:04:42 -05002870 rc = ipw_poll_bit(priv, IPW_RESET_REG,
2871 IPW_RESET_REG_MASTER_DISABLED, 100);
James Ketrenos43f66a62005-03-25 12:31:53 -06002872 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08002873 IPW_ERROR("wait for stop master failed after 100ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06002874 return -1;
2875 }
2876
2877 IPW_DEBUG_INFO("stop master %dms\n", rc);
2878
2879 return rc;
2880}
2881
2882static void ipw_arc_release(struct ipw_priv *priv)
2883{
2884 IPW_DEBUG_TRACE(">> \n");
2885 mdelay(5);
2886
James Ketrenosb095c382005-08-24 22:04:42 -05002887 ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06002888
2889 /* no one knows timing, for safety add some delay */
2890 mdelay(5);
2891}
2892
2893struct fw_header {
2894 u32 version;
2895 u32 mode;
2896};
2897
2898struct fw_chunk {
2899 u32 address;
2900 u32 length;
2901};
2902
2903#define IPW_FW_MAJOR_VERSION 2
James Ketrenos817153762005-08-25 01:37:28 -05002904#define IPW_FW_MINOR_VERSION 4
James Ketrenos43f66a62005-03-25 12:31:53 -06002905
2906#define IPW_FW_MINOR(x) ((x & 0xff) >> 8)
2907#define IPW_FW_MAJOR(x) (x & 0xff)
2908
James Ketrenosafbf30a2005-08-25 00:05:33 -05002909#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | IPW_FW_MAJOR_VERSION)
James Ketrenos43f66a62005-03-25 12:31:53 -06002910
2911#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \
2912"." __stringify(IPW_FW_MINOR_VERSION) "-"
2913
2914#if IPW_FW_MAJOR_VERSION >= 2 && IPW_FW_MINOR_VERSION > 0
2915#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw"
2916#else
2917#define IPW_FW_NAME(x) "ipw2200_" x ".fw"
2918#endif
2919
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002920static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06002921{
2922 int rc = 0, i, addr;
2923 u8 cr = 0;
2924 u16 *image;
2925
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002926 image = (u16 *) data;
Jeff Garzikbf794512005-07-31 13:07:26 -04002927
James Ketrenos43f66a62005-03-25 12:31:53 -06002928 IPW_DEBUG_TRACE(">> \n");
2929
2930 rc = ipw_stop_master(priv);
2931
2932 if (rc < 0)
2933 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04002934
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002935// spin_lock_irqsave(&priv->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04002936
James Ketrenosb095c382005-08-24 22:04:42 -05002937 for (addr = IPW_SHARED_LOWER_BOUND;
2938 addr < IPW_REGISTER_DOMAIN1_END; addr += 4) {
James Ketrenos43f66a62005-03-25 12:31:53 -06002939 ipw_write32(priv, addr, 0);
2940 }
2941
2942 /* no ucode (yet) */
2943 memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
2944 /* destroy DMA queues */
2945 /* reset sequence */
2946
James Ketrenosb095c382005-08-24 22:04:42 -05002947 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON);
James Ketrenos43f66a62005-03-25 12:31:53 -06002948 ipw_arc_release(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05002949 ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF);
James Ketrenos43f66a62005-03-25 12:31:53 -06002950 mdelay(1);
2951
2952 /* reset PHY */
James Ketrenosb095c382005-08-24 22:04:42 -05002953 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN);
James Ketrenos43f66a62005-03-25 12:31:53 -06002954 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002955
James Ketrenosb095c382005-08-24 22:04:42 -05002956 ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06002957 mdelay(1);
Jeff Garzikbf794512005-07-31 13:07:26 -04002958
James Ketrenos43f66a62005-03-25 12:31:53 -06002959 /* enable ucode store */
Zhu Yic8fe6672006-01-24 16:36:36 +08002960 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0);
2961 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002962 mdelay(1);
2963
2964 /* write ucode */
2965 /**
2966 * @bug
2967 * Do NOT set indirect address register once and then
2968 * store data to indirect data register in the loop.
2969 * It seems very reasonable, but in this case DINO do not
2970 * accept ucode. It is essential to set address each time.
2971 */
2972 /* load new ipw uCode */
2973 for (i = 0; i < len / 2; i++)
James Ketrenosb095c382005-08-24 22:04:42 -05002974 ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE,
James Ketrenosa613bff2005-08-24 21:43:11 -05002975 cpu_to_le16(image[i]));
James Ketrenos43f66a62005-03-25 12:31:53 -06002976
James Ketrenos43f66a62005-03-25 12:31:53 -06002977 /* enable DINO */
James Ketrenosb095c382005-08-24 22:04:42 -05002978 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
2979 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
James Ketrenos43f66a62005-03-25 12:31:53 -06002980
Jeff Garzik0edd5b42005-09-07 00:48:31 -04002981 /* this is where the igx / win driver deveates from the VAP driver. */
James Ketrenos43f66a62005-03-25 12:31:53 -06002982
2983 /* wait for alive response */
2984 for (i = 0; i < 100; i++) {
2985 /* poll for incoming data */
James Ketrenosb095c382005-08-24 22:04:42 -05002986 cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS);
James Ketrenos43f66a62005-03-25 12:31:53 -06002987 if (cr & DINO_RXFIFO_DATA)
2988 break;
2989 mdelay(1);
2990 }
2991
2992 if (cr & DINO_RXFIFO_DATA) {
2993 /* alive_command_responce size is NOT multiple of 4 */
2994 u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
Jeff Garzikbf794512005-07-31 13:07:26 -04002995
2996 for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06002997 response_buffer[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05002998 le32_to_cpu(ipw_read_reg32(priv,
James Ketrenosb095c382005-08-24 22:04:42 -05002999 IPW_BASEBAND_RX_FIFO_READ));
James Ketrenos43f66a62005-03-25 12:31:53 -06003000 memcpy(&priv->dino_alive, response_buffer,
3001 sizeof(priv->dino_alive));
3002 if (priv->dino_alive.alive_command == 1
3003 && priv->dino_alive.ucode_valid == 1) {
3004 rc = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003005 IPW_DEBUG_INFO
3006 ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
3007 "of %02d/%02d/%02d %02d:%02d\n",
3008 priv->dino_alive.software_revision,
3009 priv->dino_alive.software_revision,
3010 priv->dino_alive.device_identifier,
3011 priv->dino_alive.device_identifier,
3012 priv->dino_alive.time_stamp[0],
3013 priv->dino_alive.time_stamp[1],
3014 priv->dino_alive.time_stamp[2],
3015 priv->dino_alive.time_stamp[3],
3016 priv->dino_alive.time_stamp[4]);
James Ketrenos43f66a62005-03-25 12:31:53 -06003017 } else {
3018 IPW_DEBUG_INFO("Microcode is not alive\n");
3019 rc = -EINVAL;
3020 }
3021 } else {
3022 IPW_DEBUG_INFO("No alive response from DINO\n");
3023 rc = -ETIME;
3024 }
3025
3026 /* disable DINO, otherwise for some reason
3027 firmware have problem getting alive resp. */
James Ketrenosb095c382005-08-24 22:04:42 -05003028 ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06003029
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003030// spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003031
3032 return rc;
3033}
3034
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003035static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
James Ketrenos43f66a62005-03-25 12:31:53 -06003036{
3037 int rc = -1;
3038 int offset = 0;
3039 struct fw_chunk *chunk;
3040 dma_addr_t shared_phys;
3041 u8 *shared_virt;
3042
3043 IPW_DEBUG_TRACE("<< : \n");
3044 shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
3045
3046 if (!shared_virt)
3047 return -ENOMEM;
3048
3049 memmove(shared_virt, data, len);
3050
3051 /* Start the Dma */
3052 rc = ipw_fw_dma_enable(priv);
3053
3054 if (priv->sram_desc.last_cb_index > 0) {
3055 /* the DMA is already ready this would be a bug. */
3056 BUG();
3057 goto out;
3058 }
3059
3060 do {
3061 chunk = (struct fw_chunk *)(data + offset);
3062 offset += sizeof(struct fw_chunk);
3063 /* build DMA packet and queue up for sending */
Jeff Garzikbf794512005-07-31 13:07:26 -04003064 /* dma to chunk->address, the chunk->length bytes from data +
James Ketrenos43f66a62005-03-25 12:31:53 -06003065 * offeset*/
3066 /* Dma loading */
3067 rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
James Ketrenosa613bff2005-08-24 21:43:11 -05003068 le32_to_cpu(chunk->address),
3069 le32_to_cpu(chunk->length));
James Ketrenos43f66a62005-03-25 12:31:53 -06003070 if (rc) {
3071 IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
3072 goto out;
3073 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003074
James Ketrenosa613bff2005-08-24 21:43:11 -05003075 offset += le32_to_cpu(chunk->length);
James Ketrenos43f66a62005-03-25 12:31:53 -06003076 } while (offset < len);
3077
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003078 /* Run the DMA and wait for the answer */
James Ketrenos43f66a62005-03-25 12:31:53 -06003079 rc = ipw_fw_dma_kick(priv);
3080 if (rc) {
3081 IPW_ERROR("dmaKick Failed\n");
3082 goto out;
3083 }
3084
3085 rc = ipw_fw_dma_wait(priv);
3086 if (rc) {
3087 IPW_ERROR("dmaWaitSync Failed\n");
3088 goto out;
3089 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003090 out:
3091 pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
James Ketrenos43f66a62005-03-25 12:31:53 -06003092 return rc;
3093}
3094
3095/* stop nic */
3096static int ipw_stop_nic(struct ipw_priv *priv)
3097{
3098 int rc = 0;
3099
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003100 /* stop */
James Ketrenosb095c382005-08-24 22:04:42 -05003101 ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
Jeff Garzikbf794512005-07-31 13:07:26 -04003102
James Ketrenosb095c382005-08-24 22:04:42 -05003103 rc = ipw_poll_bit(priv, IPW_RESET_REG,
3104 IPW_RESET_REG_MASTER_DISABLED, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003105 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08003106 IPW_ERROR("wait for reg master disabled failed after 500ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003107 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003108 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003109
James Ketrenosb095c382005-08-24 22:04:42 -05003110 ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04003111
James Ketrenos43f66a62005-03-25 12:31:53 -06003112 return rc;
3113}
3114
3115static void ipw_start_nic(struct ipw_priv *priv)
3116{
3117 IPW_DEBUG_TRACE(">>\n");
3118
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003119 /* prvHwStartNic release ARC */
James Ketrenosb095c382005-08-24 22:04:42 -05003120 ipw_clear_bit(priv, IPW_RESET_REG,
3121 IPW_RESET_REG_MASTER_DISABLED |
3122 IPW_RESET_REG_STOP_MASTER |
James Ketrenos43f66a62005-03-25 12:31:53 -06003123 CBD_RESET_REG_PRINCETON_RESET);
Jeff Garzikbf794512005-07-31 13:07:26 -04003124
James Ketrenos43f66a62005-03-25 12:31:53 -06003125 /* enable power management */
James Ketrenosb095c382005-08-24 22:04:42 -05003126 ipw_set_bit(priv, IPW_GP_CNTRL_RW,
3127 IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
James Ketrenos43f66a62005-03-25 12:31:53 -06003128
3129 IPW_DEBUG_TRACE("<<\n");
3130}
Jeff Garzikbf794512005-07-31 13:07:26 -04003131
James Ketrenos43f66a62005-03-25 12:31:53 -06003132static int ipw_init_nic(struct ipw_priv *priv)
3133{
3134 int rc;
3135
3136 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04003137 /* reset */
James Ketrenos43f66a62005-03-25 12:31:53 -06003138 /*prvHwInitNic */
3139 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05003140 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003141
3142 /* low-level PLL activation */
James Ketrenosb095c382005-08-24 22:04:42 -05003143 ipw_write32(priv, IPW_READ_INT_REGISTER,
3144 IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
James Ketrenos43f66a62005-03-25 12:31:53 -06003145
3146 /* wait for clock stabilization */
James Ketrenosb095c382005-08-24 22:04:42 -05003147 rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
3148 IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003149 if (rc < 0)
James Ketrenos43f66a62005-03-25 12:31:53 -06003150 IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
3151
3152 /* assert SW reset */
James Ketrenosb095c382005-08-24 22:04:42 -05003153 ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
James Ketrenos43f66a62005-03-25 12:31:53 -06003154
3155 udelay(10);
3156
3157 /* set "initialization complete" bit to move adapter to D0 state */
James Ketrenosb095c382005-08-24 22:04:42 -05003158 ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003159
3160 IPW_DEBUG_TRACE(">>\n");
3161 return 0;
3162}
3163
Jeff Garzikbf794512005-07-31 13:07:26 -04003164/* Call this function from process context, it will sleep in request_firmware.
James Ketrenos43f66a62005-03-25 12:31:53 -06003165 * Probe is an ok place to call this from.
3166 */
3167static int ipw_reset_nic(struct ipw_priv *priv)
3168{
3169 int rc = 0;
James Ketrenosa613bff2005-08-24 21:43:11 -05003170 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06003171
3172 IPW_DEBUG_TRACE(">>\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04003173
James Ketrenos43f66a62005-03-25 12:31:53 -06003174 rc = ipw_init_nic(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -04003175
James Ketrenosa613bff2005-08-24 21:43:11 -05003176 spin_lock_irqsave(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003177 /* Clear the 'host command active' bit... */
3178 priv->status &= ~STATUS_HCMD_ACTIVE;
3179 wake_up_interruptible(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -05003180 priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
3181 wake_up_interruptible(&priv->wait_state);
James Ketrenosa613bff2005-08-24 21:43:11 -05003182 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06003183
3184 IPW_DEBUG_TRACE("<<\n");
3185 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003186}
James Ketrenos43f66a62005-03-25 12:31:53 -06003187
Jeff Garzikbf794512005-07-31 13:07:26 -04003188static int ipw_get_fw(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003189 const struct firmware **fw, const char *name)
3190{
3191 struct fw_header *header;
3192 int rc;
3193
3194 /* ask firmware_class module to get the boot firmware off disk */
3195 rc = request_firmware(fw, name, &priv->pci_dev->dev);
3196 if (rc < 0) {
3197 IPW_ERROR("%s load failed: Reason %d\n", name, rc);
3198 return rc;
Jeff Garzikbf794512005-07-31 13:07:26 -04003199 }
James Ketrenos43f66a62005-03-25 12:31:53 -06003200
3201 header = (struct fw_header *)(*fw)->data;
James Ketrenosa613bff2005-08-24 21:43:11 -05003202 if (IPW_FW_MAJOR(le32_to_cpu(header->version)) != IPW_FW_MAJOR_VERSION) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003203 IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n",
3204 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05003205 IPW_FW_MAJOR(le32_to_cpu(header->version)),
3206 IPW_FW_MAJOR_VERSION);
James Ketrenos43f66a62005-03-25 12:31:53 -06003207 return -EINVAL;
3208 }
3209
Jiri Bencaaa4d302005-06-07 14:58:41 +02003210 IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%zd bytes)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06003211 name,
James Ketrenosa613bff2005-08-24 21:43:11 -05003212 IPW_FW_MAJOR(le32_to_cpu(header->version)),
3213 IPW_FW_MINOR(le32_to_cpu(header->version)),
James Ketrenos43f66a62005-03-25 12:31:53 -06003214 (*fw)->size - sizeof(struct fw_header));
3215 return 0;
3216}
3217
James Ketrenosb095c382005-08-24 22:04:42 -05003218#define IPW_RX_BUF_SIZE (3000)
James Ketrenos43f66a62005-03-25 12:31:53 -06003219
Arjan van de Ven858119e2006-01-14 13:20:43 -08003220static void ipw_rx_queue_reset(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003221 struct ipw_rx_queue *rxq)
3222{
3223 unsigned long flags;
3224 int i;
3225
3226 spin_lock_irqsave(&rxq->lock, flags);
3227
3228 INIT_LIST_HEAD(&rxq->rx_free);
3229 INIT_LIST_HEAD(&rxq->rx_used);
3230
3231 /* Fill the rx_used queue with _all_ of the Rx buffers */
3232 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
3233 /* In the reset function, these buffers may have been allocated
3234 * to an SKB, so we need to unmap and free potential storage */
3235 if (rxq->pool[i].skb != NULL) {
3236 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05003237 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003238 dev_kfree_skb(rxq->pool[i].skb);
James Ketrenosa613bff2005-08-24 21:43:11 -05003239 rxq->pool[i].skb = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -06003240 }
3241 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
3242 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003243
James Ketrenos43f66a62005-03-25 12:31:53 -06003244 /* Set us so that we have processed and used all buffers, but have
3245 * not restocked the Rx queue with fresh buffers */
3246 rxq->read = rxq->write = 0;
3247 rxq->processed = RX_QUEUE_SIZE - 1;
3248 rxq->free_count = 0;
3249 spin_unlock_irqrestore(&rxq->lock, flags);
3250}
3251
3252#ifdef CONFIG_PM
3253static int fw_loaded = 0;
3254static const struct firmware *bootfw = NULL;
3255static const struct firmware *firmware = NULL;
3256static const struct firmware *ucode = NULL;
James Ketrenosafbf30a2005-08-25 00:05:33 -05003257
3258static void free_firmware(void)
3259{
3260 if (fw_loaded) {
3261 release_firmware(bootfw);
3262 release_firmware(ucode);
3263 release_firmware(firmware);
3264 bootfw = ucode = firmware = NULL;
3265 fw_loaded = 0;
3266 }
3267}
3268#else
3269#define free_firmware() do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06003270#endif
3271
3272static int ipw_load(struct ipw_priv *priv)
3273{
3274#ifndef CONFIG_PM
3275 const struct firmware *bootfw = NULL;
3276 const struct firmware *firmware = NULL;
3277 const struct firmware *ucode = NULL;
3278#endif
3279 int rc = 0, retries = 3;
3280
3281#ifdef CONFIG_PM
3282 if (!fw_loaded) {
3283#endif
3284 rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003285 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003286 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003287
James Ketrenos43f66a62005-03-25 12:31:53 -06003288 switch (priv->ieee->iw_mode) {
3289 case IW_MODE_ADHOC:
Jeff Garzikbf794512005-07-31 13:07:26 -04003290 rc = ipw_get_fw(priv, &ucode,
James Ketrenos43f66a62005-03-25 12:31:53 -06003291 IPW_FW_NAME("ibss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003292 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003293 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003294
James Ketrenos43f66a62005-03-25 12:31:53 -06003295 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss"));
3296 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003297
James Ketrenosb095c382005-08-24 22:04:42 -05003298#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06003299 case IW_MODE_MONITOR:
Jeff Garzikbf794512005-07-31 13:07:26 -04003300 rc = ipw_get_fw(priv, &ucode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05003301 IPW_FW_NAME("sniffer_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003302 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003303 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003304
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003305 rc = ipw_get_fw(priv, &firmware,
3306 IPW_FW_NAME("sniffer"));
James Ketrenos43f66a62005-03-25 12:31:53 -06003307 break;
3308#endif
3309 case IW_MODE_INFRA:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003310 rc = ipw_get_fw(priv, &ucode, IPW_FW_NAME("bss_ucode"));
Jeff Garzikbf794512005-07-31 13:07:26 -04003311 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003312 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -04003313
James Ketrenos43f66a62005-03-25 12:31:53 -06003314 rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss"));
3315 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04003316
James Ketrenos43f66a62005-03-25 12:31:53 -06003317 default:
3318 rc = -EINVAL;
3319 }
3320
Jeff Garzikbf794512005-07-31 13:07:26 -04003321 if (rc)
James Ketrenos43f66a62005-03-25 12:31:53 -06003322 goto error;
3323
3324#ifdef CONFIG_PM
3325 fw_loaded = 1;
3326 }
3327#endif
3328
3329 if (!priv->rxq)
3330 priv->rxq = ipw_rx_queue_alloc(priv);
3331 else
3332 ipw_rx_queue_reset(priv, priv->rxq);
3333 if (!priv->rxq) {
3334 IPW_ERROR("Unable to initialize Rx queue\n");
3335 goto error;
3336 }
3337
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003338 retry:
James Ketrenos43f66a62005-03-25 12:31:53 -06003339 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003340 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003341 priv->status &= ~STATUS_INT_ENABLED;
3342
3343 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003344 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003345
James Ketrenos43f66a62005-03-25 12:31:53 -06003346 ipw_stop_nic(priv);
3347
3348 rc = ipw_reset_nic(priv);
3349 if (rc) {
3350 IPW_ERROR("Unable to reset NIC\n");
3351 goto error;
3352 }
3353
James Ketrenosb095c382005-08-24 22:04:42 -05003354 ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
3355 IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
James Ketrenos43f66a62005-03-25 12:31:53 -06003356
3357 /* DMA the initial boot firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003358 rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003359 bootfw->size - sizeof(struct fw_header));
3360 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003361 IPW_ERROR("Unable to load boot firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003362 goto error;
3363 }
3364
3365 /* kick start the device */
3366 ipw_start_nic(priv);
3367
Zhu Yic7b6a672006-01-24 16:37:05 +08003368 /* wait for the device to finish its initial startup sequence */
James Ketrenosb095c382005-08-24 22:04:42 -05003369 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3370 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003371 if (rc < 0) {
3372 IPW_ERROR("device failed to boot initial fw image\n");
3373 goto error;
3374 }
3375 IPW_DEBUG_INFO("initial device response after %dms\n", rc);
3376
Jeff Garzikbf794512005-07-31 13:07:26 -04003377 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003378 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003379
3380 /* DMA the ucode into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003381 rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003382 ucode->size - sizeof(struct fw_header));
3383 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003384 IPW_ERROR("Unable to load ucode: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003385 goto error;
3386 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003387
James Ketrenos43f66a62005-03-25 12:31:53 -06003388 /* stop nic */
3389 ipw_stop_nic(priv);
3390
3391 /* DMA bss firmware into the device */
Jeff Garzikbf794512005-07-31 13:07:26 -04003392 rc = ipw_load_firmware(priv, firmware->data +
3393 sizeof(struct fw_header),
James Ketrenos43f66a62005-03-25 12:31:53 -06003394 firmware->size - sizeof(struct fw_header));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003395 if (rc < 0) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -05003396 IPW_ERROR("Unable to load firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -06003397 goto error;
3398 }
3399
3400 ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
3401
3402 rc = ipw_queue_reset(priv);
3403 if (rc) {
3404 IPW_ERROR("Unable to initialize queues\n");
3405 goto error;
3406 }
3407
3408 /* Ensure interrupts are disabled */
James Ketrenosb095c382005-08-24 22:04:42 -05003409 ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003410 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003411 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
Jeff Garzikbf794512005-07-31 13:07:26 -04003412
James Ketrenos43f66a62005-03-25 12:31:53 -06003413 /* kick start the device */
3414 ipw_start_nic(priv);
3415
James Ketrenosb095c382005-08-24 22:04:42 -05003416 if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003417 if (retries > 0) {
3418 IPW_WARNING("Parity error. Retrying init.\n");
3419 retries--;
3420 goto retry;
3421 }
3422
3423 IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
3424 rc = -EIO;
3425 goto error;
3426 }
3427
3428 /* wait for the device */
James Ketrenosb095c382005-08-24 22:04:42 -05003429 rc = ipw_poll_bit(priv, IPW_INTA_RW,
3430 IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
James Ketrenos43f66a62005-03-25 12:31:53 -06003431 if (rc < 0) {
Zhu Yic7b6a672006-01-24 16:37:05 +08003432 IPW_ERROR("device failed to start within 500ms\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06003433 goto error;
3434 }
3435 IPW_DEBUG_INFO("device response after %dms\n", rc);
3436
3437 /* ack fw init done interrupt */
James Ketrenosb095c382005-08-24 22:04:42 -05003438 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003439
3440 /* read eeprom data and initialize the eeprom region of sram */
3441 priv->eeprom_delay = 1;
Jeff Garzikbf794512005-07-31 13:07:26 -04003442 ipw_eeprom_init_sram(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06003443
3444 /* enable interrupts */
3445 ipw_enable_interrupts(priv);
3446
3447 /* Ensure our queue has valid packets */
3448 ipw_rx_queue_replenish(priv);
3449
James Ketrenosb095c382005-08-24 22:04:42 -05003450 ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read);
James Ketrenos43f66a62005-03-25 12:31:53 -06003451
3452 /* ack pending interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -05003453 ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
James Ketrenos43f66a62005-03-25 12:31:53 -06003454
3455#ifndef CONFIG_PM
3456 release_firmware(bootfw);
3457 release_firmware(ucode);
3458 release_firmware(firmware);
3459#endif
3460 return 0;
3461
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003462 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06003463 if (priv->rxq) {
3464 ipw_rx_queue_free(priv, priv->rxq);
3465 priv->rxq = NULL;
3466 }
3467 ipw_tx_queue_free(priv);
3468 if (bootfw)
3469 release_firmware(bootfw);
3470 if (ucode)
3471 release_firmware(ucode);
3472 if (firmware)
3473 release_firmware(firmware);
3474#ifdef CONFIG_PM
3475 fw_loaded = 0;
3476 bootfw = ucode = firmware = NULL;
3477#endif
3478
3479 return rc;
3480}
3481
Jeff Garzikbf794512005-07-31 13:07:26 -04003482/**
James Ketrenos43f66a62005-03-25 12:31:53 -06003483 * DMA services
3484 *
3485 * Theory of operation
3486 *
3487 * A queue is a circular buffers with 'Read' and 'Write' pointers.
3488 * 2 empty entries always kept in the buffer to protect from overflow.
3489 *
3490 * For Tx queue, there are low mark and high mark limits. If, after queuing
Jeff Garzikbf794512005-07-31 13:07:26 -04003491 * the packet for Tx, free space become < low mark, Tx queue stopped. When
3492 * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
James Ketrenos43f66a62005-03-25 12:31:53 -06003493 * Tx queue resumed.
3494 *
3495 * The IPW operates with six queues, one receive queue in the device's
3496 * sram, one transmit queue for sending commands to the device firmware,
Jeff Garzikbf794512005-07-31 13:07:26 -04003497 * and four transmit queues for data.
James Ketrenos43f66a62005-03-25 12:31:53 -06003498 *
Jeff Garzikbf794512005-07-31 13:07:26 -04003499 * The four transmit queues allow for performing quality of service (qos)
James Ketrenos43f66a62005-03-25 12:31:53 -06003500 * transmissions as per the 802.11 protocol. Currently Linux does not
Jeff Garzikbf794512005-07-31 13:07:26 -04003501 * provide a mechanism to the user for utilizing prioritized queues, so
James Ketrenos43f66a62005-03-25 12:31:53 -06003502 * we only utilize the first data transmit queue (queue1).
3503 */
3504
3505/**
3506 * Driver allocates buffers of this size for Rx
3507 */
3508
3509static inline int ipw_queue_space(const struct clx2_queue *q)
3510{
3511 int s = q->last_used - q->first_empty;
3512 if (s <= 0)
3513 s += q->n_bd;
3514 s -= 2; /* keep some reserve to not confuse empty and full situations */
3515 if (s < 0)
3516 s = 0;
3517 return s;
3518}
3519
3520static inline int ipw_queue_inc_wrap(int index, int n_bd)
3521{
3522 return (++index == n_bd) ? 0 : index;
3523}
3524
3525/**
3526 * Initialize common DMA queue structure
Jeff Garzikbf794512005-07-31 13:07:26 -04003527 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003528 * @param q queue to init
3529 * @param count Number of BD's to allocate. Should be power of 2
3530 * @param read_register Address for 'read' register
3531 * (not offset within BAR, full address)
3532 * @param write_register Address for 'write' register
3533 * (not offset within BAR, full address)
3534 * @param base_register Address for 'base' register
3535 * (not offset within BAR, full address)
3536 * @param size Address for 'size' register
3537 * (not offset within BAR, full address)
3538 */
Jeff Garzikbf794512005-07-31 13:07:26 -04003539static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003540 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003541{
3542 q->n_bd = count;
3543
3544 q->low_mark = q->n_bd / 4;
3545 if (q->low_mark < 4)
3546 q->low_mark = 4;
3547
3548 q->high_mark = q->n_bd / 8;
3549 if (q->high_mark < 2)
3550 q->high_mark = 2;
3551
3552 q->first_empty = q->last_used = 0;
3553 q->reg_r = read;
3554 q->reg_w = write;
3555
3556 ipw_write32(priv, base, q->dma_addr);
3557 ipw_write32(priv, size, count);
3558 ipw_write32(priv, read, 0);
3559 ipw_write32(priv, write, 0);
3560
3561 _ipw_read32(priv, 0x90);
3562}
3563
Jeff Garzikbf794512005-07-31 13:07:26 -04003564static int ipw_queue_tx_init(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06003565 struct clx2_tx_queue *q,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003566 int count, u32 read, u32 write, u32 base, u32 size)
James Ketrenos43f66a62005-03-25 12:31:53 -06003567{
3568 struct pci_dev *dev = priv->pci_dev;
3569
3570 q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
3571 if (!q->txb) {
3572 IPW_ERROR("vmalloc for auxilary BD structures failed\n");
3573 return -ENOMEM;
3574 }
3575
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003576 q->bd =
3577 pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
James Ketrenos43f66a62005-03-25 12:31:53 -06003578 if (!q->bd) {
Jiri Bencaaa4d302005-06-07 14:58:41 +02003579 IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003580 sizeof(q->bd[0]) * count);
James Ketrenos43f66a62005-03-25 12:31:53 -06003581 kfree(q->txb);
3582 q->txb = NULL;
3583 return -ENOMEM;
3584 }
3585
3586 ipw_queue_init(priv, &q->q, count, read, write, base, size);
3587 return 0;
3588}
3589
3590/**
3591 * Free one TFD, those at index [txq->q.last_used].
3592 * Do NOT advance any indexes
Jeff Garzikbf794512005-07-31 13:07:26 -04003593 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003594 * @param dev
3595 * @param txq
3596 */
3597static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
3598 struct clx2_tx_queue *txq)
3599{
3600 struct tfd_frame *bd = &txq->bd[txq->q.last_used];
3601 struct pci_dev *dev = priv->pci_dev;
3602 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003603
James Ketrenos43f66a62005-03-25 12:31:53 -06003604 /* classify bd */
3605 if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
3606 /* nothing to cleanup after for host commands */
3607 return;
3608
3609 /* sanity check */
James Ketrenosa613bff2005-08-24 21:43:11 -05003610 if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
3611 IPW_ERROR("Too many chunks: %i\n",
3612 le32_to_cpu(bd->u.data.num_chunks));
James Ketrenos43f66a62005-03-25 12:31:53 -06003613 /** @todo issue fatal error, it is quite serious situation */
3614 return;
3615 }
3616
3617 /* unmap chunks if any */
James Ketrenosa613bff2005-08-24 21:43:11 -05003618 for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
3619 pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]),
3620 le16_to_cpu(bd->u.data.chunk_len[i]),
3621 PCI_DMA_TODEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06003622 if (txq->txb[txq->q.last_used]) {
3623 ieee80211_txb_free(txq->txb[txq->q.last_used]);
3624 txq->txb[txq->q.last_used] = NULL;
3625 }
3626 }
3627}
3628
3629/**
3630 * Deallocate DMA queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04003631 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003632 * Empty queue by removing and destroying all BD's.
3633 * Free all buffers.
Jeff Garzikbf794512005-07-31 13:07:26 -04003634 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003635 * @param dev
3636 * @param q
3637 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003638static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
James Ketrenos43f66a62005-03-25 12:31:53 -06003639{
3640 struct clx2_queue *q = &txq->q;
3641 struct pci_dev *dev = priv->pci_dev;
3642
Jeff Garzikbf794512005-07-31 13:07:26 -04003643 if (q->n_bd == 0)
3644 return;
James Ketrenos43f66a62005-03-25 12:31:53 -06003645
3646 /* first, empty all BD's */
3647 for (; q->first_empty != q->last_used;
3648 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
3649 ipw_queue_tx_free_tfd(priv, txq);
3650 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003651
James Ketrenos43f66a62005-03-25 12:31:53 -06003652 /* free buffers belonging to queue itself */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003653 pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
James Ketrenos43f66a62005-03-25 12:31:53 -06003654 q->dma_addr);
3655 kfree(txq->txb);
3656
3657 /* 0 fill whole structure */
3658 memset(txq, 0, sizeof(*txq));
3659}
3660
James Ketrenos43f66a62005-03-25 12:31:53 -06003661/**
3662 * Destroy all DMA queues and structures
Jeff Garzikbf794512005-07-31 13:07:26 -04003663 *
James Ketrenos43f66a62005-03-25 12:31:53 -06003664 * @param priv
3665 */
3666static void ipw_tx_queue_free(struct ipw_priv *priv)
3667{
3668 /* Tx CMD queue */
3669 ipw_queue_tx_free(priv, &priv->txq_cmd);
3670
3671 /* Tx queues */
3672 ipw_queue_tx_free(priv, &priv->txq[0]);
3673 ipw_queue_tx_free(priv, &priv->txq[1]);
3674 ipw_queue_tx_free(priv, &priv->txq[2]);
3675 ipw_queue_tx_free(priv, &priv->txq[3]);
3676}
3677
Arjan van de Ven858119e2006-01-14 13:20:43 -08003678static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003679{
3680 /* First 3 bytes are manufacturer */
3681 bssid[0] = priv->mac_addr[0];
3682 bssid[1] = priv->mac_addr[1];
3683 bssid[2] = priv->mac_addr[2];
3684
3685 /* Last bytes are random */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003686 get_random_bytes(&bssid[3], ETH_ALEN - 3);
James Ketrenos43f66a62005-03-25 12:31:53 -06003687
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003688 bssid[0] &= 0xfe; /* clear multicast bit */
3689 bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
James Ketrenos43f66a62005-03-25 12:31:53 -06003690}
3691
Arjan van de Ven858119e2006-01-14 13:20:43 -08003692static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003693{
3694 struct ipw_station_entry entry;
3695 int i;
3696
3697 for (i = 0; i < priv->num_stations; i++) {
3698 if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
3699 /* Another node is active in network */
3700 priv->missed_adhoc_beacons = 0;
3701 if (!(priv->config & CFG_STATIC_CHANNEL))
3702 /* when other nodes drop out, we drop out */
3703 priv->config &= ~CFG_ADHOC_PERSIST;
3704
3705 return i;
3706 }
3707 }
3708
3709 if (i == MAX_STATIONS)
3710 return IPW_INVALID_STATION;
3711
3712 IPW_DEBUG_SCAN("Adding AdHoc station: " MAC_FMT "\n", MAC_ARG(bssid));
3713
3714 entry.reserved = 0;
3715 entry.support_mode = 0;
3716 memcpy(entry.mac_addr, bssid, ETH_ALEN);
3717 memcpy(priv->stations[i], bssid, ETH_ALEN);
3718 ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003719 &entry, sizeof(entry));
James Ketrenos43f66a62005-03-25 12:31:53 -06003720 priv->num_stations++;
3721
3722 return i;
3723}
3724
Arjan van de Ven858119e2006-01-14 13:20:43 -08003725static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
James Ketrenos43f66a62005-03-25 12:31:53 -06003726{
3727 int i;
3728
Jeff Garzikbf794512005-07-31 13:07:26 -04003729 for (i = 0; i < priv->num_stations; i++)
3730 if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
James Ketrenos43f66a62005-03-25 12:31:53 -06003731 return i;
3732
3733 return IPW_INVALID_STATION;
3734}
3735
3736static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
3737{
3738 int err;
3739
Hong Liu7b996592005-08-25 17:36:13 +08003740 if (priv->status & STATUS_ASSOCIATING) {
3741 IPW_DEBUG_ASSOC("Disassociating while associating.\n");
3742 queue_work(priv->workqueue, &priv->disassociate);
3743 return;
3744 }
3745
3746 if (!(priv->status & STATUS_ASSOCIATED)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06003747 IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
3748 return;
3749 }
3750
3751 IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " "
3752 "on channel %d.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04003753 MAC_ARG(priv->assoc_request.bssid),
James Ketrenos43f66a62005-03-25 12:31:53 -06003754 priv->assoc_request.channel);
3755
3756 priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
3757 priv->status |= STATUS_DISASSOCIATING;
3758
3759 if (quiet)
3760 priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
3761 else
3762 priv->assoc_request.assoc_type = HC_DISASSOCIATE;
Hong Liue6324722005-09-14 21:04:15 -05003763
James Ketrenos43f66a62005-03-25 12:31:53 -06003764 err = ipw_send_associate(priv, &priv->assoc_request);
3765 if (err) {
3766 IPW_DEBUG_HC("Attempt to send [dis]associate command "
3767 "failed.\n");
3768 return;
3769 }
3770
3771}
3772
James Ketrenosc848d0a2005-08-24 21:56:24 -05003773static int ipw_disassociate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06003774{
James Ketrenosc848d0a2005-08-24 21:56:24 -05003775 struct ipw_priv *priv = data;
3776 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
3777 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06003778 ipw_send_disassociate(data, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05003779 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003780}
3781
James Ketrenosc848d0a2005-08-24 21:56:24 -05003782static void ipw_bg_disassociate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06003783{
James Ketrenosc848d0a2005-08-24 21:56:24 -05003784 struct ipw_priv *priv = data;
3785 down(&priv->sem);
3786 ipw_disassociate(data);
3787 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06003788}
3789
Zhu Yid8bad6d2005-07-13 12:25:38 -05003790static void ipw_system_config(void *data)
3791{
3792 struct ipw_priv *priv = data;
3793 ipw_send_system_config(priv, &priv->sys_config);
James Ketrenos43f66a62005-03-25 12:31:53 -06003794}
3795
3796struct ipw_status_code {
3797 u16 status;
3798 const char *reason;
3799};
3800
3801static const struct ipw_status_code ipw_status_codes[] = {
3802 {0x00, "Successful"},
3803 {0x01, "Unspecified failure"},
3804 {0x0A, "Cannot support all requested capabilities in the "
3805 "Capability information field"},
3806 {0x0B, "Reassociation denied due to inability to confirm that "
3807 "association exists"},
3808 {0x0C, "Association denied due to reason outside the scope of this "
3809 "standard"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003810 {0x0D,
3811 "Responding station does not support the specified authentication "
James Ketrenos43f66a62005-03-25 12:31:53 -06003812 "algorithm"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003813 {0x0E,
3814 "Received an Authentication frame with authentication sequence "
James Ketrenos43f66a62005-03-25 12:31:53 -06003815 "transaction sequence number out of expected sequence"},
3816 {0x0F, "Authentication rejected because of challenge failure"},
3817 {0x10, "Authentication rejected due to timeout waiting for next "
3818 "frame in sequence"},
3819 {0x11, "Association denied because AP is unable to handle additional "
3820 "associated stations"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003821 {0x12,
3822 "Association denied due to requesting station not supporting all "
James Ketrenos43f66a62005-03-25 12:31:53 -06003823 "of the datarates in the BSSBasicServiceSet Parameter"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003824 {0x13,
3825 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003826 "short preamble operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003827 {0x14,
3828 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003829 "PBCC encoding"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003830 {0x15,
3831 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003832 "channel agility"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003833 {0x19,
3834 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003835 "short slot operation"},
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003836 {0x1A,
3837 "Association denied due to requesting station not supporting "
James Ketrenos43f66a62005-03-25 12:31:53 -06003838 "DSSS-OFDM operation"},
3839 {0x28, "Invalid Information Element"},
3840 {0x29, "Group Cipher is not valid"},
3841 {0x2A, "Pairwise Cipher is not valid"},
3842 {0x2B, "AKMP is not valid"},
3843 {0x2C, "Unsupported RSN IE version"},
3844 {0x2D, "Invalid RSN IE Capabilities"},
3845 {0x2E, "Cipher suite is rejected per security policy"},
3846};
3847
Brice Goglin0f52bf92005-12-01 01:41:46 -08003848#ifdef CONFIG_IPW2200_DEBUG
Jeff Garzikbf794512005-07-31 13:07:26 -04003849static const char *ipw_get_status_code(u16 status)
James Ketrenos43f66a62005-03-25 12:31:53 -06003850{
3851 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -04003852 for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
James Ketrenosea2b26e2005-08-24 21:25:16 -05003853 if (ipw_status_codes[i].status == (status & 0xff))
James Ketrenos43f66a62005-03-25 12:31:53 -06003854 return ipw_status_codes[i].reason;
3855 return "Unknown status value.";
3856}
3857#endif
3858
3859static void inline average_init(struct average *avg)
3860{
3861 memset(avg, 0, sizeof(*avg));
3862}
3863
Arjan van de Ven858119e2006-01-14 13:20:43 -08003864static void average_add(struct average *avg, s16 val)
James Ketrenos43f66a62005-03-25 12:31:53 -06003865{
3866 avg->sum -= avg->entries[avg->pos];
3867 avg->sum += val;
3868 avg->entries[avg->pos++] = val;
3869 if (unlikely(avg->pos == AVG_ENTRIES)) {
3870 avg->init = 1;
3871 avg->pos = 0;
3872 }
3873}
3874
Arjan van de Ven858119e2006-01-14 13:20:43 -08003875static s16 average_value(struct average *avg)
James Ketrenos43f66a62005-03-25 12:31:53 -06003876{
3877 if (!unlikely(avg->init)) {
3878 if (avg->pos)
3879 return avg->sum / avg->pos;
3880 return 0;
3881 }
3882
3883 return avg->sum / AVG_ENTRIES;
3884}
3885
3886static void ipw_reset_stats(struct ipw_priv *priv)
3887{
3888 u32 len = sizeof(u32);
3889
3890 priv->quality = 0;
3891
3892 average_init(&priv->average_missed_beacons);
3893 average_init(&priv->average_rssi);
3894 average_init(&priv->average_noise);
3895
3896 priv->last_rate = 0;
3897 priv->last_missed_beacons = 0;
3898 priv->last_rx_packets = 0;
3899 priv->last_tx_packets = 0;
3900 priv->last_tx_failures = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04003901
James Ketrenos43f66a62005-03-25 12:31:53 -06003902 /* Firmware managed, reset only when NIC is restarted, so we have to
3903 * normalize on the current value */
Jeff Garzikbf794512005-07-31 13:07:26 -04003904 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
James Ketrenos43f66a62005-03-25 12:31:53 -06003905 &priv->last_rx_err, &len);
Jeff Garzikbf794512005-07-31 13:07:26 -04003906 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
James Ketrenos43f66a62005-03-25 12:31:53 -06003907 &priv->last_tx_failures, &len);
3908
3909 /* Driver managed, reset with each association */
3910 priv->missed_adhoc_beacons = 0;
3911 priv->missed_beacons = 0;
3912 priv->tx_packets = 0;
3913 priv->rx_packets = 0;
3914
3915}
3916
Arjan van de Ven858119e2006-01-14 13:20:43 -08003917static u32 ipw_get_max_rate(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -06003918{
3919 u32 i = 0x80000000;
3920 u32 mask = priv->rates_mask;
3921 /* If currently associated in B mode, restrict the maximum
3922 * rate match to B rates */
3923 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
3924 mask &= IEEE80211_CCK_RATES_MASK;
3925
3926 /* TODO: Verify that the rate is supported by the current rates
3927 * list. */
3928
Jeff Garzik0edd5b42005-09-07 00:48:31 -04003929 while (i && !(mask & i))
3930 i >>= 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06003931 switch (i) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003932 case IEEE80211_CCK_RATE_1MB_MASK:
3933 return 1000000;
3934 case IEEE80211_CCK_RATE_2MB_MASK:
3935 return 2000000;
3936 case IEEE80211_CCK_RATE_5MB_MASK:
3937 return 5500000;
3938 case IEEE80211_OFDM_RATE_6MB_MASK:
3939 return 6000000;
3940 case IEEE80211_OFDM_RATE_9MB_MASK:
3941 return 9000000;
3942 case IEEE80211_CCK_RATE_11MB_MASK:
3943 return 11000000;
3944 case IEEE80211_OFDM_RATE_12MB_MASK:
3945 return 12000000;
3946 case IEEE80211_OFDM_RATE_18MB_MASK:
3947 return 18000000;
3948 case IEEE80211_OFDM_RATE_24MB_MASK:
3949 return 24000000;
3950 case IEEE80211_OFDM_RATE_36MB_MASK:
3951 return 36000000;
3952 case IEEE80211_OFDM_RATE_48MB_MASK:
3953 return 48000000;
3954 case IEEE80211_OFDM_RATE_54MB_MASK:
3955 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06003956 }
3957
Jeff Garzikbf794512005-07-31 13:07:26 -04003958 if (priv->ieee->mode == IEEE_B)
James Ketrenos43f66a62005-03-25 12:31:53 -06003959 return 11000000;
3960 else
3961 return 54000000;
3962}
3963
3964static u32 ipw_get_current_rate(struct ipw_priv *priv)
3965{
3966 u32 rate, len = sizeof(rate);
3967 int err;
3968
Jeff Garzikbf794512005-07-31 13:07:26 -04003969 if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06003970 return 0;
3971
3972 if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
Jeff Garzikbf794512005-07-31 13:07:26 -04003973 err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
James Ketrenos43f66a62005-03-25 12:31:53 -06003974 &len);
3975 if (err) {
3976 IPW_DEBUG_INFO("failed querying ordinals.\n");
3977 return 0;
3978 }
Jeff Garzikbf794512005-07-31 13:07:26 -04003979 } else
James Ketrenos43f66a62005-03-25 12:31:53 -06003980 return ipw_get_max_rate(priv);
3981
3982 switch (rate) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05003983 case IPW_TX_RATE_1MB:
3984 return 1000000;
3985 case IPW_TX_RATE_2MB:
3986 return 2000000;
3987 case IPW_TX_RATE_5MB:
3988 return 5500000;
3989 case IPW_TX_RATE_6MB:
3990 return 6000000;
3991 case IPW_TX_RATE_9MB:
3992 return 9000000;
3993 case IPW_TX_RATE_11MB:
3994 return 11000000;
3995 case IPW_TX_RATE_12MB:
3996 return 12000000;
3997 case IPW_TX_RATE_18MB:
3998 return 18000000;
3999 case IPW_TX_RATE_24MB:
4000 return 24000000;
4001 case IPW_TX_RATE_36MB:
4002 return 36000000;
4003 case IPW_TX_RATE_48MB:
4004 return 48000000;
4005 case IPW_TX_RATE_54MB:
4006 return 54000000;
James Ketrenos43f66a62005-03-25 12:31:53 -06004007 }
4008
4009 return 0;
4010}
4011
James Ketrenos43f66a62005-03-25 12:31:53 -06004012#define IPW_STATS_INTERVAL (2 * HZ)
4013static void ipw_gather_stats(struct ipw_priv *priv)
4014{
4015 u32 rx_err, rx_err_delta, rx_packets_delta;
4016 u32 tx_failures, tx_failures_delta, tx_packets_delta;
4017 u32 missed_beacons_percent, missed_beacons_delta;
4018 u32 quality = 0;
4019 u32 len = sizeof(u32);
4020 s16 rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04004021 u32 beacon_quality, signal_quality, tx_quality, rx_quality,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004022 rate_quality;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004023 u32 max_rate;
James Ketrenos43f66a62005-03-25 12:31:53 -06004024
4025 if (!(priv->status & STATUS_ASSOCIATED)) {
4026 priv->quality = 0;
4027 return;
4028 }
4029
4030 /* Update the statistics */
Jeff Garzikbf794512005-07-31 13:07:26 -04004031 ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
James Ketrenos43f66a62005-03-25 12:31:53 -06004032 &priv->missed_beacons, &len);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004033 missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
James Ketrenos43f66a62005-03-25 12:31:53 -06004034 priv->last_missed_beacons = priv->missed_beacons;
4035 if (priv->assoc_request.beacon_interval) {
4036 missed_beacons_percent = missed_beacons_delta *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004037 (HZ * priv->assoc_request.beacon_interval) /
4038 (IPW_STATS_INTERVAL * 10);
James Ketrenos43f66a62005-03-25 12:31:53 -06004039 } else {
4040 missed_beacons_percent = 0;
4041 }
4042 average_add(&priv->average_missed_beacons, missed_beacons_percent);
4043
4044 ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
4045 rx_err_delta = rx_err - priv->last_rx_err;
4046 priv->last_rx_err = rx_err;
4047
4048 ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
4049 tx_failures_delta = tx_failures - priv->last_tx_failures;
4050 priv->last_tx_failures = tx_failures;
4051
4052 rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
4053 priv->last_rx_packets = priv->rx_packets;
4054
4055 tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
4056 priv->last_tx_packets = priv->tx_packets;
4057
4058 /* Calculate quality based on the following:
Jeff Garzikbf794512005-07-31 13:07:26 -04004059 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004060 * Missed beacon: 100% = 0, 0% = 70% missed
4061 * Rate: 60% = 1Mbs, 100% = Max
4062 * Rx and Tx errors represent a straight % of total Rx/Tx
4063 * RSSI: 100% = > -50, 0% = < -80
4064 * Rx errors: 100% = 0, 0% = 50% missed
Jeff Garzikbf794512005-07-31 13:07:26 -04004065 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004066 * The lowest computed quality is used.
4067 *
4068 */
4069#define BEACON_THRESHOLD 5
4070 beacon_quality = 100 - missed_beacons_percent;
4071 if (beacon_quality < BEACON_THRESHOLD)
4072 beacon_quality = 0;
4073 else
Jeff Garzikbf794512005-07-31 13:07:26 -04004074 beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004075 (100 - BEACON_THRESHOLD);
Jeff Garzikbf794512005-07-31 13:07:26 -04004076 IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06004077 beacon_quality, missed_beacons_percent);
Jeff Garzikbf794512005-07-31 13:07:26 -04004078
James Ketrenos43f66a62005-03-25 12:31:53 -06004079 priv->last_rate = ipw_get_current_rate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004080 max_rate = ipw_get_max_rate(priv);
4081 rate_quality = priv->last_rate * 40 / max_rate + 60;
James Ketrenos43f66a62005-03-25 12:31:53 -06004082 IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
4083 rate_quality, priv->last_rate / 1000000);
Jeff Garzikbf794512005-07-31 13:07:26 -04004084
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004085 if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04004086 rx_quality = 100 - (rx_err_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004087 (rx_packets_delta + rx_err_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06004088 else
4089 rx_quality = 100;
4090 IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
4091 rx_quality, rx_err_delta, rx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04004092
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004093 if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
Jeff Garzikbf794512005-07-31 13:07:26 -04004094 tx_quality = 100 - (tx_failures_delta * 100) /
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004095 (tx_packets_delta + tx_failures_delta);
James Ketrenos43f66a62005-03-25 12:31:53 -06004096 else
4097 tx_quality = 100;
4098 IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
4099 tx_quality, tx_failures_delta, tx_packets_delta);
Jeff Garzikbf794512005-07-31 13:07:26 -04004100
James Ketrenos43f66a62005-03-25 12:31:53 -06004101 rssi = average_value(&priv->average_rssi);
James Ketrenosc848d0a2005-08-24 21:56:24 -05004102 signal_quality =
4103 (100 *
4104 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
4105 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
4106 (priv->ieee->perfect_rssi - rssi) *
4107 (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
4108 62 * (priv->ieee->perfect_rssi - rssi))) /
4109 ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
4110 (priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
4111 if (signal_quality > 100)
James Ketrenos43f66a62005-03-25 12:31:53 -06004112 signal_quality = 100;
James Ketrenosc848d0a2005-08-24 21:56:24 -05004113 else if (signal_quality < 1)
James Ketrenos43f66a62005-03-25 12:31:53 -06004114 signal_quality = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05004115
James Ketrenos43f66a62005-03-25 12:31:53 -06004116 IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
4117 signal_quality, rssi);
Jeff Garzikbf794512005-07-31 13:07:26 -04004118
4119 quality = min(beacon_quality,
James Ketrenos43f66a62005-03-25 12:31:53 -06004120 min(rate_quality,
4121 min(tx_quality, min(rx_quality, signal_quality))));
4122 if (quality == beacon_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004123 IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
4124 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004125 if (quality == rate_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004126 IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
4127 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004128 if (quality == tx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004129 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
4130 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004131 if (quality == rx_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004132 IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
4133 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004134 if (quality == signal_quality)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004135 IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
4136 quality);
James Ketrenos43f66a62005-03-25 12:31:53 -06004137
4138 priv->quality = quality;
Jeff Garzikbf794512005-07-31 13:07:26 -04004139
4140 queue_delayed_work(priv->workqueue, &priv->gather_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06004141 IPW_STATS_INTERVAL);
4142}
4143
James Ketrenosc848d0a2005-08-24 21:56:24 -05004144static void ipw_bg_gather_stats(void *data)
4145{
4146 struct ipw_priv *priv = data;
4147 down(&priv->sem);
4148 ipw_gather_stats(data);
4149 up(&priv->sem);
4150}
4151
Ben Cahille7582562005-10-06 15:34:41 -05004152/* Missed beacon behavior:
4153 * 1st missed -> roaming_threshold, just wait, don't do any scan/roam.
4154 * roaming_threshold -> disassociate_threshold, scan and roam for better signal.
4155 * Above disassociate threshold, give up and stop scanning.
4156 * Roaming is disabled if disassociate_threshold <= roaming_threshold */
Arjan van de Ven858119e2006-01-14 13:20:43 -08004157static void ipw_handle_missed_beacon(struct ipw_priv *priv,
James Ketrenosea2b26e2005-08-24 21:25:16 -05004158 int missed_count)
4159{
4160 priv->notif_missed_beacons = missed_count;
4161
James Ketrenosafbf30a2005-08-25 00:05:33 -05004162 if (missed_count > priv->disassociate_threshold &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05004163 priv->status & STATUS_ASSOCIATED) {
4164 /* If associated and we've hit the missed
4165 * beacon threshold, disassociate, turn
4166 * off roaming, and abort any active scans */
4167 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
James Ketrenosafbf30a2005-08-25 00:05:33 -05004168 IPW_DL_STATE | IPW_DL_ASSOC,
James Ketrenosea2b26e2005-08-24 21:25:16 -05004169 "Missed beacon: %d - disassociate\n", missed_count);
4170 priv->status &= ~STATUS_ROAMING;
James Ketrenosa613bff2005-08-24 21:43:11 -05004171 if (priv->status & STATUS_SCANNING) {
4172 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
4173 IPW_DL_STATE,
4174 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05004175 queue_work(priv->workqueue, &priv->abort_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004176 }
4177
James Ketrenosea2b26e2005-08-24 21:25:16 -05004178 queue_work(priv->workqueue, &priv->disassociate);
4179 return;
4180 }
4181
4182 if (priv->status & STATUS_ROAMING) {
4183 /* If we are currently roaming, then just
4184 * print a debug statement... */
4185 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4186 "Missed beacon: %d - roam in progress\n",
4187 missed_count);
4188 return;
4189 }
4190
Zhu Yi4bfdb912006-01-24 16:37:16 +08004191 if (roaming &&
4192 (missed_count > priv->roaming_threshold &&
4193 missed_count <= priv->disassociate_threshold)) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05004194 /* If we are not already roaming, set the ROAM
Ben Cahille7582562005-10-06 15:34:41 -05004195 * bit in the status and kick off a scan.
4196 * This can happen several times before we reach
4197 * disassociate_threshold. */
James Ketrenosea2b26e2005-08-24 21:25:16 -05004198 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4199 "Missed beacon: %d - initiate "
4200 "roaming\n", missed_count);
4201 if (!(priv->status & STATUS_ROAMING)) {
4202 priv->status |= STATUS_ROAMING;
4203 if (!(priv->status & STATUS_SCANNING))
4204 queue_work(priv->workqueue,
4205 &priv->request_scan);
4206 }
4207 return;
4208 }
4209
4210 if (priv->status & STATUS_SCANNING) {
4211 /* Stop scan to keep fw from getting
4212 * stuck (only if we aren't roaming --
4213 * otherwise we'll never scan more than 2 or 3
4214 * channels..) */
James Ketrenosb095c382005-08-24 22:04:42 -05004215 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE,
4216 "Aborting scan with missed beacon.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05004217 queue_work(priv->workqueue, &priv->abort_scan);
4218 }
4219
4220 IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
James Ketrenosea2b26e2005-08-24 21:25:16 -05004221}
4222
James Ketrenos43f66a62005-03-25 12:31:53 -06004223/**
4224 * Handle host notification packet.
4225 * Called from interrupt routine
4226 */
Arjan van de Ven858119e2006-01-14 13:20:43 -08004227static void ipw_rx_notification(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004228 struct ipw_rx_notification *notif)
4229{
James Ketrenosa613bff2005-08-24 21:43:11 -05004230 notif->size = le16_to_cpu(notif->size);
4231
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004232 IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, notif->size);
Jeff Garzikbf794512005-07-31 13:07:26 -04004233
James Ketrenos43f66a62005-03-25 12:31:53 -06004234 switch (notif->subtype) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004235 case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
4236 struct notif_association *assoc = &notif->u.assoc;
Jeff Garzikbf794512005-07-31 13:07:26 -04004237
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004238 switch (assoc->state) {
4239 case CMAS_ASSOCIATED:{
4240 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4241 IPW_DL_ASSOC,
4242 "associated: '%s' " MAC_FMT
4243 " \n",
4244 escape_essid(priv->essid,
4245 priv->essid_len),
4246 MAC_ARG(priv->bssid));
Jeff Garzikbf794512005-07-31 13:07:26 -04004247
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004248 switch (priv->ieee->iw_mode) {
4249 case IW_MODE_INFRA:
4250 memcpy(priv->ieee->bssid,
4251 priv->bssid, ETH_ALEN);
4252 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004253
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004254 case IW_MODE_ADHOC:
4255 memcpy(priv->ieee->bssid,
4256 priv->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04004257
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004258 /* clear out the station table */
4259 priv->num_stations = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06004260
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004261 IPW_DEBUG_ASSOC
4262 ("queueing adhoc check\n");
4263 queue_delayed_work(priv->
4264 workqueue,
4265 &priv->
4266 adhoc_check,
4267 priv->
4268 assoc_request.
4269 beacon_interval);
4270 break;
4271 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004272
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004273 priv->status &= ~STATUS_ASSOCIATING;
4274 priv->status |= STATUS_ASSOCIATED;
Zhu Yid8bad6d2005-07-13 12:25:38 -05004275 queue_work(priv->workqueue,
4276 &priv->system_config);
James Ketrenos43f66a62005-03-25 12:31:53 -06004277
James Ketrenosb095c382005-08-24 22:04:42 -05004278#ifdef CONFIG_IPW_QOS
James Ketrenosafbf30a2005-08-25 00:05:33 -05004279#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
4280 le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_ctl))
4281 if ((priv->status & STATUS_AUTH) &&
4282 (IPW_GET_PACKET_STYPE(&notif->u.raw)
4283 == IEEE80211_STYPE_ASSOC_RESP)) {
James Ketrenosb095c382005-08-24 22:04:42 -05004284 if ((sizeof
4285 (struct
James Ketrenos2b184d52005-08-03 20:33:14 -05004286 ieee80211_assoc_response)
James Ketrenosb095c382005-08-24 22:04:42 -05004287 <= notif->size)
4288 && (notif->size <= 2314)) {
4289 struct
4290 ieee80211_rx_stats
4291 stats = {
4292 .len =
4293 notif->
4294 size - 1,
4295 };
4296
4297 IPW_DEBUG_QOS
4298 ("QoS Associate "
4299 "size %d\n",
4300 notif->size);
4301 ieee80211_rx_mgt(priv->
4302 ieee,
4303 (struct
James Ketrenos2b184d52005-08-03 20:33:14 -05004304 ieee80211_hdr_4addr
James Ketrenosb095c382005-08-24 22:04:42 -05004305 *)
4306 &notif->u.raw, &stats);
4307 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004308 }
James Ketrenosb095c382005-08-24 22:04:42 -05004309#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06004310
James Ketrenosa613bff2005-08-24 21:43:11 -05004311 schedule_work(&priv->link_up);
James Ketrenos43f66a62005-03-25 12:31:53 -06004312
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004313 break;
4314 }
4315
4316 case CMAS_AUTHENTICATED:{
4317 if (priv->
4318 status & (STATUS_ASSOCIATED |
4319 STATUS_AUTH)) {
Brice Goglin0f52bf92005-12-01 01:41:46 -08004320#ifdef CONFIG_IPW2200_DEBUG
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004321 struct notif_authenticate *auth
4322 = &notif->u.auth;
4323 IPW_DEBUG(IPW_DL_NOTIF |
4324 IPW_DL_STATE |
4325 IPW_DL_ASSOC,
4326 "deauthenticated: '%s' "
4327 MAC_FMT
4328 ": (0x%04X) - %s \n",
4329 escape_essid(priv->
4330 essid,
4331 priv->
4332 essid_len),
4333 MAC_ARG(priv->bssid),
4334 ntohs(auth->status),
4335 ipw_get_status_code
4336 (ntohs
4337 (auth->status)));
4338#endif
4339
4340 priv->status &=
4341 ~(STATUS_ASSOCIATING |
4342 STATUS_AUTH |
4343 STATUS_ASSOCIATED);
4344
James Ketrenosa613bff2005-08-24 21:43:11 -05004345 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004346 break;
4347 }
4348
4349 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4350 IPW_DL_ASSOC,
4351 "authenticated: '%s' " MAC_FMT
4352 "\n",
4353 escape_essid(priv->essid,
4354 priv->essid_len),
4355 MAC_ARG(priv->bssid));
4356 break;
4357 }
4358
4359 case CMAS_INIT:{
James Ketrenosea2b26e2005-08-24 21:25:16 -05004360 if (priv->status & STATUS_AUTH) {
4361 struct
4362 ieee80211_assoc_response
4363 *resp;
4364 resp =
4365 (struct
4366 ieee80211_assoc_response
4367 *)&notif->u.raw;
4368 IPW_DEBUG(IPW_DL_NOTIF |
4369 IPW_DL_STATE |
4370 IPW_DL_ASSOC,
4371 "association failed (0x%04X): %s\n",
4372 ntohs(resp->status),
4373 ipw_get_status_code
4374 (ntohs
4375 (resp->status)));
4376 }
4377
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004378 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4379 IPW_DL_ASSOC,
4380 "disassociated: '%s' " MAC_FMT
4381 " \n",
4382 escape_essid(priv->essid,
4383 priv->essid_len),
4384 MAC_ARG(priv->bssid));
4385
4386 priv->status &=
4387 ~(STATUS_DISASSOCIATING |
4388 STATUS_ASSOCIATING |
4389 STATUS_ASSOCIATED | STATUS_AUTH);
James Ketrenosb095c382005-08-24 22:04:42 -05004390 if (priv->assoc_network
4391 && (priv->assoc_network->
4392 capability &
4393 WLAN_CAPABILITY_IBSS))
4394 ipw_remove_current_network
4395 (priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004396
James Ketrenosa613bff2005-08-24 21:43:11 -05004397 schedule_work(&priv->link_down);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004398
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004399 break;
4400 }
4401
James Ketrenosb095c382005-08-24 22:04:42 -05004402 case CMAS_RX_ASSOC_RESP:
4403 break;
4404
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004405 default:
4406 IPW_ERROR("assoc: unknown (%d)\n",
4407 assoc->state);
4408 break;
4409 }
4410
James Ketrenos43f66a62005-03-25 12:31:53 -06004411 break;
4412 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004413
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004414 case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
4415 struct notif_authenticate *auth = &notif->u.auth;
4416 switch (auth->state) {
4417 case CMAS_AUTHENTICATED:
4418 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4419 "authenticated: '%s' " MAC_FMT " \n",
4420 escape_essid(priv->essid,
4421 priv->essid_len),
4422 MAC_ARG(priv->bssid));
4423 priv->status |= STATUS_AUTH;
4424 break;
4425
4426 case CMAS_INIT:
4427 if (priv->status & STATUS_AUTH) {
4428 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4429 IPW_DL_ASSOC,
4430 "authentication failed (0x%04X): %s\n",
4431 ntohs(auth->status),
4432 ipw_get_status_code(ntohs
4433 (auth->
4434 status)));
4435 }
4436 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4437 IPW_DL_ASSOC,
4438 "deauthenticated: '%s' " MAC_FMT "\n",
4439 escape_essid(priv->essid,
4440 priv->essid_len),
4441 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06004442
4443 priv->status &= ~(STATUS_ASSOCIATING |
4444 STATUS_AUTH |
4445 STATUS_ASSOCIATED);
4446
James Ketrenosa613bff2005-08-24 21:43:11 -05004447 schedule_work(&priv->link_down);
James Ketrenos43f66a62005-03-25 12:31:53 -06004448 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004449
4450 case CMAS_TX_AUTH_SEQ_1:
4451 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4452 IPW_DL_ASSOC, "AUTH_SEQ_1\n");
4453 break;
4454 case CMAS_RX_AUTH_SEQ_2:
4455 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4456 IPW_DL_ASSOC, "AUTH_SEQ_2\n");
4457 break;
4458 case CMAS_AUTH_SEQ_1_PASS:
4459 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4460 IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
4461 break;
4462 case CMAS_AUTH_SEQ_1_FAIL:
4463 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4464 IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
4465 break;
4466 case CMAS_TX_AUTH_SEQ_3:
4467 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4468 IPW_DL_ASSOC, "AUTH_SEQ_3\n");
4469 break;
4470 case CMAS_RX_AUTH_SEQ_4:
4471 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4472 IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
4473 break;
4474 case CMAS_AUTH_SEQ_2_PASS:
4475 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4476 IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
4477 break;
4478 case CMAS_AUTH_SEQ_2_FAIL:
4479 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4480 IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
4481 break;
4482 case CMAS_TX_ASSOC:
4483 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4484 IPW_DL_ASSOC, "TX_ASSOC\n");
4485 break;
4486 case CMAS_RX_ASSOC_RESP:
4487 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4488 IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
James Ketrenosb095c382005-08-24 22:04:42 -05004489
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004490 break;
4491 case CMAS_ASSOCIATED:
4492 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
4493 IPW_DL_ASSOC, "ASSOCIATED\n");
4494 break;
4495 default:
4496 IPW_DEBUG_NOTIF("auth: failure - %d\n",
4497 auth->state);
4498 break;
4499 }
4500 break;
4501 }
4502
4503 case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
4504 struct notif_channel_result *x =
4505 &notif->u.channel_result;
4506
4507 if (notif->size == sizeof(*x)) {
4508 IPW_DEBUG_SCAN("Scan result for channel %d\n",
4509 x->channel_num);
4510 } else {
4511 IPW_DEBUG_SCAN("Scan result of wrong size %d "
4512 "(should be %zd)\n",
4513 notif->size, sizeof(*x));
4514 }
4515 break;
4516 }
4517
4518 case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
4519 struct notif_scan_complete *x = &notif->u.scan_complete;
4520 if (notif->size == sizeof(*x)) {
4521 IPW_DEBUG_SCAN
4522 ("Scan completed: type %d, %d channels, "
4523 "%d status\n", x->scan_type,
4524 x->num_channels, x->status);
4525 } else {
4526 IPW_ERROR("Scan completed of wrong size %d "
4527 "(should be %zd)\n",
4528 notif->size, sizeof(*x));
4529 }
4530
4531 priv->status &=
4532 ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
4533
James Ketrenosa0e04ab2005-08-25 00:49:43 -05004534 wake_up_interruptible(&priv->wait_state);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004535 cancel_delayed_work(&priv->scan_check);
4536
James Ketrenosb095c382005-08-24 22:04:42 -05004537 if (priv->status & STATUS_EXIT_PENDING)
4538 break;
4539
4540 priv->ieee->scans++;
4541
4542#ifdef CONFIG_IPW2200_MONITOR
4543 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05004544 priv->status |= STATUS_SCAN_FORCED;
James Ketrenosb095c382005-08-24 22:04:42 -05004545 queue_work(priv->workqueue,
4546 &priv->request_scan);
4547 break;
4548 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05004549 priv->status &= ~STATUS_SCAN_FORCED;
James Ketrenosb095c382005-08-24 22:04:42 -05004550#endif /* CONFIG_IPW2200_MONITOR */
4551
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004552 if (!(priv->status & (STATUS_ASSOCIATED |
4553 STATUS_ASSOCIATING |
4554 STATUS_ROAMING |
4555 STATUS_DISASSOCIATING)))
4556 queue_work(priv->workqueue, &priv->associate);
4557 else if (priv->status & STATUS_ROAMING) {
Ben Cahille7582562005-10-06 15:34:41 -05004558 if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
4559 /* If a scan completed and we are in roam mode, then
4560 * the scan that completed was the one requested as a
4561 * result of entering roam... so, schedule the
4562 * roam work */
4563 queue_work(priv->workqueue,
4564 &priv->roam);
4565 else
4566 /* Don't schedule if we aborted the scan */
4567 priv->status &= ~STATUS_ROAMING;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004568 } else if (priv->status & STATUS_SCAN_PENDING)
4569 queue_work(priv->workqueue,
4570 &priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -05004571 else if (priv->config & CFG_BACKGROUND_SCAN
4572 && priv->status & STATUS_ASSOCIATED)
4573 queue_delayed_work(priv->workqueue,
4574 &priv->request_scan, HZ);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004575 break;
4576 }
4577
4578 case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
4579 struct notif_frag_length *x = &notif->u.frag_len;
4580
James Ketrenosa613bff2005-08-24 21:43:11 -05004581 if (notif->size == sizeof(*x))
4582 IPW_ERROR("Frag length: %d\n",
4583 le16_to_cpu(x->frag_length));
4584 else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004585 IPW_ERROR("Frag length of wrong size %d "
4586 "(should be %zd)\n",
4587 notif->size, sizeof(*x));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004588 break;
4589 }
4590
4591 case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
4592 struct notif_link_deterioration *x =
4593 &notif->u.link_deterioration;
James Ketrenosafbf30a2005-08-25 00:05:33 -05004594
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004595 if (notif->size == sizeof(*x)) {
4596 IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
4597 "link deterioration: '%s' " MAC_FMT
4598 " \n", escape_essid(priv->essid,
4599 priv->essid_len),
4600 MAC_ARG(priv->bssid));
4601 memcpy(&priv->last_link_deterioration, x,
4602 sizeof(*x));
4603 } else {
4604 IPW_ERROR("Link Deterioration of wrong size %d "
4605 "(should be %zd)\n",
4606 notif->size, sizeof(*x));
4607 }
4608 break;
4609 }
4610
4611 case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
4612 IPW_ERROR("Dino config\n");
4613 if (priv->hcmd
James Ketrenosa613bff2005-08-24 21:43:11 -05004614 && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004615 IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05004616
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004617 break;
4618 }
4619
4620 case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
4621 struct notif_beacon_state *x = &notif->u.beacon_state;
4622 if (notif->size != sizeof(*x)) {
4623 IPW_ERROR
4624 ("Beacon state of wrong size %d (should "
4625 "be %zd)\n", notif->size, sizeof(*x));
4626 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004627 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004628
James Ketrenosa613bff2005-08-24 21:43:11 -05004629 if (le32_to_cpu(x->state) ==
4630 HOST_NOTIFICATION_STATUS_BEACON_MISSING)
4631 ipw_handle_missed_beacon(priv,
4632 le32_to_cpu(x->
4633 number));
Jeff Garzikbf794512005-07-31 13:07:26 -04004634
James Ketrenos43f66a62005-03-25 12:31:53 -06004635 break;
4636 }
Jeff Garzikbf794512005-07-31 13:07:26 -04004637
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004638 case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
4639 struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
4640 if (notif->size == sizeof(*x)) {
4641 IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
4642 "0x%02x station %d\n",
4643 x->key_state, x->security_type,
4644 x->station_index);
4645 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004646 }
4647
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004648 IPW_ERROR
4649 ("TGi Tx Key of wrong size %d (should be %zd)\n",
4650 notif->size, sizeof(*x));
4651 break;
4652 }
4653
4654 case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
4655 struct notif_calibration *x = &notif->u.calibration;
4656
4657 if (notif->size == sizeof(*x)) {
4658 memcpy(&priv->calib, x, sizeof(*x));
4659 IPW_DEBUG_INFO("TODO: Calibration\n");
4660 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06004661 }
4662
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004663 IPW_ERROR
4664 ("Calibration of wrong size %d (should be %zd)\n",
4665 notif->size, sizeof(*x));
James Ketrenos43f66a62005-03-25 12:31:53 -06004666 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004667 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004668
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004669 case HOST_NOTIFICATION_NOISE_STATS:{
4670 if (notif->size == sizeof(u32)) {
4671 priv->last_noise =
James Ketrenosa613bff2005-08-24 21:43:11 -05004672 (u8) (le32_to_cpu(notif->u.noise.value) &
4673 0xff);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004674 average_add(&priv->average_noise,
4675 priv->last_noise);
4676 break;
4677 }
James Ketrenos43f66a62005-03-25 12:31:53 -06004678
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004679 IPW_ERROR
4680 ("Noise stat is wrong size %d (should be %zd)\n",
4681 notif->size, sizeof(u32));
James Ketrenos43f66a62005-03-25 12:31:53 -06004682 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04004683 }
4684
James Ketrenos43f66a62005-03-25 12:31:53 -06004685 default:
4686 IPW_ERROR("Unknown notification: "
4687 "subtype=%d,flags=0x%2x,size=%d\n",
4688 notif->subtype, notif->flags, notif->size);
4689 }
4690}
4691
4692/**
4693 * Destroys all DMA structures and initialise them again
Jeff Garzikbf794512005-07-31 13:07:26 -04004694 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004695 * @param priv
4696 * @return error code
4697 */
4698static int ipw_queue_reset(struct ipw_priv *priv)
4699{
4700 int rc = 0;
4701 /** @todo customize queue sizes */
4702 int nTx = 64, nTxCmd = 8;
4703 ipw_tx_queue_free(priv);
4704 /* Tx CMD queue */
4705 rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
James Ketrenosb095c382005-08-24 22:04:42 -05004706 IPW_TX_CMD_QUEUE_READ_INDEX,
4707 IPW_TX_CMD_QUEUE_WRITE_INDEX,
4708 IPW_TX_CMD_QUEUE_BD_BASE,
4709 IPW_TX_CMD_QUEUE_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004710 if (rc) {
4711 IPW_ERROR("Tx Cmd queue init failed\n");
4712 goto error;
4713 }
4714 /* Tx queue(s) */
4715 rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004716 IPW_TX_QUEUE_0_READ_INDEX,
4717 IPW_TX_QUEUE_0_WRITE_INDEX,
4718 IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004719 if (rc) {
4720 IPW_ERROR("Tx 0 queue init failed\n");
4721 goto error;
4722 }
4723 rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004724 IPW_TX_QUEUE_1_READ_INDEX,
4725 IPW_TX_QUEUE_1_WRITE_INDEX,
4726 IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004727 if (rc) {
4728 IPW_ERROR("Tx 1 queue init failed\n");
4729 goto error;
4730 }
4731 rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004732 IPW_TX_QUEUE_2_READ_INDEX,
4733 IPW_TX_QUEUE_2_WRITE_INDEX,
4734 IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004735 if (rc) {
4736 IPW_ERROR("Tx 2 queue init failed\n");
4737 goto error;
4738 }
4739 rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
James Ketrenosb095c382005-08-24 22:04:42 -05004740 IPW_TX_QUEUE_3_READ_INDEX,
4741 IPW_TX_QUEUE_3_WRITE_INDEX,
4742 IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06004743 if (rc) {
4744 IPW_ERROR("Tx 3 queue init failed\n");
4745 goto error;
4746 }
4747 /* statistics */
4748 priv->rx_bufs_min = 0;
4749 priv->rx_pend_max = 0;
4750 return rc;
4751
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004752 error:
James Ketrenos43f66a62005-03-25 12:31:53 -06004753 ipw_tx_queue_free(priv);
4754 return rc;
4755}
4756
4757/**
4758 * Reclaim Tx queue entries no more used by NIC.
Jeff Garzikbf794512005-07-31 13:07:26 -04004759 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004760 * When FW adwances 'R' index, all entries between old and
4761 * new 'R' index need to be reclaimed. As result, some free space
4762 * forms. If there is enough free space (> low mark), wake Tx queue.
Jeff Garzikbf794512005-07-31 13:07:26 -04004763 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004764 * @note Need to protect against garbage in 'R' index
4765 * @param priv
4766 * @param txq
4767 * @param qindex
4768 * @return Number of used entries remains in the queue
4769 */
Jeff Garzikbf794512005-07-31 13:07:26 -04004770static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06004771 struct clx2_tx_queue *txq, int qindex)
4772{
4773 u32 hw_tail;
4774 int used;
4775 struct clx2_queue *q = &txq->q;
4776
4777 hw_tail = ipw_read32(priv, q->reg_r);
4778 if (hw_tail >= q->n_bd) {
4779 IPW_ERROR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004780 ("Read index for DMA queue (%d) is out of range [0-%d)\n",
4781 hw_tail, q->n_bd);
James Ketrenos43f66a62005-03-25 12:31:53 -06004782 goto done;
4783 }
4784 for (; q->last_used != hw_tail;
4785 q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
4786 ipw_queue_tx_free_tfd(priv, txq);
4787 priv->tx_packets++;
4788 }
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004789 done:
James Ketrenos9ddf84f2005-08-16 17:07:11 -05004790 if ((ipw_queue_space(q) > q->low_mark) &&
4791 (qindex >= 0) &&
4792 (priv->status & STATUS_ASSOCIATED) && netif_running(priv->net_dev))
4793 netif_wake_queue(priv->net_dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06004794 used = q->first_empty - q->last_used;
4795 if (used < 0)
4796 used += q->n_bd;
4797
4798 return used;
4799}
4800
4801static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
4802 int len, int sync)
4803{
4804 struct clx2_tx_queue *txq = &priv->txq_cmd;
4805 struct clx2_queue *q = &txq->q;
4806 struct tfd_frame *tfd;
4807
4808 if (ipw_queue_space(q) < (sync ? 1 : 2)) {
4809 IPW_ERROR("No space for Tx\n");
4810 return -EBUSY;
4811 }
4812
4813 tfd = &txq->bd[q->first_empty];
4814 txq->txb[q->first_empty] = NULL;
4815
4816 memset(tfd, 0, sizeof(*tfd));
4817 tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
4818 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
4819 priv->hcmd_seq++;
4820 tfd->u.cmd.index = hcmd;
4821 tfd->u.cmd.length = len;
4822 memcpy(tfd->u.cmd.payload, buf, len);
4823 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
4824 ipw_write32(priv, q->reg_w, q->first_empty);
4825 _ipw_read32(priv, 0x90);
4826
4827 return 0;
4828}
4829
Jeff Garzikbf794512005-07-31 13:07:26 -04004830/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004831 * Rx theory of operation
4832 *
4833 * The host allocates 32 DMA target addresses and passes the host address
James Ketrenosb095c382005-08-24 22:04:42 -05004834 * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
James Ketrenos43f66a62005-03-25 12:31:53 -06004835 * 0 to 31
4836 *
4837 * Rx Queue Indexes
4838 * The host/firmware share two index registers for managing the Rx buffers.
4839 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004840 * The READ index maps to the first position that the firmware may be writing
4841 * to -- the driver can read up to (but not including) this position and get
4842 * good data.
James Ketrenos43f66a62005-03-25 12:31:53 -06004843 * The READ index is managed by the firmware once the card is enabled.
4844 *
4845 * The WRITE index maps to the last position the driver has read from -- the
4846 * position preceding WRITE is the last slot the firmware can place a packet.
4847 *
4848 * The queue is empty (no good data) if WRITE = READ - 1, and is full if
Jeff Garzikbf794512005-07-31 13:07:26 -04004849 * WRITE = READ.
James Ketrenos43f66a62005-03-25 12:31:53 -06004850 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004851 * During initialization the host sets up the READ queue position to the first
James Ketrenos43f66a62005-03-25 12:31:53 -06004852 * INDEX position, and WRITE to the last (READ - 1 wrapped)
4853 *
4854 * When the firmware places a packet in a buffer it will advance the READ index
4855 * and fire the RX interrupt. The driver can then query the READ index and
4856 * process as many packets as possible, moving the WRITE index forward as it
4857 * resets the Rx queue buffers with new memory.
Jeff Garzikbf794512005-07-31 13:07:26 -04004858 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004859 * The management in the driver is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04004860 * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
James Ketrenos43f66a62005-03-25 12:31:53 -06004861 * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
Jeff Garzikbf794512005-07-31 13:07:26 -04004862 * to replensish the ipw->rxq->rx_free.
James Ketrenos43f66a62005-03-25 12:31:53 -06004863 * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
4864 * ipw->rxq is replenished and the READ INDEX is updated (updating the
4865 * 'processed' and 'read' driver indexes as well)
4866 * + A received packet is processed and handed to the kernel network stack,
4867 * detached from the ipw->rxq. The driver 'processed' index is updated.
4868 * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
Jeff Garzikbf794512005-07-31 13:07:26 -04004869 * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
4870 * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
James Ketrenos43f66a62005-03-25 12:31:53 -06004871 * were enough free buffers and RX_STALLED is set it is cleared.
4872 *
4873 *
4874 * Driver sequence:
4875 *
Jeff Garzikbf794512005-07-31 13:07:26 -04004876 * ipw_rx_queue_alloc() Allocates rx_free
James Ketrenos43f66a62005-03-25 12:31:53 -06004877 * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
4878 * ipw_rx_queue_restock
4879 * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
4880 * queue, updates firmware pointers, and updates
4881 * the WRITE index. If insufficient rx_free buffers
4882 * are available, schedules ipw_rx_queue_replenish
4883 *
4884 * -- enable interrupts --
4885 * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
Jeff Garzikbf794512005-07-31 13:07:26 -04004886 * READ INDEX, detaching the SKB from the pool.
James Ketrenos43f66a62005-03-25 12:31:53 -06004887 * Moves the packet buffer from queue to rx_used.
4888 * Calls ipw_rx_queue_restock to refill any empty
4889 * slots.
4890 * ...
4891 *
4892 */
4893
Jeff Garzikbf794512005-07-31 13:07:26 -04004894/*
James Ketrenos43f66a62005-03-25 12:31:53 -06004895 * If there are slots in the RX queue that need to be restocked,
4896 * and we have free pre-allocated buffers, fill the ranks as much
4897 * as we can pulling from rx_free.
4898 *
4899 * This moves the 'write' index forward to catch up with 'processed', and
4900 * also updates the memory address in the firmware to reference the new
4901 * target buffer.
4902 */
4903static void ipw_rx_queue_restock(struct ipw_priv *priv)
4904{
4905 struct ipw_rx_queue *rxq = priv->rxq;
4906 struct list_head *element;
4907 struct ipw_rx_mem_buffer *rxb;
4908 unsigned long flags;
4909 int write;
4910
4911 spin_lock_irqsave(&rxq->lock, flags);
4912 write = rxq->write;
4913 while ((rxq->write != rxq->processed) && (rxq->free_count)) {
4914 element = rxq->rx_free.next;
4915 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
4916 list_del(element);
4917
James Ketrenosb095c382005-08-24 22:04:42 -05004918 ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06004919 rxb->dma_addr);
4920 rxq->queue[rxq->write] = rxb;
4921 rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
4922 rxq->free_count--;
4923 }
4924 spin_unlock_irqrestore(&rxq->lock, flags);
4925
Jeff Garzikbf794512005-07-31 13:07:26 -04004926 /* If the pre-allocated buffer pool is dropping low, schedule to
James Ketrenos43f66a62005-03-25 12:31:53 -06004927 * refill it */
4928 if (rxq->free_count <= RX_LOW_WATERMARK)
4929 queue_work(priv->workqueue, &priv->rx_replenish);
4930
4931 /* If we've added more space for the firmware to place data, tell it */
Jeff Garzikbf794512005-07-31 13:07:26 -04004932 if (write != rxq->write)
James Ketrenosb095c382005-08-24 22:04:42 -05004933 ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
James Ketrenos43f66a62005-03-25 12:31:53 -06004934}
4935
4936/*
4937 * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
Jeff Garzikbf794512005-07-31 13:07:26 -04004938 * Also restock the Rx queue via ipw_rx_queue_restock.
4939 *
James Ketrenos43f66a62005-03-25 12:31:53 -06004940 * This is called as a scheduled work item (except for during intialization)
4941 */
4942static void ipw_rx_queue_replenish(void *data)
4943{
4944 struct ipw_priv *priv = data;
4945 struct ipw_rx_queue *rxq = priv->rxq;
4946 struct list_head *element;
4947 struct ipw_rx_mem_buffer *rxb;
4948 unsigned long flags;
4949
4950 spin_lock_irqsave(&rxq->lock, flags);
4951 while (!list_empty(&rxq->rx_used)) {
4952 element = rxq->rx_used.next;
4953 rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
James Ketrenosb095c382005-08-24 22:04:42 -05004954 rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC);
James Ketrenos43f66a62005-03-25 12:31:53 -06004955 if (!rxb->skb) {
4956 printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
4957 priv->net_dev->name);
4958 /* We don't reschedule replenish work here -- we will
4959 * call the restock method and if it still needs
4960 * more buffers it will schedule replenish */
4961 break;
4962 }
4963 list_del(element);
Jeff Garzikbf794512005-07-31 13:07:26 -04004964
James Ketrenos43f66a62005-03-25 12:31:53 -06004965 rxb->rxb = (struct ipw_rx_buffer *)rxb->skb->data;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004966 rxb->dma_addr =
4967 pci_map_single(priv->pci_dev, rxb->skb->data,
James Ketrenosb095c382005-08-24 22:04:42 -05004968 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
Jeff Garzikbf794512005-07-31 13:07:26 -04004969
James Ketrenos43f66a62005-03-25 12:31:53 -06004970 list_add_tail(&rxb->list, &rxq->rx_free);
4971 rxq->free_count++;
4972 }
4973 spin_unlock_irqrestore(&rxq->lock, flags);
4974
4975 ipw_rx_queue_restock(priv);
4976}
4977
James Ketrenosc848d0a2005-08-24 21:56:24 -05004978static void ipw_bg_rx_queue_replenish(void *data)
4979{
4980 struct ipw_priv *priv = data;
4981 down(&priv->sem);
4982 ipw_rx_queue_replenish(data);
4983 up(&priv->sem);
4984}
4985
James Ketrenos43f66a62005-03-25 12:31:53 -06004986/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
Zhu Yic7b6a672006-01-24 16:37:05 +08004987 * If an SKB has been detached, the POOL needs to have its SKB set to NULL
Jeff Garzikbf794512005-07-31 13:07:26 -04004988 * This free routine walks the list of POOL entries and if SKB is set to
James Ketrenos43f66a62005-03-25 12:31:53 -06004989 * non NULL it is unmapped and freed
4990 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04004991static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
James Ketrenos43f66a62005-03-25 12:31:53 -06004992{
4993 int i;
4994
4995 if (!rxq)
4996 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04004997
James Ketrenos43f66a62005-03-25 12:31:53 -06004998 for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
4999 if (rxq->pool[i].skb != NULL) {
5000 pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05005001 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06005002 dev_kfree_skb(rxq->pool[i].skb);
5003 }
5004 }
5005
5006 kfree(rxq);
5007}
5008
5009static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
5010{
5011 struct ipw_rx_queue *rxq;
5012 int i;
5013
Takisc75f4742005-12-01 01:41:45 -08005014 rxq = kzalloc(sizeof(*rxq), GFP_KERNEL);
Panagiotis Issarisad18b0e2005-09-05 04:14:10 +02005015 if (unlikely(!rxq)) {
5016 IPW_ERROR("memory allocation failed\n");
5017 return NULL;
5018 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005019 spin_lock_init(&rxq->lock);
5020 INIT_LIST_HEAD(&rxq->rx_free);
5021 INIT_LIST_HEAD(&rxq->rx_used);
5022
5023 /* Fill the rx_used queue with _all_ of the Rx buffers */
Jeff Garzikbf794512005-07-31 13:07:26 -04005024 for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -06005025 list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
5026
5027 /* Set us so that we have processed and used all buffers, but have
5028 * not restocked the Rx queue with fresh buffers */
5029 rxq->read = rxq->write = 0;
5030 rxq->processed = RX_QUEUE_SIZE - 1;
5031 rxq->free_count = 0;
5032
5033 return rxq;
5034}
5035
5036static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
5037{
5038 rate &= ~IEEE80211_BASIC_RATE_MASK;
5039 if (ieee_mode == IEEE_A) {
5040 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04005041 case IEEE80211_OFDM_RATE_6MB:
5042 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005043 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005044 case IEEE80211_OFDM_RATE_9MB:
5045 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005046 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005047 case IEEE80211_OFDM_RATE_12MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005048 return priv->
5049 rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005050 case IEEE80211_OFDM_RATE_18MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005051 return priv->
5052 rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005053 case IEEE80211_OFDM_RATE_24MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005054 return priv->
5055 rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005056 case IEEE80211_OFDM_RATE_36MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005057 return priv->
5058 rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005059 case IEEE80211_OFDM_RATE_48MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005060 return priv->
5061 rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005062 case IEEE80211_OFDM_RATE_54MB:
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005063 return priv->
5064 rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005065 default:
5066 return 0;
5067 }
5068 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005069
James Ketrenos43f66a62005-03-25 12:31:53 -06005070 /* B and G mixed */
5071 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04005072 case IEEE80211_CCK_RATE_1MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005073 return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005074 case IEEE80211_CCK_RATE_2MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005075 return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005076 case IEEE80211_CCK_RATE_5MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005077 return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005078 case IEEE80211_CCK_RATE_11MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005079 return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
5080 }
5081
5082 /* If we are limited to B modulations, bail at this point */
5083 if (ieee_mode == IEEE_B)
5084 return 0;
5085
5086 /* G */
5087 switch (rate) {
Jeff Garzikbf794512005-07-31 13:07:26 -04005088 case IEEE80211_OFDM_RATE_6MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005089 return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005090 case IEEE80211_OFDM_RATE_9MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005091 return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005092 case IEEE80211_OFDM_RATE_12MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005093 return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005094 case IEEE80211_OFDM_RATE_18MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005095 return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005096 case IEEE80211_OFDM_RATE_24MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005097 return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005098 case IEEE80211_OFDM_RATE_36MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005099 return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005100 case IEEE80211_OFDM_RATE_48MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005101 return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005102 case IEEE80211_OFDM_RATE_54MB:
James Ketrenos43f66a62005-03-25 12:31:53 -06005103 return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
5104 }
5105
5106 return 0;
5107}
5108
Jeff Garzikbf794512005-07-31 13:07:26 -04005109static int ipw_compatible_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -06005110 const struct ieee80211_network *network,
5111 struct ipw_supported_rates *rates)
5112{
5113 int num_rates, i;
5114
5115 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005116 num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06005117 rates->num_rates = 0;
5118 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005119 if (!ipw_is_rate_in_mask(priv, network->mode,
5120 network->rates[i])) {
5121
James Ketrenosea2b26e2005-08-24 21:25:16 -05005122 if (network->rates[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005123 IPW_DEBUG_SCAN("Adding masked mandatory "
5124 "rate %02X\n",
5125 network->rates[i]);
5126 rates->supported_rates[rates->num_rates++] =
5127 network->rates[i];
5128 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005129 }
5130
James Ketrenos43f66a62005-03-25 12:31:53 -06005131 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
5132 network->rates[i], priv->rates_mask);
5133 continue;
5134 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005135
James Ketrenos43f66a62005-03-25 12:31:53 -06005136 rates->supported_rates[rates->num_rates++] = network->rates[i];
5137 }
5138
James Ketrenosa613bff2005-08-24 21:43:11 -05005139 num_rates = min(network->rates_ex_len,
5140 (u8) (IPW_MAX_RATES - num_rates));
James Ketrenos43f66a62005-03-25 12:31:53 -06005141 for (i = 0; i < num_rates; i++) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005142 if (!ipw_is_rate_in_mask(priv, network->mode,
5143 network->rates_ex[i])) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05005144 if (network->rates_ex[i] & IEEE80211_BASIC_RATE_MASK) {
James Ketrenosa613bff2005-08-24 21:43:11 -05005145 IPW_DEBUG_SCAN("Adding masked mandatory "
5146 "rate %02X\n",
5147 network->rates_ex[i]);
5148 rates->supported_rates[rates->num_rates++] =
5149 network->rates[i];
5150 continue;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005151 }
5152
James Ketrenos43f66a62005-03-25 12:31:53 -06005153 IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
5154 network->rates_ex[i], priv->rates_mask);
5155 continue;
5156 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005157
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005158 rates->supported_rates[rates->num_rates++] =
5159 network->rates_ex[i];
James Ketrenos43f66a62005-03-25 12:31:53 -06005160 }
5161
James Ketrenosea2b26e2005-08-24 21:25:16 -05005162 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06005163}
5164
Arjan van de Ven858119e2006-01-14 13:20:43 -08005165static void ipw_copy_rates(struct ipw_supported_rates *dest,
James Ketrenos43f66a62005-03-25 12:31:53 -06005166 const struct ipw_supported_rates *src)
5167{
5168 u8 i;
5169 for (i = 0; i < src->num_rates; i++)
5170 dest->supported_rates[i] = src->supported_rates[i];
5171 dest->num_rates = src->num_rates;
5172}
5173
5174/* TODO: Look at sniffed packets in the air to determine if the basic rate
5175 * mask should ever be used -- right now all callers to add the scan rates are
5176 * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
5177static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005178 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06005179{
Jeff Garzikbf794512005-07-31 13:07:26 -04005180 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005181 IEEE80211_BASIC_RATE_MASK : 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005182
James Ketrenos43f66a62005-03-25 12:31:53 -06005183 if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005184 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005185 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005186
5187 if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005188 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005189 IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005190
5191 if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005192 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005193 IEEE80211_CCK_RATE_5MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005194
5195 if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005196 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005197 IEEE80211_CCK_RATE_11MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005198}
5199
5200static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005201 u8 modulation, u32 rate_mask)
James Ketrenos43f66a62005-03-25 12:31:53 -06005202{
Jeff Garzikbf794512005-07-31 13:07:26 -04005203 u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005204 IEEE80211_BASIC_RATE_MASK : 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005205
5206 if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005207 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005208 IEEE80211_OFDM_RATE_6MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005209
5210 if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005211 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005212 IEEE80211_OFDM_RATE_9MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005213
5214 if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005215 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005216 IEEE80211_OFDM_RATE_12MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005217
5218 if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005219 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005220 IEEE80211_OFDM_RATE_18MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005221
5222 if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005223 rates->supported_rates[rates->num_rates++] = basic_mask |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005224 IEEE80211_OFDM_RATE_24MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005225
5226 if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005227 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005228 IEEE80211_OFDM_RATE_36MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005229
5230 if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005231 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005232 IEEE80211_OFDM_RATE_48MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005233
5234 if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
Jeff Garzikbf794512005-07-31 13:07:26 -04005235 rates->supported_rates[rates->num_rates++] =
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005236 IEEE80211_OFDM_RATE_54MB;
James Ketrenos43f66a62005-03-25 12:31:53 -06005237}
5238
5239struct ipw_network_match {
5240 struct ieee80211_network *network;
5241 struct ipw_supported_rates rates;
5242};
5243
James Ketrenosc848d0a2005-08-24 21:56:24 -05005244static int ipw_find_adhoc_network(struct ipw_priv *priv,
5245 struct ipw_network_match *match,
5246 struct ieee80211_network *network,
5247 int roaming)
5248{
5249 struct ipw_supported_rates rates;
5250
5251 /* Verify that this network's capability is compatible with the
5252 * current mode (AdHoc or Infrastructure) */
5253 if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
5254 !(network->capability & WLAN_CAPABILITY_IBSS))) {
5255 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded due to "
5256 "capability mismatch.\n",
5257 escape_essid(network->ssid, network->ssid_len),
5258 MAC_ARG(network->bssid));
5259 return 0;
5260 }
5261
5262 /* If we do not have an ESSID for this AP, we can not associate with
5263 * it */
5264 if (network->flags & NETWORK_EMPTY_ESSID) {
5265 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5266 "because of hidden ESSID.\n",
5267 escape_essid(network->ssid, network->ssid_len),
5268 MAC_ARG(network->bssid));
5269 return 0;
5270 }
5271
5272 if (unlikely(roaming)) {
5273 /* If we are roaming, then ensure check if this is a valid
5274 * network to try and roam to */
5275 if ((network->ssid_len != match->network->ssid_len) ||
5276 memcmp(network->ssid, match->network->ssid,
5277 network->ssid_len)) {
5278 IPW_DEBUG_MERGE("Netowrk '%s (" MAC_FMT ")' excluded "
5279 "because of non-network ESSID.\n",
5280 escape_essid(network->ssid,
5281 network->ssid_len),
5282 MAC_ARG(network->bssid));
5283 return 0;
5284 }
5285 } else {
5286 /* If an ESSID has been configured then compare the broadcast
5287 * ESSID to ours */
5288 if ((priv->config & CFG_STATIC_ESSID) &&
5289 ((network->ssid_len != priv->essid_len) ||
5290 memcmp(network->ssid, priv->essid,
5291 min(network->ssid_len, priv->essid_len)))) {
5292 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
James Ketrenosafbf30a2005-08-25 00:05:33 -05005293
James Ketrenosc848d0a2005-08-24 21:56:24 -05005294 strncpy(escaped,
5295 escape_essid(network->ssid, network->ssid_len),
5296 sizeof(escaped));
5297 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5298 "because of ESSID mismatch: '%s'.\n",
5299 escaped, MAC_ARG(network->bssid),
5300 escape_essid(priv->essid,
5301 priv->essid_len));
5302 return 0;
5303 }
5304 }
5305
5306 /* If the old network rate is better than this one, don't bother
5307 * testing everything else. */
5308
5309 if (network->time_stamp[0] < match->network->time_stamp[0]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005310 IPW_DEBUG_MERGE("Network '%s excluded because newer than "
5311 "current network.\n",
5312 escape_essid(match->network->ssid,
5313 match->network->ssid_len));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005314 return 0;
5315 } else if (network->time_stamp[1] < match->network->time_stamp[1]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005316 IPW_DEBUG_MERGE("Network '%s excluded because newer than "
5317 "current network.\n",
5318 escape_essid(match->network->ssid,
5319 match->network->ssid_len));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005320 return 0;
5321 }
5322
5323 /* Now go through and see if the requested network is valid... */
5324 if (priv->ieee->scan_age != 0 &&
5325 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
5326 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
Zhu Yic7b6a672006-01-24 16:37:05 +08005327 "because of age: %ums.\n",
James Ketrenosc848d0a2005-08-24 21:56:24 -05005328 escape_essid(network->ssid, network->ssid_len),
5329 MAC_ARG(network->bssid),
Zhu Yic7b6a672006-01-24 16:37:05 +08005330 jiffies_to_msecs(jiffies - network->last_scanned));
James Ketrenosc848d0a2005-08-24 21:56:24 -05005331 return 0;
5332 }
5333
5334 if ((priv->config & CFG_STATIC_CHANNEL) &&
5335 (network->channel != priv->channel)) {
5336 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5337 "because of channel mismatch: %d != %d.\n",
5338 escape_essid(network->ssid, network->ssid_len),
5339 MAC_ARG(network->bssid),
5340 network->channel, priv->channel);
5341 return 0;
5342 }
5343
5344 /* Verify privacy compatability */
5345 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
5346 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5347 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5348 "because of privacy mismatch: %s != %s.\n",
5349 escape_essid(network->ssid, network->ssid_len),
5350 MAC_ARG(network->bssid),
James Ketrenosafbf30a2005-08-25 00:05:33 -05005351 priv->
5352 capability & CAP_PRIVACY_ON ? "on" : "off",
5353 network->
5354 capability & WLAN_CAPABILITY_PRIVACY ? "on" :
5355 "off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05005356 return 0;
5357 }
5358
5359 if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5360 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5361 "because of the same BSSID match: " MAC_FMT
5362 ".\n", escape_essid(network->ssid,
5363 network->ssid_len),
5364 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
5365 return 0;
5366 }
5367
5368 /* Filter out any incompatible freq / mode combinations */
5369 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5370 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5371 "because of invalid frequency/mode "
5372 "combination.\n",
5373 escape_essid(network->ssid, network->ssid_len),
5374 MAC_ARG(network->bssid));
5375 return 0;
5376 }
5377
5378 /* Ensure that the rates supported by the driver are compatible with
5379 * this AP, including verification of basic rates (mandatory) */
5380 if (!ipw_compatible_rates(priv, network, &rates)) {
5381 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5382 "because configured rate mask excludes "
5383 "AP mandatory rate.\n",
5384 escape_essid(network->ssid, network->ssid_len),
5385 MAC_ARG(network->bssid));
5386 return 0;
5387 }
5388
5389 if (rates.num_rates == 0) {
5390 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' excluded "
5391 "because of no compatible rates.\n",
5392 escape_essid(network->ssid, network->ssid_len),
5393 MAC_ARG(network->bssid));
5394 return 0;
5395 }
5396
5397 /* TODO: Perform any further minimal comparititive tests. We do not
5398 * want to put too much policy logic here; intelligent scan selection
5399 * should occur within a generic IEEE 802.11 user space tool. */
5400
5401 /* Set up 'new' AP to this network */
5402 ipw_copy_rates(&match->rates, &rates);
5403 match->network = network;
5404 IPW_DEBUG_MERGE("Network '%s (" MAC_FMT ")' is a viable match.\n",
5405 escape_essid(network->ssid, network->ssid_len),
5406 MAC_ARG(network->bssid));
5407
5408 return 1;
5409}
5410
5411static void ipw_merge_adhoc_network(void *data)
5412{
5413 struct ipw_priv *priv = data;
5414 struct ieee80211_network *network = NULL;
5415 struct ipw_network_match match = {
5416 .network = priv->assoc_network
5417 };
5418
James Ketrenosafbf30a2005-08-25 00:05:33 -05005419 if ((priv->status & STATUS_ASSOCIATED) &&
5420 (priv->ieee->iw_mode == IW_MODE_ADHOC)) {
James Ketrenosc848d0a2005-08-24 21:56:24 -05005421 /* First pass through ROAM process -- look for a better
5422 * network */
5423 unsigned long flags;
5424
5425 spin_lock_irqsave(&priv->ieee->lock, flags);
5426 list_for_each_entry(network, &priv->ieee->network_list, list) {
5427 if (network != priv->assoc_network)
5428 ipw_find_adhoc_network(priv, &match, network,
5429 1);
5430 }
5431 spin_unlock_irqrestore(&priv->ieee->lock, flags);
5432
5433 if (match.network == priv->assoc_network) {
5434 IPW_DEBUG_MERGE("No better ADHOC in this network to "
5435 "merge to.\n");
5436 return;
5437 }
5438
5439 down(&priv->sem);
5440 if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
5441 IPW_DEBUG_MERGE("remove network %s\n",
5442 escape_essid(priv->essid,
5443 priv->essid_len));
5444 ipw_remove_current_network(priv);
5445 }
5446
5447 ipw_disassociate(priv);
5448 priv->assoc_network = match.network;
5449 up(&priv->sem);
5450 return;
5451 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05005452}
5453
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005454static int ipw_best_network(struct ipw_priv *priv,
5455 struct ipw_network_match *match,
5456 struct ieee80211_network *network, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06005457{
5458 struct ipw_supported_rates rates;
5459
5460 /* Verify that this network's capability is compatible with the
5461 * current mode (AdHoc or Infrastructure) */
5462 if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
Jouni Malinen24743852005-08-14 20:59:59 -07005463 !(network->capability & WLAN_CAPABILITY_ESS)) ||
James Ketrenos43f66a62005-03-25 12:31:53 -06005464 (priv->ieee->iw_mode == IW_MODE_ADHOC &&
5465 !(network->capability & WLAN_CAPABILITY_IBSS))) {
5466 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to "
Jeff Garzikbf794512005-07-31 13:07:26 -04005467 "capability mismatch.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005468 escape_essid(network->ssid, network->ssid_len),
5469 MAC_ARG(network->bssid));
5470 return 0;
5471 }
5472
5473 /* If we do not have an ESSID for this AP, we can not associate with
5474 * it */
5475 if (network->flags & NETWORK_EMPTY_ESSID) {
5476 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5477 "because of hidden ESSID.\n",
5478 escape_essid(network->ssid, network->ssid_len),
5479 MAC_ARG(network->bssid));
5480 return 0;
5481 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005482
James Ketrenos43f66a62005-03-25 12:31:53 -06005483 if (unlikely(roaming)) {
5484 /* If we are roaming, then ensure check if this is a valid
5485 * network to try and roam to */
5486 if ((network->ssid_len != match->network->ssid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005487 memcmp(network->ssid, match->network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005488 network->ssid_len)) {
5489 IPW_DEBUG_ASSOC("Netowrk '%s (" MAC_FMT ")' excluded "
5490 "because of non-network ESSID.\n",
Jeff Garzikbf794512005-07-31 13:07:26 -04005491 escape_essid(network->ssid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005492 network->ssid_len),
5493 MAC_ARG(network->bssid));
5494 return 0;
5495 }
5496 } else {
Jeff Garzikbf794512005-07-31 13:07:26 -04005497 /* If an ESSID has been configured then compare the broadcast
5498 * ESSID to ours */
5499 if ((priv->config & CFG_STATIC_ESSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005500 ((network->ssid_len != priv->essid_len) ||
Jeff Garzikbf794512005-07-31 13:07:26 -04005501 memcmp(network->ssid, priv->essid,
James Ketrenos43f66a62005-03-25 12:31:53 -06005502 min(network->ssid_len, priv->essid_len)))) {
5503 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005504 strncpy(escaped,
5505 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005506 sizeof(escaped));
5507 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Jeff Garzikbf794512005-07-31 13:07:26 -04005508 "because of ESSID mismatch: '%s'.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005509 escaped, MAC_ARG(network->bssid),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005510 escape_essid(priv->essid,
5511 priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005512 return 0;
5513 }
5514 }
5515
5516 /* If the old network rate is better than this one, don't bother
5517 * testing everything else. */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005518 if (match->network && match->network->stats.rssi > network->stats.rssi) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005519 char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
Jeff Garzikbf794512005-07-31 13:07:26 -04005520 strncpy(escaped,
5521 escape_essid(network->ssid, network->ssid_len),
James Ketrenos43f66a62005-03-25 12:31:53 -06005522 sizeof(escaped));
5523 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded because "
5524 "'%s (" MAC_FMT ")' has a stronger signal.\n",
5525 escaped, MAC_ARG(network->bssid),
5526 escape_essid(match->network->ssid,
5527 match->network->ssid_len),
5528 MAC_ARG(match->network->bssid));
5529 return 0;
5530 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005531
James Ketrenos43f66a62005-03-25 12:31:53 -06005532 /* If this network has already had an association attempt within the
5533 * last 3 seconds, do not try and associate again... */
5534 if (network->last_associate &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005535 time_after(network->last_associate + (HZ * 3UL), jiffies)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005536 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Zhu Yic7b6a672006-01-24 16:37:05 +08005537 "because of storming (%ums since last "
James Ketrenos43f66a62005-03-25 12:31:53 -06005538 "assoc attempt).\n",
5539 escape_essid(network->ssid, network->ssid_len),
5540 MAC_ARG(network->bssid),
Zhu Yic7b6a672006-01-24 16:37:05 +08005541 jiffies_to_msecs(jiffies - network->last_associate));
James Ketrenos43f66a62005-03-25 12:31:53 -06005542 return 0;
5543 }
5544
5545 /* Now go through and see if the requested network is valid... */
Jeff Garzikbf794512005-07-31 13:07:26 -04005546 if (priv->ieee->scan_age != 0 &&
James Ketrenosea2b26e2005-08-24 21:25:16 -05005547 time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005548 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
Zhu Yic7b6a672006-01-24 16:37:05 +08005549 "because of age: %ums.\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06005550 escape_essid(network->ssid, network->ssid_len),
5551 MAC_ARG(network->bssid),
Zhu Yic7b6a672006-01-24 16:37:05 +08005552 jiffies_to_msecs(jiffies - network->last_scanned));
James Ketrenos43f66a62005-03-25 12:31:53 -06005553 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04005554 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005555
Jeff Garzikbf794512005-07-31 13:07:26 -04005556 if ((priv->config & CFG_STATIC_CHANNEL) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005557 (network->channel != priv->channel)) {
5558 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5559 "because of channel mismatch: %d != %d.\n",
5560 escape_essid(network->ssid, network->ssid_len),
5561 MAC_ARG(network->bssid),
5562 network->channel, priv->channel);
5563 return 0;
5564 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005565
James Ketrenos43f66a62005-03-25 12:31:53 -06005566 /* Verify privacy compatability */
Jeff Garzikbf794512005-07-31 13:07:26 -04005567 if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
James Ketrenos43f66a62005-03-25 12:31:53 -06005568 ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
5569 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5570 "because of privacy mismatch: %s != %s.\n",
5571 escape_essid(network->ssid, network->ssid_len),
5572 MAC_ARG(network->bssid),
Jeff Garzikbf794512005-07-31 13:07:26 -04005573 priv->capability & CAP_PRIVACY_ON ? "on" :
James Ketrenos43f66a62005-03-25 12:31:53 -06005574 "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04005575 network->capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005576 WLAN_CAPABILITY_PRIVACY ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06005577 return 0;
5578 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005579
Hong Liucdd1fa12005-08-31 18:14:27 +08005580 if (!priv->ieee->wpa_enabled && (network->wpa_ie_len > 0 ||
5581 network->rsn_ie_len > 0)) {
5582 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5583 "because of WPA capability mismatch.\n",
5584 escape_essid(network->ssid, network->ssid_len),
5585 MAC_ARG(network->bssid));
5586 return 0;
5587 }
5588
Jeff Garzikbf794512005-07-31 13:07:26 -04005589 if ((priv->config & CFG_STATIC_BSSID) &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005590 memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
5591 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5592 "because of BSSID mismatch: " MAC_FMT ".\n",
5593 escape_essid(network->ssid, network->ssid_len),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005594 MAC_ARG(network->bssid), MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005595 return 0;
5596 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005597
James Ketrenos43f66a62005-03-25 12:31:53 -06005598 /* Filter out any incompatible freq / mode combinations */
5599 if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
5600 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5601 "because of invalid frequency/mode "
5602 "combination.\n",
5603 escape_essid(network->ssid, network->ssid_len),
5604 MAC_ARG(network->bssid));
5605 return 0;
5606 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005607
Liu Hong1fe0adb2005-08-19 09:33:10 -05005608 /* Filter out invalid channel in current GEO */
5609 if (!ipw_is_valid_channel(priv->ieee, network->channel)) {
5610 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5611 "because of invalid channel in current GEO\n",
5612 escape_essid(network->ssid, network->ssid_len),
5613 MAC_ARG(network->bssid));
5614 return 0;
5615 }
5616
James Ketrenosea2b26e2005-08-24 21:25:16 -05005617 /* Ensure that the rates supported by the driver are compatible with
5618 * this AP, including verification of basic rates (mandatory) */
5619 if (!ipw_compatible_rates(priv, network, &rates)) {
5620 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5621 "because configured rate mask excludes "
5622 "AP mandatory rate.\n",
5623 escape_essid(network->ssid, network->ssid_len),
5624 MAC_ARG(network->bssid));
5625 return 0;
5626 }
5627
James Ketrenos43f66a62005-03-25 12:31:53 -06005628 if (rates.num_rates == 0) {
5629 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded "
5630 "because of no compatible rates.\n",
5631 escape_essid(network->ssid, network->ssid_len),
5632 MAC_ARG(network->bssid));
5633 return 0;
5634 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005635
James Ketrenos43f66a62005-03-25 12:31:53 -06005636 /* TODO: Perform any further minimal comparititive tests. We do not
5637 * want to put too much policy logic here; intelligent scan selection
5638 * should occur within a generic IEEE 802.11 user space tool. */
5639
5640 /* Set up 'new' AP to this network */
5641 ipw_copy_rates(&match->rates, &rates);
5642 match->network = network;
5643
5644 IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' is a viable match.\n",
5645 escape_essid(network->ssid, network->ssid_len),
5646 MAC_ARG(network->bssid));
5647
5648 return 1;
5649}
5650
Jeff Garzikbf794512005-07-31 13:07:26 -04005651static void ipw_adhoc_create(struct ipw_priv *priv,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005652 struct ieee80211_network *network)
James Ketrenos43f66a62005-03-25 12:31:53 -06005653{
Liu Hong1fe0adb2005-08-19 09:33:10 -05005654 const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
James Ketrenosafbf30a2005-08-25 00:05:33 -05005655 int i;
5656
James Ketrenos43f66a62005-03-25 12:31:53 -06005657 /*
5658 * For the purposes of scanning, we can set our wireless mode
5659 * to trigger scans across combinations of bands, but when it
5660 * comes to creating a new ad-hoc network, we have tell the FW
5661 * exactly which band to use.
5662 *
Jeff Garzikbf794512005-07-31 13:07:26 -04005663 * We also have the possibility of an invalid channel for the
James Ketrenos43f66a62005-03-25 12:31:53 -06005664 * chossen band. Attempting to create a new ad-hoc network
5665 * with an invalid channel for wireless mode will trigger a
5666 * FW fatal error.
James Ketrenosafbf30a2005-08-25 00:05:33 -05005667 *
James Ketrenos43f66a62005-03-25 12:31:53 -06005668 */
Liu Hong1fe0adb2005-08-19 09:33:10 -05005669 switch (ipw_is_valid_channel(priv->ieee, priv->channel)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005670 case IEEE80211_52GHZ_BAND:
5671 network->mode = IEEE_A;
Liu Hong1fe0adb2005-08-19 09:33:10 -05005672 i = ipw_channel_to_index(priv->ieee, priv->channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05005673 if (i == -1)
5674 BUG();
5675 if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
5676 IPW_WARNING("Overriding invalid channel\n");
5677 priv->channel = geo->a[0].channel;
5678 }
5679 break;
5680
5681 case IEEE80211_24GHZ_BAND:
5682 if (priv->ieee->mode & IEEE_G)
5683 network->mode = IEEE_G;
5684 else
5685 network->mode = IEEE_B;
Liu Hong1fe0adb2005-08-19 09:33:10 -05005686 i = ipw_channel_to_index(priv->ieee, priv->channel);
5687 if (i == -1)
5688 BUG();
5689 if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
5690 IPW_WARNING("Overriding invalid channel\n");
5691 priv->channel = geo->bg[0].channel;
5692 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05005693 break;
5694
5695 default:
James Ketrenos43f66a62005-03-25 12:31:53 -06005696 IPW_WARNING("Overriding invalid channel\n");
5697 if (priv->ieee->mode & IEEE_A) {
5698 network->mode = IEEE_A;
James Ketrenosb095c382005-08-24 22:04:42 -05005699 priv->channel = geo->a[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005700 } else if (priv->ieee->mode & IEEE_G) {
5701 network->mode = IEEE_G;
James Ketrenosb095c382005-08-24 22:04:42 -05005702 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005703 } else {
5704 network->mode = IEEE_B;
James Ketrenosb095c382005-08-24 22:04:42 -05005705 priv->channel = geo->bg[0].channel;
James Ketrenos43f66a62005-03-25 12:31:53 -06005706 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05005707 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06005708 }
5709
5710 network->channel = priv->channel;
5711 priv->config |= CFG_ADHOC_PERSIST;
5712 ipw_create_bssid(priv, network->bssid);
5713 network->ssid_len = priv->essid_len;
5714 memcpy(network->ssid, priv->essid, priv->essid_len);
5715 memset(&network->stats, 0, sizeof(network->stats));
5716 network->capability = WLAN_CAPABILITY_IBSS;
James Ketrenosea2b26e2005-08-24 21:25:16 -05005717 if (!(priv->config & CFG_PREAMBLE_LONG))
5718 network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06005719 if (priv->capability & CAP_PRIVACY_ON)
5720 network->capability |= WLAN_CAPABILITY_PRIVACY;
5721 network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005722 memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06005723 network->rates_ex_len = priv->rates.num_rates - network->rates_len;
Jeff Garzikbf794512005-07-31 13:07:26 -04005724 memcpy(network->rates_ex,
James Ketrenos43f66a62005-03-25 12:31:53 -06005725 &priv->rates.supported_rates[network->rates_len],
5726 network->rates_ex_len);
5727 network->last_scanned = 0;
5728 network->flags = 0;
5729 network->last_associate = 0;
5730 network->time_stamp[0] = 0;
5731 network->time_stamp[1] = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005732 network->beacon_interval = 100; /* Default */
5733 network->listen_interval = 10; /* Default */
5734 network->atim_window = 0; /* Default */
James Ketrenos43f66a62005-03-25 12:31:53 -06005735 network->wpa_ie_len = 0;
5736 network->rsn_ie_len = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06005737}
5738
James Ketrenosb095c382005-08-24 22:04:42 -05005739static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
5740{
5741 struct ipw_tgi_tx_key *key;
5742 struct host_cmd cmd = {
5743 .cmd = IPW_CMD_TGI_TX_KEY,
5744 .len = sizeof(*key)
5745 };
5746
5747 if (!(priv->ieee->sec.flags & (1 << index)))
5748 return;
5749
5750 key = (struct ipw_tgi_tx_key *)&cmd.param;
5751 key->key_id = index;
5752 memcpy(key->key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
5753 key->security_type = type;
5754 key->station_index = 0; /* always 0 for BSS */
5755 key->flags = 0;
5756 /* 0 for new key; previous value of counter (after fatal error) */
5757 key->tx_counter[0] = 0;
5758 key->tx_counter[1] = 0;
5759
James Ketrenos9ddf84f2005-08-16 17:07:11 -05005760 ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05005761}
5762
5763static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
James Ketrenos43f66a62005-03-25 12:31:53 -06005764{
5765 struct ipw_wep_key *key;
5766 int i;
5767 struct host_cmd cmd = {
5768 .cmd = IPW_CMD_WEP_KEY,
5769 .len = sizeof(*key)
5770 };
5771
5772 key = (struct ipw_wep_key *)&cmd.param;
5773 key->cmd_id = DINO_CMD_WEP_KEY;
5774 key->seq_num = 0;
5775
James Ketrenosb095c382005-08-24 22:04:42 -05005776 /* Note: AES keys cannot be set for multiple times.
5777 * Only set it at the first time. */
Jeff Garzikbf794512005-07-31 13:07:26 -04005778 for (i = 0; i < 4; i++) {
James Ketrenosb095c382005-08-24 22:04:42 -05005779 key->key_index = i | type;
5780 if (!(priv->ieee->sec.flags & (1 << i))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005781 key->key_size = 0;
James Ketrenosb095c382005-08-24 22:04:42 -05005782 continue;
James Ketrenos43f66a62005-03-25 12:31:53 -06005783 }
5784
James Ketrenosb095c382005-08-24 22:04:42 -05005785 key->key_size = priv->ieee->sec.key_sizes[i];
5786 memcpy(key->key, priv->ieee->sec.keys[i], key->key_size);
5787
James Ketrenos9ddf84f2005-08-16 17:07:11 -05005788 ipw_send_cmd(priv, &cmd);
Jeff Garzikbf794512005-07-31 13:07:26 -04005789 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005790}
5791
Zhu Yi1fbfea52005-08-05 17:22:56 +08005792static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level)
5793{
5794 if (priv->ieee->host_encrypt)
5795 return;
5796
5797 switch (level) {
5798 case SEC_LEVEL_3:
5799 priv->sys_config.disable_unicast_decryption = 0;
5800 priv->ieee->host_decrypt = 0;
5801 break;
5802 case SEC_LEVEL_2:
5803 priv->sys_config.disable_unicast_decryption = 1;
5804 priv->ieee->host_decrypt = 1;
5805 break;
5806 case SEC_LEVEL_1:
5807 priv->sys_config.disable_unicast_decryption = 0;
5808 priv->ieee->host_decrypt = 0;
5809 break;
5810 case SEC_LEVEL_0:
5811 priv->sys_config.disable_unicast_decryption = 1;
5812 break;
5813 default:
5814 break;
5815 }
5816}
5817
5818static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level)
5819{
5820 if (priv->ieee->host_encrypt)
5821 return;
5822
5823 switch (level) {
5824 case SEC_LEVEL_3:
5825 priv->sys_config.disable_multicast_decryption = 0;
5826 break;
5827 case SEC_LEVEL_2:
5828 priv->sys_config.disable_multicast_decryption = 1;
5829 break;
5830 case SEC_LEVEL_1:
5831 priv->sys_config.disable_multicast_decryption = 0;
5832 break;
5833 case SEC_LEVEL_0:
5834 priv->sys_config.disable_multicast_decryption = 1;
5835 break;
5836 default:
5837 break;
5838 }
5839}
5840
James Ketrenosb095c382005-08-24 22:04:42 -05005841static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
5842{
5843 switch (priv->ieee->sec.level) {
5844 case SEC_LEVEL_3:
Zhu Yid8bad6d2005-07-13 12:25:38 -05005845 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
5846 ipw_send_tgi_tx_key(priv,
5847 DCT_FLAG_EXT_SECURITY_CCM,
5848 priv->ieee->sec.active_key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05005849
Hong Liu567deaf2005-08-31 18:07:22 +08005850 if (!priv->ieee->host_mc_decrypt)
5851 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
James Ketrenosb095c382005-08-24 22:04:42 -05005852 break;
5853 case SEC_LEVEL_2:
Zhu Yid8bad6d2005-07-13 12:25:38 -05005854 if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
5855 ipw_send_tgi_tx_key(priv,
5856 DCT_FLAG_EXT_SECURITY_TKIP,
5857 priv->ieee->sec.active_key);
James Ketrenosb095c382005-08-24 22:04:42 -05005858 break;
5859 case SEC_LEVEL_1:
5860 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
Hong Liu29cb8432005-09-12 10:43:33 -05005861 ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
5862 ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
James Ketrenosb095c382005-08-24 22:04:42 -05005863 break;
5864 case SEC_LEVEL_0:
5865 default:
5866 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06005867 }
5868}
5869
5870static void ipw_adhoc_check(void *data)
5871{
5872 struct ipw_priv *priv = data;
Jeff Garzikbf794512005-07-31 13:07:26 -04005873
James Ketrenosafbf30a2005-08-25 00:05:33 -05005874 if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold &&
James Ketrenos43f66a62005-03-25 12:31:53 -06005875 !(priv->config & CFG_ADHOC_PERSIST)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05005876 IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
5877 IPW_DL_STATE | IPW_DL_ASSOC,
5878 "Missed beacon: %d - disassociate\n",
5879 priv->missed_adhoc_beacons);
James Ketrenos43f66a62005-03-25 12:31:53 -06005880 ipw_remove_current_network(priv);
5881 ipw_disassociate(priv);
5882 return;
5883 }
5884
Jeff Garzikbf794512005-07-31 13:07:26 -04005885 queue_delayed_work(priv->workqueue, &priv->adhoc_check,
James Ketrenos43f66a62005-03-25 12:31:53 -06005886 priv->assoc_request.beacon_interval);
5887}
5888
James Ketrenosc848d0a2005-08-24 21:56:24 -05005889static void ipw_bg_adhoc_check(void *data)
5890{
5891 struct ipw_priv *priv = data;
5892 down(&priv->sem);
5893 ipw_adhoc_check(data);
5894 up(&priv->sem);
5895}
5896
Brice Goglin0f52bf92005-12-01 01:41:46 -08005897#ifdef CONFIG_IPW2200_DEBUG
James Ketrenos43f66a62005-03-25 12:31:53 -06005898static void ipw_debug_config(struct ipw_priv *priv)
5899{
5900 IPW_DEBUG_INFO("Scan completed, no valid APs matched "
5901 "[CFG 0x%08X]\n", priv->config);
5902 if (priv->config & CFG_STATIC_CHANNEL)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005903 IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06005904 else
5905 IPW_DEBUG_INFO("Channel unlocked.\n");
5906 if (priv->config & CFG_STATIC_ESSID)
Jeff Garzikbf794512005-07-31 13:07:26 -04005907 IPW_DEBUG_INFO("ESSID locked to '%s'\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005908 escape_essid(priv->essid, priv->essid_len));
James Ketrenos43f66a62005-03-25 12:31:53 -06005909 else
5910 IPW_DEBUG_INFO("ESSID unlocked.\n");
5911 if (priv->config & CFG_STATIC_BSSID)
James Ketrenosea2b26e2005-08-24 21:25:16 -05005912 IPW_DEBUG_INFO("BSSID locked to " MAC_FMT "\n",
5913 MAC_ARG(priv->bssid));
James Ketrenos43f66a62005-03-25 12:31:53 -06005914 else
5915 IPW_DEBUG_INFO("BSSID unlocked.\n");
5916 if (priv->capability & CAP_PRIVACY_ON)
5917 IPW_DEBUG_INFO("PRIVACY on\n");
5918 else
5919 IPW_DEBUG_INFO("PRIVACY off\n");
5920 IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
5921}
5922#else
Jiri Benc8d45ff72005-08-25 20:09:39 -04005923#define ipw_debug_config(x) do {} while (0)
James Ketrenos43f66a62005-03-25 12:31:53 -06005924#endif
5925
Arjan van de Ven858119e2006-01-14 13:20:43 -08005926static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode)
James Ketrenos43f66a62005-03-25 12:31:53 -06005927{
5928 /* TODO: Verify that this works... */
5929 struct ipw_fixed_rate fr = {
5930 .tx_rates = priv->rates_mask
5931 };
5932 u32 reg;
5933 u16 mask = 0;
5934
Jeff Garzikbf794512005-07-31 13:07:26 -04005935 /* Identify 'current FW band' and match it with the fixed
James Ketrenos43f66a62005-03-25 12:31:53 -06005936 * Tx rates */
Jeff Garzikbf794512005-07-31 13:07:26 -04005937
James Ketrenos43f66a62005-03-25 12:31:53 -06005938 switch (priv->ieee->freq_band) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005939 case IEEE80211_52GHZ_BAND: /* A only */
James Ketrenos43f66a62005-03-25 12:31:53 -06005940 /* IEEE_A */
5941 if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
5942 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005943 IPW_DEBUG_WX
5944 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005945 fr.tx_rates = 0;
5946 break;
5947 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005948
James Ketrenos43f66a62005-03-25 12:31:53 -06005949 fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
5950 break;
5951
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005952 default: /* 2.4Ghz or Mixed */
James Ketrenos43f66a62005-03-25 12:31:53 -06005953 /* IEEE_B */
James Ketrenosb095c382005-08-24 22:04:42 -05005954 if (mode == IEEE_B) {
James Ketrenos43f66a62005-03-25 12:31:53 -06005955 if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
5956 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005957 IPW_DEBUG_WX
5958 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005959 fr.tx_rates = 0;
5960 }
5961 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04005962 }
James Ketrenos43f66a62005-03-25 12:31:53 -06005963
5964 /* IEEE_G */
5965 if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
5966 IEEE80211_OFDM_RATES_MASK)) {
5967 /* Invalid fixed rate mask */
James Ketrenosea2b26e2005-08-24 21:25:16 -05005968 IPW_DEBUG_WX
5969 ("invalid fixed rate mask in ipw_set_fixed_rate\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06005970 fr.tx_rates = 0;
5971 break;
5972 }
5973
5974 if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
5975 mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
5976 fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
5977 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005978
James Ketrenos43f66a62005-03-25 12:31:53 -06005979 if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
5980 mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
5981 fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
5982 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005983
James Ketrenos43f66a62005-03-25 12:31:53 -06005984 if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
5985 mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
5986 fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
5987 }
Jeff Garzikbf794512005-07-31 13:07:26 -04005988
James Ketrenos43f66a62005-03-25 12:31:53 -06005989 fr.tx_rates |= mask;
5990 break;
5991 }
5992
5993 reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04005994 ipw_write_reg32(priv, reg, *(u32 *) & fr);
James Ketrenos43f66a62005-03-25 12:31:53 -06005995}
5996
James Ketrenosea2b26e2005-08-24 21:25:16 -05005997static void ipw_abort_scan(struct ipw_priv *priv)
5998{
5999 int err;
6000
6001 if (priv->status & STATUS_SCAN_ABORTING) {
6002 IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
6003 return;
6004 }
6005 priv->status |= STATUS_SCAN_ABORTING;
6006
6007 err = ipw_send_scan_abort(priv);
6008 if (err)
6009 IPW_DEBUG_HC("Request to abort scan failed.\n");
6010}
6011
James Ketrenosafbf30a2005-08-25 00:05:33 -05006012static void ipw_add_scan_channels(struct ipw_priv *priv,
6013 struct ipw_scan_request_ext *scan,
6014 int scan_type)
6015{
6016 int channel_index = 0;
6017 const struct ieee80211_geo *geo;
6018 int i;
6019
Liu Hong1fe0adb2005-08-19 09:33:10 -05006020 geo = ipw_get_geo(priv->ieee);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006021
6022 if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
6023 int start = channel_index;
6024 for (i = 0; i < geo->a_channels; i++) {
6025 if ((priv->status & STATUS_ASSOCIATED) &&
6026 geo->a[i].channel == priv->channel)
6027 continue;
6028 channel_index++;
6029 scan->channels_list[channel_index] = geo->a[i].channel;
Liu Hong1fe0adb2005-08-19 09:33:10 -05006030 ipw_set_scan_type(scan, channel_index,
6031 geo->a[i].
6032 flags & IEEE80211_CH_PASSIVE_ONLY ?
6033 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN :
6034 scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006035 }
6036
6037 if (start != channel_index) {
6038 scan->channels_list[start] = (u8) (IPW_A_MODE << 6) |
6039 (channel_index - start);
6040 channel_index++;
6041 }
6042 }
6043
6044 if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
6045 int start = channel_index;
6046 if (priv->config & CFG_SPEED_SCAN) {
Liu Hong1fe0adb2005-08-19 09:33:10 -05006047 int index;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006048 u8 channels[IEEE80211_24GHZ_CHANNELS] = {
6049 /* nop out the list */
6050 [0] = 0
6051 };
6052
6053 u8 channel;
6054 while (channel_index < IPW_SCAN_CHANNELS) {
6055 channel =
6056 priv->speed_scan[priv->speed_scan_pos];
6057 if (channel == 0) {
6058 priv->speed_scan_pos = 0;
6059 channel = priv->speed_scan[0];
6060 }
6061 if ((priv->status & STATUS_ASSOCIATED) &&
6062 channel == priv->channel) {
6063 priv->speed_scan_pos++;
6064 continue;
6065 }
6066
6067 /* If this channel has already been
6068 * added in scan, break from loop
6069 * and this will be the first channel
6070 * in the next scan.
6071 */
6072 if (channels[channel - 1] != 0)
6073 break;
6074
6075 channels[channel - 1] = 1;
6076 priv->speed_scan_pos++;
6077 channel_index++;
6078 scan->channels_list[channel_index] = channel;
Liu Hong1fe0adb2005-08-19 09:33:10 -05006079 index =
6080 ipw_channel_to_index(priv->ieee, channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006081 ipw_set_scan_type(scan, channel_index,
Liu Hong1fe0adb2005-08-19 09:33:10 -05006082 geo->bg[index].
6083 flags &
6084 IEEE80211_CH_PASSIVE_ONLY ?
6085 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
6086 : scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006087 }
6088 } else {
6089 for (i = 0; i < geo->bg_channels; i++) {
6090 if ((priv->status & STATUS_ASSOCIATED) &&
6091 geo->bg[i].channel == priv->channel)
6092 continue;
6093 channel_index++;
6094 scan->channels_list[channel_index] =
6095 geo->bg[i].channel;
6096 ipw_set_scan_type(scan, channel_index,
Liu Hong1fe0adb2005-08-19 09:33:10 -05006097 geo->bg[i].
6098 flags &
6099 IEEE80211_CH_PASSIVE_ONLY ?
6100 IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
6101 : scan_type);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006102 }
6103 }
6104
6105 if (start != channel_index) {
6106 scan->channels_list[start] = (u8) (IPW_B_MODE << 6) |
6107 (channel_index - start);
6108 }
6109 }
6110}
6111
James Ketrenosea2b26e2005-08-24 21:25:16 -05006112static int ipw_request_scan(struct ipw_priv *priv)
6113{
6114 struct ipw_scan_request_ext scan;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006115 int err = 0, scan_type;
6116
6117 if (!(priv->status & STATUS_INIT) ||
6118 (priv->status & STATUS_EXIT_PENDING))
6119 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006120
James Ketrenosb095c382005-08-24 22:04:42 -05006121 down(&priv->sem);
6122
James Ketrenosea2b26e2005-08-24 21:25:16 -05006123 if (priv->status & STATUS_SCANNING) {
James Ketrenosa613bff2005-08-24 21:43:11 -05006124 IPW_DEBUG_HC("Concurrent scan requested. Ignoring.\n");
James Ketrenosea2b26e2005-08-24 21:25:16 -05006125 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006126 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006127 }
6128
James Ketrenosafbf30a2005-08-25 00:05:33 -05006129 if (!(priv->status & STATUS_SCAN_FORCED) &&
6130 priv->status & STATUS_SCAN_ABORTING) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006131 IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
6132 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006133 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006134 }
6135
6136 if (priv->status & STATUS_RF_KILL_MASK) {
6137 IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
6138 priv->status |= STATUS_SCAN_PENDING;
James Ketrenosb095c382005-08-24 22:04:42 -05006139 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006140 }
6141
6142 memset(&scan, 0, sizeof(scan));
6143
James Ketrenosb095c382005-08-24 22:04:42 -05006144 if (priv->config & CFG_SPEED_SCAN)
6145 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
6146 cpu_to_le16(30);
6147 else
6148 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
6149 cpu_to_le16(20);
6150
James Ketrenosa613bff2005-08-24 21:43:11 -05006151 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
6152 cpu_to_le16(20);
Liu Hong1fe0adb2005-08-19 09:33:10 -05006153 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006154
James Ketrenosa613bff2005-08-24 21:43:11 -05006155 scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
James Ketrenosea2b26e2005-08-24 21:25:16 -05006156
James Ketrenosb095c382005-08-24 22:04:42 -05006157#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05006158 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006159 u8 channel;
James Ketrenosb095c382005-08-24 22:04:42 -05006160 u8 band = 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006161
Liu Hong1fe0adb2005-08-19 09:33:10 -05006162 switch (ipw_is_valid_channel(priv->ieee, priv->channel)) {
James Ketrenosb095c382005-08-24 22:04:42 -05006163 case IEEE80211_52GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006164 band = (u8) (IPW_A_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006165 channel = priv->channel;
6166 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006167
James Ketrenosb095c382005-08-24 22:04:42 -05006168 case IEEE80211_24GHZ_BAND:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006169 band = (u8) (IPW_B_MODE << 6) | 1;
James Ketrenosb095c382005-08-24 22:04:42 -05006170 channel = priv->channel;
6171 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006172
James Ketrenosb095c382005-08-24 22:04:42 -05006173 default:
James Ketrenosea2b26e2005-08-24 21:25:16 -05006174 band = (u8) (IPW_B_MODE << 6) | 1;
6175 channel = 9;
James Ketrenosb095c382005-08-24 22:04:42 -05006176 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006177 }
6178
James Ketrenosb095c382005-08-24 22:04:42 -05006179 scan.channels_list[0] = band;
6180 scan.channels_list[1] = channel;
6181 ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006182
James Ketrenosb095c382005-08-24 22:04:42 -05006183 /* NOTE: The card will sit on this channel for this time
6184 * period. Scan aborts are timing sensitive and frequently
6185 * result in firmware restarts. As such, it is best to
6186 * set a small dwell_time here and just keep re-issuing
6187 * scans. Otherwise fast channel hopping will not actually
6188 * hop channels.
6189 *
6190 * TODO: Move SPEED SCAN support to all modes and bands */
James Ketrenosa613bff2005-08-24 21:43:11 -05006191 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
6192 cpu_to_le16(2000);
James Ketrenosea2b26e2005-08-24 21:25:16 -05006193 } else {
James Ketrenosb095c382005-08-24 22:04:42 -05006194#endif /* CONFIG_IPW2200_MONITOR */
6195 /* If we are roaming, then make this a directed scan for the
6196 * current network. Otherwise, ensure that every other scan
6197 * is a fast channel hop scan */
6198 if ((priv->status & STATUS_ROAMING)
6199 || (!(priv->status & STATUS_ASSOCIATED)
6200 && (priv->config & CFG_STATIC_ESSID)
6201 && (le32_to_cpu(scan.full_scan_index) % 2))) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006202 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
6203 if (err) {
James Ketrenosb095c382005-08-24 22:04:42 -05006204 IPW_DEBUG_HC("Attempt to send SSID command "
6205 "failed.\n");
6206 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006207 }
6208
6209 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006210 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05006211 scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006212
James Ketrenosafbf30a2005-08-25 00:05:33 -05006213 ipw_add_scan_channels(priv, &scan, scan_type);
James Ketrenosb095c382005-08-24 22:04:42 -05006214#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05006215 }
6216#endif
6217
6218 err = ipw_send_scan_request_ext(priv, &scan);
6219 if (err) {
6220 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
James Ketrenosb095c382005-08-24 22:04:42 -05006221 goto done;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006222 }
6223
6224 priv->status |= STATUS_SCANNING;
6225 priv->status &= ~STATUS_SCAN_PENDING;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006226 queue_delayed_work(priv->workqueue, &priv->scan_check,
6227 IPW_SCAN_CHECK_WATCHDOG);
James Ketrenosb095c382005-08-24 22:04:42 -05006228 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05006229 up(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05006230 return err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05006231}
6232
6233static void ipw_bg_abort_scan(void *data)
6234{
6235 struct ipw_priv *priv = data;
6236 down(&priv->sem);
6237 ipw_abort_scan(data);
6238 up(&priv->sem);
6239}
6240
James Ketrenosea2b26e2005-08-24 21:25:16 -05006241static int ipw_wpa_enable(struct ipw_priv *priv, int value)
6242{
James Ketrenosb095c382005-08-24 22:04:42 -05006243 /* This is called when wpa_supplicant loads and closes the driver
6244 * interface. */
Hong Liucdd1fa12005-08-31 18:14:27 +08006245 priv->ieee->wpa_enabled = value;
James Ketrenosb095c382005-08-24 22:04:42 -05006246 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006247}
6248
James Ketrenosea2b26e2005-08-24 21:25:16 -05006249static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
6250{
6251 struct ieee80211_device *ieee = priv->ieee;
6252 struct ieee80211_security sec = {
6253 .flags = SEC_AUTH_MODE,
6254 };
6255 int ret = 0;
6256
James Ketrenosafbf30a2005-08-25 00:05:33 -05006257 if (value & IW_AUTH_ALG_SHARED_KEY) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006258 sec.auth_mode = WLAN_AUTH_SHARED_KEY;
6259 ieee->open_wep = 0;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006260 } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05006261 sec.auth_mode = WLAN_AUTH_OPEN;
6262 ieee->open_wep = 1;
Zhu Yi3e234b42006-01-24 16:36:52 +08006263 } else if (value & IW_AUTH_ALG_LEAP) {
6264 sec.auth_mode = WLAN_AUTH_LEAP;
6265 ieee->open_wep = 1;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006266 } else
6267 return -EINVAL;
James Ketrenosea2b26e2005-08-24 21:25:16 -05006268
6269 if (ieee->set_security)
6270 ieee->set_security(ieee->dev, &sec);
6271 else
6272 ret = -EOPNOTSUPP;
6273
6274 return ret;
6275}
6276
Adrian Bunka73e22b2006-01-21 01:39:42 +01006277static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie,
6278 int wpa_ie_len)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006279{
6280 /* make sure WPA is enabled */
6281 ipw_wpa_enable(priv, 1);
6282
6283 ipw_disassociate(priv);
6284}
6285
6286static int ipw_set_rsn_capa(struct ipw_priv *priv,
6287 char *capabilities, int length)
6288{
6289 struct host_cmd cmd = {
6290 .cmd = IPW_CMD_RSN_CAPABILITIES,
6291 .len = length,
6292 };
6293
6294 IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
6295
6296 memcpy(cmd.param, capabilities, length);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05006297 return ipw_send_cmd(priv, &cmd);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006298}
6299
James Ketrenosafbf30a2005-08-25 00:05:33 -05006300/*
6301 * WE-18 support
6302 */
6303
6304/* SIOCSIWGENIE */
6305static int ipw_wx_set_genie(struct net_device *dev,
6306 struct iw_request_info *info,
6307 union iwreq_data *wrqu, char *extra)
6308{
6309 struct ipw_priv *priv = ieee80211_priv(dev);
6310 struct ieee80211_device *ieee = priv->ieee;
6311 u8 *buf;
6312 int err = 0;
6313
6314 if (wrqu->data.length > MAX_WPA_IE_LEN ||
6315 (wrqu->data.length && extra == NULL))
6316 return -EINVAL;
6317
6318 //down(&priv->sem);
6319
6320 //if (!ieee->wpa_enabled) {
6321 // err = -EOPNOTSUPP;
6322 // goto out;
6323 //}
6324
6325 if (wrqu->data.length) {
6326 buf = kmalloc(wrqu->data.length, GFP_KERNEL);
6327 if (buf == NULL) {
6328 err = -ENOMEM;
6329 goto out;
6330 }
6331
6332 memcpy(buf, extra, wrqu->data.length);
6333 kfree(ieee->wpa_ie);
6334 ieee->wpa_ie = buf;
6335 ieee->wpa_ie_len = wrqu->data.length;
6336 } else {
6337 kfree(ieee->wpa_ie);
6338 ieee->wpa_ie = NULL;
6339 ieee->wpa_ie_len = 0;
6340 }
6341
6342 ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
6343 out:
6344 //up(&priv->sem);
6345 return err;
6346}
6347
6348/* SIOCGIWGENIE */
6349static int ipw_wx_get_genie(struct net_device *dev,
6350 struct iw_request_info *info,
6351 union iwreq_data *wrqu, char *extra)
6352{
6353 struct ipw_priv *priv = ieee80211_priv(dev);
6354 struct ieee80211_device *ieee = priv->ieee;
6355 int err = 0;
6356
6357 //down(&priv->sem);
6358
6359 //if (!ieee->wpa_enabled) {
6360 // err = -EOPNOTSUPP;
6361 // goto out;
6362 //}
6363
6364 if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
6365 wrqu->data.length = 0;
6366 goto out;
6367 }
6368
6369 if (wrqu->data.length < ieee->wpa_ie_len) {
6370 err = -E2BIG;
6371 goto out;
6372 }
6373
6374 wrqu->data.length = ieee->wpa_ie_len;
6375 memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
6376
6377 out:
6378 //up(&priv->sem);
6379 return err;
6380}
6381
Zhu Yi1fbfea52005-08-05 17:22:56 +08006382static int wext_cipher2level(int cipher)
6383{
6384 switch (cipher) {
6385 case IW_AUTH_CIPHER_NONE:
6386 return SEC_LEVEL_0;
6387 case IW_AUTH_CIPHER_WEP40:
6388 case IW_AUTH_CIPHER_WEP104:
6389 return SEC_LEVEL_1;
6390 case IW_AUTH_CIPHER_TKIP:
6391 return SEC_LEVEL_2;
6392 case IW_AUTH_CIPHER_CCMP:
6393 return SEC_LEVEL_3;
6394 default:
6395 return -1;
6396 }
6397}
6398
James Ketrenosafbf30a2005-08-25 00:05:33 -05006399/* SIOCSIWAUTH */
6400static int ipw_wx_set_auth(struct net_device *dev,
6401 struct iw_request_info *info,
6402 union iwreq_data *wrqu, char *extra)
6403{
6404 struct ipw_priv *priv = ieee80211_priv(dev);
6405 struct ieee80211_device *ieee = priv->ieee;
6406 struct iw_param *param = &wrqu->param;
6407 struct ieee80211_crypt_data *crypt;
6408 unsigned long flags;
6409 int ret = 0;
6410
6411 switch (param->flags & IW_AUTH_INDEX) {
6412 case IW_AUTH_WPA_VERSION:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006413 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006414 case IW_AUTH_CIPHER_PAIRWISE:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006415 ipw_set_hw_decrypt_unicast(priv,
6416 wext_cipher2level(param->value));
6417 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006418 case IW_AUTH_CIPHER_GROUP:
Zhu Yi1fbfea52005-08-05 17:22:56 +08006419 ipw_set_hw_decrypt_multicast(priv,
6420 wext_cipher2level(param->value));
6421 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006422 case IW_AUTH_KEY_MGMT:
6423 /*
6424 * ipw2200 does not use these parameters
6425 */
6426 break;
6427
6428 case IW_AUTH_TKIP_COUNTERMEASURES:
6429 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
James Ketrenos991d1cc2005-10-13 09:26:48 +00006430 if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006431 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006432
6433 flags = crypt->ops->get_flags(crypt->priv);
6434
6435 if (param->value)
6436 flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6437 else
6438 flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
6439
6440 crypt->ops->set_flags(flags, crypt->priv);
6441
6442 break;
6443
6444 case IW_AUTH_DROP_UNENCRYPTED:{
6445 /* HACK:
6446 *
6447 * wpa_supplicant calls set_wpa_enabled when the driver
6448 * is loaded and unloaded, regardless of if WPA is being
6449 * used. No other calls are made which can be used to
6450 * determine if encryption will be used or not prior to
6451 * association being expected. If encryption is not being
6452 * used, drop_unencrypted is set to false, else true -- we
6453 * can use this to determine if the CAP_PRIVACY_ON bit should
6454 * be set.
6455 */
6456 struct ieee80211_security sec = {
6457 .flags = SEC_ENABLED,
6458 .enabled = param->value,
6459 };
6460 priv->ieee->drop_unencrypted = param->value;
6461 /* We only change SEC_LEVEL for open mode. Others
6462 * are set by ipw_wpa_set_encryption.
6463 */
6464 if (!param->value) {
6465 sec.flags |= SEC_LEVEL;
6466 sec.level = SEC_LEVEL_0;
6467 } else {
6468 sec.flags |= SEC_LEVEL;
6469 sec.level = SEC_LEVEL_1;
6470 }
6471 if (priv->ieee->set_security)
6472 priv->ieee->set_security(priv->ieee->dev, &sec);
6473 break;
6474 }
6475
6476 case IW_AUTH_80211_AUTH_ALG:
6477 ret = ipw_wpa_set_auth_algs(priv, param->value);
6478 break;
6479
6480 case IW_AUTH_WPA_ENABLED:
6481 ret = ipw_wpa_enable(priv, param->value);
6482 break;
6483
6484 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6485 ieee->ieee802_1x = param->value;
6486 break;
6487
6488 //case IW_AUTH_ROAMING_CONTROL:
6489 case IW_AUTH_PRIVACY_INVOKED:
6490 ieee->privacy_invoked = param->value;
6491 break;
6492
6493 default:
6494 return -EOPNOTSUPP;
6495 }
6496 return ret;
6497}
6498
6499/* SIOCGIWAUTH */
6500static int ipw_wx_get_auth(struct net_device *dev,
6501 struct iw_request_info *info,
6502 union iwreq_data *wrqu, char *extra)
6503{
6504 struct ipw_priv *priv = ieee80211_priv(dev);
6505 struct ieee80211_device *ieee = priv->ieee;
6506 struct ieee80211_crypt_data *crypt;
6507 struct iw_param *param = &wrqu->param;
6508 int ret = 0;
6509
6510 switch (param->flags & IW_AUTH_INDEX) {
6511 case IW_AUTH_WPA_VERSION:
6512 case IW_AUTH_CIPHER_PAIRWISE:
6513 case IW_AUTH_CIPHER_GROUP:
6514 case IW_AUTH_KEY_MGMT:
6515 /*
6516 * wpa_supplicant will control these internally
6517 */
6518 ret = -EOPNOTSUPP;
6519 break;
6520
6521 case IW_AUTH_TKIP_COUNTERMEASURES:
6522 crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
James Ketrenos991d1cc2005-10-13 09:26:48 +00006523 if (!crypt || !crypt->ops->get_flags)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006524 break;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006525
6526 param->value = (crypt->ops->get_flags(crypt->priv) &
6527 IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
6528
6529 break;
6530
6531 case IW_AUTH_DROP_UNENCRYPTED:
6532 param->value = ieee->drop_unencrypted;
6533 break;
6534
6535 case IW_AUTH_80211_AUTH_ALG:
6536 param->value = ieee->sec.auth_mode;
6537 break;
6538
6539 case IW_AUTH_WPA_ENABLED:
6540 param->value = ieee->wpa_enabled;
6541 break;
6542
6543 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
6544 param->value = ieee->ieee802_1x;
6545 break;
6546
6547 case IW_AUTH_ROAMING_CONTROL:
6548 case IW_AUTH_PRIVACY_INVOKED:
6549 param->value = ieee->privacy_invoked;
6550 break;
6551
6552 default:
6553 return -EOPNOTSUPP;
6554 }
6555 return 0;
6556}
6557
6558/* SIOCSIWENCODEEXT */
6559static int ipw_wx_set_encodeext(struct net_device *dev,
6560 struct iw_request_info *info,
6561 union iwreq_data *wrqu, char *extra)
6562{
6563 struct ipw_priv *priv = ieee80211_priv(dev);
6564 struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
6565
6566 if (hwcrypto) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006567 if (ext->alg == IW_ENCODE_ALG_TKIP) {
Hong Liu567deaf2005-08-31 18:07:22 +08006568 /* IPW HW can't build TKIP MIC,
6569 host decryption still needed */
6570 if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
6571 priv->ieee->host_mc_decrypt = 1;
6572 else {
6573 priv->ieee->host_encrypt = 0;
6574 priv->ieee->host_encrypt_msdu = 1;
6575 priv->ieee->host_decrypt = 1;
6576 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006577 } else {
6578 priv->ieee->host_encrypt = 0;
6579 priv->ieee->host_encrypt_msdu = 0;
6580 priv->ieee->host_decrypt = 0;
Hong Liu567deaf2005-08-31 18:07:22 +08006581 priv->ieee->host_mc_decrypt = 0;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006582 }
6583 }
6584
6585 return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra);
6586}
6587
6588/* SIOCGIWENCODEEXT */
6589static int ipw_wx_get_encodeext(struct net_device *dev,
6590 struct iw_request_info *info,
6591 union iwreq_data *wrqu, char *extra)
6592{
6593 struct ipw_priv *priv = ieee80211_priv(dev);
6594 return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra);
6595}
6596
6597/* SIOCSIWMLME */
6598static int ipw_wx_set_mlme(struct net_device *dev,
6599 struct iw_request_info *info,
6600 union iwreq_data *wrqu, char *extra)
6601{
6602 struct ipw_priv *priv = ieee80211_priv(dev);
6603 struct iw_mlme *mlme = (struct iw_mlme *)extra;
6604 u16 reason;
6605
6606 reason = cpu_to_le16(mlme->reason_code);
6607
6608 switch (mlme->cmd) {
6609 case IW_MLME_DEAUTH:
6610 // silently ignore
6611 break;
6612
6613 case IW_MLME_DISASSOC:
6614 ipw_disassociate(priv);
6615 break;
6616
6617 default:
6618 return -EOPNOTSUPP;
6619 }
6620 return 0;
6621}
James Ketrenosea2b26e2005-08-24 21:25:16 -05006622
James Ketrenosb095c382005-08-24 22:04:42 -05006623#ifdef CONFIG_IPW_QOS
6624
6625/* QoS */
6626/*
6627* get the modulation type of the current network or
6628* the card current mode
6629*/
6630u8 ipw_qos_current_mode(struct ipw_priv * priv)
6631{
6632 u8 mode = 0;
6633
6634 if (priv->status & STATUS_ASSOCIATED) {
6635 unsigned long flags;
6636
6637 spin_lock_irqsave(&priv->ieee->lock, flags);
6638 mode = priv->assoc_network->mode;
6639 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6640 } else {
6641 mode = priv->ieee->mode;
6642 }
6643 IPW_DEBUG_QOS("QoS network/card mode %d \n", mode);
6644 return mode;
6645}
6646
6647/*
6648* Handle management frame beacon and probe response
6649*/
James Ketrenos3b9990c2005-08-19 13:18:55 -05006650static int ipw_qos_handle_probe_response(struct ipw_priv *priv,
6651 int active_network,
6652 struct ieee80211_network *network)
James Ketrenosb095c382005-08-24 22:04:42 -05006653{
6654 u32 size = sizeof(struct ieee80211_qos_parameters);
6655
James Ketrenosafbf30a2005-08-25 00:05:33 -05006656 if (network->capability & WLAN_CAPABILITY_IBSS)
James Ketrenosb095c382005-08-24 22:04:42 -05006657 network->qos_data.active = network->qos_data.supported;
6658
6659 if (network->flags & NETWORK_HAS_QOS_MASK) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006660 if (active_network &&
6661 (network->flags & NETWORK_HAS_QOS_PARAMETERS))
James Ketrenosb095c382005-08-24 22:04:42 -05006662 network->qos_data.active = network->qos_data.supported;
6663
6664 if ((network->qos_data.active == 1) && (active_network == 1) &&
6665 (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
6666 (network->qos_data.old_param_count !=
6667 network->qos_data.param_count)) {
6668 network->qos_data.old_param_count =
6669 network->qos_data.param_count;
6670 schedule_work(&priv->qos_activate);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006671 IPW_DEBUG_QOS("QoS parameters change call "
6672 "qos_activate\n");
James Ketrenosb095c382005-08-24 22:04:42 -05006673 }
6674 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006675 if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B))
6676 memcpy(&network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006677 &def_parameters_CCK, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006678 else
6679 memcpy(&network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006680 &def_parameters_OFDM, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006681
James Ketrenosb095c382005-08-24 22:04:42 -05006682 if ((network->qos_data.active == 1) && (active_network == 1)) {
6683 IPW_DEBUG_QOS("QoS was disabled call qos_activate \n");
6684 schedule_work(&priv->qos_activate);
6685 }
6686
6687 network->qos_data.active = 0;
6688 network->qos_data.supported = 0;
6689 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006690 if ((priv->status & STATUS_ASSOCIATED) &&
6691 (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) {
6692 if (memcmp(network->bssid, priv->bssid, ETH_ALEN))
6693 if ((network->capability & WLAN_CAPABILITY_IBSS) &&
6694 !(network->flags & NETWORK_EMPTY_ESSID))
James Ketrenosb095c382005-08-24 22:04:42 -05006695 if ((network->ssid_len ==
James Ketrenosafbf30a2005-08-25 00:05:33 -05006696 priv->assoc_network->ssid_len) &&
6697 !memcmp(network->ssid,
6698 priv->assoc_network->ssid,
6699 network->ssid_len)) {
James Ketrenosb095c382005-08-24 22:04:42 -05006700 queue_work(priv->workqueue,
6701 &priv->merge_networks);
6702 }
James Ketrenosb095c382005-08-24 22:04:42 -05006703 }
6704
6705 return 0;
6706}
6707
6708/*
6709* This function set up the firmware to support QoS. It sends
6710* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO
6711*/
6712static int ipw_qos_activate(struct ipw_priv *priv,
6713 struct ieee80211_qos_data *qos_network_data)
6714{
6715 int err;
6716 struct ieee80211_qos_parameters qos_parameters[QOS_QOS_SETS];
6717 struct ieee80211_qos_parameters *active_one = NULL;
6718 u32 size = sizeof(struct ieee80211_qos_parameters);
6719 u32 burst_duration;
6720 int i;
6721 u8 type;
6722
6723 type = ipw_qos_current_mode(priv);
6724
6725 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]);
6726 memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size);
6727 active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]);
6728 memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size);
6729
6730 if (qos_network_data == NULL) {
6731 if (type == IEEE_B) {
6732 IPW_DEBUG_QOS("QoS activate network mode %d\n", type);
6733 active_one = &def_parameters_CCK;
6734 } else
6735 active_one = &def_parameters_OFDM;
6736
James Ketrenosafbf30a2005-08-25 00:05:33 -05006737 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006738 burst_duration = ipw_qos_get_burst_duration(priv);
6739 for (i = 0; i < QOS_QUEUE_NUM; i++)
James Ketrenosafbf30a2005-08-25 00:05:33 -05006740 qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] =
6741 (u16) burst_duration;
6742 } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
James Ketrenosb095c382005-08-24 22:04:42 -05006743 if (type == IEEE_B) {
6744 IPW_DEBUG_QOS("QoS activate IBSS nework mode %d\n",
6745 type);
6746 if (priv->qos_data.qos_enable == 0)
6747 active_one = &def_parameters_CCK;
6748 else
6749 active_one = priv->qos_data.def_qos_parm_CCK;
6750 } else {
6751 if (priv->qos_data.qos_enable == 0)
6752 active_one = &def_parameters_OFDM;
6753 else
6754 active_one = priv->qos_data.def_qos_parm_OFDM;
6755 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05006756 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006757 } else {
6758 unsigned long flags;
6759 int active;
6760
6761 spin_lock_irqsave(&priv->ieee->lock, flags);
6762 active_one = &(qos_network_data->parameters);
6763 qos_network_data->old_param_count =
6764 qos_network_data->param_count;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006765 memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006766 active = qos_network_data->supported;
6767 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6768
6769 if (active == 0) {
6770 burst_duration = ipw_qos_get_burst_duration(priv);
6771 for (i = 0; i < QOS_QUEUE_NUM; i++)
6772 qos_parameters[QOS_PARAM_SET_ACTIVE].
6773 tx_op_limit[i] = (u16) burst_duration;
6774 }
6775 }
6776
6777 IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05006778 err = ipw_send_qos_params_command(priv,
6779 (struct ieee80211_qos_parameters *)
6780 &(qos_parameters[0]));
James Ketrenosb095c382005-08-24 22:04:42 -05006781 if (err)
6782 IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n");
6783
6784 return err;
6785}
6786
6787/*
6788* send IPW_CMD_WME_INFO to the firmware
6789*/
6790static int ipw_qos_set_info_element(struct ipw_priv *priv)
6791{
6792 int ret = 0;
6793 struct ieee80211_qos_information_element qos_info;
6794
6795 if (priv == NULL)
6796 return -1;
6797
6798 qos_info.elementID = QOS_ELEMENT_ID;
6799 qos_info.length = sizeof(struct ieee80211_qos_information_element) - 2;
6800
6801 qos_info.version = QOS_VERSION_1;
6802 qos_info.ac_info = 0;
6803
6804 memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN);
6805 qos_info.qui_type = QOS_OUI_TYPE;
6806 qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE;
6807
6808 ret = ipw_send_qos_info_command(priv, &qos_info);
6809 if (ret != 0) {
6810 IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n");
6811 }
6812 return ret;
6813}
6814
6815/*
6816* Set the QoS parameter with the association request structure
6817*/
6818static int ipw_qos_association(struct ipw_priv *priv,
6819 struct ieee80211_network *network)
6820{
6821 int err = 0;
6822 struct ieee80211_qos_data *qos_data = NULL;
6823 struct ieee80211_qos_data ibss_data = {
6824 .supported = 1,
6825 .active = 1,
6826 };
6827
6828 switch (priv->ieee->iw_mode) {
6829 case IW_MODE_ADHOC:
6830 if (!(network->capability & WLAN_CAPABILITY_IBSS))
6831 BUG();
6832
6833 qos_data = &ibss_data;
6834 break;
6835
6836 case IW_MODE_INFRA:
6837 qos_data = &network->qos_data;
6838 break;
6839
6840 default:
6841 BUG();
6842 break;
6843 }
6844
6845 err = ipw_qos_activate(priv, qos_data);
6846 if (err) {
6847 priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC;
6848 return err;
6849 }
6850
6851 if (priv->qos_data.qos_enable && qos_data->supported) {
6852 IPW_DEBUG_QOS("QoS will be enabled for this association\n");
6853 priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC;
6854 return ipw_qos_set_info_element(priv);
6855 }
6856
6857 return 0;
6858}
6859
6860/*
6861* handling the beaconing responces. if we get different QoS setting
6862* of the network from the the associated setting adjust the QoS
6863* setting
6864*/
6865static int ipw_qos_association_resp(struct ipw_priv *priv,
6866 struct ieee80211_network *network)
6867{
6868 int ret = 0;
6869 unsigned long flags;
6870 u32 size = sizeof(struct ieee80211_qos_parameters);
6871 int set_qos_param = 0;
6872
James Ketrenosafbf30a2005-08-25 00:05:33 -05006873 if ((priv == NULL) || (network == NULL) ||
6874 (priv->assoc_network == NULL))
James Ketrenosb095c382005-08-24 22:04:42 -05006875 return ret;
6876
6877 if (!(priv->status & STATUS_ASSOCIATED))
6878 return ret;
6879
James Ketrenosafbf30a2005-08-25 00:05:33 -05006880 if ((priv->ieee->iw_mode != IW_MODE_INFRA))
James Ketrenosb095c382005-08-24 22:04:42 -05006881 return ret;
James Ketrenosb095c382005-08-24 22:04:42 -05006882
6883 spin_lock_irqsave(&priv->ieee->lock, flags);
6884 if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006885 memcpy(&priv->assoc_network->qos_data, &network->qos_data,
James Ketrenosb095c382005-08-24 22:04:42 -05006886 sizeof(struct ieee80211_qos_data));
6887 priv->assoc_network->qos_data.active = 1;
6888 if ((network->qos_data.old_param_count !=
6889 network->qos_data.param_count)) {
6890 set_qos_param = 1;
6891 network->qos_data.old_param_count =
6892 network->qos_data.param_count;
6893 }
6894
6895 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05006896 if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B))
6897 memcpy(&priv->assoc_network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006898 &def_parameters_CCK, size);
James Ketrenosafbf30a2005-08-25 00:05:33 -05006899 else
6900 memcpy(&priv->assoc_network->qos_data.parameters,
James Ketrenosb095c382005-08-24 22:04:42 -05006901 &def_parameters_OFDM, size);
James Ketrenosb095c382005-08-24 22:04:42 -05006902 priv->assoc_network->qos_data.active = 0;
6903 priv->assoc_network->qos_data.supported = 0;
6904 set_qos_param = 1;
6905 }
6906
6907 spin_unlock_irqrestore(&priv->ieee->lock, flags);
6908
6909 if (set_qos_param == 1)
6910 schedule_work(&priv->qos_activate);
6911
6912 return ret;
6913}
6914
6915static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv)
6916{
6917 u32 ret = 0;
6918
6919 if ((priv == NULL))
6920 return 0;
6921
James Ketrenosafbf30a2005-08-25 00:05:33 -05006922 if (!(priv->ieee->modulation & IEEE80211_OFDM_MODULATION))
James Ketrenosb095c382005-08-24 22:04:42 -05006923 ret = priv->qos_data.burst_duration_CCK;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006924 else
James Ketrenosb095c382005-08-24 22:04:42 -05006925 ret = priv->qos_data.burst_duration_OFDM;
James Ketrenosafbf30a2005-08-25 00:05:33 -05006926
James Ketrenosb095c382005-08-24 22:04:42 -05006927 return ret;
6928}
6929
6930/*
6931* Initialize the setting of QoS global
6932*/
6933static void ipw_qos_init(struct ipw_priv *priv, int enable,
6934 int burst_enable, u32 burst_duration_CCK,
6935 u32 burst_duration_OFDM)
6936{
6937 priv->qos_data.qos_enable = enable;
6938
6939 if (priv->qos_data.qos_enable) {
6940 priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK;
6941 priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM;
6942 IPW_DEBUG_QOS("QoS is enabled\n");
6943 } else {
6944 priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK;
6945 priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM;
6946 IPW_DEBUG_QOS("QoS is not enabled\n");
6947 }
6948
6949 priv->qos_data.burst_enable = burst_enable;
6950
6951 if (burst_enable) {
6952 priv->qos_data.burst_duration_CCK = burst_duration_CCK;
6953 priv->qos_data.burst_duration_OFDM = burst_duration_OFDM;
6954 } else {
6955 priv->qos_data.burst_duration_CCK = 0;
6956 priv->qos_data.burst_duration_OFDM = 0;
6957 }
6958}
6959
6960/*
6961* map the packet priority to the right TX Queue
6962*/
6963static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority)
6964{
6965 if (priority > 7 || !priv->qos_data.qos_enable)
6966 priority = 0;
6967
6968 return from_priority_to_tx_queue[priority] - 1;
6969}
6970
6971/*
6972* add QoS parameter to the TX command
6973*/
6974static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
6975 u16 priority,
6976 struct tfd_data *tfd, u8 unicast)
6977{
6978 int ret = 0;
6979 int tx_queue_id = 0;
6980 struct ieee80211_qos_data *qos_data = NULL;
6981 int active, supported;
6982 unsigned long flags;
6983
6984 if (!(priv->status & STATUS_ASSOCIATED))
6985 return 0;
6986
6987 qos_data = &priv->assoc_network->qos_data;
6988
6989 spin_lock_irqsave(&priv->ieee->lock, flags);
6990
6991 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
6992 if (unicast == 0)
6993 qos_data->active = 0;
6994 else
6995 qos_data->active = qos_data->supported;
6996 }
6997
6998 active = qos_data->active;
6999 supported = qos_data->supported;
7000
7001 spin_unlock_irqrestore(&priv->ieee->lock, flags);
7002
James Ketrenosafbf30a2005-08-25 00:05:33 -05007003 IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d "
7004 "unicast %d\n",
7005 priv->qos_data.qos_enable, active, supported, unicast);
James Ketrenosb095c382005-08-24 22:04:42 -05007006 if (active && priv->qos_data.qos_enable) {
7007 ret = from_priority_to_tx_queue[priority];
7008 tx_queue_id = ret - 1;
7009 IPW_DEBUG_QOS("QoS packet priority is %d \n", priority);
7010 if (priority <= 7) {
7011 tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
7012 tfd->tfd.tfd_26.mchdr.qos_ctrl = priority;
7013 tfd->tfd.tfd_26.mchdr.frame_ctl |=
7014 IEEE80211_STYPE_QOS_DATA;
7015
7016 if (priv->qos_data.qos_no_ack_mask &
7017 (1UL << tx_queue_id)) {
7018 tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
7019 tfd->tfd.tfd_26.mchdr.qos_ctrl |=
7020 CTRL_QOS_NO_ACK;
7021 }
7022 }
7023 }
7024
7025 return ret;
7026}
7027
7028/*
7029* background support to run QoS activate functionality
7030*/
7031static void ipw_bg_qos_activate(void *data)
7032{
7033 struct ipw_priv *priv = data;
7034
7035 if (priv == NULL)
7036 return;
7037
7038 down(&priv->sem);
7039
7040 if (priv->status & STATUS_ASSOCIATED)
7041 ipw_qos_activate(priv, &(priv->assoc_network->qos_data));
7042
7043 up(&priv->sem);
7044}
7045
James Ketrenos3b9990c2005-08-19 13:18:55 -05007046static int ipw_handle_probe_response(struct net_device *dev,
7047 struct ieee80211_probe_response *resp,
7048 struct ieee80211_network *network)
James Ketrenosb095c382005-08-24 22:04:42 -05007049{
7050 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos3b9990c2005-08-19 13:18:55 -05007051 int active_network = ((priv->status & STATUS_ASSOCIATED) &&
7052 (network == priv->assoc_network));
James Ketrenosb095c382005-08-24 22:04:42 -05007053
James Ketrenos3b9990c2005-08-19 13:18:55 -05007054 ipw_qos_handle_probe_response(priv, active_network, network);
James Ketrenosb095c382005-08-24 22:04:42 -05007055
James Ketrenos3b9990c2005-08-19 13:18:55 -05007056 return 0;
7057}
James Ketrenosb095c382005-08-24 22:04:42 -05007058
James Ketrenos3b9990c2005-08-19 13:18:55 -05007059static int ipw_handle_beacon(struct net_device *dev,
7060 struct ieee80211_beacon *resp,
7061 struct ieee80211_network *network)
7062{
7063 struct ipw_priv *priv = ieee80211_priv(dev);
7064 int active_network = ((priv->status & STATUS_ASSOCIATED) &&
7065 (network == priv->assoc_network));
7066
7067 ipw_qos_handle_probe_response(priv, active_network, network);
7068
7069 return 0;
7070}
7071
7072static int ipw_handle_assoc_response(struct net_device *dev,
7073 struct ieee80211_assoc_response *resp,
7074 struct ieee80211_network *network)
7075{
7076 struct ipw_priv *priv = ieee80211_priv(dev);
7077 ipw_qos_association_resp(priv, network);
James Ketrenosb095c382005-08-24 22:04:42 -05007078 return 0;
7079}
7080
7081static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
7082 *qos_param)
7083{
7084 struct host_cmd cmd = {
7085 .cmd = IPW_CMD_QOS_PARAMETERS,
7086 .len = (sizeof(struct ieee80211_qos_parameters) * 3)
7087 };
7088
James Ketrenosafbf30a2005-08-25 00:05:33 -05007089 memcpy(cmd.param, qos_param, sizeof(*qos_param) * 3);
James Ketrenos9ddf84f2005-08-16 17:07:11 -05007090 return ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05007091}
7092
7093static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
7094 *qos_param)
7095{
7096 struct host_cmd cmd = {
7097 .cmd = IPW_CMD_WME_INFO,
7098 .len = sizeof(*qos_param)
7099 };
7100
James Ketrenosafbf30a2005-08-25 00:05:33 -05007101 memcpy(cmd.param, qos_param, sizeof(*qos_param));
James Ketrenos9ddf84f2005-08-16 17:07:11 -05007102 return ipw_send_cmd(priv, &cmd);
James Ketrenosb095c382005-08-24 22:04:42 -05007103}
7104
7105#endif /* CONFIG_IPW_QOS */
7106
James Ketrenos43f66a62005-03-25 12:31:53 -06007107static int ipw_associate_network(struct ipw_priv *priv,
7108 struct ieee80211_network *network,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007109 struct ipw_supported_rates *rates, int roaming)
James Ketrenos43f66a62005-03-25 12:31:53 -06007110{
7111 int err;
7112
7113 if (priv->config & CFG_FIXED_RATE)
James Ketrenosb095c382005-08-24 22:04:42 -05007114 ipw_set_fixed_rate(priv, network->mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06007115
7116 if (!(priv->config & CFG_STATIC_ESSID)) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007117 priv->essid_len = min(network->ssid_len,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007118 (u8) IW_ESSID_MAX_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06007119 memcpy(priv->essid, network->ssid, priv->essid_len);
7120 }
7121
7122 network->last_associate = jiffies;
7123
7124 memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
7125 priv->assoc_request.channel = network->channel;
Zhu Yi3e234b42006-01-24 16:36:52 +08007126 priv->assoc_request.auth_key = 0;
7127
James Ketrenos43f66a62005-03-25 12:31:53 -06007128 if ((priv->capability & CAP_PRIVACY_ON) &&
Zhu Yi3e234b42006-01-24 16:36:52 +08007129 (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007130 priv->assoc_request.auth_type = AUTH_SHARED_KEY;
James Ketrenosb095c382005-08-24 22:04:42 -05007131 priv->assoc_request.auth_key = priv->ieee->sec.active_key;
7132
Zhu Yi3e234b42006-01-24 16:36:52 +08007133 if ((priv->ieee->sec.level == SEC_LEVEL_1) &&
James Ketrenosb095c382005-08-24 22:04:42 -05007134 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
7135 ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
Zhu Yi3e234b42006-01-24 16:36:52 +08007136
7137 } else if ((priv->capability & CAP_PRIVACY_ON) &&
7138 (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP))
7139 priv->assoc_request.auth_type = AUTH_LEAP;
7140 else
James Ketrenos43f66a62005-03-25 12:31:53 -06007141 priv->assoc_request.auth_type = AUTH_OPEN;
James Ketrenos43f66a62005-03-25 12:31:53 -06007142
James Ketrenosa613bff2005-08-24 21:43:11 -05007143 if (priv->ieee->wpa_ie_len) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05007144 priv->assoc_request.policy_support = 0x02; /* RSN active */
7145 ipw_set_rsn_capa(priv, priv->ieee->wpa_ie,
7146 priv->ieee->wpa_ie_len);
7147 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007148
Jeff Garzikbf794512005-07-31 13:07:26 -04007149 /*
7150 * It is valid for our ieee device to support multiple modes, but
7151 * when it comes to associating to a given network we have to choose
James Ketrenos43f66a62005-03-25 12:31:53 -06007152 * just one mode.
7153 */
7154 if (network->mode & priv->ieee->mode & IEEE_A)
7155 priv->assoc_request.ieee_mode = IPW_A_MODE;
7156 else if (network->mode & priv->ieee->mode & IEEE_G)
7157 priv->assoc_request.ieee_mode = IPW_G_MODE;
7158 else if (network->mode & priv->ieee->mode & IEEE_B)
7159 priv->assoc_request.ieee_mode = IPW_B_MODE;
7160
James Ketrenosea2b26e2005-08-24 21:25:16 -05007161 priv->assoc_request.capability = network->capability;
7162 if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
7163 && !(priv->config & CFG_PREAMBLE_LONG)) {
7164 priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE;
7165 } else {
7166 priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE;
7167
7168 /* Clear the short preamble if we won't be supporting it */
7169 priv->assoc_request.capability &=
7170 ~WLAN_CAPABILITY_SHORT_PREAMBLE;
7171 }
7172
James Ketrenosafbf30a2005-08-25 00:05:33 -05007173 /* Clear capability bits that aren't used in Ad Hoc */
7174 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
7175 priv->assoc_request.capability &=
7176 ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
7177
James Ketrenos43f66a62005-03-25 12:31:53 -06007178 IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
James Ketrenosea2b26e2005-08-24 21:25:16 -05007179 "802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007180 roaming ? "Rea" : "A",
Jeff Garzikbf794512005-07-31 13:07:26 -04007181 escape_essid(priv->essid, priv->essid_len),
7182 network->channel,
7183 ipw_modes[priv->assoc_request.ieee_mode],
7184 rates->num_rates,
James Ketrenosea2b26e2005-08-24 21:25:16 -05007185 (priv->assoc_request.preamble_length ==
7186 DCT_FLAG_LONG_PREAMBLE) ? "long" : "short",
7187 network->capability &
7188 WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long",
James Ketrenos43f66a62005-03-25 12:31:53 -06007189 priv->capability & CAP_PRIVACY_ON ? "on " : "off",
Jeff Garzikbf794512005-07-31 13:07:26 -04007190 priv->capability & CAP_PRIVACY_ON ?
7191 (priv->capability & CAP_SHARED_KEY ? "(shared)" :
James Ketrenos43f66a62005-03-25 12:31:53 -06007192 "(open)") : "",
7193 priv->capability & CAP_PRIVACY_ON ? " key=" : "",
Jeff Garzikbf794512005-07-31 13:07:26 -04007194 priv->capability & CAP_PRIVACY_ON ?
James Ketrenosb095c382005-08-24 22:04:42 -05007195 '1' + priv->ieee->sec.active_key : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007196 priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
James Ketrenos43f66a62005-03-25 12:31:53 -06007197
7198 priv->assoc_request.beacon_interval = network->beacon_interval;
7199 if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007200 (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007201 priv->assoc_request.assoc_type = HC_IBSS_START;
7202 priv->assoc_request.assoc_tsf_msw = 0;
7203 priv->assoc_request.assoc_tsf_lsw = 0;
7204 } else {
7205 if (unlikely(roaming))
7206 priv->assoc_request.assoc_type = HC_REASSOCIATE;
7207 else
7208 priv->assoc_request.assoc_type = HC_ASSOCIATE;
7209 priv->assoc_request.assoc_tsf_msw = network->time_stamp[1];
7210 priv->assoc_request.assoc_tsf_lsw = network->time_stamp[0];
7211 }
7212
James Ketrenosafbf30a2005-08-25 00:05:33 -05007213 memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06007214
7215 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
7216 memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
7217 priv->assoc_request.atim_window = network->atim_window;
7218 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007219 memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06007220 priv->assoc_request.atim_window = 0;
7221 }
7222
James Ketrenos43f66a62005-03-25 12:31:53 -06007223 priv->assoc_request.listen_interval = network->listen_interval;
Jeff Garzikbf794512005-07-31 13:07:26 -04007224
James Ketrenos43f66a62005-03-25 12:31:53 -06007225 err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
7226 if (err) {
7227 IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
7228 return err;
7229 }
7230
7231 rates->ieee_mode = priv->assoc_request.ieee_mode;
7232 rates->purpose = IPW_RATE_CONNECT;
7233 ipw_send_supported_rates(priv, rates);
Jeff Garzikbf794512005-07-31 13:07:26 -04007234
James Ketrenos43f66a62005-03-25 12:31:53 -06007235 if (priv->assoc_request.ieee_mode == IPW_G_MODE)
7236 priv->sys_config.dot11g_auto_detection = 1;
7237 else
7238 priv->sys_config.dot11g_auto_detection = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007239
7240 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
7241 priv->sys_config.answer_broadcast_ssid_probe = 1;
7242 else
7243 priv->sys_config.answer_broadcast_ssid_probe = 0;
7244
James Ketrenos43f66a62005-03-25 12:31:53 -06007245 err = ipw_send_system_config(priv, &priv->sys_config);
7246 if (err) {
7247 IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
7248 return err;
7249 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007250
James Ketrenos43f66a62005-03-25 12:31:53 -06007251 IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007252 err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM);
James Ketrenos43f66a62005-03-25 12:31:53 -06007253 if (err) {
7254 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7255 return err;
7256 }
7257
7258 /*
7259 * If preemption is enabled, it is possible for the association
7260 * to complete before we return from ipw_send_associate. Therefore
7261 * we have to be sure and update our priviate data first.
7262 */
7263 priv->channel = network->channel;
7264 memcpy(priv->bssid, network->bssid, ETH_ALEN);
Jeff Garzikbf794512005-07-31 13:07:26 -04007265 priv->status |= STATUS_ASSOCIATING;
James Ketrenos43f66a62005-03-25 12:31:53 -06007266 priv->status &= ~STATUS_SECURITY_UPDATED;
7267
7268 priv->assoc_network = network;
7269
James Ketrenosb095c382005-08-24 22:04:42 -05007270#ifdef CONFIG_IPW_QOS
7271 ipw_qos_association(priv, network);
7272#endif
7273
James Ketrenos43f66a62005-03-25 12:31:53 -06007274 err = ipw_send_associate(priv, &priv->assoc_request);
7275 if (err) {
7276 IPW_DEBUG_HC("Attempt to send associate command failed.\n");
7277 return err;
7278 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007279
7280 IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n",
James Ketrenos43f66a62005-03-25 12:31:53 -06007281 escape_essid(priv->essid, priv->essid_len),
7282 MAC_ARG(priv->bssid));
7283
7284 return 0;
7285}
7286
7287static void ipw_roam(void *data)
7288{
7289 struct ipw_priv *priv = data;
7290 struct ieee80211_network *network = NULL;
7291 struct ipw_network_match match = {
7292 .network = priv->assoc_network
7293 };
7294
7295 /* The roaming process is as follows:
Jeff Garzikbf794512005-07-31 13:07:26 -04007296 *
7297 * 1. Missed beacon threshold triggers the roaming process by
James Ketrenos43f66a62005-03-25 12:31:53 -06007298 * setting the status ROAM bit and requesting a scan.
7299 * 2. When the scan completes, it schedules the ROAM work
7300 * 3. The ROAM work looks at all of the known networks for one that
7301 * is a better network than the currently associated. If none
7302 * found, the ROAM process is over (ROAM bit cleared)
7303 * 4. If a better network is found, a disassociation request is
7304 * sent.
7305 * 5. When the disassociation completes, the roam work is again
7306 * scheduled. The second time through, the driver is no longer
7307 * associated, and the newly selected network is sent an
Jeff Garzikbf794512005-07-31 13:07:26 -04007308 * association request.
James Ketrenos43f66a62005-03-25 12:31:53 -06007309 * 6. At this point ,the roaming process is complete and the ROAM
7310 * status bit is cleared.
7311 */
7312
7313 /* If we are no longer associated, and the roaming bit is no longer
7314 * set, then we are not actively roaming, so just return */
7315 if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
7316 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007317
James Ketrenos43f66a62005-03-25 12:31:53 -06007318 if (priv->status & STATUS_ASSOCIATED) {
Jeff Garzikbf794512005-07-31 13:07:26 -04007319 /* First pass through ROAM process -- look for a better
James Ketrenos43f66a62005-03-25 12:31:53 -06007320 * network */
James Ketrenosa613bff2005-08-24 21:43:11 -05007321 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007322 u8 rssi = priv->assoc_network->stats.rssi;
7323 priv->assoc_network->stats.rssi = -128;
James Ketrenosa613bff2005-08-24 21:43:11 -05007324 spin_lock_irqsave(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007325 list_for_each_entry(network, &priv->ieee->network_list, list) {
7326 if (network != priv->assoc_network)
7327 ipw_best_network(priv, &match, network, 1);
7328 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007329 spin_unlock_irqrestore(&priv->ieee->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06007330 priv->assoc_network->stats.rssi = rssi;
Jeff Garzikbf794512005-07-31 13:07:26 -04007331
James Ketrenos43f66a62005-03-25 12:31:53 -06007332 if (match.network == priv->assoc_network) {
7333 IPW_DEBUG_ASSOC("No better APs in this network to "
7334 "roam to.\n");
7335 priv->status &= ~STATUS_ROAMING;
7336 ipw_debug_config(priv);
7337 return;
7338 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007339
James Ketrenos43f66a62005-03-25 12:31:53 -06007340 ipw_send_disassociate(priv, 1);
7341 priv->assoc_network = match.network;
7342
7343 return;
Jeff Garzikbf794512005-07-31 13:07:26 -04007344 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007345
7346 /* Second pass through ROAM process -- request association */
7347 ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
7348 ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
7349 priv->status &= ~STATUS_ROAMING;
7350}
7351
James Ketrenosc848d0a2005-08-24 21:56:24 -05007352static void ipw_bg_roam(void *data)
7353{
7354 struct ipw_priv *priv = data;
7355 down(&priv->sem);
7356 ipw_roam(data);
7357 up(&priv->sem);
7358}
7359
7360static int ipw_associate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06007361{
7362 struct ipw_priv *priv = data;
7363
7364 struct ieee80211_network *network = NULL;
7365 struct ipw_network_match match = {
7366 .network = NULL
7367 };
7368 struct ipw_supported_rates *rates;
7369 struct list_head *element;
James Ketrenosa613bff2005-08-24 21:43:11 -05007370 unsigned long flags;
James Ketrenos43f66a62005-03-25 12:31:53 -06007371
James Ketrenosb095c382005-08-24 22:04:42 -05007372 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
7373 IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n");
7374 return 0;
7375 }
7376
James Ketrenosc848d0a2005-08-24 21:56:24 -05007377 if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007378 IPW_DEBUG_ASSOC("Not attempting association (already in "
7379 "progress)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007380 return 0;
7381 }
7382
Hong Liue6324722005-09-14 21:04:15 -05007383 if (priv->status & STATUS_DISASSOCIATING) {
7384 IPW_DEBUG_ASSOC("Not attempting association (in "
7385 "disassociating)\n ");
7386 queue_work(priv->workqueue, &priv->associate);
7387 return 0;
7388 }
7389
James Ketrenosc848d0a2005-08-24 21:56:24 -05007390 if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05007391 IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
7392 "initialized)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007393 return 0;
7394 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007395
7396 if (!(priv->config & CFG_ASSOCIATE) &&
7397 !(priv->config & (CFG_STATIC_ESSID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007398 CFG_STATIC_CHANNEL | CFG_STATIC_BSSID))) {
James Ketrenos43f66a62005-03-25 12:31:53 -06007399 IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05007400 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007401 }
7402
James Ketrenosa613bff2005-08-24 21:43:11 -05007403 /* Protect our use of the network_list */
7404 spin_lock_irqsave(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007405 list_for_each_entry(network, &priv->ieee->network_list, list)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007406 ipw_best_network(priv, &match, network, 0);
James Ketrenos43f66a62005-03-25 12:31:53 -06007407
7408 network = match.network;
7409 rates = &match.rates;
7410
7411 if (network == NULL &&
7412 priv->ieee->iw_mode == IW_MODE_ADHOC &&
7413 priv->config & CFG_ADHOC_CREATE &&
7414 priv->config & CFG_STATIC_ESSID &&
James Ketrenosa613bff2005-08-24 21:43:11 -05007415 priv->config & CFG_STATIC_CHANNEL &&
James Ketrenos43f66a62005-03-25 12:31:53 -06007416 !list_empty(&priv->ieee->network_free_list)) {
7417 element = priv->ieee->network_free_list.next;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007418 network = list_entry(element, struct ieee80211_network, list);
James Ketrenos43f66a62005-03-25 12:31:53 -06007419 ipw_adhoc_create(priv, network);
7420 rates = &priv->rates;
7421 list_del(element);
7422 list_add_tail(&network->list, &priv->ieee->network_list);
7423 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007424 spin_unlock_irqrestore(&priv->ieee->lock, flags);
Jeff Garzikbf794512005-07-31 13:07:26 -04007425
James Ketrenos43f66a62005-03-25 12:31:53 -06007426 /* If we reached the end of the list, then we don't have any valid
7427 * matching APs */
7428 if (!network) {
7429 ipw_debug_config(priv);
7430
James Ketrenosb095c382005-08-24 22:04:42 -05007431 if (!(priv->status & STATUS_SCANNING)) {
7432 if (!(priv->config & CFG_SPEED_SCAN))
7433 queue_delayed_work(priv->workqueue,
7434 &priv->request_scan,
7435 SCAN_INTERVAL);
7436 else
7437 queue_work(priv->workqueue,
7438 &priv->request_scan);
7439 }
Jeff Garzikbf794512005-07-31 13:07:26 -04007440
James Ketrenosc848d0a2005-08-24 21:56:24 -05007441 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06007442 }
7443
7444 ipw_associate_network(priv, network, rates, 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05007445
7446 return 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06007447}
Jeff Garzikbf794512005-07-31 13:07:26 -04007448
James Ketrenosc848d0a2005-08-24 21:56:24 -05007449static void ipw_bg_associate(void *data)
James Ketrenos43f66a62005-03-25 12:31:53 -06007450{
James Ketrenosc848d0a2005-08-24 21:56:24 -05007451 struct ipw_priv *priv = data;
7452 down(&priv->sem);
7453 ipw_associate(data);
7454 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06007455}
7456
James Ketrenosb095c382005-08-24 22:04:42 -05007457static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
7458 struct sk_buff *skb)
7459{
7460 struct ieee80211_hdr *hdr;
7461 u16 fc;
7462
7463 hdr = (struct ieee80211_hdr *)skb->data;
7464 fc = le16_to_cpu(hdr->frame_ctl);
7465 if (!(fc & IEEE80211_FCTL_PROTECTED))
7466 return;
7467
7468 fc &= ~IEEE80211_FCTL_PROTECTED;
7469 hdr->frame_ctl = cpu_to_le16(fc);
7470 switch (priv->ieee->sec.level) {
7471 case SEC_LEVEL_3:
7472 /* Remove CCMP HDR */
7473 memmove(skb->data + IEEE80211_3ADDR_LEN,
7474 skb->data + IEEE80211_3ADDR_LEN + 8,
7475 skb->len - IEEE80211_3ADDR_LEN - 8);
Zhu Yif4ff4972005-09-12 10:48:48 -05007476 skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */
James Ketrenosb095c382005-08-24 22:04:42 -05007477 break;
7478 case SEC_LEVEL_2:
7479 break;
7480 case SEC_LEVEL_1:
7481 /* Remove IV */
7482 memmove(skb->data + IEEE80211_3ADDR_LEN,
7483 skb->data + IEEE80211_3ADDR_LEN + 4,
7484 skb->len - IEEE80211_3ADDR_LEN - 4);
Zhu Yif4ff4972005-09-12 10:48:48 -05007485 skb_trim(skb, skb->len - 8); /* IV + ICV */
James Ketrenosb095c382005-08-24 22:04:42 -05007486 break;
7487 case SEC_LEVEL_0:
7488 break;
7489 default:
7490 printk(KERN_ERR "Unknow security level %d\n",
7491 priv->ieee->sec.level);
7492 break;
7493 }
7494}
7495
7496static void ipw_handle_data_packet(struct ipw_priv *priv,
7497 struct ipw_rx_mem_buffer *rxb,
7498 struct ieee80211_rx_stats *stats)
James Ketrenos43f66a62005-03-25 12:31:53 -06007499{
Hong Liu567deaf2005-08-31 18:07:22 +08007500 struct ieee80211_hdr_4addr *hdr;
James Ketrenos43f66a62005-03-25 12:31:53 -06007501 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7502
7503 /* We received data from the HW, so stop the watchdog */
7504 priv->net_dev->trans_start = jiffies;
7505
Jeff Garzikbf794512005-07-31 13:07:26 -04007506 /* We only process data packets if the
James Ketrenos43f66a62005-03-25 12:31:53 -06007507 * interface is open */
James Ketrenosa613bff2005-08-24 21:43:11 -05007508 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
James Ketrenos43f66a62005-03-25 12:31:53 -06007509 skb_tailroom(rxb->skb))) {
7510 priv->ieee->stats.rx_errors++;
7511 priv->wstats.discard.misc++;
7512 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7513 return;
7514 } else if (unlikely(!netif_running(priv->net_dev))) {
7515 priv->ieee->stats.rx_dropped++;
7516 priv->wstats.discard.misc++;
7517 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7518 return;
7519 }
7520
7521 /* Advance skb->data to the start of the actual payload */
Jiri Bencaaa4d302005-06-07 14:58:41 +02007522 skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
James Ketrenos43f66a62005-03-25 12:31:53 -06007523
7524 /* Set the size of the skb to the size of the frame */
James Ketrenosa613bff2005-08-24 21:43:11 -05007525 skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
James Ketrenos43f66a62005-03-25 12:31:53 -06007526
7527 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7528
James Ketrenosb095c382005-08-24 22:04:42 -05007529 /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
Hong Liu567deaf2005-08-31 18:07:22 +08007530 hdr = (struct ieee80211_hdr_4addr *)rxb->skb->data;
7531 if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
Stephen Hemminger3c190652006-01-03 15:27:38 -08007532 (is_multicast_ether_addr(hdr->addr1) ?
Hong Liu567deaf2005-08-31 18:07:22 +08007533 !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
James Ketrenosb095c382005-08-24 22:04:42 -05007534 ipw_rebuild_decrypted_skb(priv, rxb->skb);
7535
Jeff Garzikbf794512005-07-31 13:07:26 -04007536 if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
James Ketrenos43f66a62005-03-25 12:31:53 -06007537 priv->ieee->stats.rx_errors++;
James Ketrenosa613bff2005-08-24 21:43:11 -05007538 else { /* ieee80211_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007539 rxb->skb = NULL;
James Ketrenosb095c382005-08-24 22:04:42 -05007540 __ipw_led_activity_on(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -05007541 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007542}
7543
Mike Kershaw24a47db2005-08-26 00:41:54 -05007544#ifdef CONFIG_IEEE80211_RADIOTAP
7545static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
7546 struct ipw_rx_mem_buffer *rxb,
7547 struct ieee80211_rx_stats *stats)
7548{
7549 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
7550 struct ipw_rx_frame *frame = &pkt->u.frame;
7551
7552 /* initial pull of some data */
7553 u16 received_channel = frame->received_channel;
7554 u8 antennaAndPhy = frame->antennaAndPhy;
7555 s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */
7556 u16 pktrate = frame->rate;
7557
7558 /* Magic struct that slots into the radiotap header -- no reason
7559 * to build this manually element by element, we can write it much
7560 * more efficiently than we can parse it. ORDER MATTERS HERE */
7561 struct ipw_rt_hdr {
7562 struct ieee80211_radiotap_header rt_hdr;
7563 u8 rt_flags; /* radiotap packet flags */
7564 u8 rt_rate; /* rate in 500kb/s */
7565 u16 rt_channel; /* channel in mhz */
7566 u16 rt_chbitmask; /* channel bitfield */
7567 s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
7568 u8 rt_antenna; /* antenna number */
7569 } *ipw_rt;
7570
7571 short len = le16_to_cpu(pkt->u.frame.length);
7572
7573 /* We received data from the HW, so stop the watchdog */
7574 priv->net_dev->trans_start = jiffies;
7575
7576 /* We only process data packets if the
7577 * interface is open */
7578 if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
7579 skb_tailroom(rxb->skb))) {
7580 priv->ieee->stats.rx_errors++;
7581 priv->wstats.discard.misc++;
7582 IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
7583 return;
7584 } else if (unlikely(!netif_running(priv->net_dev))) {
7585 priv->ieee->stats.rx_dropped++;
7586 priv->wstats.discard.misc++;
7587 IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
7588 return;
7589 }
7590
7591 /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
7592 * that now */
7593 if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
7594 /* FIXME: Should alloc bigger skb instead */
7595 priv->ieee->stats.rx_dropped++;
7596 priv->wstats.discard.misc++;
7597 IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
7598 return;
7599 }
7600
7601 /* copy the frame itself */
7602 memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr),
7603 rxb->skb->data + IPW_RX_FRAME_SIZE, len);
7604
7605 /* Zero the radiotap static buffer ... We only need to zero the bytes NOT
7606 * part of our real header, saves a little time.
7607 *
7608 * No longer necessary since we fill in all our data. Purge before merging
7609 * patch officially.
7610 * memset(rxb->skb->data + sizeof(struct ipw_rt_hdr), 0,
7611 * IEEE80211_RADIOTAP_HDRLEN - sizeof(struct ipw_rt_hdr));
7612 */
7613
7614 ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data;
7615
7616 ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
7617 ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
7618 ipw_rt->rt_hdr.it_len = sizeof(struct ipw_rt_hdr); /* total header+data */
7619
7620 /* Big bitfield of all the fields we provide in radiotap */
7621 ipw_rt->rt_hdr.it_present =
7622 ((1 << IEEE80211_RADIOTAP_FLAGS) |
7623 (1 << IEEE80211_RADIOTAP_RATE) |
7624 (1 << IEEE80211_RADIOTAP_CHANNEL) |
7625 (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
7626 (1 << IEEE80211_RADIOTAP_ANTENNA));
7627
7628 /* Zero the flags, we'll add to them as we go */
7629 ipw_rt->rt_flags = 0;
7630
7631 /* Convert signal to DBM */
7632 ipw_rt->rt_dbmsignal = antsignal;
7633
7634 /* Convert the channel data and set the flags */
7635 ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
7636 if (received_channel > 14) { /* 802.11a */
7637 ipw_rt->rt_chbitmask =
7638 cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
7639 } else if (antennaAndPhy & 32) { /* 802.11b */
7640 ipw_rt->rt_chbitmask =
7641 cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
7642 } else { /* 802.11g */
7643 ipw_rt->rt_chbitmask =
7644 (IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
7645 }
7646
7647 /* set the rate in multiples of 500k/s */
7648 switch (pktrate) {
7649 case IPW_TX_RATE_1MB:
7650 ipw_rt->rt_rate = 2;
7651 break;
7652 case IPW_TX_RATE_2MB:
7653 ipw_rt->rt_rate = 4;
7654 break;
7655 case IPW_TX_RATE_5MB:
7656 ipw_rt->rt_rate = 10;
7657 break;
7658 case IPW_TX_RATE_6MB:
7659 ipw_rt->rt_rate = 12;
7660 break;
7661 case IPW_TX_RATE_9MB:
7662 ipw_rt->rt_rate = 18;
7663 break;
7664 case IPW_TX_RATE_11MB:
7665 ipw_rt->rt_rate = 22;
7666 break;
7667 case IPW_TX_RATE_12MB:
7668 ipw_rt->rt_rate = 24;
7669 break;
7670 case IPW_TX_RATE_18MB:
7671 ipw_rt->rt_rate = 36;
7672 break;
7673 case IPW_TX_RATE_24MB:
7674 ipw_rt->rt_rate = 48;
7675 break;
7676 case IPW_TX_RATE_36MB:
7677 ipw_rt->rt_rate = 72;
7678 break;
7679 case IPW_TX_RATE_48MB:
7680 ipw_rt->rt_rate = 96;
7681 break;
7682 case IPW_TX_RATE_54MB:
7683 ipw_rt->rt_rate = 108;
7684 break;
7685 default:
7686 ipw_rt->rt_rate = 0;
7687 break;
7688 }
7689
7690 /* antenna number */
7691 ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */
7692
7693 /* set the preamble flag if we have it */
7694 if ((antennaAndPhy & 64))
7695 ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
7696
7697 /* Set the size of the skb to the size of the frame */
7698 skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr));
James Ketrenos43f66a62005-03-25 12:31:53 -06007699
7700 IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
7701
7702 if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
7703 priv->ieee->stats.rx_errors++;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007704 else { /* ieee80211_rx succeeded, so it now owns the SKB */
James Ketrenos43f66a62005-03-25 12:31:53 -06007705 rxb->skb = NULL;
Mike Kershaw24a47db2005-08-26 00:41:54 -05007706 /* no LED during capture */
7707 }
7708}
7709#endif
7710
Arjan van de Ven858119e2006-01-14 13:20:43 -08007711static int is_network_packet(struct ipw_priv *priv,
James Ketrenosea2b26e2005-08-24 21:25:16 -05007712 struct ieee80211_hdr_4addr *header)
7713{
7714 /* Filter incoming packets to determine if they are targetted toward
7715 * this network, discarding packets coming from ourselves */
7716 switch (priv->ieee->iw_mode) {
James Ketrenosa613bff2005-08-24 21:43:11 -05007717 case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007718 /* packets from our adapter are dropped (echo) */
7719 if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
7720 return 0;
7721
Peter Jones90700fd2005-08-26 16:51:06 -05007722 /* {broad,multi}cast packets to our BSSID go through */
Stephen Hemminger3c190652006-01-03 15:27:38 -08007723 if (is_multicast_ether_addr(header->addr1))
James Ketrenosea2b26e2005-08-24 21:25:16 -05007724 return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05007725
7726 /* packets to our adapter go through */
7727 return !memcmp(header->addr1, priv->net_dev->dev_addr,
7728 ETH_ALEN);
James Ketrenosa613bff2005-08-24 21:43:11 -05007729
Peter Jones90700fd2005-08-26 16:51:06 -05007730 case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */
James Ketrenosc848d0a2005-08-24 21:56:24 -05007731 /* packets from our adapter are dropped (echo) */
7732 if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
7733 return 0;
7734
Peter Jones90700fd2005-08-26 16:51:06 -05007735 /* {broad,multi}cast packets to our BSS go through */
Stephen Hemminger3c190652006-01-03 15:27:38 -08007736 if (is_multicast_ether_addr(header->addr1))
James Ketrenosa613bff2005-08-24 21:43:11 -05007737 return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
7738
7739 /* packets to our adapter go through */
7740 return !memcmp(header->addr1, priv->net_dev->dev_addr,
7741 ETH_ALEN);
James Ketrenosea2b26e2005-08-24 21:25:16 -05007742 }
James Ketrenosa613bff2005-08-24 21:43:11 -05007743
James Ketrenosea2b26e2005-08-24 21:25:16 -05007744 return 1;
7745}
7746
James Ketrenosafbf30a2005-08-25 00:05:33 -05007747#define IPW_PACKET_RETRY_TIME HZ
7748
Arjan van de Ven858119e2006-01-14 13:20:43 -08007749static int is_duplicate_packet(struct ipw_priv *priv,
James Ketrenosafbf30a2005-08-25 00:05:33 -05007750 struct ieee80211_hdr_4addr *header)
7751{
James Ketrenosafbf30a2005-08-25 00:05:33 -05007752 u16 sc = le16_to_cpu(header->seq_ctl);
7753 u16 seq = WLAN_GET_SEQ_SEQ(sc);
7754 u16 frag = WLAN_GET_SEQ_FRAG(sc);
7755 u16 *last_seq, *last_frag;
7756 unsigned long *last_time;
7757
7758 switch (priv->ieee->iw_mode) {
7759 case IW_MODE_ADHOC:
7760 {
7761 struct list_head *p;
7762 struct ipw_ibss_seq *entry = NULL;
7763 u8 *mac = header->addr2;
7764 int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE;
7765
7766 __list_for_each(p, &priv->ibss_mac_hash[index]) {
7767 entry =
7768 list_entry(p, struct ipw_ibss_seq, list);
7769 if (!memcmp(entry->mac, mac, ETH_ALEN))
7770 break;
7771 }
7772 if (p == &priv->ibss_mac_hash[index]) {
7773 entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
7774 if (!entry) {
7775 IPW_ERROR
7776 ("Cannot malloc new mac entry\n");
7777 return 0;
7778 }
7779 memcpy(entry->mac, mac, ETH_ALEN);
7780 entry->seq_num = seq;
7781 entry->frag_num = frag;
7782 entry->packet_time = jiffies;
7783 list_add(&entry->list,
7784 &priv->ibss_mac_hash[index]);
7785 return 0;
7786 }
7787 last_seq = &entry->seq_num;
7788 last_frag = &entry->frag_num;
7789 last_time = &entry->packet_time;
7790 break;
7791 }
7792 case IW_MODE_INFRA:
7793 last_seq = &priv->last_seq_num;
7794 last_frag = &priv->last_frag_num;
7795 last_time = &priv->last_packet_time;
7796 break;
7797 default:
7798 return 0;
7799 }
7800 if ((*last_seq == seq) &&
7801 time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) {
7802 if (*last_frag == frag)
7803 goto drop;
7804 if (*last_frag + 1 != frag)
7805 /* out-of-order fragment */
7806 goto drop;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007807 } else
7808 *last_seq = seq;
7809
Zhu Yif57ce7c2005-07-13 12:22:15 -05007810 *last_frag = frag;
James Ketrenosafbf30a2005-08-25 00:05:33 -05007811 *last_time = jiffies;
7812 return 0;
7813
7814 drop:
Zhu Yi87b016c2005-08-05 17:17:35 +08007815 /* Comment this line now since we observed the card receives
7816 * duplicate packets but the FCTL_RETRY bit is not set in the
7817 * IBSS mode with fragmentation enabled.
7818 BUG_ON(!(le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_RETRY)); */
James Ketrenosafbf30a2005-08-25 00:05:33 -05007819 return 1;
7820}
7821
James Ketrenosb095c382005-08-24 22:04:42 -05007822static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
7823 struct ipw_rx_mem_buffer *rxb,
7824 struct ieee80211_rx_stats *stats)
7825{
7826 struct sk_buff *skb = rxb->skb;
7827 struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data;
7828 struct ieee80211_hdr_4addr *header = (struct ieee80211_hdr_4addr *)
7829 (skb->data + IPW_RX_FRAME_SIZE);
7830
7831 ieee80211_rx_mgt(priv->ieee, header, stats);
7832
7833 if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
7834 ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
7835 IEEE80211_STYPE_PROBE_RESP) ||
7836 (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
7837 IEEE80211_STYPE_BEACON))) {
7838 if (!memcmp(header->addr3, priv->bssid, ETH_ALEN))
7839 ipw_add_station(priv, header->addr2);
7840 }
7841
7842 if (priv->config & CFG_NET_STATS) {
7843 IPW_DEBUG_HC("sending stat packet\n");
7844
7845 /* Set the size of the skb to the size of the full
7846 * ipw header and 802.11 frame */
7847 skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
7848 IPW_RX_FRAME_SIZE);
7849
7850 /* Advance past the ipw packet header to the 802.11 frame */
7851 skb_pull(skb, IPW_RX_FRAME_SIZE);
7852
7853 /* Push the ieee80211_rx_stats before the 802.11 frame */
7854 memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
7855
7856 skb->dev = priv->ieee->dev;
7857
7858 /* Point raw at the ieee80211_stats */
7859 skb->mac.raw = skb->data;
7860
7861 skb->pkt_type = PACKET_OTHERHOST;
7862 skb->protocol = __constant_htons(ETH_P_80211_STATS);
7863 memset(skb->cb, 0, sizeof(rxb->skb->cb));
7864 netif_rx(skb);
7865 rxb->skb = NULL;
7866 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007867}
7868
James Ketrenos43f66a62005-03-25 12:31:53 -06007869/*
7870 * Main entry function for recieving a packet with 80211 headers. This
7871 * should be called when ever the FW has notified us that there is a new
7872 * skb in the recieve queue.
7873 */
7874static void ipw_rx(struct ipw_priv *priv)
7875{
7876 struct ipw_rx_mem_buffer *rxb;
7877 struct ipw_rx_packet *pkt;
James Ketrenos0dacca12005-09-21 12:23:41 -05007878 struct ieee80211_hdr_4addr *header;
James Ketrenos43f66a62005-03-25 12:31:53 -06007879 u32 r, w, i;
7880 u8 network_packet;
7881
James Ketrenosb095c382005-08-24 22:04:42 -05007882 r = ipw_read32(priv, IPW_RX_READ_INDEX);
7883 w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
James Ketrenos43f66a62005-03-25 12:31:53 -06007884 i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE;
7885
7886 while (i != r) {
7887 rxb = priv->rxq->queue[i];
Brice Goglin0f52bf92005-12-01 01:41:46 -08007888#ifdef CONFIG_IPW2200_DEBUG
James Ketrenos43f66a62005-03-25 12:31:53 -06007889 if (unlikely(rxb == NULL)) {
7890 printk(KERN_CRIT "Queue not allocated!\n");
7891 break;
7892 }
7893#endif
7894 priv->rxq->queue[i] = NULL;
7895
7896 pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05007897 IPW_RX_BUF_SIZE,
James Ketrenos43f66a62005-03-25 12:31:53 -06007898 PCI_DMA_FROMDEVICE);
7899
7900 pkt = (struct ipw_rx_packet *)rxb->skb->data;
7901 IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
7902 pkt->header.message_type,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007903 pkt->header.rx_seq_num, pkt->header.control_bits);
James Ketrenos43f66a62005-03-25 12:31:53 -06007904
7905 switch (pkt->header.message_type) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007906 case RX_FRAME_TYPE: /* 802.11 frame */ {
7907 struct ieee80211_rx_stats stats = {
James Ketrenosc848d0a2005-08-24 21:56:24 -05007908 .rssi =
7909 le16_to_cpu(pkt->u.frame.rssi_dbm) -
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007910 IPW_RSSI_TO_DBM,
James Ketrenosc848d0a2005-08-24 21:56:24 -05007911 .signal =
7912 le16_to_cpu(pkt->u.frame.signal),
7913 .noise =
7914 le16_to_cpu(pkt->u.frame.noise),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007915 .rate = pkt->u.frame.rate,
7916 .mac_time = jiffies,
7917 .received_channel =
7918 pkt->u.frame.received_channel,
7919 .freq =
7920 (pkt->u.frame.
7921 control & (1 << 0)) ?
7922 IEEE80211_24GHZ_BAND :
7923 IEEE80211_52GHZ_BAND,
James Ketrenosa613bff2005-08-24 21:43:11 -05007924 .len = le16_to_cpu(pkt->u.frame.length),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007925 };
James Ketrenos43f66a62005-03-25 12:31:53 -06007926
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007927 if (stats.rssi != 0)
7928 stats.mask |= IEEE80211_STATMASK_RSSI;
7929 if (stats.signal != 0)
7930 stats.mask |= IEEE80211_STATMASK_SIGNAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05007931 if (stats.noise != 0)
7932 stats.mask |= IEEE80211_STATMASK_NOISE;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007933 if (stats.rate != 0)
7934 stats.mask |= IEEE80211_STATMASK_RATE;
James Ketrenos43f66a62005-03-25 12:31:53 -06007935
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007936 priv->rx_packets++;
James Ketrenos43f66a62005-03-25 12:31:53 -06007937
James Ketrenosb095c382005-08-24 22:04:42 -05007938#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007939 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
Mike Kershaw24a47db2005-08-26 00:41:54 -05007940#ifdef CONFIG_IEEE80211_RADIOTAP
7941 ipw_handle_data_packet_monitor(priv,
7942 rxb,
7943 &stats);
7944#else
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007945 ipw_handle_data_packet(priv, rxb,
7946 &stats);
Mike Kershaw24a47db2005-08-26 00:41:54 -05007947#endif
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007948 break;
7949 }
James Ketrenos43f66a62005-03-25 12:31:53 -06007950#endif
Jeff Garzikbf794512005-07-31 13:07:26 -04007951
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007952 header =
James Ketrenos0dacca12005-09-21 12:23:41 -05007953 (struct ieee80211_hdr_4addr *)(rxb->skb->
7954 data +
7955 IPW_RX_FRAME_SIZE);
James Ketrenos43f66a62005-03-25 12:31:53 -06007956 /* TODO: Check Ad-Hoc dest/source and make sure
7957 * that we are actually parsing these packets
Jeff Garzikbf794512005-07-31 13:07:26 -04007958 * correctly -- we should probably use the
James Ketrenos43f66a62005-03-25 12:31:53 -06007959 * frame control of the packet and disregard
7960 * the current iw_mode */
James Ketrenos43f66a62005-03-25 12:31:53 -06007961
James Ketrenosea2b26e2005-08-24 21:25:16 -05007962 network_packet =
7963 is_network_packet(priv, header);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007964 if (network_packet && priv->assoc_network) {
7965 priv->assoc_network->stats.rssi =
7966 stats.rssi;
7967 average_add(&priv->average_rssi,
7968 stats.rssi);
7969 priv->last_rx_rssi = stats.rssi;
7970 }
7971
7972 IPW_DEBUG_RX("Frame: len=%u\n",
James Ketrenosa613bff2005-08-24 21:43:11 -05007973 le16_to_cpu(pkt->u.frame.length));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007974
James Ketrenosa613bff2005-08-24 21:43:11 -05007975 if (le16_to_cpu(pkt->u.frame.length) <
7976 frame_hdr_len(header)) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007977 IPW_DEBUG_DROP
7978 ("Received packet is too small. "
7979 "Dropping.\n");
7980 priv->ieee->stats.rx_errors++;
7981 priv->wstats.discard.misc++;
7982 break;
7983 }
7984
James Ketrenosa613bff2005-08-24 21:43:11 -05007985 switch (WLAN_FC_GET_TYPE
7986 (le16_to_cpu(header->frame_ctl))) {
James Ketrenosb095c382005-08-24 22:04:42 -05007987
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007988 case IEEE80211_FTYPE_MGMT:
James Ketrenosb095c382005-08-24 22:04:42 -05007989 ipw_handle_mgmt_packet(priv, rxb,
7990 &stats);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04007991 break;
7992
7993 case IEEE80211_FTYPE_CTL:
7994 break;
7995
7996 case IEEE80211_FTYPE_DATA:
James Ketrenosafbf30a2005-08-25 00:05:33 -05007997 if (unlikely(!network_packet ||
7998 is_duplicate_packet(priv,
7999 header)))
8000 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008001 IPW_DEBUG_DROP("Dropping: "
8002 MAC_FMT ", "
8003 MAC_FMT ", "
8004 MAC_FMT "\n",
8005 MAC_ARG(header->
8006 addr1),
8007 MAC_ARG(header->
8008 addr2),
8009 MAC_ARG(header->
8010 addr3));
James Ketrenosb095c382005-08-24 22:04:42 -05008011 break;
8012 }
8013
8014 ipw_handle_data_packet(priv, rxb,
8015 &stats);
8016
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008017 break;
8018 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008019 break;
8020 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008021
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008022 case RX_HOST_NOTIFICATION_TYPE:{
8023 IPW_DEBUG_RX
8024 ("Notification: subtype=%02X flags=%02X size=%d\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008025 pkt->u.notification.subtype,
8026 pkt->u.notification.flags,
8027 pkt->u.notification.size);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008028 ipw_rx_notification(priv, &pkt->u.notification);
8029 break;
8030 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008031
8032 default:
8033 IPW_DEBUG_RX("Bad Rx packet of type %d\n",
8034 pkt->header.message_type);
8035 break;
8036 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008037
8038 /* For now we just don't re-use anything. We can tweak this
8039 * later to try and re-use notification packets and SKBs that
James Ketrenos43f66a62005-03-25 12:31:53 -06008040 * fail to Rx correctly */
8041 if (rxb->skb != NULL) {
8042 dev_kfree_skb_any(rxb->skb);
8043 rxb->skb = NULL;
8044 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008045
James Ketrenos43f66a62005-03-25 12:31:53 -06008046 pci_unmap_single(priv->pci_dev, rxb->dma_addr,
James Ketrenosb095c382005-08-24 22:04:42 -05008047 IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
James Ketrenos43f66a62005-03-25 12:31:53 -06008048 list_add_tail(&rxb->list, &priv->rxq->rx_used);
Jeff Garzikbf794512005-07-31 13:07:26 -04008049
James Ketrenos43f66a62005-03-25 12:31:53 -06008050 i = (i + 1) % RX_QUEUE_SIZE;
8051 }
8052
8053 /* Backtrack one entry */
8054 priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1;
8055
8056 ipw_rx_queue_restock(priv);
8057}
8058
James Ketrenosafbf30a2005-08-25 00:05:33 -05008059#define DEFAULT_RTS_THRESHOLD 2304U
8060#define MIN_RTS_THRESHOLD 1U
8061#define MAX_RTS_THRESHOLD 2304U
8062#define DEFAULT_BEACON_INTERVAL 100U
8063#define DEFAULT_SHORT_RETRY_LIMIT 7U
8064#define DEFAULT_LONG_RETRY_LIMIT 4U
8065
8066static int ipw_sw_reset(struct ipw_priv *priv, int init)
James Ketrenos43f66a62005-03-25 12:31:53 -06008067{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008068 int band, modulation;
8069 int old_mode = priv->ieee->iw_mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06008070
James Ketrenosafbf30a2005-08-25 00:05:33 -05008071 /* Initialize module parameter values here */
8072 priv->config = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008073
James Ketrenosafbf30a2005-08-25 00:05:33 -05008074 /* We default to disabling the LED code as right now it causes
8075 * too many systems to lock up... */
8076 if (!led)
8077 priv->config |= CFG_NO_LED;
James Ketrenos43f66a62005-03-25 12:31:53 -06008078
James Ketrenosafbf30a2005-08-25 00:05:33 -05008079 if (associate)
8080 priv->config |= CFG_ASSOCIATE;
8081 else
8082 IPW_DEBUG_INFO("Auto associate disabled.\n");
Jeff Garzikbf794512005-07-31 13:07:26 -04008083
James Ketrenosafbf30a2005-08-25 00:05:33 -05008084 if (auto_create)
8085 priv->config |= CFG_ADHOC_CREATE;
8086 else
8087 IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
8088
Zhu Yi17ed0812006-01-24 16:36:31 +08008089 priv->config &= ~CFG_STATIC_ESSID;
8090 priv->essid_len = 0;
8091 memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
8092
James Ketrenosafbf30a2005-08-25 00:05:33 -05008093 if (disable) {
8094 priv->status |= STATUS_RF_KILL_SW;
8095 IPW_DEBUG_INFO("Radio disabled.\n");
James Ketrenos43f66a62005-03-25 12:31:53 -06008096 }
8097
James Ketrenosafbf30a2005-08-25 00:05:33 -05008098 if (channel != 0) {
8099 priv->config |= CFG_STATIC_CHANNEL;
8100 priv->channel = channel;
8101 IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
8102 /* TODO: Validate that provided channel is in range */
8103 }
8104#ifdef CONFIG_IPW_QOS
8105 ipw_qos_init(priv, qos_enable, qos_burst_enable,
8106 burst_duration_CCK, burst_duration_OFDM);
8107#endif /* CONFIG_IPW_QOS */
8108
8109 switch (mode) {
8110 case 1:
8111 priv->ieee->iw_mode = IW_MODE_ADHOC;
8112 priv->net_dev->type = ARPHRD_ETHER;
8113
8114 break;
8115#ifdef CONFIG_IPW2200_MONITOR
8116 case 2:
8117 priv->ieee->iw_mode = IW_MODE_MONITOR;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008118#ifdef CONFIG_IEEE80211_RADIOTAP
8119 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
8120#else
James Ketrenosafbf30a2005-08-25 00:05:33 -05008121 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008122#endif
James Ketrenosafbf30a2005-08-25 00:05:33 -05008123 break;
8124#endif
8125 default:
8126 case 0:
8127 priv->net_dev->type = ARPHRD_ETHER;
8128 priv->ieee->iw_mode = IW_MODE_INFRA;
8129 break;
James Ketrenos43f66a62005-03-25 12:31:53 -06008130 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008131
James Ketrenosafbf30a2005-08-25 00:05:33 -05008132 if (hwcrypto) {
8133 priv->ieee->host_encrypt = 0;
8134 priv->ieee->host_encrypt_msdu = 0;
8135 priv->ieee->host_decrypt = 0;
Hong Liu567deaf2005-08-31 18:07:22 +08008136 priv->ieee->host_mc_decrypt = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008137 }
James Ketrenosafbf30a2005-08-25 00:05:33 -05008138 IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
James Ketrenos43f66a62005-03-25 12:31:53 -06008139
Zhu Yie402c932005-08-05 17:20:40 +08008140 /* IPW2200/2915 is abled to do hardware fragmentation. */
8141 priv->ieee->host_open_frag = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008142
James Ketrenosafbf30a2005-08-25 00:05:33 -05008143 if ((priv->pci_dev->device == 0x4223) ||
8144 (priv->pci_dev->device == 0x4224)) {
8145 if (init)
8146 printk(KERN_INFO DRV_NAME
8147 ": Detected Intel PRO/Wireless 2915ABG Network "
8148 "Connection\n");
8149 priv->ieee->abg_true = 1;
8150 band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
8151 modulation = IEEE80211_OFDM_MODULATION |
8152 IEEE80211_CCK_MODULATION;
8153 priv->adapter = IPW_2915ABG;
8154 priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008155 } else {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008156 if (init)
8157 printk(KERN_INFO DRV_NAME
8158 ": Detected Intel PRO/Wireless 2200BG Network "
8159 "Connection\n");
8160
8161 priv->ieee->abg_true = 0;
8162 band = IEEE80211_24GHZ_BAND;
8163 modulation = IEEE80211_OFDM_MODULATION |
8164 IEEE80211_CCK_MODULATION;
8165 priv->adapter = IPW_2200BG;
8166 priv->ieee->mode = IEEE_G | IEEE_B;
James Ketrenos43f66a62005-03-25 12:31:53 -06008167 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008168
James Ketrenosafbf30a2005-08-25 00:05:33 -05008169 priv->ieee->freq_band = band;
8170 priv->ieee->modulation = modulation;
Jeff Garzikbf794512005-07-31 13:07:26 -04008171
James Ketrenosafbf30a2005-08-25 00:05:33 -05008172 priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
James Ketrenos43f66a62005-03-25 12:31:53 -06008173
James Ketrenosafbf30a2005-08-25 00:05:33 -05008174 priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
8175 priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008176
James Ketrenosafbf30a2005-08-25 00:05:33 -05008177 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8178 priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
8179 priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
Jeff Garzikbf794512005-07-31 13:07:26 -04008180
James Ketrenosafbf30a2005-08-25 00:05:33 -05008181 /* If power management is turned on, default to AC mode */
8182 priv->power_mode = IPW_POWER_AC;
8183 priv->tx_power = IPW_TX_POWER_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008184
Zhu Yi0ece35b2005-08-05 17:26:51 +08008185 return old_mode == priv->ieee->iw_mode;
James Ketrenos43f66a62005-03-25 12:31:53 -06008186}
8187
8188/*
8189 * This file defines the Wireless Extension handlers. It does not
8190 * define any methods of hardware manipulation and relies on the
8191 * functions defined in ipw_main to provide the HW interaction.
Jeff Garzikbf794512005-07-31 13:07:26 -04008192 *
8193 * The exception to this is the use of the ipw_get_ordinal()
James Ketrenos43f66a62005-03-25 12:31:53 -06008194 * function used to poll the hardware vs. making unecessary calls.
8195 *
8196 */
8197
Jeff Garzikbf794512005-07-31 13:07:26 -04008198static int ipw_wx_get_name(struct net_device *dev,
8199 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008200 union iwreq_data *wrqu, char *extra)
8201{
8202 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008203 down(&priv->sem);
8204 if (priv->status & STATUS_RF_KILL_MASK)
James Ketrenosa613bff2005-08-24 21:43:11 -05008205 strcpy(wrqu->name, "radio off");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008206 else if (!(priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06008207 strcpy(wrqu->name, "unassociated");
Jeff Garzikbf794512005-07-31 13:07:26 -04008208 else
James Ketrenos43f66a62005-03-25 12:31:53 -06008209 snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
8210 ipw_modes[priv->assoc_request.ieee_mode]);
8211 IPW_DEBUG_WX("Name: %s\n", wrqu->name);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008212 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008213 return 0;
8214}
8215
8216static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
8217{
8218 if (channel == 0) {
8219 IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
8220 priv->config &= ~CFG_STATIC_CHANNEL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008221 IPW_DEBUG_ASSOC("Attempting to associate with new "
8222 "parameters.\n");
8223 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008224 return 0;
8225 }
8226
8227 priv->config |= CFG_STATIC_CHANNEL;
8228
8229 if (priv->channel == channel) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008230 IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
8231 channel);
James Ketrenos43f66a62005-03-25 12:31:53 -06008232 return 0;
8233 }
8234
8235 IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
8236 priv->channel = channel;
8237
James Ketrenosb095c382005-08-24 22:04:42 -05008238#ifdef CONFIG_IPW2200_MONITOR
8239 if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008240 int i;
James Ketrenosb095c382005-08-24 22:04:42 -05008241 if (priv->status & STATUS_SCANNING) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008242 IPW_DEBUG_SCAN("Scan abort triggered due to "
James Ketrenosb095c382005-08-24 22:04:42 -05008243 "channel change.\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008244 ipw_abort_scan(priv);
James Ketrenosb095c382005-08-24 22:04:42 -05008245 }
8246
8247 for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
8248 udelay(10);
8249
8250 if (priv->status & STATUS_SCANNING)
8251 IPW_DEBUG_SCAN("Still scanning...\n");
8252 else
8253 IPW_DEBUG_SCAN("Took %dms to abort current scan\n",
8254 1000 - i);
8255
8256 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008257 }
James Ketrenosb095c382005-08-24 22:04:42 -05008258#endif /* CONFIG_IPW2200_MONITOR */
8259
James Ketrenosc848d0a2005-08-24 21:56:24 -05008260 /* Network configuration changed -- force [re]association */
8261 IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
8262 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008263 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008264
8265 return 0;
8266}
8267
Jeff Garzikbf794512005-07-31 13:07:26 -04008268static int ipw_wx_set_freq(struct net_device *dev,
8269 struct iw_request_info *info,
8270 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008271{
8272 struct ipw_priv *priv = ieee80211_priv(dev);
Liu Hong1fe0adb2005-08-19 09:33:10 -05008273 const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
James Ketrenos43f66a62005-03-25 12:31:53 -06008274 struct iw_freq *fwrq = &wrqu->freq;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008275 int ret = 0, i;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008276 u8 channel, flags;
8277 int band;
Jeff Garzikbf794512005-07-31 13:07:26 -04008278
James Ketrenosb095c382005-08-24 22:04:42 -05008279 if (fwrq->m == 0) {
8280 IPW_DEBUG_WX("SET Freq/Channel -> any\n");
8281 down(&priv->sem);
8282 ret = ipw_set_channel(priv, 0);
8283 up(&priv->sem);
8284 return ret;
8285 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008286 /* if setting by freq convert to channel */
8287 if (fwrq->e == 1) {
Liu Hong1fe0adb2005-08-19 09:33:10 -05008288 channel = ipw_freq_to_channel(priv->ieee, fwrq->m);
James Ketrenosb095c382005-08-24 22:04:42 -05008289 if (channel == 0)
8290 return -EINVAL;
8291 } else
8292 channel = fwrq->m;
Jeff Garzikbf794512005-07-31 13:07:26 -04008293
Liu Hong1fe0adb2005-08-19 09:33:10 -05008294 if (!(band = ipw_is_valid_channel(priv->ieee, channel)))
James Ketrenosb095c382005-08-24 22:04:42 -05008295 return -EINVAL;
Jeff Garzikbf794512005-07-31 13:07:26 -04008296
Liu Hong1fe0adb2005-08-19 09:33:10 -05008297 if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
8298 i = ipw_channel_to_index(priv->ieee, channel);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008299 if (i == -1)
8300 return -EINVAL;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008301
8302 flags = (band == IEEE80211_24GHZ_BAND) ?
8303 geo->bg[i].flags : geo->a[i].flags;
8304 if (flags & IEEE80211_CH_PASSIVE_ONLY) {
James Ketrenosafbf30a2005-08-25 00:05:33 -05008305 IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
8306 return -EINVAL;
James Ketrenos43f66a62005-03-25 12:31:53 -06008307 }
8308 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008309
James Ketrenos43f66a62005-03-25 12:31:53 -06008310 IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008311 down(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05008312 ret = ipw_set_channel(priv, channel);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008313 up(&priv->sem);
8314 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06008315}
8316
Jeff Garzikbf794512005-07-31 13:07:26 -04008317static int ipw_wx_get_freq(struct net_device *dev,
8318 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008319 union iwreq_data *wrqu, char *extra)
8320{
8321 struct ipw_priv *priv = ieee80211_priv(dev);
8322
8323 wrqu->freq.e = 0;
8324
8325 /* If we are associated, trying to associate, or have a statically
8326 * configured CHANNEL then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008327 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008328 if (priv->config & CFG_STATIC_CHANNEL ||
8329 priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))
8330 wrqu->freq.m = priv->channel;
Jeff Garzikbf794512005-07-31 13:07:26 -04008331 else
James Ketrenos43f66a62005-03-25 12:31:53 -06008332 wrqu->freq.m = 0;
8333
James Ketrenosc848d0a2005-08-24 21:56:24 -05008334 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008335 IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
8336 return 0;
8337}
8338
Jeff Garzikbf794512005-07-31 13:07:26 -04008339static int ipw_wx_set_mode(struct net_device *dev,
8340 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008341 union iwreq_data *wrqu, char *extra)
8342{
8343 struct ipw_priv *priv = ieee80211_priv(dev);
8344 int err = 0;
8345
8346 IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
8347
James Ketrenos43f66a62005-03-25 12:31:53 -06008348 switch (wrqu->mode) {
James Ketrenosb095c382005-08-24 22:04:42 -05008349#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -06008350 case IW_MODE_MONITOR:
8351#endif
8352 case IW_MODE_ADHOC:
8353 case IW_MODE_INFRA:
8354 break;
8355 case IW_MODE_AUTO:
8356 wrqu->mode = IW_MODE_INFRA;
8357 break;
8358 default:
8359 return -EINVAL;
8360 }
James Ketrenosb095c382005-08-24 22:04:42 -05008361 if (wrqu->mode == priv->ieee->iw_mode)
8362 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008363
James Ketrenosb095c382005-08-24 22:04:42 -05008364 down(&priv->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -05008365
8366 ipw_sw_reset(priv, 0);
8367
James Ketrenosb095c382005-08-24 22:04:42 -05008368#ifdef CONFIG_IPW2200_MONITOR
Jeff Garzikbf794512005-07-31 13:07:26 -04008369 if (priv->ieee->iw_mode == IW_MODE_MONITOR)
James Ketrenos43f66a62005-03-25 12:31:53 -06008370 priv->net_dev->type = ARPHRD_ETHER;
Jeff Garzikbf794512005-07-31 13:07:26 -04008371
8372 if (wrqu->mode == IW_MODE_MONITOR)
Mike Kershaw24a47db2005-08-26 00:41:54 -05008373#ifdef CONFIG_IEEE80211_RADIOTAP
8374 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
8375#else
James Ketrenos43f66a62005-03-25 12:31:53 -06008376 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05008377#endif
James Ketrenosb095c382005-08-24 22:04:42 -05008378#endif /* CONFIG_IPW2200_MONITOR */
Jeff Garzikbf794512005-07-31 13:07:26 -04008379
Jeff Garzikbf794512005-07-31 13:07:26 -04008380 /* Free the existing firmware and reset the fw_loaded
James Ketrenos43f66a62005-03-25 12:31:53 -06008381 * flag so ipw_load() will bring in the new firmawre */
James Ketrenosafbf30a2005-08-25 00:05:33 -05008382 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -06008383
8384 priv->ieee->iw_mode = wrqu->mode;
Jeff Garzikbf794512005-07-31 13:07:26 -04008385
James Ketrenosc848d0a2005-08-24 21:56:24 -05008386 queue_work(priv->workqueue, &priv->adapter_restart);
8387 up(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008388 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06008389}
8390
Jeff Garzikbf794512005-07-31 13:07:26 -04008391static int ipw_wx_get_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008392 struct iw_request_info *info,
8393 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008394{
8395 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008396 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008397 wrqu->mode = priv->ieee->iw_mode;
8398 IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008399 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008400 return 0;
8401}
8402
James Ketrenos43f66a62005-03-25 12:31:53 -06008403/* Values are in microsecond */
8404static const s32 timeout_duration[] = {
8405 350000,
8406 250000,
8407 75000,
8408 37000,
8409 25000,
8410};
8411
8412static const s32 period_duration[] = {
8413 400000,
8414 700000,
8415 1000000,
8416 1000000,
8417 1000000
8418};
8419
Jeff Garzikbf794512005-07-31 13:07:26 -04008420static int ipw_wx_get_range(struct net_device *dev,
8421 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008422 union iwreq_data *wrqu, char *extra)
8423{
8424 struct ipw_priv *priv = ieee80211_priv(dev);
8425 struct iw_range *range = (struct iw_range *)extra;
Liu Hong1fe0adb2005-08-19 09:33:10 -05008426 const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee);
James Ketrenosb095c382005-08-24 22:04:42 -05008427 int i = 0, j;
James Ketrenos43f66a62005-03-25 12:31:53 -06008428
8429 wrqu->data.length = sizeof(*range);
8430 memset(range, 0, sizeof(*range));
8431
8432 /* 54Mbs == ~27 Mb/s real (802.11g) */
Jeff Garzikbf794512005-07-31 13:07:26 -04008433 range->throughput = 27 * 1000 * 1000;
James Ketrenos43f66a62005-03-25 12:31:53 -06008434
8435 range->max_qual.qual = 100;
8436 /* TODO: Find real max RSSI and stick here */
8437 range->max_qual.level = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008438 range->max_qual.noise = priv->ieee->worst_rssi + 0x100;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008439 range->max_qual.updated = 7; /* Updated all three */
James Ketrenos43f66a62005-03-25 12:31:53 -06008440
8441 range->avg_qual.qual = 70;
8442 /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008443 range->avg_qual.level = 0; /* FIXME to real average level */
James Ketrenos43f66a62005-03-25 12:31:53 -06008444 range->avg_qual.noise = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008445 range->avg_qual.updated = 7; /* Updated all three */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008446 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008447 range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
James Ketrenos43f66a62005-03-25 12:31:53 -06008448
Jeff Garzikbf794512005-07-31 13:07:26 -04008449 for (i = 0; i < range->num_bitrates; i++)
8450 range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008451 500000;
Jeff Garzikbf794512005-07-31 13:07:26 -04008452
James Ketrenos43f66a62005-03-25 12:31:53 -06008453 range->max_rts = DEFAULT_RTS_THRESHOLD;
8454 range->min_frag = MIN_FRAG_THRESHOLD;
8455 range->max_frag = MAX_FRAG_THRESHOLD;
8456
8457 range->encoding_size[0] = 5;
Jeff Garzikbf794512005-07-31 13:07:26 -04008458 range->encoding_size[1] = 13;
James Ketrenos43f66a62005-03-25 12:31:53 -06008459 range->num_encoding_sizes = 2;
8460 range->max_encoding_tokens = WEP_KEYS;
8461
8462 /* Set the Wireless Extension versions */
8463 range->we_version_compiled = WIRELESS_EXT;
8464 range->we_version_source = 16;
8465
James Ketrenosb095c382005-08-24 22:04:42 -05008466 i = 0;
8467 if (priv->ieee->mode & (IEEE_B | IEEE_G)) {
8468 for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES;
8469 i++, j++) {
8470 range->freq[i].i = geo->bg[j].channel;
8471 range->freq[i].m = geo->bg[j].freq * 100000;
8472 range->freq[i].e = 1;
8473 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008474 }
James Ketrenosb095c382005-08-24 22:04:42 -05008475
8476 if (priv->ieee->mode & IEEE_A) {
8477 for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES;
8478 i++, j++) {
8479 range->freq[i].i = geo->a[j].channel;
8480 range->freq[i].m = geo->a[j].freq * 100000;
8481 range->freq[i].e = 1;
8482 }
8483 }
8484
8485 range->num_channels = i;
8486 range->num_frequency = i;
8487
James Ketrenosc848d0a2005-08-24 21:56:24 -05008488 up(&priv->sem);
Benoit Boissinot97a78ca2005-09-15 17:30:28 +00008489
8490 /* Event capability (kernel + driver) */
8491 range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
8492 IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
8493 IW_EVENT_CAPA_MASK(SIOCGIWAP));
8494 range->event_capa[1] = IW_EVENT_CAPA_K_1;
James Ketrenos43f66a62005-03-25 12:31:53 -06008495
8496 IPW_DEBUG_WX("GET Range\n");
8497 return 0;
8498}
8499
Jeff Garzikbf794512005-07-31 13:07:26 -04008500static int ipw_wx_set_wap(struct net_device *dev,
8501 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008502 union iwreq_data *wrqu, char *extra)
8503{
8504 struct ipw_priv *priv = ieee80211_priv(dev);
8505
8506 static const unsigned char any[] = {
8507 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
8508 };
8509 static const unsigned char off[] = {
8510 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
8511 };
8512
Jeff Garzikbf794512005-07-31 13:07:26 -04008513 if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
James Ketrenos43f66a62005-03-25 12:31:53 -06008514 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008515 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008516 if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
8517 !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
8518 /* we disable mandatory BSSID association */
8519 IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
8520 priv->config &= ~CFG_STATIC_BSSID;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008521 IPW_DEBUG_ASSOC("Attempting to associate with new "
8522 "parameters.\n");
8523 ipw_associate(priv);
8524 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008525 return 0;
8526 }
8527
8528 priv->config |= CFG_STATIC_BSSID;
8529 if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
8530 IPW_DEBUG_WX("BSSID set to current BSSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008531 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008532 return 0;
8533 }
8534
8535 IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n",
8536 MAC_ARG(wrqu->ap_addr.sa_data));
8537
8538 memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
8539
James Ketrenosc848d0a2005-08-24 21:56:24 -05008540 /* Network configuration changed -- force [re]association */
8541 IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
8542 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008543 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008544
James Ketrenosc848d0a2005-08-24 21:56:24 -05008545 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008546 return 0;
8547}
8548
Jeff Garzikbf794512005-07-31 13:07:26 -04008549static int ipw_wx_get_wap(struct net_device *dev,
8550 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008551 union iwreq_data *wrqu, char *extra)
8552{
8553 struct ipw_priv *priv = ieee80211_priv(dev);
8554 /* If we are associated, trying to associate, or have a statically
8555 * configured BSSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008556 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04008557 if (priv->config & CFG_STATIC_BSSID ||
James Ketrenos43f66a62005-03-25 12:31:53 -06008558 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
8559 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
James Ketrenosafbf30a2005-08-25 00:05:33 -05008560 memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
James Ketrenos43f66a62005-03-25 12:31:53 -06008561 } else
8562 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
8563
8564 IPW_DEBUG_WX("Getting WAP BSSID: " MAC_FMT "\n",
8565 MAC_ARG(wrqu->ap_addr.sa_data));
James Ketrenosc848d0a2005-08-24 21:56:24 -05008566 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008567 return 0;
8568}
8569
Jeff Garzikbf794512005-07-31 13:07:26 -04008570static int ipw_wx_set_essid(struct net_device *dev,
8571 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008572 union iwreq_data *wrqu, char *extra)
8573{
8574 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008575 char *essid = ""; /* ANY */
James Ketrenos43f66a62005-03-25 12:31:53 -06008576 int length = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008577 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008578 if (wrqu->essid.flags && wrqu->essid.length) {
8579 length = wrqu->essid.length - 1;
8580 essid = extra;
8581 }
8582 if (length == 0) {
8583 IPW_DEBUG_WX("Setting ESSID to ANY\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008584 if ((priv->config & CFG_STATIC_ESSID) &&
8585 !(priv->status & (STATUS_ASSOCIATED |
James Ketrenos43f66a62005-03-25 12:31:53 -06008586 STATUS_ASSOCIATING))) {
8587 IPW_DEBUG_ASSOC("Attempting to associate with new "
8588 "parameters.\n");
James Ketrenosafbf30a2005-08-25 00:05:33 -05008589 priv->config &= ~CFG_STATIC_ESSID;
James Ketrenos43f66a62005-03-25 12:31:53 -06008590 ipw_associate(priv);
8591 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008592 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008593 return 0;
8594 }
8595
8596 length = min(length, IW_ESSID_MAX_SIZE);
8597
8598 priv->config |= CFG_STATIC_ESSID;
8599
8600 if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
8601 IPW_DEBUG_WX("ESSID set to current ESSID.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008602 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008603 return 0;
8604 }
8605
8606 IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
8607 length);
8608
8609 priv->essid_len = length;
8610 memcpy(priv->essid, essid, priv->essid_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04008611
James Ketrenosc848d0a2005-08-24 21:56:24 -05008612 /* Network configuration changed -- force [re]association */
8613 IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
8614 if (!ipw_disassociate(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -06008615 ipw_associate(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06008616
James Ketrenosc848d0a2005-08-24 21:56:24 -05008617 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008618 return 0;
8619}
8620
Jeff Garzikbf794512005-07-31 13:07:26 -04008621static int ipw_wx_get_essid(struct net_device *dev,
8622 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008623 union iwreq_data *wrqu, char *extra)
8624{
8625 struct ipw_priv *priv = ieee80211_priv(dev);
8626
8627 /* If we are associated, trying to associate, or have a statically
8628 * configured ESSID then return that; otherwise return ANY */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008629 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008630 if (priv->config & CFG_STATIC_ESSID ||
Jeff Garzikbf794512005-07-31 13:07:26 -04008631 priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
8632 IPW_DEBUG_WX("Getting essid: '%s'\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06008633 escape_essid(priv->essid, priv->essid_len));
Jeff Garzikbf794512005-07-31 13:07:26 -04008634 memcpy(extra, priv->essid, priv->essid_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06008635 wrqu->essid.length = priv->essid_len;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008636 wrqu->essid.flags = 1; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06008637 } else {
8638 IPW_DEBUG_WX("Getting essid: ANY\n");
8639 wrqu->essid.length = 0;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008640 wrqu->essid.flags = 0; /* active */
James Ketrenos43f66a62005-03-25 12:31:53 -06008641 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05008642 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008643 return 0;
8644}
8645
Jeff Garzikbf794512005-07-31 13:07:26 -04008646static int ipw_wx_set_nick(struct net_device *dev,
8647 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008648 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008649{
James Ketrenos43f66a62005-03-25 12:31:53 -06008650 struct ipw_priv *priv = ieee80211_priv(dev);
8651
8652 IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
8653 if (wrqu->data.length > IW_ESSID_MAX_SIZE)
8654 return -E2BIG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008655 down(&priv->sem);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008656 wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
James Ketrenos43f66a62005-03-25 12:31:53 -06008657 memset(priv->nick, 0, sizeof(priv->nick));
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008658 memcpy(priv->nick, extra, wrqu->data.length);
James Ketrenos43f66a62005-03-25 12:31:53 -06008659 IPW_DEBUG_TRACE("<<\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008660 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008661 return 0;
8662
8663}
8664
Jeff Garzikbf794512005-07-31 13:07:26 -04008665static int ipw_wx_get_nick(struct net_device *dev,
8666 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008667 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008668{
James Ketrenos43f66a62005-03-25 12:31:53 -06008669 struct ipw_priv *priv = ieee80211_priv(dev);
8670 IPW_DEBUG_WX("Getting nick\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008671 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008672 wrqu->data.length = strlen(priv->nick) + 1;
8673 memcpy(extra, priv->nick, wrqu->data.length);
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008674 wrqu->data.flags = 1; /* active */
James Ketrenosc848d0a2005-08-24 21:56:24 -05008675 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008676 return 0;
8677}
8678
James Ketrenos43f66a62005-03-25 12:31:53 -06008679static int ipw_wx_set_rate(struct net_device *dev,
8680 struct iw_request_info *info,
8681 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008682{
James Ketrenosea2b26e2005-08-24 21:25:16 -05008683 /* TODO: We should use semaphores or locks for access to priv */
8684 struct ipw_priv *priv = ieee80211_priv(dev);
8685 u32 target_rate = wrqu->bitrate.value;
8686 u32 fixed, mask;
8687
8688 /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
8689 /* value = X, fixed = 1 means only rate X */
8690 /* value = X, fixed = 0 means all rates lower equal X */
8691
8692 if (target_rate == -1) {
8693 fixed = 0;
8694 mask = IEEE80211_DEFAULT_RATES_MASK;
8695 /* Now we should reassociate */
8696 goto apply;
8697 }
8698
8699 mask = 0;
8700 fixed = wrqu->bitrate.fixed;
8701
8702 if (target_rate == 1000000 || !fixed)
8703 mask |= IEEE80211_CCK_RATE_1MB_MASK;
8704 if (target_rate == 1000000)
8705 goto apply;
8706
8707 if (target_rate == 2000000 || !fixed)
8708 mask |= IEEE80211_CCK_RATE_2MB_MASK;
8709 if (target_rate == 2000000)
8710 goto apply;
8711
8712 if (target_rate == 5500000 || !fixed)
8713 mask |= IEEE80211_CCK_RATE_5MB_MASK;
8714 if (target_rate == 5500000)
8715 goto apply;
8716
8717 if (target_rate == 6000000 || !fixed)
8718 mask |= IEEE80211_OFDM_RATE_6MB_MASK;
8719 if (target_rate == 6000000)
8720 goto apply;
8721
8722 if (target_rate == 9000000 || !fixed)
8723 mask |= IEEE80211_OFDM_RATE_9MB_MASK;
8724 if (target_rate == 9000000)
8725 goto apply;
8726
8727 if (target_rate == 11000000 || !fixed)
8728 mask |= IEEE80211_CCK_RATE_11MB_MASK;
8729 if (target_rate == 11000000)
8730 goto apply;
8731
8732 if (target_rate == 12000000 || !fixed)
8733 mask |= IEEE80211_OFDM_RATE_12MB_MASK;
8734 if (target_rate == 12000000)
8735 goto apply;
8736
8737 if (target_rate == 18000000 || !fixed)
8738 mask |= IEEE80211_OFDM_RATE_18MB_MASK;
8739 if (target_rate == 18000000)
8740 goto apply;
8741
8742 if (target_rate == 24000000 || !fixed)
8743 mask |= IEEE80211_OFDM_RATE_24MB_MASK;
8744 if (target_rate == 24000000)
8745 goto apply;
8746
8747 if (target_rate == 36000000 || !fixed)
8748 mask |= IEEE80211_OFDM_RATE_36MB_MASK;
8749 if (target_rate == 36000000)
8750 goto apply;
8751
8752 if (target_rate == 48000000 || !fixed)
8753 mask |= IEEE80211_OFDM_RATE_48MB_MASK;
8754 if (target_rate == 48000000)
8755 goto apply;
8756
8757 if (target_rate == 54000000 || !fixed)
8758 mask |= IEEE80211_OFDM_RATE_54MB_MASK;
8759 if (target_rate == 54000000)
8760 goto apply;
8761
8762 IPW_DEBUG_WX("invalid rate specified, returning error\n");
8763 return -EINVAL;
8764
8765 apply:
8766 IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
8767 mask, fixed ? "fixed" : "sub-rates");
James Ketrenosc848d0a2005-08-24 21:56:24 -05008768 down(&priv->sem);
James Ketrenosb095c382005-08-24 22:04:42 -05008769 if (mask == IEEE80211_DEFAULT_RATES_MASK) {
James Ketrenosea2b26e2005-08-24 21:25:16 -05008770 priv->config &= ~CFG_FIXED_RATE;
James Ketrenosb095c382005-08-24 22:04:42 -05008771 ipw_set_fixed_rate(priv, priv->ieee->mode);
8772 } else
James Ketrenosea2b26e2005-08-24 21:25:16 -05008773 priv->config |= CFG_FIXED_RATE;
8774
James Ketrenosc848d0a2005-08-24 21:56:24 -05008775 if (priv->rates_mask == mask) {
8776 IPW_DEBUG_WX("Mask set to current mask.\n");
8777 up(&priv->sem);
8778 return 0;
James Ketrenosea2b26e2005-08-24 21:25:16 -05008779 }
8780
James Ketrenosc848d0a2005-08-24 21:56:24 -05008781 priv->rates_mask = mask;
8782
8783 /* Network configuration changed -- force [re]association */
8784 IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
8785 if (!ipw_disassociate(priv))
8786 ipw_associate(priv);
8787
8788 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05008789 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008790}
8791
Jeff Garzikbf794512005-07-31 13:07:26 -04008792static int ipw_wx_get_rate(struct net_device *dev,
8793 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008794 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008795{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008796 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008797 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008798 wrqu->bitrate.value = priv->last_rate;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008799 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008800 IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
8801 return 0;
8802}
8803
Jeff Garzikbf794512005-07-31 13:07:26 -04008804static int ipw_wx_set_rts(struct net_device *dev,
8805 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008806 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008807{
James Ketrenos43f66a62005-03-25 12:31:53 -06008808 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008809 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008810 if (wrqu->rts.disabled)
8811 priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
8812 else {
8813 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
James Ketrenosc848d0a2005-08-24 21:56:24 -05008814 wrqu->rts.value > MAX_RTS_THRESHOLD) {
8815 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008816 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008817 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008818 priv->rts_threshold = wrqu->rts.value;
8819 }
8820
8821 ipw_send_rts_threshold(priv, priv->rts_threshold);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008822 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008823 IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
8824 return 0;
8825}
8826
Jeff Garzikbf794512005-07-31 13:07:26 -04008827static int ipw_wx_get_rts(struct net_device *dev,
8828 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008829 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008830{
James Ketrenos43f66a62005-03-25 12:31:53 -06008831 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008832 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008833 wrqu->rts.value = priv->rts_threshold;
8834 wrqu->rts.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008835 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008836 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008837 IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
8838 return 0;
8839}
8840
Jeff Garzikbf794512005-07-31 13:07:26 -04008841static int ipw_wx_set_txpow(struct net_device *dev,
8842 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008843 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008844{
James Ketrenos43f66a62005-03-25 12:31:53 -06008845 struct ipw_priv *priv = ieee80211_priv(dev);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008846 int err = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008847
James Ketrenosc848d0a2005-08-24 21:56:24 -05008848 down(&priv->sem);
8849 if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008850 err = -EINPROGRESS;
8851 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008852 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008853
James Ketrenosb095c382005-08-24 22:04:42 -05008854 if (!wrqu->power.fixed)
8855 wrqu->power.value = IPW_TX_POWER_DEFAULT;
James Ketrenos43f66a62005-03-25 12:31:53 -06008856
James Ketrenosc848d0a2005-08-24 21:56:24 -05008857 if (wrqu->power.flags != IW_TXPOW_DBM) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008858 err = -EINVAL;
8859 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008860 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008861
James Ketrenosb095c382005-08-24 22:04:42 -05008862 if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
James Ketrenosafbf30a2005-08-25 00:05:33 -05008863 (wrqu->power.value < IPW_TX_POWER_MIN)) {
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008864 err = -EINVAL;
8865 goto out;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008866 }
James Ketrenos43f66a62005-03-25 12:31:53 -06008867
8868 priv->tx_power = wrqu->power.value;
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008869 err = ipw_set_tx_power(priv);
8870 out:
James Ketrenosc848d0a2005-08-24 21:56:24 -05008871 up(&priv->sem);
Zhu Yi6de9f7f2005-08-11 14:39:33 +08008872 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06008873}
8874
Jeff Garzikbf794512005-07-31 13:07:26 -04008875static int ipw_wx_get_txpow(struct net_device *dev,
8876 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008877 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008878{
James Ketrenos43f66a62005-03-25 12:31:53 -06008879 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008880 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008881 wrqu->power.value = priv->tx_power;
8882 wrqu->power.fixed = 1;
8883 wrqu->power.flags = IW_TXPOW_DBM;
8884 wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05008885 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008886
Jeff Garzikbf794512005-07-31 13:07:26 -04008887 IPW_DEBUG_WX("GET TX Power -> %s %d \n",
Zhu Yi22501c82005-08-11 10:49:17 +08008888 wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
James Ketrenos43f66a62005-03-25 12:31:53 -06008889
8890 return 0;
8891}
8892
Jeff Garzikbf794512005-07-31 13:07:26 -04008893static int ipw_wx_set_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008894 struct iw_request_info *info,
8895 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008896{
8897 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008898 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008899 if (wrqu->frag.disabled)
8900 priv->ieee->fts = DEFAULT_FTS;
8901 else {
8902 if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
James Ketrenosb095c382005-08-24 22:04:42 -05008903 wrqu->frag.value > MAX_FRAG_THRESHOLD) {
8904 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008905 return -EINVAL;
James Ketrenosb095c382005-08-24 22:04:42 -05008906 }
Jeff Garzikbf794512005-07-31 13:07:26 -04008907
James Ketrenos43f66a62005-03-25 12:31:53 -06008908 priv->ieee->fts = wrqu->frag.value & ~0x1;
8909 }
8910
8911 ipw_send_frag_threshold(priv, wrqu->frag.value);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008912 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008913 IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
8914 return 0;
8915}
8916
Jeff Garzikbf794512005-07-31 13:07:26 -04008917static int ipw_wx_get_frag(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008918 struct iw_request_info *info,
8919 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06008920{
8921 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008922 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008923 wrqu->frag.value = priv->ieee->fts;
8924 wrqu->frag.fixed = 0; /* no auto select */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04008925 wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
James Ketrenosc848d0a2005-08-24 21:56:24 -05008926 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06008927 IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
8928
8929 return 0;
8930}
8931
Jeff Garzikbf794512005-07-31 13:07:26 -04008932static int ipw_wx_set_retry(struct net_device *dev,
8933 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008934 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008935{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008936 struct ipw_priv *priv = ieee80211_priv(dev);
8937
8938 if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
8939 return -EINVAL;
8940
8941 if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
8942 return 0;
8943
8944 if (wrqu->retry.value < 0 || wrqu->retry.value > 255)
8945 return -EINVAL;
8946
8947 down(&priv->sem);
8948 if (wrqu->retry.flags & IW_RETRY_MIN)
8949 priv->short_retry_limit = (u8) wrqu->retry.value;
8950 else if (wrqu->retry.flags & IW_RETRY_MAX)
8951 priv->long_retry_limit = (u8) wrqu->retry.value;
8952 else {
8953 priv->short_retry_limit = (u8) wrqu->retry.value;
8954 priv->long_retry_limit = (u8) wrqu->retry.value;
8955 }
8956
8957 ipw_send_retry_limit(priv, priv->short_retry_limit,
8958 priv->long_retry_limit);
8959 up(&priv->sem);
8960 IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n",
8961 priv->short_retry_limit, priv->long_retry_limit);
8962 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008963}
8964
Jeff Garzikbf794512005-07-31 13:07:26 -04008965static int ipw_wx_get_retry(struct net_device *dev,
8966 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06008967 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04008968{
James Ketrenosafbf30a2005-08-25 00:05:33 -05008969 struct ipw_priv *priv = ieee80211_priv(dev);
8970
8971 down(&priv->sem);
8972 wrqu->retry.disabled = 0;
8973
8974 if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
8975 up(&priv->sem);
8976 return -EINVAL;
8977 }
8978
8979 if (wrqu->retry.flags & IW_RETRY_MAX) {
8980 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
8981 wrqu->retry.value = priv->long_retry_limit;
8982 } else if (wrqu->retry.flags & IW_RETRY_MIN) {
8983 wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
8984 wrqu->retry.value = priv->short_retry_limit;
8985 } else {
8986 wrqu->retry.flags = IW_RETRY_LIMIT;
8987 wrqu->retry.value = priv->short_retry_limit;
8988 }
8989 up(&priv->sem);
8990
8991 IPW_DEBUG_WX("GET retry -> %d \n", wrqu->retry.value);
8992
8993 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06008994}
8995
James Ketrenosafbf30a2005-08-25 00:05:33 -05008996static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
8997 int essid_len)
8998{
8999 struct ipw_scan_request_ext scan;
9000 int err = 0, scan_type;
9001
Pekka Enbergefb34422005-11-16 21:55:05 +02009002 if (!(priv->status & STATUS_INIT) ||
9003 (priv->status & STATUS_EXIT_PENDING))
9004 return 0;
9005
James Ketrenosafbf30a2005-08-25 00:05:33 -05009006 down(&priv->sem);
9007
9008 if (priv->status & STATUS_RF_KILL_MASK) {
9009 IPW_DEBUG_HC("Aborting scan due to RF kill activation\n");
9010 priv->status |= STATUS_SCAN_PENDING;
9011 goto done;
9012 }
9013
9014 IPW_DEBUG_HC("starting request direct scan!\n");
9015
9016 if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
Olaf Kirchd834a412006-01-09 17:00:37 +01009017 /* We should not sleep here; otherwise we will block most
9018 * of the system (for instance, we hold rtnl_lock when we
9019 * get here).
9020 */
9021 err = -EAGAIN;
9022 goto done;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009023 }
9024 memset(&scan, 0, sizeof(scan));
9025
9026 if (priv->config & CFG_SPEED_SCAN)
9027 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
9028 cpu_to_le16(30);
9029 else
9030 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
9031 cpu_to_le16(20);
9032
9033 scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
9034 cpu_to_le16(20);
Liu Hong1fe0adb2005-08-19 09:33:10 -05009035 scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009036 scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
9037
9038 scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
9039
9040 err = ipw_send_ssid(priv, essid, essid_len);
9041 if (err) {
9042 IPW_DEBUG_HC("Attempt to send SSID command failed\n");
9043 goto done;
9044 }
9045 scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
9046
9047 ipw_add_scan_channels(priv, &scan, scan_type);
9048
9049 err = ipw_send_scan_request_ext(priv, &scan);
9050 if (err) {
9051 IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
9052 goto done;
9053 }
9054
9055 priv->status |= STATUS_SCANNING;
9056
9057 done:
9058 up(&priv->sem);
9059 return err;
James Ketrenos43f66a62005-03-25 12:31:53 -06009060}
9061
Jeff Garzikbf794512005-07-31 13:07:26 -04009062static int ipw_wx_set_scan(struct net_device *dev,
9063 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009064 union iwreq_data *wrqu, char *extra)
9065{
9066 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009067 struct iw_scan_req *req = NULL;
9068 if (wrqu->data.length
9069 && wrqu->data.length == sizeof(struct iw_scan_req)) {
9070 req = (struct iw_scan_req *)extra;
9071 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
9072 ipw_request_direct_scan(priv, req->essid,
9073 req->essid_len);
9074 return 0;
9075 }
9076 }
James Ketrenos8935f392005-10-05 15:59:08 -05009077
James Ketrenos43f66a62005-03-25 12:31:53 -06009078 IPW_DEBUG_WX("Start scan\n");
James Ketrenosb095c382005-08-24 22:04:42 -05009079
9080 queue_work(priv->workqueue, &priv->request_scan);
9081
James Ketrenos43f66a62005-03-25 12:31:53 -06009082 return 0;
9083}
9084
Jeff Garzikbf794512005-07-31 13:07:26 -04009085static int ipw_wx_get_scan(struct net_device *dev,
9086 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009087 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009088{
James Ketrenos43f66a62005-03-25 12:31:53 -06009089 struct ipw_priv *priv = ieee80211_priv(dev);
9090 return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
9091}
9092
Jeff Garzikbf794512005-07-31 13:07:26 -04009093static int ipw_wx_set_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009094 struct iw_request_info *info,
9095 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009096{
9097 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009098 int ret;
Hong Liucaeff812005-08-05 17:25:50 +08009099 u32 cap = priv->capability;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009100
9101 down(&priv->sem);
9102 ret = ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009103
Hong Liucaeff812005-08-05 17:25:50 +08009104 /* In IBSS mode, we need to notify the firmware to update
9105 * the beacon info after we changed the capability. */
9106 if (cap != priv->capability &&
9107 priv->ieee->iw_mode == IW_MODE_ADHOC &&
9108 priv->status & STATUS_ASSOCIATED)
9109 ipw_disassociate(priv);
9110
9111 up(&priv->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -05009112 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009113}
9114
Jeff Garzikbf794512005-07-31 13:07:26 -04009115static int ipw_wx_get_encode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009116 struct iw_request_info *info,
9117 union iwreq_data *wrqu, char *key)
James Ketrenos43f66a62005-03-25 12:31:53 -06009118{
9119 struct ipw_priv *priv = ieee80211_priv(dev);
9120 return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
9121}
9122
Jeff Garzikbf794512005-07-31 13:07:26 -04009123static int ipw_wx_set_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009124 struct iw_request_info *info,
9125 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009126{
9127 struct ipw_priv *priv = ieee80211_priv(dev);
9128 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009129 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009130 if (wrqu->power.disabled) {
9131 priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
9132 err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
9133 if (err) {
9134 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009135 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009136 return err;
9137 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009138 IPW_DEBUG_WX("SET Power Management Mode -> off\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009139 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009140 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009141 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009142
9143 switch (wrqu->power.flags & IW_POWER_MODE) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009144 case IW_POWER_ON: /* If not specified */
9145 case IW_POWER_MODE: /* If set all mask */
9146 case IW_POWER_ALL_R: /* If explicitely state all */
James Ketrenos43f66a62005-03-25 12:31:53 -06009147 break;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009148 default: /* Otherwise we don't support it */
James Ketrenos43f66a62005-03-25 12:31:53 -06009149 IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
9150 wrqu->power.flags);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009151 up(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04009152 return -EOPNOTSUPP;
James Ketrenos43f66a62005-03-25 12:31:53 -06009153 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009154
James Ketrenos43f66a62005-03-25 12:31:53 -06009155 /* If the user hasn't specified a power management mode yet, default
9156 * to BATTERY */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009157 if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
James Ketrenos43f66a62005-03-25 12:31:53 -06009158 priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
Jeff Garzikbf794512005-07-31 13:07:26 -04009159 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009160 priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
9161 err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
9162 if (err) {
9163 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009164 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009165 return err;
9166 }
9167
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009168 IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009169 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009170 return 0;
9171}
9172
Jeff Garzikbf794512005-07-31 13:07:26 -04009173static int ipw_wx_get_power(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009174 struct iw_request_info *info,
9175 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009176{
9177 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009178 down(&priv->sem);
James Ketrenosa613bff2005-08-24 21:43:11 -05009179 if (!(priv->power_mode & IPW_POWER_ENABLED))
James Ketrenos43f66a62005-03-25 12:31:53 -06009180 wrqu->power.disabled = 1;
James Ketrenosa613bff2005-08-24 21:43:11 -05009181 else
James Ketrenos43f66a62005-03-25 12:31:53 -06009182 wrqu->power.disabled = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009183
James Ketrenosc848d0a2005-08-24 21:56:24 -05009184 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009185 IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04009186
James Ketrenos43f66a62005-03-25 12:31:53 -06009187 return 0;
9188}
9189
Jeff Garzikbf794512005-07-31 13:07:26 -04009190static int ipw_wx_set_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009191 struct iw_request_info *info,
9192 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009193{
9194 struct ipw_priv *priv = ieee80211_priv(dev);
9195 int mode = *(int *)extra;
9196 int err;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009197 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009198 if ((mode < 1) || (mode > IPW_POWER_LIMIT)) {
9199 mode = IPW_POWER_AC;
9200 priv->power_mode = mode;
9201 } else {
9202 priv->power_mode = IPW_POWER_ENABLED | mode;
9203 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009204
James Ketrenos43f66a62005-03-25 12:31:53 -06009205 if (priv->power_mode != mode) {
9206 err = ipw_send_power_mode(priv, mode);
Jeff Garzikbf794512005-07-31 13:07:26 -04009207
James Ketrenos43f66a62005-03-25 12:31:53 -06009208 if (err) {
9209 IPW_DEBUG_WX("failed setting power mode.\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009210 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009211 return err;
9212 }
9213 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009214 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009215 return 0;
9216}
9217
9218#define MAX_WX_STRING 80
Jeff Garzikbf794512005-07-31 13:07:26 -04009219static int ipw_wx_get_powermode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009220 struct iw_request_info *info,
9221 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009222{
9223 struct ipw_priv *priv = ieee80211_priv(dev);
9224 int level = IPW_POWER_LEVEL(priv->power_mode);
9225 char *p = extra;
9226
9227 p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
9228
9229 switch (level) {
9230 case IPW_POWER_AC:
9231 p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
9232 break;
9233 case IPW_POWER_BATTERY:
9234 p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
9235 break;
9236 default:
9237 p += snprintf(p, MAX_WX_STRING - (p - extra),
Jeff Garzikbf794512005-07-31 13:07:26 -04009238 "(Timeout %dms, Period %dms)",
James Ketrenos43f66a62005-03-25 12:31:53 -06009239 timeout_duration[level - 1] / 1000,
9240 period_duration[level - 1] / 1000);
9241 }
9242
9243 if (!(priv->power_mode & IPW_POWER_ENABLED))
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009244 p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
James Ketrenos43f66a62005-03-25 12:31:53 -06009245
9246 wrqu->data.length = p - extra + 1;
9247
9248 return 0;
9249}
9250
9251static int ipw_wx_set_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009252 struct iw_request_info *info,
9253 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009254{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009255 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenos43f66a62005-03-25 12:31:53 -06009256 int mode = *(int *)extra;
9257 u8 band = 0, modulation = 0;
9258
9259 if (mode == 0 || mode & ~IEEE_MODE_MASK) {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009260 IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
James Ketrenos43f66a62005-03-25 12:31:53 -06009261 return -EINVAL;
9262 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009263 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009264 if (priv->adapter == IPW_2915ABG) {
James Ketrenosa33a1982005-09-14 14:28:59 -05009265 priv->ieee->abg_true = 1;
James Ketrenos43f66a62005-03-25 12:31:53 -06009266 if (mode & IEEE_A) {
9267 band |= IEEE80211_52GHZ_BAND;
9268 modulation |= IEEE80211_OFDM_MODULATION;
9269 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009270 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009271 } else {
9272 if (mode & IEEE_A) {
9273 IPW_WARNING("Attempt to set 2200BG into "
9274 "802.11a mode\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009275 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009276 return -EINVAL;
9277 }
9278
James Ketrenosa33a1982005-09-14 14:28:59 -05009279 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009280 }
9281
9282 if (mode & IEEE_B) {
9283 band |= IEEE80211_24GHZ_BAND;
9284 modulation |= IEEE80211_CCK_MODULATION;
9285 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009286 priv->ieee->abg_true = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009287
James Ketrenos43f66a62005-03-25 12:31:53 -06009288 if (mode & IEEE_G) {
9289 band |= IEEE80211_24GHZ_BAND;
9290 modulation |= IEEE80211_OFDM_MODULATION;
9291 } else
James Ketrenosa33a1982005-09-14 14:28:59 -05009292 priv->ieee->abg_true = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009293
9294 priv->ieee->mode = mode;
9295 priv->ieee->freq_band = band;
9296 priv->ieee->modulation = modulation;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009297 init_supported_rates(priv, &priv->rates);
James Ketrenos43f66a62005-03-25 12:31:53 -06009298
James Ketrenosc848d0a2005-08-24 21:56:24 -05009299 /* Network configuration changed -- force [re]association */
9300 IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
9301 if (!ipw_disassociate(priv)) {
James Ketrenos43f66a62005-03-25 12:31:53 -06009302 ipw_send_supported_rates(priv, &priv->rates);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009303 ipw_associate(priv);
9304 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009305
James Ketrenosa613bff2005-08-24 21:43:11 -05009306 /* Update the band LEDs */
9307 ipw_led_band_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009308
Jeff Garzikbf794512005-07-31 13:07:26 -04009309 IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009310 mode & IEEE_A ? 'a' : '.',
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009311 mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
James Ketrenosc848d0a2005-08-24 21:56:24 -05009312 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009313 return 0;
9314}
9315
9316static int ipw_wx_get_wireless_mode(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009317 struct iw_request_info *info,
9318 union iwreq_data *wrqu, char *extra)
James Ketrenos43f66a62005-03-25 12:31:53 -06009319{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009320 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009321 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009322 switch (priv->ieee->mode) {
9323 case IEEE_A:
James Ketrenos43f66a62005-03-25 12:31:53 -06009324 strncpy(extra, "802.11a (1)", MAX_WX_STRING);
9325 break;
James Ketrenosea2b26e2005-08-24 21:25:16 -05009326 case IEEE_B:
9327 strncpy(extra, "802.11b (2)", MAX_WX_STRING);
9328 break;
9329 case IEEE_A | IEEE_B:
9330 strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
9331 break;
9332 case IEEE_G:
9333 strncpy(extra, "802.11g (4)", MAX_WX_STRING);
9334 break;
9335 case IEEE_A | IEEE_G:
9336 strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
9337 break;
9338 case IEEE_B | IEEE_G:
9339 strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
9340 break;
9341 case IEEE_A | IEEE_B | IEEE_G:
9342 strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
9343 break;
9344 default:
9345 strncpy(extra, "unknown", MAX_WX_STRING);
James Ketrenos43f66a62005-03-25 12:31:53 -06009346 break;
Jeff Garzikbf794512005-07-31 13:07:26 -04009347 }
9348
James Ketrenos43f66a62005-03-25 12:31:53 -06009349 IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
9350
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009351 wrqu->data.length = strlen(extra) + 1;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009352 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009353
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009354 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009355}
9356
James Ketrenosea2b26e2005-08-24 21:25:16 -05009357static int ipw_wx_set_preamble(struct net_device *dev,
9358 struct iw_request_info *info,
9359 union iwreq_data *wrqu, char *extra)
9360{
9361 struct ipw_priv *priv = ieee80211_priv(dev);
9362 int mode = *(int *)extra;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009363 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009364 /* Switching from SHORT -> LONG requires a disassociation */
9365 if (mode == 1) {
9366 if (!(priv->config & CFG_PREAMBLE_LONG)) {
9367 priv->config |= CFG_PREAMBLE_LONG;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009368
9369 /* Network configuration changed -- force [re]association */
9370 IPW_DEBUG_ASSOC
9371 ("[re]association triggered due to preamble change.\n");
9372 if (!ipw_disassociate(priv))
9373 ipw_associate(priv);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009374 }
9375 goto done;
9376 }
9377
9378 if (mode == 0) {
9379 priv->config &= ~CFG_PREAMBLE_LONG;
9380 goto done;
9381 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009382 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009383 return -EINVAL;
9384
9385 done:
James Ketrenosc848d0a2005-08-24 21:56:24 -05009386 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009387 return 0;
9388}
9389
9390static int ipw_wx_get_preamble(struct net_device *dev,
9391 struct iw_request_info *info,
9392 union iwreq_data *wrqu, char *extra)
9393{
9394 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009395 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009396 if (priv->config & CFG_PREAMBLE_LONG)
9397 snprintf(wrqu->name, IFNAMSIZ, "long (1)");
9398 else
9399 snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
James Ketrenosc848d0a2005-08-24 21:56:24 -05009400 up(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009401 return 0;
9402}
9403
James Ketrenosb095c382005-08-24 22:04:42 -05009404#ifdef CONFIG_IPW2200_MONITOR
James Ketrenosea2b26e2005-08-24 21:25:16 -05009405static int ipw_wx_set_monitor(struct net_device *dev,
Jeff Garzikbf794512005-07-31 13:07:26 -04009406 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009407 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009408{
James Ketrenos43f66a62005-03-25 12:31:53 -06009409 struct ipw_priv *priv = ieee80211_priv(dev);
9410 int *parms = (int *)extra;
9411 int enable = (parms[0] > 0);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009412 down(&priv->sem);
James Ketrenosea2b26e2005-08-24 21:25:16 -05009413 IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
James Ketrenos43f66a62005-03-25 12:31:53 -06009414 if (enable) {
9415 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
Mike Kershaw24a47db2005-08-26 00:41:54 -05009416#ifdef CONFIG_IEEE80211_RADIOTAP
9417 priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
9418#else
James Ketrenos43f66a62005-03-25 12:31:53 -06009419 priv->net_dev->type = ARPHRD_IEEE80211;
Mike Kershaw24a47db2005-08-26 00:41:54 -05009420#endif
James Ketrenosa613bff2005-08-24 21:43:11 -05009421 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009422 }
Jeff Garzikbf794512005-07-31 13:07:26 -04009423
James Ketrenos43f66a62005-03-25 12:31:53 -06009424 ipw_set_channel(priv, parms[1]);
9425 } else {
James Ketrenosc848d0a2005-08-24 21:56:24 -05009426 if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
9427 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009428 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009429 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009430 priv->net_dev->type = ARPHRD_ETHER;
James Ketrenosa613bff2005-08-24 21:43:11 -05009431 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009432 }
James Ketrenosc848d0a2005-08-24 21:56:24 -05009433 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009434 return 0;
9435}
9436
James Ketrenosb095c382005-08-24 22:04:42 -05009437#endif // CONFIG_IPW2200_MONITOR
9438
Jeff Garzikbf794512005-07-31 13:07:26 -04009439static int ipw_wx_reset(struct net_device *dev,
9440 struct iw_request_info *info,
James Ketrenos43f66a62005-03-25 12:31:53 -06009441 union iwreq_data *wrqu, char *extra)
Jeff Garzikbf794512005-07-31 13:07:26 -04009442{
James Ketrenos43f66a62005-03-25 12:31:53 -06009443 struct ipw_priv *priv = ieee80211_priv(dev);
9444 IPW_DEBUG_WX("RESET\n");
James Ketrenosa613bff2005-08-24 21:43:11 -05009445 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenos43f66a62005-03-25 12:31:53 -06009446 return 0;
9447}
James Ketrenosb095c382005-08-24 22:04:42 -05009448
James Ketrenosb095c382005-08-24 22:04:42 -05009449static int ipw_wx_sw_reset(struct net_device *dev,
9450 struct iw_request_info *info,
9451 union iwreq_data *wrqu, char *extra)
9452{
9453 struct ipw_priv *priv = ieee80211_priv(dev);
9454 union iwreq_data wrqu_sec = {
9455 .encoding = {
9456 .flags = IW_ENCODE_DISABLED,
9457 },
9458 };
James Ketrenosafbf30a2005-08-25 00:05:33 -05009459 int ret;
James Ketrenosb095c382005-08-24 22:04:42 -05009460
9461 IPW_DEBUG_WX("SW_RESET\n");
9462
9463 down(&priv->sem);
9464
James Ketrenosafbf30a2005-08-25 00:05:33 -05009465 ret = ipw_sw_reset(priv, 0);
9466 if (!ret) {
9467 free_firmware();
9468 ipw_adapter_restart(priv);
9469 }
James Ketrenosb095c382005-08-24 22:04:42 -05009470
9471 /* The SW reset bit might have been toggled on by the 'disable'
9472 * module parameter, so take appropriate action */
9473 ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
9474
9475 up(&priv->sem);
9476 ieee80211_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL);
9477 down(&priv->sem);
9478
9479 if (!(priv->status & STATUS_RF_KILL_MASK)) {
9480 /* Configuration likely changed -- force [re]association */
9481 IPW_DEBUG_ASSOC("[re]association triggered due to sw "
9482 "reset.\n");
9483 if (!ipw_disassociate(priv))
9484 ipw_associate(priv);
9485 }
9486
9487 up(&priv->sem);
9488
9489 return 0;
9490}
James Ketrenos43f66a62005-03-25 12:31:53 -06009491
9492/* Rebase the WE IOCTLs to zero for the handler array */
9493#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009494static iw_handler ipw_wx_handlers[] = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009495 IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
9496 IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
9497 IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
9498 IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
9499 IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
9500 IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
9501 IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
9502 IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
9503 IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
9504 IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
9505 IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
9506 IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
9507 IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
9508 IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
9509 IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
9510 IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
9511 IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
9512 IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
9513 IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
9514 IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
9515 IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
9516 IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
9517 IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
9518 IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
9519 IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
9520 IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
9521 IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
9522 IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
James Ketrenosa613bff2005-08-24 21:43:11 -05009523 IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
9524 IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
9525 IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
9526 IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
James Ketrenosafbf30a2005-08-25 00:05:33 -05009527 IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
9528 IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
9529 IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
9530 IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth,
9531 IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
9532 IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
9533 IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
James Ketrenos43f66a62005-03-25 12:31:53 -06009534};
9535
James Ketrenosb095c382005-08-24 22:04:42 -05009536enum {
9537 IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV,
9538 IPW_PRIV_GET_POWER,
9539 IPW_PRIV_SET_MODE,
9540 IPW_PRIV_GET_MODE,
9541 IPW_PRIV_SET_PREAMBLE,
9542 IPW_PRIV_GET_PREAMBLE,
9543 IPW_PRIV_RESET,
9544 IPW_PRIV_SW_RESET,
9545#ifdef CONFIG_IPW2200_MONITOR
9546 IPW_PRIV_SET_MONITOR,
9547#endif
9548};
James Ketrenos43f66a62005-03-25 12:31:53 -06009549
Jeff Garzikbf794512005-07-31 13:07:26 -04009550static struct iw_priv_args ipw_priv_args[] = {
James Ketrenos43f66a62005-03-25 12:31:53 -06009551 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009552 .cmd = IPW_PRIV_SET_POWER,
9553 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9554 .name = "set_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009555 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009556 .cmd = IPW_PRIV_GET_POWER,
9557 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9558 .name = "get_power"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009559 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009560 .cmd = IPW_PRIV_SET_MODE,
9561 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9562 .name = "set_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009563 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009564 .cmd = IPW_PRIV_GET_MODE,
9565 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
9566 .name = "get_mode"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009567 {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009568 .cmd = IPW_PRIV_SET_PREAMBLE,
9569 .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
9570 .name = "set_preamble"},
9571 {
9572 .cmd = IPW_PRIV_GET_PREAMBLE,
9573 .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
9574 .name = "get_preamble"},
James Ketrenos43f66a62005-03-25 12:31:53 -06009575 {
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009576 IPW_PRIV_RESET,
9577 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
James Ketrenosb095c382005-08-24 22:04:42 -05009578 {
9579 IPW_PRIV_SW_RESET,
9580 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"},
9581#ifdef CONFIG_IPW2200_MONITOR
9582 {
9583 IPW_PRIV_SET_MONITOR,
9584 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
9585#endif /* CONFIG_IPW2200_MONITOR */
James Ketrenos43f66a62005-03-25 12:31:53 -06009586};
9587
9588static iw_handler ipw_priv_handler[] = {
9589 ipw_wx_set_powermode,
9590 ipw_wx_get_powermode,
9591 ipw_wx_set_wireless_mode,
9592 ipw_wx_get_wireless_mode,
James Ketrenosea2b26e2005-08-24 21:25:16 -05009593 ipw_wx_set_preamble,
9594 ipw_wx_get_preamble,
Jeff Garzikbf794512005-07-31 13:07:26 -04009595 ipw_wx_reset,
James Ketrenosb095c382005-08-24 22:04:42 -05009596 ipw_wx_sw_reset,
9597#ifdef CONFIG_IPW2200_MONITOR
9598 ipw_wx_set_monitor,
James Ketrenos43f66a62005-03-25 12:31:53 -06009599#endif
9600};
9601
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009602static struct iw_handler_def ipw_wx_handler_def = {
James Ketrenosea2b26e2005-08-24 21:25:16 -05009603 .standard = ipw_wx_handlers,
9604 .num_standard = ARRAY_SIZE(ipw_wx_handlers),
9605 .num_private = ARRAY_SIZE(ipw_priv_handler),
9606 .num_private_args = ARRAY_SIZE(ipw_priv_args),
9607 .private = ipw_priv_handler,
9608 .private_args = ipw_priv_args,
Benoit Boissinot97a78ca2005-09-15 17:30:28 +00009609 .get_wireless_stats = ipw_get_wireless_stats,
James Ketrenos43f66a62005-03-25 12:31:53 -06009610};
9611
James Ketrenos43f66a62005-03-25 12:31:53 -06009612/*
9613 * Get wireless statistics.
9614 * Called by /proc/net/wireless
9615 * Also called by SIOCGIWSTATS
9616 */
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009617static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
James Ketrenos43f66a62005-03-25 12:31:53 -06009618{
9619 struct ipw_priv *priv = ieee80211_priv(dev);
9620 struct iw_statistics *wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04009621
James Ketrenos43f66a62005-03-25 12:31:53 -06009622 wstats = &priv->wstats;
9623
James Ketrenosea2b26e2005-08-24 21:25:16 -05009624 /* if hw is disabled, then ipw_get_ordinal() can't be called.
James Ketrenosafbf30a2005-08-25 00:05:33 -05009625 * netdev->get_wireless_stats seems to be called before fw is
James Ketrenos43f66a62005-03-25 12:31:53 -06009626 * initialized. STATUS_ASSOCIATED will only be set if the hw is up
9627 * and associated; if not associcated, the values are all meaningless
9628 * anyway, so set them all to NULL and INVALID */
9629 if (!(priv->status & STATUS_ASSOCIATED)) {
9630 wstats->miss.beacon = 0;
9631 wstats->discard.retries = 0;
9632 wstats->qual.qual = 0;
9633 wstats->qual.level = 0;
9634 wstats->qual.noise = 0;
9635 wstats->qual.updated = 7;
9636 wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009637 IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
James Ketrenos43f66a62005-03-25 12:31:53 -06009638 return wstats;
Jeff Garzikbf794512005-07-31 13:07:26 -04009639 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009640
9641 wstats->qual.qual = priv->quality;
9642 wstats->qual.level = average_value(&priv->average_rssi);
9643 wstats->qual.noise = average_value(&priv->average_noise);
9644 wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009645 IW_QUAL_NOISE_UPDATED;
James Ketrenos43f66a62005-03-25 12:31:53 -06009646
9647 wstats->miss.beacon = average_value(&priv->average_missed_beacons);
9648 wstats->discard.retries = priv->last_tx_failures;
9649 wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
Jeff Garzikbf794512005-07-31 13:07:26 -04009650
James Ketrenos43f66a62005-03-25 12:31:53 -06009651/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
9652 goto fail_get_ordinal;
9653 wstats->discard.retries += tx_retry; */
Jeff Garzikbf794512005-07-31 13:07:26 -04009654
James Ketrenos43f66a62005-03-25 12:31:53 -06009655 return wstats;
9656}
9657
James Ketrenos43f66a62005-03-25 12:31:53 -06009658/* net device stuff */
9659
Arjan van de Ven858119e2006-01-14 13:20:43 -08009660static void init_sys_config(struct ipw_sys_config *sys_config)
James Ketrenos43f66a62005-03-25 12:31:53 -06009661{
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009662 memset(sys_config, 0, sizeof(struct ipw_sys_config));
Zhu Yi810dabd2006-01-24 16:36:59 +08009663 sys_config->bt_coexistence = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009664 sys_config->answer_broadcast_ssid_probe = 0;
9665 sys_config->accept_all_data_frames = 0;
9666 sys_config->accept_non_directed_frames = 1;
9667 sys_config->exclude_unicast_unencrypted = 0;
9668 sys_config->disable_unicast_decryption = 1;
9669 sys_config->exclude_multicast_unencrypted = 0;
9670 sys_config->disable_multicast_decryption = 1;
9671 sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009672 sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
James Ketrenos43f66a62005-03-25 12:31:53 -06009673 sys_config->dot11g_auto_detection = 0;
Jeff Garzikbf794512005-07-31 13:07:26 -04009674 sys_config->enable_cts_to_self = 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009675 sys_config->bt_coexist_collision_thr = 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009676 sys_config->pass_noise_stats_to_host = 1; //1 -- fix for 256
James Ketrenos43f66a62005-03-25 12:31:53 -06009677}
9678
9679static int ipw_net_open(struct net_device *dev)
9680{
9681 struct ipw_priv *priv = ieee80211_priv(dev);
9682 IPW_DEBUG_INFO("dev->open\n");
9683 /* we should be verifying the device is ready to be opened */
James Ketrenosc848d0a2005-08-24 21:56:24 -05009684 down(&priv->sem);
Jeff Garzikbf794512005-07-31 13:07:26 -04009685 if (!(priv->status & STATUS_RF_KILL_MASK) &&
9686 (priv->status & STATUS_ASSOCIATED))
James Ketrenos43f66a62005-03-25 12:31:53 -06009687 netif_start_queue(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009688 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009689 return 0;
9690}
9691
9692static int ipw_net_stop(struct net_device *dev)
9693{
9694 IPW_DEBUG_INFO("dev->close\n");
9695 netif_stop_queue(dev);
9696 return 0;
9697}
9698
9699/*
9700todo:
9701
9702modify to send one tfd per fragment instead of using chunking. otherwise
9703we need to heavily modify the ieee80211_skb_to_txb.
9704*/
9705
Arjan van de Ven858119e2006-01-14 13:20:43 -08009706static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
James Ketrenos227d2dc2005-07-28 16:25:55 -05009707 int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06009708{
James Ketrenos0dacca12005-09-21 12:23:41 -05009709 struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009710 txb->fragments[0]->data;
James Ketrenos43f66a62005-03-25 12:31:53 -06009711 int i = 0;
9712 struct tfd_frame *tfd;
James Ketrenosb095c382005-08-24 22:04:42 -05009713#ifdef CONFIG_IPW_QOS
9714 int tx_id = ipw_get_tx_queue_number(priv, pri);
9715 struct clx2_tx_queue *txq = &priv->txq[tx_id];
9716#else
James Ketrenos43f66a62005-03-25 12:31:53 -06009717 struct clx2_tx_queue *txq = &priv->txq[0];
James Ketrenosb095c382005-08-24 22:04:42 -05009718#endif
James Ketrenos43f66a62005-03-25 12:31:53 -06009719 struct clx2_queue *q = &txq->q;
9720 u8 id, hdr_len, unicast;
9721 u16 remaining_bytes;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009722 int fc;
James Ketrenos43f66a62005-03-25 12:31:53 -06009723
James Ketrenos227d2dc2005-07-28 16:25:55 -05009724 /* If there isn't room in the queue, we return busy and let the
9725 * network stack requeue the packet for us */
9726 if (ipw_queue_space(q) < q->high_mark)
9727 return NETDEV_TX_BUSY;
James Ketrenos43f66a62005-03-25 12:31:53 -06009728
9729 switch (priv->ieee->iw_mode) {
9730 case IW_MODE_ADHOC:
9731 hdr_len = IEEE80211_3ADDR_LEN;
Stephen Hemminger3c190652006-01-03 15:27:38 -08009732 unicast = !is_multicast_ether_addr(hdr->addr1);
James Ketrenos43f66a62005-03-25 12:31:53 -06009733 id = ipw_find_station(priv, hdr->addr1);
9734 if (id == IPW_INVALID_STATION) {
9735 id = ipw_add_station(priv, hdr->addr1);
9736 if (id == IPW_INVALID_STATION) {
9737 IPW_WARNING("Attempt to send data to "
Jeff Garzikbf794512005-07-31 13:07:26 -04009738 "invalid cell: " MAC_FMT "\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009739 MAC_ARG(hdr->addr1));
9740 goto drop;
9741 }
9742 }
9743 break;
9744
9745 case IW_MODE_INFRA:
9746 default:
Stephen Hemminger3c190652006-01-03 15:27:38 -08009747 unicast = !is_multicast_ether_addr(hdr->addr3);
James Ketrenos43f66a62005-03-25 12:31:53 -06009748 hdr_len = IEEE80211_3ADDR_LEN;
9749 id = 0;
9750 break;
9751 }
9752
9753 tfd = &txq->bd[q->first_empty];
9754 txq->txb[q->first_empty] = txb;
9755 memset(tfd, 0, sizeof(*tfd));
9756 tfd->u.data.station_number = id;
9757
9758 tfd->control_flags.message_type = TX_FRAME_TYPE;
9759 tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
9760
9761 tfd->u.data.cmd_id = DINO_CMD_TX;
James Ketrenosa613bff2005-08-24 21:43:11 -05009762 tfd->u.data.len = cpu_to_le16(txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009763 remaining_bytes = txb->payload_size;
Jeff Garzikbf794512005-07-31 13:07:26 -04009764
James Ketrenos43f66a62005-03-25 12:31:53 -06009765 if (priv->assoc_request.ieee_mode == IPW_B_MODE)
James Ketrenosb095c382005-08-24 22:04:42 -05009766 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK;
James Ketrenos43f66a62005-03-25 12:31:53 -06009767 else
James Ketrenosb095c382005-08-24 22:04:42 -05009768 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM;
James Ketrenos43f66a62005-03-25 12:31:53 -06009769
James Ketrenosea2b26e2005-08-24 21:25:16 -05009770 if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
9771 tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
James Ketrenos43f66a62005-03-25 12:31:53 -06009772
James Ketrenosc848d0a2005-08-24 21:56:24 -05009773 fc = le16_to_cpu(hdr->frame_ctl);
9774 hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
James Ketrenos43f66a62005-03-25 12:31:53 -06009775
9776 memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
9777
James Ketrenosb095c382005-08-24 22:04:42 -05009778 if (likely(unicast))
9779 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
9780
9781 if (txb->encrypted && !priv->ieee->host_encrypt) {
9782 switch (priv->ieee->sec.level) {
9783 case SEC_LEVEL_3:
9784 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9785 IEEE80211_FCTL_PROTECTED;
9786 /* XXX: ACK flag must be set for CCMP even if it
9787 * is a multicast/broadcast packet, because CCMP
9788 * group communication encrypted by GTK is
9789 * actually done by the AP. */
9790 if (!unicast)
9791 tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
9792
9793 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
9794 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM;
9795 tfd->u.data.key_index = 0;
9796 tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE;
9797 break;
9798 case SEC_LEVEL_2:
9799 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9800 IEEE80211_FCTL_PROTECTED;
9801 tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
9802 tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP;
9803 tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE;
9804 break;
9805 case SEC_LEVEL_1:
9806 tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
9807 IEEE80211_FCTL_PROTECTED;
9808 tfd->u.data.key_index = priv->ieee->tx_keyidx;
9809 if (priv->ieee->sec.key_sizes[priv->ieee->tx_keyidx] <=
9810 40)
9811 tfd->u.data.key_index |= DCT_WEP_KEY_64Bit;
9812 else
9813 tfd->u.data.key_index |= DCT_WEP_KEY_128Bit;
9814 break;
9815 case SEC_LEVEL_0:
9816 break;
9817 default:
9818 printk(KERN_ERR "Unknow security level %d\n",
9819 priv->ieee->sec.level);
9820 break;
9821 }
9822 } else
9823 /* No hardware encryption */
9824 tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
9825
9826#ifdef CONFIG_IPW_QOS
9827 ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data), unicast);
9828#endif /* CONFIG_IPW_QOS */
9829
James Ketrenos43f66a62005-03-25 12:31:53 -06009830 /* payload */
James Ketrenosa613bff2005-08-24 21:43:11 -05009831 tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
9832 txb->nr_frags));
9833 IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
9834 txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
9835 for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
9836 IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
9837 i, le32_to_cpu(tfd->u.data.num_chunks),
9838 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009839 IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
James Ketrenos43f66a62005-03-25 12:31:53 -06009840 i, tfd->u.data.num_chunks,
9841 txb->fragments[i]->len - hdr_len);
Jeff Garzikbf794512005-07-31 13:07:26 -04009842 printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
James Ketrenos43f66a62005-03-25 12:31:53 -06009843 txb->fragments[i]->len - hdr_len);
9844
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009845 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05009846 cpu_to_le32(pci_map_single
9847 (priv->pci_dev,
9848 txb->fragments[i]->data + hdr_len,
9849 txb->fragments[i]->len - hdr_len,
9850 PCI_DMA_TODEVICE));
9851 tfd->u.data.chunk_len[i] =
9852 cpu_to_le16(txb->fragments[i]->len - hdr_len);
James Ketrenos43f66a62005-03-25 12:31:53 -06009853 }
9854
9855 if (i != txb->nr_frags) {
9856 struct sk_buff *skb;
9857 u16 remaining_bytes = 0;
9858 int j;
9859
9860 for (j = i; j < txb->nr_frags; j++)
9861 remaining_bytes += txb->fragments[j]->len - hdr_len;
9862
9863 printk(KERN_INFO "Trying to reallocate for %d bytes\n",
9864 remaining_bytes);
9865 skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
9866 if (skb != NULL) {
James Ketrenosa613bff2005-08-24 21:43:11 -05009867 tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
James Ketrenos43f66a62005-03-25 12:31:53 -06009868 for (j = i; j < txb->nr_frags; j++) {
9869 int size = txb->fragments[j]->len - hdr_len;
James Ketrenosafbf30a2005-08-25 00:05:33 -05009870
James Ketrenos43f66a62005-03-25 12:31:53 -06009871 printk(KERN_INFO "Adding frag %d %d...\n",
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009872 j, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009873 memcpy(skb_put(skb, size),
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009874 txb->fragments[j]->data + hdr_len, size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009875 }
9876 dev_kfree_skb_any(txb->fragments[i]);
9877 txb->fragments[i] = skb;
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009878 tfd->u.data.chunk_ptr[i] =
James Ketrenosa613bff2005-08-24 21:43:11 -05009879 cpu_to_le32(pci_map_single
9880 (priv->pci_dev, skb->data,
9881 tfd->u.data.chunk_len[i],
9882 PCI_DMA_TODEVICE));
9883
9884 tfd->u.data.num_chunks =
9885 cpu_to_le32(le32_to_cpu(tfd->u.data.num_chunks) +
9886 1);
Jeff Garzikbf794512005-07-31 13:07:26 -04009887 }
James Ketrenos43f66a62005-03-25 12:31:53 -06009888 }
9889
9890 /* kick DMA */
9891 q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
9892 ipw_write32(priv, q->reg_w, q->first_empty);
9893
James Ketrenos227d2dc2005-07-28 16:25:55 -05009894 return NETDEV_TX_OK;
James Ketrenos43f66a62005-03-25 12:31:53 -06009895
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009896 drop:
James Ketrenos43f66a62005-03-25 12:31:53 -06009897 IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
9898 ieee80211_txb_free(txb);
James Ketrenos227d2dc2005-07-28 16:25:55 -05009899 return NETDEV_TX_OK;
9900}
9901
9902static int ipw_net_is_queue_full(struct net_device *dev, int pri)
9903{
9904 struct ipw_priv *priv = ieee80211_priv(dev);
9905#ifdef CONFIG_IPW_QOS
9906 int tx_id = ipw_get_tx_queue_number(priv, pri);
9907 struct clx2_tx_queue *txq = &priv->txq[tx_id];
9908#else
9909 struct clx2_tx_queue *txq = &priv->txq[0];
9910#endif /* CONFIG_IPW_QOS */
9911
9912 if (ipw_queue_space(&txq->q) < txq->q.high_mark)
9913 return 1;
9914
9915 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -06009916}
9917
9918static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
James Ketrenosc8d42d12005-09-21 12:23:43 -05009919 struct net_device *dev, int pri)
James Ketrenos43f66a62005-03-25 12:31:53 -06009920{
9921 struct ipw_priv *priv = ieee80211_priv(dev);
9922 unsigned long flags;
James Ketrenos227d2dc2005-07-28 16:25:55 -05009923 int ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009924
9925 IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
James Ketrenos43f66a62005-03-25 12:31:53 -06009926 spin_lock_irqsave(&priv->lock, flags);
9927
9928 if (!(priv->status & STATUS_ASSOCIATED)) {
9929 IPW_DEBUG_INFO("Tx attempt while not associated.\n");
9930 priv->ieee->stats.tx_carrier_errors++;
9931 netif_stop_queue(dev);
9932 goto fail_unlock;
9933 }
9934
James Ketrenos227d2dc2005-07-28 16:25:55 -05009935 ret = ipw_tx_skb(priv, txb, pri);
9936 if (ret == NETDEV_TX_OK)
9937 __ipw_led_activity_on(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -06009938 spin_unlock_irqrestore(&priv->lock, flags);
James Ketrenos43f66a62005-03-25 12:31:53 -06009939
James Ketrenos227d2dc2005-07-28 16:25:55 -05009940 return ret;
James Ketrenos43f66a62005-03-25 12:31:53 -06009941
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009942 fail_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -06009943 spin_unlock_irqrestore(&priv->lock, flags);
9944 return 1;
9945}
9946
9947static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
9948{
9949 struct ipw_priv *priv = ieee80211_priv(dev);
Jeff Garzikbf794512005-07-31 13:07:26 -04009950
James Ketrenos43f66a62005-03-25 12:31:53 -06009951 priv->ieee->stats.tx_packets = priv->tx_packets;
9952 priv->ieee->stats.rx_packets = priv->rx_packets;
9953 return &priv->ieee->stats;
9954}
9955
9956static void ipw_net_set_multicast_list(struct net_device *dev)
9957{
9958
9959}
9960
9961static int ipw_net_set_mac_address(struct net_device *dev, void *p)
9962{
9963 struct ipw_priv *priv = ieee80211_priv(dev);
9964 struct sockaddr *addr = p;
9965 if (!is_valid_ether_addr(addr->sa_data))
9966 return -EADDRNOTAVAIL;
James Ketrenosc848d0a2005-08-24 21:56:24 -05009967 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009968 priv->config |= CFG_CUSTOM_MAC;
9969 memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
9970 printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n",
9971 priv->net_dev->name, MAC_ARG(priv->mac_addr));
James Ketrenosa613bff2005-08-24 21:43:11 -05009972 queue_work(priv->workqueue, &priv->adapter_restart);
James Ketrenosc848d0a2005-08-24 21:56:24 -05009973 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -06009974 return 0;
9975}
9976
Jeff Garzikbf794512005-07-31 13:07:26 -04009977static void ipw_ethtool_get_drvinfo(struct net_device *dev,
James Ketrenos43f66a62005-03-25 12:31:53 -06009978 struct ethtool_drvinfo *info)
9979{
9980 struct ipw_priv *p = ieee80211_priv(dev);
9981 char vers[64];
9982 char date[32];
9983 u32 len;
9984
9985 strcpy(info->driver, DRV_NAME);
9986 strcpy(info->version, DRV_VERSION);
9987
9988 len = sizeof(vers);
9989 ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
9990 len = sizeof(date);
9991 ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
9992
Jeff Garzik0edd5b42005-09-07 00:48:31 -04009993 snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
James Ketrenos43f66a62005-03-25 12:31:53 -06009994 vers, date);
9995 strcpy(info->bus_info, pci_name(p->pci_dev));
James Ketrenosb095c382005-08-24 22:04:42 -05009996 info->eedump_len = IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -06009997}
9998
9999static u32 ipw_ethtool_get_link(struct net_device *dev)
10000{
10001 struct ipw_priv *priv = ieee80211_priv(dev);
10002 return (priv->status & STATUS_ASSOCIATED) != 0;
10003}
10004
10005static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
10006{
James Ketrenosb095c382005-08-24 22:04:42 -050010007 return IPW_EEPROM_IMAGE_SIZE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010008}
10009
10010static int ipw_ethtool_get_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010011 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010012{
10013 struct ipw_priv *p = ieee80211_priv(dev);
10014
James Ketrenosb095c382005-08-24 22:04:42 -050010015 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010016 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010017 down(&p->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010018 memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010019 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010020 return 0;
10021}
10022
10023static int ipw_ethtool_set_eeprom(struct net_device *dev,
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010024 struct ethtool_eeprom *eeprom, u8 * bytes)
James Ketrenos43f66a62005-03-25 12:31:53 -060010025{
10026 struct ipw_priv *p = ieee80211_priv(dev);
10027 int i;
10028
James Ketrenosb095c382005-08-24 22:04:42 -050010029 if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
James Ketrenos43f66a62005-03-25 12:31:53 -060010030 return -EINVAL;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010031 down(&p->sem);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010032 memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len);
Jeff Garzikbf794512005-07-31 13:07:26 -040010033 for (i = IPW_EEPROM_DATA;
James Ketrenosb095c382005-08-24 22:04:42 -050010034 i < IPW_EEPROM_DATA + IPW_EEPROM_IMAGE_SIZE; i++)
James Ketrenos43f66a62005-03-25 12:31:53 -060010035 ipw_write8(p, i, p->eeprom[i]);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010036 up(&p->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010037 return 0;
10038}
10039
10040static struct ethtool_ops ipw_ethtool_ops = {
James Ketrenosea2b26e2005-08-24 21:25:16 -050010041 .get_link = ipw_ethtool_get_link,
10042 .get_drvinfo = ipw_ethtool_get_drvinfo,
10043 .get_eeprom_len = ipw_ethtool_get_eeprom_len,
10044 .get_eeprom = ipw_ethtool_get_eeprom,
10045 .set_eeprom = ipw_ethtool_set_eeprom,
James Ketrenos43f66a62005-03-25 12:31:53 -060010046};
10047
10048static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs)
10049{
10050 struct ipw_priv *priv = data;
10051 u32 inta, inta_mask;
Jeff Garzikbf794512005-07-31 13:07:26 -040010052
James Ketrenos43f66a62005-03-25 12:31:53 -060010053 if (!priv)
10054 return IRQ_NONE;
10055
10056 spin_lock(&priv->lock);
10057
10058 if (!(priv->status & STATUS_INT_ENABLED)) {
10059 /* Shared IRQ */
10060 goto none;
10061 }
10062
James Ketrenosb095c382005-08-24 22:04:42 -050010063 inta = ipw_read32(priv, IPW_INTA_RW);
10064 inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
Jeff Garzikbf794512005-07-31 13:07:26 -040010065
James Ketrenos43f66a62005-03-25 12:31:53 -060010066 if (inta == 0xFFFFFFFF) {
10067 /* Hardware disappeared */
10068 IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
10069 goto none;
10070 }
10071
James Ketrenosb095c382005-08-24 22:04:42 -050010072 if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010073 /* Shared interrupt */
10074 goto none;
10075 }
10076
10077 /* tell the device to stop sending interrupts */
10078 ipw_disable_interrupts(priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010079
James Ketrenos43f66a62005-03-25 12:31:53 -060010080 /* ack current interrupts */
James Ketrenosb095c382005-08-24 22:04:42 -050010081 inta &= (IPW_INTA_MASK_ALL & inta_mask);
10082 ipw_write32(priv, IPW_INTA_RW, inta);
Jeff Garzikbf794512005-07-31 13:07:26 -040010083
James Ketrenos43f66a62005-03-25 12:31:53 -060010084 /* Cache INTA value for our tasklet */
10085 priv->isr_inta = inta;
10086
10087 tasklet_schedule(&priv->irq_tasklet);
10088
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010089 spin_unlock(&priv->lock);
James Ketrenos43f66a62005-03-25 12:31:53 -060010090
10091 return IRQ_HANDLED;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010092 none:
James Ketrenos43f66a62005-03-25 12:31:53 -060010093 spin_unlock(&priv->lock);
10094 return IRQ_NONE;
10095}
10096
10097static void ipw_rf_kill(void *adapter)
10098{
10099 struct ipw_priv *priv = adapter;
10100 unsigned long flags;
Jeff Garzikbf794512005-07-31 13:07:26 -040010101
James Ketrenos43f66a62005-03-25 12:31:53 -060010102 spin_lock_irqsave(&priv->lock, flags);
10103
10104 if (rf_kill_active(priv)) {
10105 IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
10106 if (priv->workqueue)
10107 queue_delayed_work(priv->workqueue,
10108 &priv->rf_kill, 2 * HZ);
10109 goto exit_unlock;
10110 }
10111
10112 /* RF Kill is now disabled, so bring the device back up */
10113
10114 if (!(priv->status & STATUS_RF_KILL_MASK)) {
10115 IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
10116 "device\n");
10117
10118 /* we can not do an adapter restart while inside an irq lock */
10119 queue_work(priv->workqueue, &priv->adapter_restart);
Jeff Garzikbf794512005-07-31 13:07:26 -040010120 } else
James Ketrenos43f66a62005-03-25 12:31:53 -060010121 IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
10122 "enabled\n");
10123
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010124 exit_unlock:
James Ketrenos43f66a62005-03-25 12:31:53 -060010125 spin_unlock_irqrestore(&priv->lock, flags);
10126}
10127
James Ketrenosc848d0a2005-08-24 21:56:24 -050010128static void ipw_bg_rf_kill(void *data)
10129{
10130 struct ipw_priv *priv = data;
10131 down(&priv->sem);
10132 ipw_rf_kill(data);
10133 up(&priv->sem);
10134}
10135
Adrian Bunka73e22b2006-01-21 01:39:42 +010010136static void ipw_link_up(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -050010137{
James Ketrenosafbf30a2005-08-25 00:05:33 -050010138 priv->last_seq_num = -1;
10139 priv->last_frag_num = -1;
10140 priv->last_packet_time = 0;
10141
James Ketrenosa613bff2005-08-24 21:43:11 -050010142 netif_carrier_on(priv->net_dev);
10143 if (netif_queue_stopped(priv->net_dev)) {
10144 IPW_DEBUG_NOTIF("waking queue\n");
10145 netif_wake_queue(priv->net_dev);
10146 } else {
10147 IPW_DEBUG_NOTIF("starting queue\n");
10148 netif_start_queue(priv->net_dev);
10149 }
10150
James Ketrenosc848d0a2005-08-24 21:56:24 -050010151 cancel_delayed_work(&priv->request_scan);
James Ketrenosa613bff2005-08-24 21:43:11 -050010152 ipw_reset_stats(priv);
10153 /* Ensure the rate is updated immediately */
10154 priv->last_rate = ipw_get_current_rate(priv);
10155 ipw_gather_stats(priv);
10156 ipw_led_link_up(priv);
10157 notify_wx_assoc_event(priv);
10158
10159 if (priv->config & CFG_BACKGROUND_SCAN)
10160 queue_delayed_work(priv->workqueue, &priv->request_scan, HZ);
10161}
10162
James Ketrenosc848d0a2005-08-24 21:56:24 -050010163static void ipw_bg_link_up(void *data)
10164{
10165 struct ipw_priv *priv = data;
10166 down(&priv->sem);
10167 ipw_link_up(data);
10168 up(&priv->sem);
10169}
10170
Adrian Bunka73e22b2006-01-21 01:39:42 +010010171static void ipw_link_down(struct ipw_priv *priv)
James Ketrenosa613bff2005-08-24 21:43:11 -050010172{
10173 ipw_led_link_down(priv);
10174 netif_carrier_off(priv->net_dev);
10175 netif_stop_queue(priv->net_dev);
10176 notify_wx_assoc_event(priv);
10177
10178 /* Cancel any queued work ... */
10179 cancel_delayed_work(&priv->request_scan);
10180 cancel_delayed_work(&priv->adhoc_check);
10181 cancel_delayed_work(&priv->gather_stats);
10182
10183 ipw_reset_stats(priv);
10184
James Ketrenosafbf30a2005-08-25 00:05:33 -050010185 if (!(priv->status & STATUS_EXIT_PENDING)) {
10186 /* Queue up another scan... */
10187 queue_work(priv->workqueue, &priv->request_scan);
10188 }
James Ketrenosa613bff2005-08-24 21:43:11 -050010189}
10190
James Ketrenosc848d0a2005-08-24 21:56:24 -050010191static void ipw_bg_link_down(void *data)
10192{
10193 struct ipw_priv *priv = data;
10194 down(&priv->sem);
10195 ipw_link_down(data);
10196 up(&priv->sem);
10197}
10198
James Ketrenos43f66a62005-03-25 12:31:53 -060010199static int ipw_setup_deferred_work(struct ipw_priv *priv)
10200{
10201 int ret = 0;
10202
James Ketrenos43f66a62005-03-25 12:31:53 -060010203 priv->workqueue = create_workqueue(DRV_NAME);
James Ketrenos43f66a62005-03-25 12:31:53 -060010204 init_waitqueue_head(&priv->wait_command_queue);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010205 init_waitqueue_head(&priv->wait_state);
James Ketrenos43f66a62005-03-25 12:31:53 -060010206
James Ketrenosc848d0a2005-08-24 21:56:24 -050010207 INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv);
10208 INIT_WORK(&priv->associate, ipw_bg_associate, priv);
10209 INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv);
Zhu Yid8bad6d2005-07-13 12:25:38 -050010210 INIT_WORK(&priv->system_config, ipw_system_config, priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010211 INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv);
10212 INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv);
10213 INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv);
10214 INIT_WORK(&priv->up, (void (*)(void *))ipw_bg_up, priv);
10215 INIT_WORK(&priv->down, (void (*)(void *))ipw_bg_down, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010216 INIT_WORK(&priv->request_scan,
James Ketrenos43f66a62005-03-25 12:31:53 -060010217 (void (*)(void *))ipw_request_scan, priv);
Jeff Garzikbf794512005-07-31 13:07:26 -040010218 INIT_WORK(&priv->gather_stats,
James Ketrenosc848d0a2005-08-24 21:56:24 -050010219 (void (*)(void *))ipw_bg_gather_stats, priv);
10220 INIT_WORK(&priv->abort_scan, (void (*)(void *))ipw_bg_abort_scan, priv);
10221 INIT_WORK(&priv->roam, ipw_bg_roam, priv);
10222 INIT_WORK(&priv->scan_check, ipw_bg_scan_check, priv);
10223 INIT_WORK(&priv->link_up, (void (*)(void *))ipw_bg_link_up, priv);
10224 INIT_WORK(&priv->link_down, (void (*)(void *))ipw_bg_link_down, priv);
10225 INIT_WORK(&priv->led_link_on, (void (*)(void *))ipw_bg_led_link_on,
James Ketrenosa613bff2005-08-24 21:43:11 -050010226 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010227 INIT_WORK(&priv->led_link_off, (void (*)(void *))ipw_bg_led_link_off,
James Ketrenosa613bff2005-08-24 21:43:11 -050010228 priv);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010229 INIT_WORK(&priv->led_act_off, (void (*)(void *))ipw_bg_led_activity_off,
10230 priv);
10231 INIT_WORK(&priv->merge_networks,
10232 (void (*)(void *))ipw_merge_adhoc_network, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010233
James Ketrenosb095c382005-08-24 22:04:42 -050010234#ifdef CONFIG_IPW_QOS
10235 INIT_WORK(&priv->qos_activate, (void (*)(void *))ipw_bg_qos_activate,
10236 priv);
10237#endif /* CONFIG_IPW_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010238
10239 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
10240 ipw_irq_tasklet, (unsigned long)priv);
10241
10242 return ret;
10243}
10244
James Ketrenos43f66a62005-03-25 12:31:53 -060010245static void shim__set_security(struct net_device *dev,
10246 struct ieee80211_security *sec)
10247{
10248 struct ipw_priv *priv = ieee80211_priv(dev);
10249 int i;
Jeff Garzikbf794512005-07-31 13:07:26 -040010250 for (i = 0; i < 4; i++) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010251 if (sec->flags & (1 << i)) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050010252 priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
James Ketrenosb095c382005-08-24 22:04:42 -050010253 priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
James Ketrenos43f66a62005-03-25 12:31:53 -060010254 if (sec->key_sizes[i] == 0)
James Ketrenosb095c382005-08-24 22:04:42 -050010255 priv->ieee->sec.flags &= ~(1 << i);
10256 else {
10257 memcpy(priv->ieee->sec.keys[i], sec->keys[i],
James Ketrenos43f66a62005-03-25 12:31:53 -060010258 sec->key_sizes[i]);
James Ketrenosb095c382005-08-24 22:04:42 -050010259 priv->ieee->sec.flags |= (1 << i);
10260 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010261 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010262 } else if (sec->level != SEC_LEVEL_1)
10263 priv->ieee->sec.flags &= ~(1 << i);
James Ketrenos43f66a62005-03-25 12:31:53 -060010264 }
10265
James Ketrenosb095c382005-08-24 22:04:42 -050010266 if (sec->flags & SEC_ACTIVE_KEY) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010267 if (sec->active_key <= 3) {
James Ketrenosb095c382005-08-24 22:04:42 -050010268 priv->ieee->sec.active_key = sec->active_key;
10269 priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
Jeff Garzikbf794512005-07-31 13:07:26 -040010270 } else
James Ketrenosb095c382005-08-24 22:04:42 -050010271 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010272 priv->status |= STATUS_SECURITY_UPDATED;
James Ketrenosb095c382005-08-24 22:04:42 -050010273 } else
10274 priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
James Ketrenos43f66a62005-03-25 12:31:53 -060010275
10276 if ((sec->flags & SEC_AUTH_MODE) &&
James Ketrenosb095c382005-08-24 22:04:42 -050010277 (priv->ieee->sec.auth_mode != sec->auth_mode)) {
10278 priv->ieee->sec.auth_mode = sec->auth_mode;
10279 priv->ieee->sec.flags |= SEC_AUTH_MODE;
James Ketrenos43f66a62005-03-25 12:31:53 -060010280 if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
10281 priv->capability |= CAP_SHARED_KEY;
10282 else
10283 priv->capability &= ~CAP_SHARED_KEY;
10284 priv->status |= STATUS_SECURITY_UPDATED;
10285 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010286
James Ketrenosb095c382005-08-24 22:04:42 -050010287 if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
10288 priv->ieee->sec.flags |= SEC_ENABLED;
10289 priv->ieee->sec.enabled = sec->enabled;
James Ketrenos43f66a62005-03-25 12:31:53 -060010290 priv->status |= STATUS_SECURITY_UPDATED;
Jeff Garzikbf794512005-07-31 13:07:26 -040010291 if (sec->enabled)
James Ketrenos43f66a62005-03-25 12:31:53 -060010292 priv->capability |= CAP_PRIVACY_ON;
10293 else
10294 priv->capability &= ~CAP_PRIVACY_ON;
10295 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010296
James Ketrenosafbf30a2005-08-25 00:05:33 -050010297 if (sec->flags & SEC_ENCRYPT)
10298 priv->ieee->sec.encrypt = sec->encrypt;
James Ketrenos43f66a62005-03-25 12:31:53 -060010299
James Ketrenosb095c382005-08-24 22:04:42 -050010300 if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
10301 priv->ieee->sec.level = sec->level;
10302 priv->ieee->sec.flags |= SEC_LEVEL;
James Ketrenos43f66a62005-03-25 12:31:53 -060010303 priv->status |= STATUS_SECURITY_UPDATED;
10304 }
10305
Zhu Yi1fbfea52005-08-05 17:22:56 +080010306 if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
10307 ipw_set_hwcrypto_keys(priv);
10308
Jeff Garzikbf794512005-07-31 13:07:26 -040010309 /* To match current functionality of ipw2100 (which works well w/
10310 * various supplicants, we don't force a disassociate if the
James Ketrenos43f66a62005-03-25 12:31:53 -060010311 * privacy capability changes ... */
10312#if 0
10313 if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
Jeff Garzikbf794512005-07-31 13:07:26 -040010314 (((priv->assoc_request.capability &
James Ketrenos43f66a62005-03-25 12:31:53 -060010315 WLAN_CAPABILITY_PRIVACY) && !sec->enabled) ||
Jeff Garzikbf794512005-07-31 13:07:26 -040010316 (!(priv->assoc_request.capability &
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010317 WLAN_CAPABILITY_PRIVACY) && sec->enabled))) {
James Ketrenos43f66a62005-03-25 12:31:53 -060010318 IPW_DEBUG_ASSOC("Disassociating due to capability "
10319 "change.\n");
10320 ipw_disassociate(priv);
10321 }
10322#endif
10323}
10324
Jeff Garzikbf794512005-07-31 13:07:26 -040010325static int init_supported_rates(struct ipw_priv *priv,
James Ketrenos43f66a62005-03-25 12:31:53 -060010326 struct ipw_supported_rates *rates)
10327{
10328 /* TODO: Mask out rates based on priv->rates_mask */
10329
10330 memset(rates, 0, sizeof(*rates));
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010331 /* configure supported rates */
James Ketrenos43f66a62005-03-25 12:31:53 -060010332 switch (priv->ieee->freq_band) {
10333 case IEEE80211_52GHZ_BAND:
10334 rates->ieee_mode = IPW_A_MODE;
10335 rates->purpose = IPW_RATE_CAPABILITIES;
10336 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
10337 IEEE80211_OFDM_DEFAULT_RATES_MASK);
10338 break;
10339
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010340 default: /* Mixed or 2.4Ghz */
James Ketrenos43f66a62005-03-25 12:31:53 -060010341 rates->ieee_mode = IPW_G_MODE;
10342 rates->purpose = IPW_RATE_CAPABILITIES;
10343 ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
10344 IEEE80211_CCK_DEFAULT_RATES_MASK);
10345 if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
10346 ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
10347 IEEE80211_OFDM_DEFAULT_RATES_MASK);
10348 }
10349 break;
10350 }
10351
10352 return 0;
10353}
10354
Jeff Garzikbf794512005-07-31 13:07:26 -040010355static int ipw_config(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010356{
James Ketrenos43f66a62005-03-25 12:31:53 -060010357 /* This is only called from ipw_up, which resets/reloads the firmware
10358 so, we don't need to first disable the card before we configure
10359 it */
Zhu Yi6de9f7f2005-08-11 14:39:33 +080010360 if (ipw_set_tx_power(priv))
James Ketrenos43f66a62005-03-25 12:31:53 -060010361 goto error;
10362
10363 /* initialize adapter address */
10364 if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
10365 goto error;
10366
10367 /* set basic system config settings */
10368 init_sys_config(&priv->sys_config);
Zhu Yi810dabd2006-01-24 16:36:59 +080010369
10370 /* Support Bluetooth if we have BT h/w on board, and user wants to.
10371 * Does not support BT priority yet (don't abort or defer our Tx) */
10372 if (bt_coexist) {
10373 unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY];
10374
10375 if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG)
10376 priv->sys_config.bt_coexistence
10377 |= CFG_BT_COEXISTENCE_SIGNAL_CHNL;
10378 if (bt_caps & EEPROM_SKU_CAP_BT_OOB)
10379 priv->sys_config.bt_coexistence
10380 |= CFG_BT_COEXISTENCE_OOB;
10381 }
10382
James Ketrenosc848d0a2005-08-24 21:56:24 -050010383 if (priv->ieee->iw_mode == IW_MODE_ADHOC)
10384 priv->sys_config.answer_broadcast_ssid_probe = 1;
10385 else
10386 priv->sys_config.answer_broadcast_ssid_probe = 0;
10387
James Ketrenos43f66a62005-03-25 12:31:53 -060010388 if (ipw_send_system_config(priv, &priv->sys_config))
10389 goto error;
10390
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010391 init_supported_rates(priv, &priv->rates);
10392 if (ipw_send_supported_rates(priv, &priv->rates))
James Ketrenos43f66a62005-03-25 12:31:53 -060010393 goto error;
10394
10395 /* Set request-to-send threshold */
10396 if (priv->rts_threshold) {
10397 if (ipw_send_rts_threshold(priv, priv->rts_threshold))
10398 goto error;
10399 }
James Ketrenosb095c382005-08-24 22:04:42 -050010400#ifdef CONFIG_IPW_QOS
10401 IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
10402 ipw_qos_activate(priv, NULL);
10403#endif /* CONFIG_IPW_QOS */
James Ketrenos43f66a62005-03-25 12:31:53 -060010404
10405 if (ipw_set_random_seed(priv))
10406 goto error;
Jeff Garzikbf794512005-07-31 13:07:26 -040010407
James Ketrenos43f66a62005-03-25 12:31:53 -060010408 /* final state transition to the RUN state */
10409 if (ipw_send_host_complete(priv))
10410 goto error;
10411
James Ketrenose6666192005-08-12 09:17:04 -050010412 priv->status |= STATUS_INIT;
10413
10414 ipw_led_init(priv);
10415 ipw_led_radio_on(priv);
10416 priv->notif_missed_beacons = 0;
10417
10418 /* Set hardware WEP key if it is configured. */
10419 if ((priv->capability & CAP_PRIVACY_ON) &&
10420 (priv->ieee->sec.level == SEC_LEVEL_1) &&
10421 !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
10422 ipw_set_hwcrypto_keys(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010423
10424 return 0;
Jeff Garzikbf794512005-07-31 13:07:26 -040010425
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010426 error:
James Ketrenos43f66a62005-03-25 12:31:53 -060010427 return -EIO;
10428}
10429
James Ketrenos4f36f802005-08-03 20:36:56 -050010430/*
10431 * NOTE:
10432 *
10433 * These tables have been tested in conjunction with the
10434 * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters.
10435 *
10436 * Altering this values, using it on other hardware, or in geographies
10437 * not intended for resale of the above mentioned Intel adapters has
10438 * not been tested.
10439 *
10440 */
10441static const struct ieee80211_geo ipw_geos[] = {
10442 { /* Restricted */
10443 "---",
10444 .bg_channels = 11,
10445 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10446 {2427, 4}, {2432, 5}, {2437, 6},
10447 {2442, 7}, {2447, 8}, {2452, 9},
10448 {2457, 10}, {2462, 11}},
10449 },
10450
10451 { /* Custom US/Canada */
10452 "ZZF",
10453 .bg_channels = 11,
10454 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10455 {2427, 4}, {2432, 5}, {2437, 6},
10456 {2442, 7}, {2447, 8}, {2452, 9},
10457 {2457, 10}, {2462, 11}},
10458 .a_channels = 8,
10459 .a = {{5180, 36},
10460 {5200, 40},
10461 {5220, 44},
10462 {5240, 48},
10463 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10464 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10465 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10466 {5320, 64, IEEE80211_CH_PASSIVE_ONLY}},
10467 },
10468
10469 { /* Rest of World */
10470 "ZZD",
10471 .bg_channels = 13,
10472 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10473 {2427, 4}, {2432, 5}, {2437, 6},
10474 {2442, 7}, {2447, 8}, {2452, 9},
10475 {2457, 10}, {2462, 11}, {2467, 12},
10476 {2472, 13}},
10477 },
10478
10479 { /* Custom USA & Europe & High */
10480 "ZZA",
10481 .bg_channels = 11,
10482 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10483 {2427, 4}, {2432, 5}, {2437, 6},
10484 {2442, 7}, {2447, 8}, {2452, 9},
10485 {2457, 10}, {2462, 11}},
10486 .a_channels = 13,
10487 .a = {{5180, 36},
10488 {5200, 40},
10489 {5220, 44},
10490 {5240, 48},
10491 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10492 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10493 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10494 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10495 {5745, 149},
10496 {5765, 153},
10497 {5785, 157},
10498 {5805, 161},
10499 {5825, 165}},
10500 },
10501
10502 { /* Custom NA & Europe */
10503 "ZZB",
10504 .bg_channels = 11,
10505 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10506 {2427, 4}, {2432, 5}, {2437, 6},
10507 {2442, 7}, {2447, 8}, {2452, 9},
10508 {2457, 10}, {2462, 11}},
10509 .a_channels = 13,
10510 .a = {{5180, 36},
10511 {5200, 40},
10512 {5220, 44},
10513 {5240, 48},
10514 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10515 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10516 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10517 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10518 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10519 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10520 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10521 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10522 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10523 },
10524
10525 { /* Custom Japan */
10526 "ZZC",
10527 .bg_channels = 11,
10528 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10529 {2427, 4}, {2432, 5}, {2437, 6},
10530 {2442, 7}, {2447, 8}, {2452, 9},
10531 {2457, 10}, {2462, 11}},
10532 .a_channels = 4,
10533 .a = {{5170, 34}, {5190, 38},
10534 {5210, 42}, {5230, 46}},
10535 },
10536
10537 { /* Custom */
10538 "ZZM",
10539 .bg_channels = 11,
10540 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10541 {2427, 4}, {2432, 5}, {2437, 6},
10542 {2442, 7}, {2447, 8}, {2452, 9},
10543 {2457, 10}, {2462, 11}},
10544 },
10545
10546 { /* Europe */
10547 "ZZE",
10548 .bg_channels = 13,
10549 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10550 {2427, 4}, {2432, 5}, {2437, 6},
10551 {2442, 7}, {2447, 8}, {2452, 9},
10552 {2457, 10}, {2462, 11}, {2467, 12},
10553 {2472, 13}},
10554 .a_channels = 19,
10555 .a = {{5180, 36},
10556 {5200, 40},
10557 {5220, 44},
10558 {5240, 48},
10559 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10560 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10561 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10562 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10563 {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
10564 {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
10565 {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
10566 {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
10567 {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
10568 {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
10569 {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
10570 {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
10571 {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
10572 {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
10573 {5700, 140, IEEE80211_CH_PASSIVE_ONLY}},
10574 },
10575
10576 { /* Custom Japan */
10577 "ZZJ",
10578 .bg_channels = 14,
10579 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10580 {2427, 4}, {2432, 5}, {2437, 6},
10581 {2442, 7}, {2447, 8}, {2452, 9},
10582 {2457, 10}, {2462, 11}, {2467, 12},
10583 {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY}},
10584 .a_channels = 4,
10585 .a = {{5170, 34}, {5190, 38},
10586 {5210, 42}, {5230, 46}},
10587 },
10588
James Ketrenos03520572005-10-19 16:12:31 -050010589 { /* Rest of World */
10590 "ZZR",
10591 .bg_channels = 14,
10592 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10593 {2427, 4}, {2432, 5}, {2437, 6},
10594 {2442, 7}, {2447, 8}, {2452, 9},
10595 {2457, 10}, {2462, 11}, {2467, 12},
10596 {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY |
10597 IEEE80211_CH_PASSIVE_ONLY}},
10598 },
10599
James Ketrenos4f36f802005-08-03 20:36:56 -050010600 { /* High Band */
10601 "ZZH",
10602 .bg_channels = 13,
10603 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10604 {2427, 4}, {2432, 5}, {2437, 6},
10605 {2442, 7}, {2447, 8}, {2452, 9},
10606 {2457, 10}, {2462, 11},
10607 {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
10608 {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
10609 .a_channels = 4,
10610 .a = {{5745, 149}, {5765, 153},
10611 {5785, 157}, {5805, 161}},
10612 },
10613
10614 { /* Custom Europe */
10615 "ZZG",
10616 .bg_channels = 13,
10617 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10618 {2427, 4}, {2432, 5}, {2437, 6},
10619 {2442, 7}, {2447, 8}, {2452, 9},
10620 {2457, 10}, {2462, 11},
10621 {2467, 12}, {2472, 13}},
10622 .a_channels = 4,
10623 .a = {{5180, 36}, {5200, 40},
10624 {5220, 44}, {5240, 48}},
10625 },
10626
10627 { /* Europe */
10628 "ZZK",
10629 .bg_channels = 13,
10630 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10631 {2427, 4}, {2432, 5}, {2437, 6},
10632 {2442, 7}, {2447, 8}, {2452, 9},
10633 {2457, 10}, {2462, 11},
10634 {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
10635 {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
10636 .a_channels = 24,
10637 .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
10638 {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
10639 {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
10640 {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
10641 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10642 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10643 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10644 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10645 {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
10646 {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
10647 {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
10648 {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
10649 {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
10650 {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
10651 {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
10652 {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
10653 {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
10654 {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
10655 {5700, 140, IEEE80211_CH_PASSIVE_ONLY},
10656 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10657 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10658 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10659 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10660 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10661 },
10662
10663 { /* Europe */
10664 "ZZL",
10665 .bg_channels = 11,
10666 .bg = {{2412, 1}, {2417, 2}, {2422, 3},
10667 {2427, 4}, {2432, 5}, {2437, 6},
10668 {2442, 7}, {2447, 8}, {2452, 9},
10669 {2457, 10}, {2462, 11}},
10670 .a_channels = 13,
10671 .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
10672 {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
10673 {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
10674 {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
10675 {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
10676 {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
10677 {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
10678 {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
10679 {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
10680 {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
10681 {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
10682 {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
10683 {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
10684 }
James Ketrenosafbf30a2005-08-25 00:05:33 -050010685};
10686
Liu Hong1fe0adb2005-08-19 09:33:10 -050010687/* GEO code borrowed from ieee80211_geo.c */
10688static int ipw_is_valid_channel(struct ieee80211_device *ieee, u8 channel)
10689{
10690 int i;
10691
10692 /* Driver needs to initialize the geography map before using
10693 * these helper functions */
10694 BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
10695
10696 if (ieee->freq_band & IEEE80211_24GHZ_BAND)
10697 for (i = 0; i < ieee->geo.bg_channels; i++)
10698 /* NOTE: If G mode is currently supported but
10699 * this is a B only channel, we don't see it
10700 * as valid. */
10701 if ((ieee->geo.bg[i].channel == channel) &&
10702 (!(ieee->mode & IEEE_G) ||
10703 !(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY)))
10704 return IEEE80211_24GHZ_BAND;
10705
10706 if (ieee->freq_band & IEEE80211_52GHZ_BAND)
10707 for (i = 0; i < ieee->geo.a_channels; i++)
10708 if (ieee->geo.a[i].channel == channel)
10709 return IEEE80211_52GHZ_BAND;
10710
10711 return 0;
10712}
10713
10714static int ipw_channel_to_index(struct ieee80211_device *ieee, u8 channel)
10715{
10716 int i;
10717
10718 /* Driver needs to initialize the geography map before using
10719 * these helper functions */
10720 BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
10721
10722 if (ieee->freq_band & IEEE80211_24GHZ_BAND)
10723 for (i = 0; i < ieee->geo.bg_channels; i++)
10724 if (ieee->geo.bg[i].channel == channel)
10725 return i;
10726
10727 if (ieee->freq_band & IEEE80211_52GHZ_BAND)
10728 for (i = 0; i < ieee->geo.a_channels; i++)
10729 if (ieee->geo.a[i].channel == channel)
10730 return i;
10731
10732 return -1;
10733}
10734
10735static u8 ipw_freq_to_channel(struct ieee80211_device *ieee, u32 freq)
10736{
10737 int i;
10738
10739 /* Driver needs to initialize the geography map before using
10740 * these helper functions */
10741 BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0);
10742
10743 freq /= 100000;
10744
10745 if (ieee->freq_band & IEEE80211_24GHZ_BAND)
10746 for (i = 0; i < ieee->geo.bg_channels; i++)
10747 if (ieee->geo.bg[i].freq == freq)
10748 return ieee->geo.bg[i].channel;
10749
10750 if (ieee->freq_band & IEEE80211_52GHZ_BAND)
10751 for (i = 0; i < ieee->geo.a_channels; i++)
10752 if (ieee->geo.a[i].freq == freq)
10753 return ieee->geo.a[i].channel;
10754
10755 return 0;
10756}
10757
10758static int ipw_set_geo(struct ieee80211_device *ieee,
10759 const struct ieee80211_geo *geo)
10760{
10761 memcpy(ieee->geo.name, geo->name, 3);
10762 ieee->geo.name[3] = '\0';
10763 ieee->geo.bg_channels = geo->bg_channels;
10764 ieee->geo.a_channels = geo->a_channels;
10765 memcpy(ieee->geo.bg, geo->bg, geo->bg_channels *
10766 sizeof(struct ieee80211_channel));
10767 memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels *
10768 sizeof(struct ieee80211_channel));
10769 return 0;
10770}
10771
10772static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *ieee)
10773{
10774 return &ieee->geo;
10775}
10776
James Ketrenos43f66a62005-03-25 12:31:53 -060010777#define MAX_HW_RESTARTS 5
10778static int ipw_up(struct ipw_priv *priv)
10779{
James Ketrenos4f36f802005-08-03 20:36:56 -050010780 int rc, i, j;
James Ketrenos43f66a62005-03-25 12:31:53 -060010781
10782 if (priv->status & STATUS_EXIT_PENDING)
10783 return -EIO;
10784
James Ketrenosf6c5cb72005-08-25 00:39:09 -050010785 if (cmdlog && !priv->cmdlog) {
10786 priv->cmdlog = kmalloc(sizeof(*priv->cmdlog) * cmdlog,
10787 GFP_KERNEL);
10788 if (priv->cmdlog == NULL) {
10789 IPW_ERROR("Error allocating %d command log entries.\n",
10790 cmdlog);
10791 } else {
10792 memset(priv->cmdlog, 0, sizeof(*priv->cmdlog) * cmdlog);
10793 priv->cmdlog_len = cmdlog;
10794 }
10795 }
10796
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010797 for (i = 0; i < MAX_HW_RESTARTS; i++) {
Jeff Garzikbf794512005-07-31 13:07:26 -040010798 /* Load the microcode, firmware, and eeprom.
James Ketrenos43f66a62005-03-25 12:31:53 -060010799 * Also start the clocks. */
10800 rc = ipw_load(priv);
10801 if (rc) {
Peter Jonesa4f6bbb2005-08-26 00:33:34 -050010802 IPW_ERROR("Unable to load firmware: %d\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060010803 return rc;
10804 }
10805
10806 ipw_init_ordinals(priv);
10807 if (!(priv->config & CFG_CUSTOM_MAC))
10808 eeprom_parse_mac(priv, priv->mac_addr);
10809 memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
10810
James Ketrenos4f36f802005-08-03 20:36:56 -050010811 for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
10812 if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
10813 ipw_geos[j].name, 3))
10814 break;
10815 }
James Ketrenos03520572005-10-19 16:12:31 -050010816 if (j == ARRAY_SIZE(ipw_geos)) {
10817 IPW_WARNING("SKU [%c%c%c] not recognized.\n",
10818 priv->eeprom[EEPROM_COUNTRY_CODE + 0],
10819 priv->eeprom[EEPROM_COUNTRY_CODE + 1],
10820 priv->eeprom[EEPROM_COUNTRY_CODE + 2]);
James Ketrenos4f36f802005-08-03 20:36:56 -050010821 j = 0;
James Ketrenos03520572005-10-19 16:12:31 -050010822 }
Liu Hong1fe0adb2005-08-19 09:33:10 -050010823 if (ipw_set_geo(priv->ieee, &ipw_geos[j])) {
James Ketrenos4f36f802005-08-03 20:36:56 -050010824 IPW_WARNING("Could not set geography.");
James Ketrenos43f66a62005-03-25 12:31:53 -060010825 return 0;
James Ketrenos4f36f802005-08-03 20:36:56 -050010826 }
10827
10828 IPW_DEBUG_INFO("Geography %03d [%s] detected.\n",
10829 j, priv->ieee->geo.name);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010830
James Ketrenosb095c382005-08-24 22:04:42 -050010831 if (priv->status & STATUS_RF_KILL_SW) {
10832 IPW_WARNING("Radio disabled by module parameter.\n");
10833 return 0;
10834 } else if (rf_kill_active(priv)) {
10835 IPW_WARNING("Radio Frequency Kill Switch is On:\n"
10836 "Kill switch must be turned off for "
10837 "wireless networking to work.\n");
10838 queue_delayed_work(priv->workqueue, &priv->rf_kill,
10839 2 * HZ);
James Ketrenos43f66a62005-03-25 12:31:53 -060010840 return 0;
James Ketrenosc848d0a2005-08-24 21:56:24 -050010841 }
James Ketrenos43f66a62005-03-25 12:31:53 -060010842
10843 rc = ipw_config(priv);
10844 if (!rc) {
10845 IPW_DEBUG_INFO("Configured device on count %i\n", i);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010846
James Ketrenose6666192005-08-12 09:17:04 -050010847 /* If configure to try and auto-associate, kick
10848 * off a scan. */
10849 queue_work(priv->workqueue, &priv->request_scan);
James Ketrenosafbf30a2005-08-25 00:05:33 -050010850
James Ketrenos43f66a62005-03-25 12:31:53 -060010851 return 0;
James Ketrenos43f66a62005-03-25 12:31:53 -060010852 }
Jeff Garzikbf794512005-07-31 13:07:26 -040010853
James Ketrenosc848d0a2005-08-24 21:56:24 -050010854 IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
James Ketrenos43f66a62005-03-25 12:31:53 -060010855 IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
10856 i, MAX_HW_RESTARTS);
10857
10858 /* We had an error bringing up the hardware, so take it
10859 * all the way back down so we can try again */
10860 ipw_down(priv);
10861 }
10862
Jeff Garzikbf794512005-07-31 13:07:26 -040010863 /* tried to restart and config the device for as long as our
James Ketrenos43f66a62005-03-25 12:31:53 -060010864 * patience could withstand */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010865 IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010866
James Ketrenos43f66a62005-03-25 12:31:53 -060010867 return -EIO;
10868}
10869
James Ketrenosc848d0a2005-08-24 21:56:24 -050010870static void ipw_bg_up(void *data)
10871{
10872 struct ipw_priv *priv = data;
10873 down(&priv->sem);
10874 ipw_up(data);
10875 up(&priv->sem);
10876}
10877
James Ketrenosb095c382005-08-24 22:04:42 -050010878static void ipw_deinit(struct ipw_priv *priv)
James Ketrenos43f66a62005-03-25 12:31:53 -060010879{
James Ketrenosb095c382005-08-24 22:04:42 -050010880 int i;
10881
10882 if (priv->status & STATUS_SCANNING) {
10883 IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
10884 ipw_abort_scan(priv);
10885 }
10886
10887 if (priv->status & STATUS_ASSOCIATED) {
10888 IPW_DEBUG_INFO("Disassociating during shutdown.\n");
10889 ipw_disassociate(priv);
10890 }
10891
10892 ipw_led_shutdown(priv);
10893
10894 /* Wait up to 1s for status to change to not scanning and not
10895 * associated (disassociation can take a while for a ful 802.11
10896 * exchange */
10897 for (i = 1000; i && (priv->status &
10898 (STATUS_DISASSOCIATING |
10899 STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
10900 udelay(10);
10901
10902 if (priv->status & (STATUS_DISASSOCIATING |
10903 STATUS_ASSOCIATED | STATUS_SCANNING))
10904 IPW_DEBUG_INFO("Still associated or scanning...\n");
10905 else
10906 IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
10907
James Ketrenosc848d0a2005-08-24 21:56:24 -050010908 /* Attempt to disable the card */
James Ketrenos43f66a62005-03-25 12:31:53 -060010909 ipw_send_card_disable(priv, 0);
James Ketrenosb095c382005-08-24 22:04:42 -050010910
10911 priv->status &= ~STATUS_INIT;
10912}
10913
James Ketrenos43f66a62005-03-25 12:31:53 -060010914static void ipw_down(struct ipw_priv *priv)
10915{
James Ketrenosb095c382005-08-24 22:04:42 -050010916 int exit_pending = priv->status & STATUS_EXIT_PENDING;
10917
10918 priv->status |= STATUS_EXIT_PENDING;
10919
10920 if (ipw_is_init(priv))
10921 ipw_deinit(priv);
10922
10923 /* Wipe out the EXIT_PENDING status bit if we are not actually
10924 * exiting the module */
10925 if (!exit_pending)
10926 priv->status &= ~STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060010927
10928 /* tell the device to stop sending interrupts */
10929 ipw_disable_interrupts(priv);
10930
10931 /* Clear all bits but the RF Kill */
James Ketrenosb095c382005-08-24 22:04:42 -050010932 priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060010933 netif_carrier_off(priv->net_dev);
10934 netif_stop_queue(priv->net_dev);
10935
10936 ipw_stop_nic(priv);
James Ketrenosa613bff2005-08-24 21:43:11 -050010937
10938 ipw_led_radio_off(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060010939}
10940
James Ketrenosc848d0a2005-08-24 21:56:24 -050010941static void ipw_bg_down(void *data)
10942{
10943 struct ipw_priv *priv = data;
10944 down(&priv->sem);
10945 ipw_down(data);
10946 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010947}
10948
10949/* Called by register_netdev() */
10950static int ipw_net_init(struct net_device *dev)
10951{
10952 struct ipw_priv *priv = ieee80211_priv(dev);
James Ketrenosc848d0a2005-08-24 21:56:24 -050010953 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010954
James Ketrenosc848d0a2005-08-24 21:56:24 -050010955 if (ipw_up(priv)) {
10956 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010957 return -EIO;
James Ketrenos43f66a62005-03-25 12:31:53 -060010958 }
10959
James Ketrenosc848d0a2005-08-24 21:56:24 -050010960 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060010961 return 0;
10962}
10963
10964/* PCI driver stuff */
10965static struct pci_device_id card_ids[] = {
10966 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
10967 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
10968 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
10969 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
10970 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
10971 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
10972 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
10973 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
10974 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
10975 {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
10976 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
10977 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
10978 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
10979 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
10980 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
10981 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
10982 {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
10983 {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010984 {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
James Ketrenosa613bff2005-08-24 21:43:11 -050010985 {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040010986 {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
10987 {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
Jeff Garzikbf794512005-07-31 13:07:26 -040010988
James Ketrenos43f66a62005-03-25 12:31:53 -060010989 /* required last entry */
10990 {0,}
10991};
10992
10993MODULE_DEVICE_TABLE(pci, card_ids);
10994
10995static struct attribute *ipw_sysfs_entries[] = {
10996 &dev_attr_rf_kill.attr,
10997 &dev_attr_direct_dword.attr,
10998 &dev_attr_indirect_byte.attr,
10999 &dev_attr_indirect_dword.attr,
11000 &dev_attr_mem_gpio_reg.attr,
11001 &dev_attr_command_event_reg.attr,
11002 &dev_attr_nic_type.attr,
11003 &dev_attr_status.attr,
11004 &dev_attr_cfg.attr,
James Ketrenosb39860c2005-08-12 09:36:32 -050011005 &dev_attr_error.attr,
11006 &dev_attr_event_log.attr,
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011007 &dev_attr_cmd_log.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -060011008 &dev_attr_eeprom_delay.attr,
11009 &dev_attr_ucode_version.attr,
11010 &dev_attr_rtc.attr,
James Ketrenosa613bff2005-08-24 21:43:11 -050011011 &dev_attr_scan_age.attr,
11012 &dev_attr_led.attr,
James Ketrenosb095c382005-08-24 22:04:42 -050011013 &dev_attr_speed_scan.attr,
11014 &dev_attr_net_stats.attr,
James Ketrenos43f66a62005-03-25 12:31:53 -060011015 NULL
11016};
11017
11018static struct attribute_group ipw_attribute_group = {
11019 .name = NULL, /* put in device directory */
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011020 .attrs = ipw_sysfs_entries,
James Ketrenos43f66a62005-03-25 12:31:53 -060011021};
11022
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011023static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
James Ketrenos43f66a62005-03-25 12:31:53 -060011024{
11025 int err = 0;
11026 struct net_device *net_dev;
11027 void __iomem *base;
11028 u32 length, val;
11029 struct ipw_priv *priv;
James Ketrenosafbf30a2005-08-25 00:05:33 -050011030 int i;
James Ketrenos43f66a62005-03-25 12:31:53 -060011031
11032 net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
11033 if (net_dev == NULL) {
11034 err = -ENOMEM;
11035 goto out;
11036 }
11037
11038 priv = ieee80211_priv(net_dev);
11039 priv->ieee = netdev_priv(net_dev);
James Ketrenosa613bff2005-08-24 21:43:11 -050011040
James Ketrenos43f66a62005-03-25 12:31:53 -060011041 priv->net_dev = net_dev;
11042 priv->pci_dev = pdev;
Brice Goglin0f52bf92005-12-01 01:41:46 -080011043#ifdef CONFIG_IPW2200_DEBUG
James Ketrenos43f66a62005-03-25 12:31:53 -060011044 ipw_debug_level = debug;
11045#endif
11046 spin_lock_init(&priv->lock);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011047 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
11048 INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
James Ketrenos43f66a62005-03-25 12:31:53 -060011049
James Ketrenosc848d0a2005-08-24 21:56:24 -050011050 init_MUTEX(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011051 if (pci_enable_device(pdev)) {
11052 err = -ENODEV;
11053 goto out_free_ieee80211;
11054 }
11055
11056 pci_set_master(pdev);
11057
Tobias Klauser0e08b442005-06-20 14:28:41 -070011058 err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
Jeff Garzikbf794512005-07-31 13:07:26 -040011059 if (!err)
Tobias Klauser0e08b442005-06-20 14:28:41 -070011060 err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
James Ketrenos43f66a62005-03-25 12:31:53 -060011061 if (err) {
11062 printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
11063 goto out_pci_disable_device;
11064 }
11065
11066 pci_set_drvdata(pdev, priv);
11067
11068 err = pci_request_regions(pdev, DRV_NAME);
Jeff Garzikbf794512005-07-31 13:07:26 -040011069 if (err)
James Ketrenos43f66a62005-03-25 12:31:53 -060011070 goto out_pci_disable_device;
11071
Jeff Garzikbf794512005-07-31 13:07:26 -040011072 /* We disable the RETRY_TIMEOUT register (0x41) to keep
James Ketrenos43f66a62005-03-25 12:31:53 -060011073 * PCI Tx retries from interfering with C3 CPU state */
Jeff Garzikbf794512005-07-31 13:07:26 -040011074 pci_read_config_dword(pdev, 0x40, &val);
11075 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011076 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
Jeff Garzikbf794512005-07-31 13:07:26 -040011077
James Ketrenos43f66a62005-03-25 12:31:53 -060011078 length = pci_resource_len(pdev, 0);
11079 priv->hw_len = length;
Jeff Garzikbf794512005-07-31 13:07:26 -040011080
James Ketrenos43f66a62005-03-25 12:31:53 -060011081 base = ioremap_nocache(pci_resource_start(pdev, 0), length);
11082 if (!base) {
11083 err = -ENODEV;
11084 goto out_pci_release_regions;
11085 }
11086
11087 priv->hw_base = base;
11088 IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
11089 IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
11090
11091 err = ipw_setup_deferred_work(priv);
11092 if (err) {
11093 IPW_ERROR("Unable to setup deferred work\n");
11094 goto out_iounmap;
11095 }
11096
James Ketrenosb095c382005-08-24 22:04:42 -050011097 ipw_sw_reset(priv, 1);
James Ketrenos43f66a62005-03-25 12:31:53 -060011098
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011099 err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME, priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011100 if (err) {
11101 IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
11102 goto out_destroy_workqueue;
11103 }
11104
11105 SET_MODULE_OWNER(net_dev);
11106 SET_NETDEV_DEV(net_dev, &pdev->dev);
11107
James Ketrenosc848d0a2005-08-24 21:56:24 -050011108 down(&priv->sem);
11109
James Ketrenos43f66a62005-03-25 12:31:53 -060011110 priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
11111 priv->ieee->set_security = shim__set_security;
James Ketrenos227d2dc2005-07-28 16:25:55 -050011112 priv->ieee->is_queue_full = ipw_net_is_queue_full;
James Ketrenos43f66a62005-03-25 12:31:53 -060011113
James Ketrenosb095c382005-08-24 22:04:42 -050011114#ifdef CONFIG_IPW_QOS
James Ketrenos3b9990c2005-08-19 13:18:55 -050011115 priv->ieee->handle_probe_response = ipw_handle_beacon;
11116 priv->ieee->handle_beacon = ipw_handle_probe_response;
11117 priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
James Ketrenosb095c382005-08-24 22:04:42 -050011118#endif /* CONFIG_IPW_QOS */
11119
James Ketrenosc848d0a2005-08-24 21:56:24 -050011120 priv->ieee->perfect_rssi = -20;
11121 priv->ieee->worst_rssi = -85;
James Ketrenos43f66a62005-03-25 12:31:53 -060011122
11123 net_dev->open = ipw_net_open;
11124 net_dev->stop = ipw_net_stop;
11125 net_dev->init = ipw_net_init;
11126 net_dev->get_stats = ipw_net_get_stats;
11127 net_dev->set_multicast_list = ipw_net_set_multicast_list;
11128 net_dev->set_mac_address = ipw_net_set_mac_address;
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000011129 priv->wireless_data.spy_data = &priv->ieee->spy_data;
Benoit Boissinot97a78ca2005-09-15 17:30:28 +000011130 net_dev->wireless_data = &priv->wireless_data;
James Ketrenos43f66a62005-03-25 12:31:53 -060011131 net_dev->wireless_handlers = &ipw_wx_handler_def;
11132 net_dev->ethtool_ops = &ipw_ethtool_ops;
11133 net_dev->irq = pdev->irq;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011134 net_dev->base_addr = (unsigned long)priv->hw_base;
James Ketrenos43f66a62005-03-25 12:31:53 -060011135 net_dev->mem_start = pci_resource_start(pdev, 0);
11136 net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
11137
11138 err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
11139 if (err) {
11140 IPW_ERROR("failed to create sysfs device attributes\n");
James Ketrenosc848d0a2005-08-24 21:56:24 -050011141 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011142 goto out_release_irq;
11143 }
11144
James Ketrenosc848d0a2005-08-24 21:56:24 -050011145 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011146 err = register_netdev(net_dev);
11147 if (err) {
11148 IPW_ERROR("failed to register network device\n");
James Ketrenosa613bff2005-08-24 21:43:11 -050011149 goto out_remove_sysfs;
James Ketrenos43f66a62005-03-25 12:31:53 -060011150 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011151 return 0;
11152
James Ketrenosa613bff2005-08-24 21:43:11 -050011153 out_remove_sysfs:
James Ketrenos43f66a62005-03-25 12:31:53 -060011154 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011155 out_release_irq:
James Ketrenos43f66a62005-03-25 12:31:53 -060011156 free_irq(pdev->irq, priv);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011157 out_destroy_workqueue:
James Ketrenos43f66a62005-03-25 12:31:53 -060011158 destroy_workqueue(priv->workqueue);
11159 priv->workqueue = NULL;
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011160 out_iounmap:
James Ketrenos43f66a62005-03-25 12:31:53 -060011161 iounmap(priv->hw_base);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011162 out_pci_release_regions:
James Ketrenos43f66a62005-03-25 12:31:53 -060011163 pci_release_regions(pdev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011164 out_pci_disable_device:
James Ketrenos43f66a62005-03-25 12:31:53 -060011165 pci_disable_device(pdev);
11166 pci_set_drvdata(pdev, NULL);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011167 out_free_ieee80211:
James Ketrenos43f66a62005-03-25 12:31:53 -060011168 free_ieee80211(priv->net_dev);
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011169 out:
James Ketrenos43f66a62005-03-25 12:31:53 -060011170 return err;
11171}
11172
11173static void ipw_pci_remove(struct pci_dev *pdev)
11174{
11175 struct ipw_priv *priv = pci_get_drvdata(pdev);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011176 struct list_head *p, *q;
11177 int i;
James Ketrenosb095c382005-08-24 22:04:42 -050011178
James Ketrenos43f66a62005-03-25 12:31:53 -060011179 if (!priv)
11180 return;
11181
James Ketrenosb095c382005-08-24 22:04:42 -050011182 down(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011183
James Ketrenosafbf30a2005-08-25 00:05:33 -050011184 priv->status |= STATUS_EXIT_PENDING;
James Ketrenos43f66a62005-03-25 12:31:53 -060011185 ipw_down(priv);
James Ketrenos43f66a62005-03-25 12:31:53 -060011186 sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
11187
James Ketrenosb095c382005-08-24 22:04:42 -050011188 up(&priv->sem);
James Ketrenos43f66a62005-03-25 12:31:53 -060011189
11190 unregister_netdev(priv->net_dev);
11191
11192 if (priv->rxq) {
11193 ipw_rx_queue_free(priv, priv->rxq);
11194 priv->rxq = NULL;
11195 }
11196 ipw_tx_queue_free(priv);
11197
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011198 if (priv->cmdlog) {
11199 kfree(priv->cmdlog);
11200 priv->cmdlog = NULL;
11201 }
James Ketrenos43f66a62005-03-25 12:31:53 -060011202 /* ipw_down will ensure that there is no more pending work
11203 * in the workqueue's, so we can safely remove them now. */
James Ketrenosa613bff2005-08-24 21:43:11 -050011204 cancel_delayed_work(&priv->adhoc_check);
11205 cancel_delayed_work(&priv->gather_stats);
11206 cancel_delayed_work(&priv->request_scan);
11207 cancel_delayed_work(&priv->rf_kill);
11208 cancel_delayed_work(&priv->scan_check);
11209 destroy_workqueue(priv->workqueue);
11210 priv->workqueue = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -060011211
James Ketrenosafbf30a2005-08-25 00:05:33 -050011212 /* Free MAC hash list for ADHOC */
11213 for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) {
11214 list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
James Ketrenosafbf30a2005-08-25 00:05:33 -050011215 list_del(p);
Zhu Yi489f4452006-01-24 16:37:41 +080011216 kfree(list_entry(p, struct ipw_ibss_seq, list));
James Ketrenosafbf30a2005-08-25 00:05:33 -050011217 }
11218 }
11219
James Ketrenosb39860c2005-08-12 09:36:32 -050011220 if (priv->error) {
11221 ipw_free_error_log(priv->error);
11222 priv->error = NULL;
James Ketrenos43f66a62005-03-25 12:31:53 -060011223 }
11224
11225 free_irq(pdev->irq, priv);
11226 iounmap(priv->hw_base);
11227 pci_release_regions(pdev);
11228 pci_disable_device(pdev);
11229 pci_set_drvdata(pdev, NULL);
11230 free_ieee80211(priv->net_dev);
James Ketrenosafbf30a2005-08-25 00:05:33 -050011231 free_firmware();
James Ketrenos43f66a62005-03-25 12:31:53 -060011232}
11233
James Ketrenos43f66a62005-03-25 12:31:53 -060011234#ifdef CONFIG_PM
Pavel Machek583a4e82005-09-03 15:56:58 -070011235static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
James Ketrenos43f66a62005-03-25 12:31:53 -060011236{
11237 struct ipw_priv *priv = pci_get_drvdata(pdev);
11238 struct net_device *dev = priv->net_dev;
11239
11240 printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
11241
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011242 /* Take down the device; powers it off, etc. */
James Ketrenos43f66a62005-03-25 12:31:53 -060011243 ipw_down(priv);
11244
11245 /* Remove the PRESENT state of the device */
11246 netif_device_detach(dev);
11247
James Ketrenos43f66a62005-03-25 12:31:53 -060011248 pci_save_state(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011249 pci_disable_device(pdev);
Pavel Machek583a4e82005-09-03 15:56:58 -070011250 pci_set_power_state(pdev, pci_choose_state(pdev, state));
Jeff Garzikbf794512005-07-31 13:07:26 -040011251
James Ketrenos43f66a62005-03-25 12:31:53 -060011252 return 0;
11253}
11254
11255static int ipw_pci_resume(struct pci_dev *pdev)
11256{
11257 struct ipw_priv *priv = pci_get_drvdata(pdev);
11258 struct net_device *dev = priv->net_dev;
11259 u32 val;
Jeff Garzikbf794512005-07-31 13:07:26 -040011260
James Ketrenos43f66a62005-03-25 12:31:53 -060011261 printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
11262
James Ketrenosea2b26e2005-08-24 21:25:16 -050011263 pci_set_power_state(pdev, PCI_D0);
James Ketrenos43f66a62005-03-25 12:31:53 -060011264 pci_enable_device(pdev);
James Ketrenos43f66a62005-03-25 12:31:53 -060011265 pci_restore_state(pdev);
James Ketrenosea2b26e2005-08-24 21:25:16 -050011266
James Ketrenos43f66a62005-03-25 12:31:53 -060011267 /*
11268 * Suspend/Resume resets the PCI configuration space, so we have to
11269 * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
11270 * from interfering with C3 CPU state. pci_restore_state won't help
11271 * here since it only restores the first 64 bytes pci config header.
11272 */
Jeff Garzikbf794512005-07-31 13:07:26 -040011273 pci_read_config_dword(pdev, 0x40, &val);
11274 if ((val & 0x0000ff00) != 0)
James Ketrenos43f66a62005-03-25 12:31:53 -060011275 pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
11276
11277 /* Set the device back into the PRESENT state; this will also wake
11278 * the queue of needed */
11279 netif_device_attach(dev);
11280
11281 /* Bring the device back up */
11282 queue_work(priv->workqueue, &priv->up);
Jeff Garzikbf794512005-07-31 13:07:26 -040011283
James Ketrenos43f66a62005-03-25 12:31:53 -060011284 return 0;
11285}
11286#endif
11287
11288/* driver initialization stuff */
11289static struct pci_driver ipw_driver = {
11290 .name = DRV_NAME,
11291 .id_table = card_ids,
11292 .probe = ipw_pci_probe,
11293 .remove = __devexit_p(ipw_pci_remove),
11294#ifdef CONFIG_PM
11295 .suspend = ipw_pci_suspend,
11296 .resume = ipw_pci_resume,
11297#endif
11298};
11299
11300static int __init ipw_init(void)
11301{
11302 int ret;
11303
11304 printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
11305 printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
11306
11307 ret = pci_module_init(&ipw_driver);
11308 if (ret) {
11309 IPW_ERROR("Unable to initialize PCI module\n");
11310 return ret;
11311 }
11312
Jeff Garzik0edd5b42005-09-07 00:48:31 -040011313 ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
James Ketrenos43f66a62005-03-25 12:31:53 -060011314 if (ret) {
11315 IPW_ERROR("Unable to create driver sysfs file\n");
11316 pci_unregister_driver(&ipw_driver);
11317 return ret;
11318 }
11319
11320 return ret;
11321}
11322
11323static void __exit ipw_exit(void)
11324{
11325 driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
11326 pci_unregister_driver(&ipw_driver);
11327}
11328
11329module_param(disable, int, 0444);
11330MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
11331
11332module_param(associate, int, 0444);
11333MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
11334
11335module_param(auto_create, int, 0444);
11336MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
11337
James Ketrenosa613bff2005-08-24 21:43:11 -050011338module_param(led, int, 0444);
James Ketrenosc848d0a2005-08-24 21:56:24 -050011339MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)\n");
James Ketrenosa613bff2005-08-24 21:43:11 -050011340
James Ketrenos43f66a62005-03-25 12:31:53 -060011341module_param(debug, int, 0444);
11342MODULE_PARM_DESC(debug, "debug output mask");
11343
11344module_param(channel, int, 0444);
Jeff Garzikbf794512005-07-31 13:07:26 -040011345MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
James Ketrenos43f66a62005-03-25 12:31:53 -060011346
James Ketrenosb095c382005-08-24 22:04:42 -050011347#ifdef CONFIG_IPW_QOS
11348module_param(qos_enable, int, 0444);
11349MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
James Ketrenos43f66a62005-03-25 12:31:53 -060011350
James Ketrenosb095c382005-08-24 22:04:42 -050011351module_param(qos_burst_enable, int, 0444);
11352MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
11353
11354module_param(qos_no_ack_mask, int, 0444);
11355MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack");
11356
11357module_param(burst_duration_CCK, int, 0444);
11358MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value");
11359
11360module_param(burst_duration_OFDM, int, 0444);
11361MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
11362#endif /* CONFIG_IPW_QOS */
11363
11364#ifdef CONFIG_IPW2200_MONITOR
James Ketrenos43f66a62005-03-25 12:31:53 -060011365module_param(mode, int, 0444);
11366MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
11367#else
11368module_param(mode, int, 0444);
11369MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
11370#endif
11371
Zhu Yi810dabd2006-01-24 16:36:59 +080011372module_param(bt_coexist, int, 0444);
11373MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)");
11374
James Ketrenosb095c382005-08-24 22:04:42 -050011375module_param(hwcrypto, int, 0444);
11376MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default on)");
11377
James Ketrenosf6c5cb72005-08-25 00:39:09 -050011378module_param(cmdlog, int, 0444);
11379MODULE_PARM_DESC(cmdlog,
11380 "allocate a ring buffer for logging firmware commands");
11381
Zhu Yi4bfdb912006-01-24 16:37:16 +080011382module_param(roaming, int, 0444);
11383MODULE_PARM_DESC(roaming, "enable roaming support (default on)");
11384
James Ketrenos43f66a62005-03-25 12:31:53 -060011385module_exit(ipw_exit);
11386module_init(ipw_init);